From a4b5b8fa1846e41e401f9d9f79cdd38dad248685 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 9 Mar 2022 09:33:34 -0500 Subject: [PATCH 01/71] chore: Remove GetClientID() From Misbehaviour Interface (#897) * chore: remove GetClientID() from Misbehaviour interface * chore: changed too much * chore: fix test and add changelog entry Co-authored-by: Shake Shack --- CHANGELOG.md | 1 + modules/core/02-client/client/cli/tx.go | 11 ++++++----- modules/core/02-client/keeper/client.go | 18 +++++++++--------- modules/core/02-client/keeper/client_test.go | 2 +- modules/core/02-client/types/msgs.go | 7 ------- modules/core/exported/client.go | 1 - modules/core/keeper/msg_server.go | 2 +- 7 files changed, 18 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c42454c3ed..2e0015683ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -289,6 +289,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Client Breaking Changes * (02-client/cli) [\#196](https://github.com/cosmos/ibc-go/pull/196) Rename `node-state` cli command to `self-consensus-state`. +* (02-client/cli) [\#897](https://github.com/cosmos/ibc-go/pull/897) Remove `GetClientID()` from `Misbehaviour` interface. Submit client misbehaviour cli command requires an explicit client id now. ## IBC in the Cosmos SDK Repository diff --git a/modules/core/02-client/client/cli/tx.go b/modules/core/02-client/client/cli/tx.go index ca65d5b2b17..65703fb1f4c 100644 --- a/modules/core/02-client/client/cli/tx.go +++ b/modules/core/02-client/client/cli/tx.go @@ -129,11 +129,11 @@ func NewUpdateClientCmd() *cobra.Command { // future updates. func NewSubmitMisbehaviourCmd() *cobra.Command { return &cobra.Command{ - Use: "misbehaviour [path/to/misbehaviour.json]", + Use: "misbehaviour [clientID] [path/to/misbehaviour.json]", Short: "submit a client misbehaviour", Long: "submit a client misbehaviour to prevent future updates", - Example: fmt.Sprintf("%s tx ibc %s misbehaviour [path/to/misbehaviour.json] --from node0 --home ../node0/cli --chain-id $CID", version.AppName, types.SubModuleName), - Args: cobra.ExactArgs(1), + Example: fmt.Sprintf("%s tx ibc %s misbehaviour [clientID] [path/to/misbehaviour.json] --from node0 --home ../node0/cli --chain-id $CID", version.AppName, types.SubModuleName), + Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientTxContext(cmd) if err != nil { @@ -142,7 +142,8 @@ func NewSubmitMisbehaviourCmd() *cobra.Command { cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) var misbehaviour exported.Misbehaviour - misbehaviourContentOrFileName := args[0] + clientID := args[0] + misbehaviourContentOrFileName := args[1] if err := cdc.UnmarshalInterfaceJSON([]byte(misbehaviourContentOrFileName), &misbehaviour); err != nil { // check for file path if JSON input is not provided @@ -156,7 +157,7 @@ func NewSubmitMisbehaviourCmd() *cobra.Command { } } - msg, err := types.NewMsgSubmitMisbehaviour(misbehaviour.GetClientID(), misbehaviour, clientCtx.GetFromAddress().String()) + msg, err := types.NewMsgSubmitMisbehaviour(clientID, misbehaviour, clientCtx.GetFromAddress().String()) if err != nil { return err } diff --git a/modules/core/02-client/keeper/client.go b/modules/core/02-client/keeper/client.go index d8085ac719f..600519bf5f4 100644 --- a/modules/core/02-client/keeper/client.go +++ b/modules/core/02-client/keeper/client.go @@ -188,16 +188,16 @@ func (k Keeper) UpgradeClient(ctx sdk.Context, clientID string, upgradedClient e // CheckMisbehaviourAndUpdateState checks for client misbehaviour and freezes the // client if so. -func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, misbehaviour exported.Misbehaviour) error { - clientState, found := k.GetClientState(ctx, misbehaviour.GetClientID()) +func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, clientID string, misbehaviour exported.Misbehaviour) error { + clientState, found := k.GetClientState(ctx, clientID) if !found { - return sdkerrors.Wrapf(types.ErrClientNotFound, "cannot check misbehaviour for client with ID %s", misbehaviour.GetClientID()) + return sdkerrors.Wrapf(types.ErrClientNotFound, "cannot check misbehaviour for client with ID %s", clientID) } - clientStore := k.ClientStore(ctx, misbehaviour.GetClientID()) + clientStore := k.ClientStore(ctx, clientID) if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(types.ErrClientNotActive, "cannot process misbehaviour for client (%s) with status %s", misbehaviour.GetClientID(), status) + return sdkerrors.Wrapf(types.ErrClientNotActive, "cannot process misbehaviour for client (%s) with status %s", clientID, status) } if err := misbehaviour.ValidateBasic(); err != nil { @@ -209,8 +209,8 @@ func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, misbehaviour ex return err } - k.SetClientState(ctx, misbehaviour.GetClientID(), clientState) - k.Logger(ctx).Info("client frozen due to misbehaviour", "client-id", misbehaviour.GetClientID()) + k.SetClientState(ctx, clientID, clientState) + k.Logger(ctx).Info("client frozen due to misbehaviour", "client-id", clientID) defer func() { telemetry.IncrCounterWithLabels( @@ -218,12 +218,12 @@ func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, misbehaviour ex 1, []metrics.Label{ telemetry.NewLabel(types.LabelClientType, misbehaviour.ClientType()), - telemetry.NewLabel(types.LabelClientID, misbehaviour.GetClientID()), + telemetry.NewLabel(types.LabelClientID, clientID), }, ) }() - EmitSubmitMisbehaviourEvent(ctx, misbehaviour.GetClientID(), clientState) + EmitSubmitMisbehaviourEvent(ctx, clientID, clientState) return nil } diff --git a/modules/core/02-client/keeper/client_test.go b/modules/core/02-client/keeper/client_test.go index 4ca764620bf..dad38787c47 100644 --- a/modules/core/02-client/keeper/client_test.go +++ b/modules/core/02-client/keeper/client_test.go @@ -650,7 +650,7 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() { tc.misbehaviour.ClientId = clientID - err = suite.keeper.CheckMisbehaviourAndUpdateState(suite.ctx, tc.misbehaviour) + err = suite.keeper.CheckMisbehaviourAndUpdateState(suite.ctx, clientID, tc.misbehaviour) if tc.expPass { suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) diff --git a/modules/core/02-client/types/msgs.go b/modules/core/02-client/types/msgs.go index d80fe8b0592..843070b289f 100644 --- a/modules/core/02-client/types/msgs.go +++ b/modules/core/02-client/types/msgs.go @@ -255,13 +255,6 @@ func (msg MsgSubmitMisbehaviour) ValidateBasic() error { if err := misbehaviour.ValidateBasic(); err != nil { return err } - if misbehaviour.GetClientID() != msg.ClientId { - return sdkerrors.Wrapf( - ErrInvalidMisbehaviour, - "misbehaviour client-id doesn't match client-id from message (%s ≠ %s)", - misbehaviour.GetClientID(), msg.ClientId, - ) - } return host.ClientIdentifierValidator(msg.ClientId) } diff --git a/modules/core/exported/client.go b/modules/core/exported/client.go index 4dce203bea4..a4839bdbf97 100644 --- a/modules/core/exported/client.go +++ b/modules/core/exported/client.go @@ -199,7 +199,6 @@ type Misbehaviour interface { proto.Message ClientType() string - GetClientID() string ValidateBasic() error } diff --git a/modules/core/keeper/msg_server.go b/modules/core/keeper/msg_server.go index b19041f636d..a36f064e8ae 100644 --- a/modules/core/keeper/msg_server.go +++ b/modules/core/keeper/msg_server.go @@ -87,7 +87,7 @@ func (k Keeper) SubmitMisbehaviour(goCtx context.Context, msg *clienttypes.MsgSu return nil, err } - if err := k.ClientKeeper.CheckMisbehaviourAndUpdateState(ctx, misbehaviour); err != nil { + if err := k.ClientKeeper.CheckMisbehaviourAndUpdateState(ctx, msg.ClientId, misbehaviour); err != nil { return nil, sdkerrors.Wrap(err, "failed to process misbehaviour for IBC client") } From 841d21d9e57c6fd3b3456860240c2efac644b221 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 9 Mar 2022 09:41:14 -0500 Subject: [PATCH 02/71] chore: add 668 functions to localhost client (#908) --- .../09-localhost/types/client_state.go | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/modules/light-clients/09-localhost/types/client_state.go b/modules/light-clients/09-localhost/types/client_state.go index 1670d4f1e6b..d8f0af38f00 100644 --- a/modules/light-clients/09-localhost/types/client_state.go +++ b/modules/light-clients/09-localhost/types/client_state.go @@ -79,6 +79,27 @@ func (cs ClientState) ExportMetadata(_ sdk.KVStore) []exported.GenesisMetadata { // CheckHeaderAndUpdateState updates the localhost client. It only needs access to the context func (cs *ClientState) CheckHeaderAndUpdateState( + ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, header exported.Header, +) (exported.ClientState, exported.ConsensusState, error) { + return cs.UpdateState(ctx, cdc, clientStore, header) +} + +// VerifyHeader is a no-op. +func (cs *ClientState) VerifyHeader( + _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.Header, +) (exported.ClientState, exported.ConsensusState, error) { + return cs, nil, nil +} + +// CheckForMisbehaviour returns false. +func (cs *ClientState) CheckForMisbehaviour( + _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.Header, +) (bool, error) { + return false, nil +} + +// UpdateState updates the localhost client. It only needs access to the context +func (cs *ClientState) UpdateState( ctx sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.Header, ) (exported.ClientState, exported.ConsensusState, error) { // use the chain ID from context since the localhost client is from the running chain (i.e self). @@ -88,6 +109,13 @@ func (cs *ClientState) CheckHeaderAndUpdateState( return cs, nil, nil } +// UpdateStateOnMisbehaviour returns an error (no misbehaviour case). +func (cs *ClientState) UpdateStateOnMisbehaviour( + _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.Header, +) (*ClientState, error) { + return nil, sdkerrors.Wrapf(clienttypes.ErrUpdateClientFailed, "cannot update localhost client on misbehaviour") +} + // CheckMisbehaviourAndUpdateState implements ClientState // Since localhost is the client of the running chain, misbehaviour cannot be submitted to it // Thus, CheckMisbehaviourAndUpdateState returns an error for localhost From 18abb79d915f283b12f965e3032b01f73ff70ad4 Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Thu, 10 Mar 2022 11:35:30 +0100 Subject: [PATCH 03/71] chore: rename 06-solomachine type Misbehaviour to DuplicateSignatures (#1093) * updating protos and codegen * renaming solomachine Mishaviour to DuplicateSignatureHeader * renaming DuplicateSignatureHeader to DuplicateSignatures * updating changelog and godocs as per review --- CHANGELOG.md | 1 + docs/ibc/proto-docs.md | 40 ++-- modules/core/02-client/types/msgs_test.go | 2 +- .../06-solomachine/types/codec.go | 2 +- .../06-solomachine/types/misbehaviour.go | 28 +-- .../types/misbehaviour_handle.go | 6 +- .../06-solomachine/types/misbehaviour_test.go | 44 ++-- .../06-solomachine/types/solomachine.pb.go | 220 +++++++++--------- .../solomachine/v2/solomachine.proto | 4 +- testing/solomachine.go | 4 +- 10 files changed, 176 insertions(+), 175 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e0015683ae..7e0d59752b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (channel) [\#644](https://github.com/cosmos/ibc-go/pull/644) Removes `CounterpartyHops` function from the ChannelKeeper. * (testing) [\#776](https://github.com/cosmos/ibc-go/pull/776) Adding helper fn to generate capability name for testing callbacks * (channel) [\#882](https://github.com/cosmos/ibc-go/pull/882) The `WriteAcknowledgement` API now takes `exported.Acknowledgement` instead of a byte array +* (06-solomachine) [\#1093](https://github.com/cosmos/ibc-go/pull/1093) Renaming `06-solomachine` type `Misbehaviour` to `DuplicateSignatures` ### State Machine Breaking diff --git a/docs/ibc/proto-docs.md b/docs/ibc/proto-docs.md index 79d56a6a541..9972e06fcb2 100644 --- a/docs/ibc/proto-docs.md +++ b/docs/ibc/proto-docs.md @@ -245,9 +245,9 @@ - [ConnectionStateData](#ibc.lightclients.solomachine.v2.ConnectionStateData) - [ConsensusState](#ibc.lightclients.solomachine.v2.ConsensusState) - [ConsensusStateData](#ibc.lightclients.solomachine.v2.ConsensusStateData) + - [DuplicateSignatures](#ibc.lightclients.solomachine.v2.DuplicateSignatures) - [Header](#ibc.lightclients.solomachine.v2.Header) - [HeaderData](#ibc.lightclients.solomachine.v2.HeaderData) - - [Misbehaviour](#ibc.lightclients.solomachine.v2.Misbehaviour) - [NextSequenceRecvData](#ibc.lightclients.solomachine.v2.NextSequenceRecvData) - [PacketAcknowledgementData](#ibc.lightclients.solomachine.v2.PacketAcknowledgementData) - [PacketCommitmentData](#ibc.lightclients.solomachine.v2.PacketCommitmentData) @@ -3680,6 +3680,25 @@ verification. + + +### DuplicateSignatures +DuplicateSignatures defines misbehaviour for a solo machine which consists +of a sequence and two signatures over different messages at that sequence. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `client_id` | [string](#string) | | | +| `sequence` | [uint64](#uint64) | | | +| `signature_one` | [SignatureAndData](#ibc.lightclients.solomachine.v2.SignatureAndData) | | | +| `signature_two` | [SignatureAndData](#ibc.lightclients.solomachine.v2.SignatureAndData) | | | + + + + + + ### Header @@ -3715,25 +3734,6 @@ HeaderData returns the SignBytes data for update verification. - - -### Misbehaviour -Misbehaviour defines misbehaviour for a solo machine which consists -of a sequence and two signatures over different messages at that sequence. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `client_id` | [string](#string) | | | -| `sequence` | [uint64](#uint64) | | | -| `signature_one` | [SignatureAndData](#ibc.lightclients.solomachine.v2.SignatureAndData) | | | -| `signature_two` | [SignatureAndData](#ibc.lightclients.solomachine.v2.SignatureAndData) | | | - - - - - - ### NextSequenceRecvData diff --git a/modules/core/02-client/types/msgs_test.go b/modules/core/02-client/types/msgs_test.go index 35dd08aedba..cede52147af 100644 --- a/modules/core/02-client/types/msgs_test.go +++ b/modules/core/02-client/types/msgs_test.go @@ -591,7 +591,7 @@ func (suite *TypesTestSuite) TestMsgSubmitMisbehaviour_ValidateBasic() { { "invalid solomachine misbehaviour", func() { - msg, err = types.NewMsgSubmitMisbehaviour("solomachine", &solomachinetypes.Misbehaviour{}, suite.chainA.SenderAccount.GetAddress().String()) + msg, err = types.NewMsgSubmitMisbehaviour("solomachine", &solomachinetypes.DuplicateSignatures{}, suite.chainA.SenderAccount.GetAddress().String()) suite.Require().NoError(err) }, false, diff --git a/modules/light-clients/06-solomachine/types/codec.go b/modules/light-clients/06-solomachine/types/codec.go index 1db36165157..67f89394f39 100644 --- a/modules/light-clients/06-solomachine/types/codec.go +++ b/modules/light-clients/06-solomachine/types/codec.go @@ -27,7 +27,7 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) { ) registry.RegisterImplementations( (*exported.Misbehaviour)(nil), - &Misbehaviour{}, + &DuplicateSignatures{}, ) } diff --git a/modules/light-clients/06-solomachine/types/misbehaviour.go b/modules/light-clients/06-solomachine/types/misbehaviour.go index f5df3e1bad9..d07503b6f17 100644 --- a/modules/light-clients/06-solomachine/types/misbehaviour.go +++ b/modules/light-clients/06-solomachine/types/misbehaviour.go @@ -10,48 +10,48 @@ import ( "github.com/cosmos/ibc-go/v3/modules/core/exported" ) -var _ exported.Misbehaviour = &Misbehaviour{} +var _ exported.Misbehaviour = &DuplicateSignatures{} // ClientType is a Solo Machine light client. -func (misbehaviour Misbehaviour) ClientType() string { +func (ds DuplicateSignatures) ClientType() string { return exported.Solomachine } // GetClientID returns the ID of the client that committed a misbehaviour. -func (misbehaviour Misbehaviour) GetClientID() string { - return misbehaviour.ClientId +func (ds DuplicateSignatures) GetClientID() string { + return ds.ClientId } -// Type implements Evidence interface. -func (misbehaviour Misbehaviour) Type() string { +// Type implements Misbehaviour interface. +func (ds DuplicateSignatures) Type() string { return exported.TypeClientMisbehaviour } -// ValidateBasic implements Evidence interface. -func (misbehaviour Misbehaviour) ValidateBasic() error { - if err := host.ClientIdentifierValidator(misbehaviour.ClientId); err != nil { +// ValidateBasic implements Misbehaviour interface. +func (ds DuplicateSignatures) ValidateBasic() error { + if err := host.ClientIdentifierValidator(ds.ClientId); err != nil { return sdkerrors.Wrap(err, "invalid client identifier for solo machine") } - if misbehaviour.Sequence == 0 { + if ds.Sequence == 0 { return sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "sequence cannot be 0") } - if err := misbehaviour.SignatureOne.ValidateBasic(); err != nil { + if err := ds.SignatureOne.ValidateBasic(); err != nil { return sdkerrors.Wrap(err, "signature one failed basic validation") } - if err := misbehaviour.SignatureTwo.ValidateBasic(); err != nil { + if err := ds.SignatureTwo.ValidateBasic(); err != nil { return sdkerrors.Wrap(err, "signature two failed basic validation") } // misbehaviour signatures cannot be identical - if bytes.Equal(misbehaviour.SignatureOne.Signature, misbehaviour.SignatureTwo.Signature) { + if bytes.Equal(ds.SignatureOne.Signature, ds.SignatureTwo.Signature) { return sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "misbehaviour signatures cannot be equal") } // message data signed cannot be identical - if bytes.Equal(misbehaviour.SignatureOne.Data, misbehaviour.SignatureTwo.Data) { + if bytes.Equal(ds.SignatureOne.Data, ds.SignatureTwo.Data) { return sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "misbehaviour signature data must be signed over different messages") } diff --git a/modules/light-clients/06-solomachine/types/misbehaviour_handle.go b/modules/light-clients/06-solomachine/types/misbehaviour_handle.go index d5a1d57cb57..7b8a7157ff4 100644 --- a/modules/light-clients/06-solomachine/types/misbehaviour_handle.go +++ b/modules/light-clients/06-solomachine/types/misbehaviour_handle.go @@ -22,11 +22,11 @@ func (cs ClientState) CheckMisbehaviourAndUpdateState( misbehaviour exported.Misbehaviour, ) (exported.ClientState, error) { - soloMisbehaviour, ok := misbehaviour.(*Misbehaviour) + soloMisbehaviour, ok := misbehaviour.(*DuplicateSignatures) if !ok { return nil, sdkerrors.Wrapf( clienttypes.ErrInvalidClientType, - "misbehaviour type %T, expected %T", misbehaviour, &Misbehaviour{}, + "misbehaviour type %T, expected %T", misbehaviour, &DuplicateSignatures{}, ) } @@ -50,7 +50,7 @@ func (cs ClientState) CheckMisbehaviourAndUpdateState( // verifySignatureAndData verifies that the currently registered public key has signed // over the provided data and that the data is valid. The data is valid if it can be // unmarshaled into the specified data type. -func verifySignatureAndData(cdc codec.BinaryCodec, clientState ClientState, misbehaviour *Misbehaviour, sigAndData *SignatureAndData) error { +func verifySignatureAndData(cdc codec.BinaryCodec, clientState ClientState, misbehaviour *DuplicateSignatures, sigAndData *SignatureAndData) error { // do not check misbehaviour timestamp since we want to allow processing of past misbehaviour diff --git a/modules/light-clients/06-solomachine/types/misbehaviour_test.go b/modules/light-clients/06-solomachine/types/misbehaviour_test.go index 813a8520ee7..e8590bb9f21 100644 --- a/modules/light-clients/06-solomachine/types/misbehaviour_test.go +++ b/modules/light-clients/06-solomachine/types/misbehaviour_test.go @@ -19,91 +19,91 @@ func (suite *SoloMachineTestSuite) TestMisbehaviourValidateBasic() { testCases := []struct { name string - malleateMisbehaviour func(misbehaviour *types.Misbehaviour) + malleateMisbehaviour func(duplicateSigHeader *types.DuplicateSignatures) expPass bool }{ { "valid misbehaviour", - func(*types.Misbehaviour) {}, + func(*types.DuplicateSignatures) {}, true, }, { "invalid client ID", - func(misbehaviour *types.Misbehaviour) { - misbehaviour.ClientId = "(badclientid)" + func(duplicateSigHeader *types.DuplicateSignatures) { + duplicateSigHeader.ClientId = "(badclientid)" }, false, }, { "sequence is zero", - func(misbehaviour *types.Misbehaviour) { - misbehaviour.Sequence = 0 + func(duplicateSigHeader *types.DuplicateSignatures) { + duplicateSigHeader.Sequence = 0 }, false, }, { "signature one sig is empty", - func(misbehaviour *types.Misbehaviour) { - misbehaviour.SignatureOne.Signature = []byte{} + func(duplicateSigHeader *types.DuplicateSignatures) { + duplicateSigHeader.SignatureOne.Signature = []byte{} }, false, }, { "signature two sig is empty", - func(misbehaviour *types.Misbehaviour) { - misbehaviour.SignatureTwo.Signature = []byte{} + func(duplicateSigHeader *types.DuplicateSignatures) { + duplicateSigHeader.SignatureTwo.Signature = []byte{} }, false, }, { "signature one data is empty", - func(misbehaviour *types.Misbehaviour) { - misbehaviour.SignatureOne.Data = nil + func(duplicateSigHeader *types.DuplicateSignatures) { + duplicateSigHeader.SignatureOne.Data = nil }, false, }, { "signature two data is empty", - func(misbehaviour *types.Misbehaviour) { - misbehaviour.SignatureTwo.Data = []byte{} + func(duplicateSigHeader *types.DuplicateSignatures) { + duplicateSigHeader.SignatureTwo.Data = []byte{} }, false, }, { "signatures are identical", - func(misbehaviour *types.Misbehaviour) { - misbehaviour.SignatureTwo.Signature = misbehaviour.SignatureOne.Signature + func(duplicateSigHeader *types.DuplicateSignatures) { + duplicateSigHeader.SignatureTwo.Signature = duplicateSigHeader.SignatureOne.Signature }, false, }, { "data signed is identical", - func(misbehaviour *types.Misbehaviour) { - misbehaviour.SignatureTwo.Data = misbehaviour.SignatureOne.Data + func(duplicateSigHeader *types.DuplicateSignatures) { + duplicateSigHeader.SignatureTwo.Data = duplicateSigHeader.SignatureOne.Data }, false, }, { "data type for SignatureOne is unspecified", - func(misbehaviour *types.Misbehaviour) { + func(misbehaviour *types.DuplicateSignatures) { misbehaviour.SignatureOne.DataType = types.UNSPECIFIED }, false, }, { "data type for SignatureTwo is unspecified", - func(misbehaviour *types.Misbehaviour) { + func(misbehaviour *types.DuplicateSignatures) { misbehaviour.SignatureTwo.DataType = types.UNSPECIFIED }, false, }, { "timestamp for SignatureOne is zero", - func(misbehaviour *types.Misbehaviour) { + func(misbehaviour *types.DuplicateSignatures) { misbehaviour.SignatureOne.Timestamp = 0 }, false, }, { "timestamp for SignatureTwo is zero", - func(misbehaviour *types.Misbehaviour) { + func(misbehaviour *types.DuplicateSignatures) { misbehaviour.SignatureTwo.Timestamp = 0 }, false, }, diff --git a/modules/light-clients/06-solomachine/types/solomachine.pb.go b/modules/light-clients/06-solomachine/types/solomachine.pb.go index 441a7030402..8b280dcba72 100644 --- a/modules/light-clients/06-solomachine/types/solomachine.pb.go +++ b/modules/light-clients/06-solomachine/types/solomachine.pb.go @@ -222,27 +222,27 @@ func (m *Header) XXX_DiscardUnknown() { var xxx_messageInfo_Header proto.InternalMessageInfo -// Misbehaviour defines misbehaviour for a solo machine which consists +// DuplicateSignatures defines misbehaviour for a solo machine which consists // of a sequence and two signatures over different messages at that sequence. -type Misbehaviour struct { +type DuplicateSignatures struct { ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` Sequence uint64 `protobuf:"varint,2,opt,name=sequence,proto3" json:"sequence,omitempty"` SignatureOne *SignatureAndData `protobuf:"bytes,3,opt,name=signature_one,json=signatureOne,proto3" json:"signature_one,omitempty" yaml:"signature_one"` SignatureTwo *SignatureAndData `protobuf:"bytes,4,opt,name=signature_two,json=signatureTwo,proto3" json:"signature_two,omitempty" yaml:"signature_two"` } -func (m *Misbehaviour) Reset() { *m = Misbehaviour{} } -func (m *Misbehaviour) String() string { return proto.CompactTextString(m) } -func (*Misbehaviour) ProtoMessage() {} -func (*Misbehaviour) Descriptor() ([]byte, []int) { +func (m *DuplicateSignatures) Reset() { *m = DuplicateSignatures{} } +func (m *DuplicateSignatures) String() string { return proto.CompactTextString(m) } +func (*DuplicateSignatures) ProtoMessage() {} +func (*DuplicateSignatures) Descriptor() ([]byte, []int) { return fileDescriptor_141333b361aae010, []int{3} } -func (m *Misbehaviour) XXX_Unmarshal(b []byte) error { +func (m *DuplicateSignatures) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *Misbehaviour) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *DuplicateSignatures) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_Misbehaviour.Marshal(b, m, deterministic) + return xxx_messageInfo_DuplicateSignatures.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -252,17 +252,17 @@ func (m *Misbehaviour) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) return b[:n], nil } } -func (m *Misbehaviour) XXX_Merge(src proto.Message) { - xxx_messageInfo_Misbehaviour.Merge(m, src) +func (m *DuplicateSignatures) XXX_Merge(src proto.Message) { + xxx_messageInfo_DuplicateSignatures.Merge(m, src) } -func (m *Misbehaviour) XXX_Size() int { +func (m *DuplicateSignatures) XXX_Size() int { return m.Size() } -func (m *Misbehaviour) XXX_DiscardUnknown() { - xxx_messageInfo_Misbehaviour.DiscardUnknown(m) +func (m *DuplicateSignatures) XXX_DiscardUnknown() { + xxx_messageInfo_DuplicateSignatures.DiscardUnknown(m) } -var xxx_messageInfo_Misbehaviour proto.InternalMessageInfo +var xxx_messageInfo_DuplicateSignatures proto.InternalMessageInfo // SignatureAndData contains a signature and the data signed over to create that // signature. @@ -803,7 +803,7 @@ func init() { proto.RegisterType((*ClientState)(nil), "ibc.lightclients.solomachine.v2.ClientState") proto.RegisterType((*ConsensusState)(nil), "ibc.lightclients.solomachine.v2.ConsensusState") proto.RegisterType((*Header)(nil), "ibc.lightclients.solomachine.v2.Header") - proto.RegisterType((*Misbehaviour)(nil), "ibc.lightclients.solomachine.v2.Misbehaviour") + proto.RegisterType((*DuplicateSignatures)(nil), "ibc.lightclients.solomachine.v2.DuplicateSignatures") proto.RegisterType((*SignatureAndData)(nil), "ibc.lightclients.solomachine.v2.SignatureAndData") proto.RegisterType((*TimestampedSignatureData)(nil), "ibc.lightclients.solomachine.v2.TimestampedSignatureData") proto.RegisterType((*SignBytes)(nil), "ibc.lightclients.solomachine.v2.SignBytes") @@ -823,93 +823,93 @@ func init() { } var fileDescriptor_141333b361aae010 = []byte{ - // 1370 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0x5f, 0x8f, 0xdb, 0x44, - 0x10, 0x3f, 0xa7, 0xe9, 0xf5, 0x32, 0xb9, 0xde, 0x05, 0x37, 0x6d, 0x73, 0x6e, 0x95, 0x18, 0x23, - 0xca, 0x81, 0x68, 0xc2, 0x5d, 0x45, 0x85, 0x2a, 0x04, 0x38, 0x8e, 0x4b, 0xd3, 0xde, 0xf9, 0x82, - 0xe3, 0x03, 0x5a, 0x21, 0x19, 0xc7, 0xd9, 0x4b, 0xac, 0x26, 0xde, 0x34, 0x76, 0x92, 0x06, 0x09, - 0x09, 0xf1, 0x54, 0x22, 0x1e, 0xf8, 0x02, 0x91, 0x10, 0x88, 0xcf, 0xc1, 0x1b, 0xf0, 0xd8, 0x47, - 0x9e, 0x02, 0x6a, 0xbf, 0x41, 0x3e, 0x01, 0xb2, 0x77, 0x13, 0xdb, 0xb9, 0x5e, 0x4e, 0xfc, 0x7b, - 0xdb, 0x9d, 0xdf, 0xcc, 0x6f, 0x66, 0x67, 0xc6, 0xb3, 0x6b, 0xd8, 0xb1, 0x6a, 0x66, 0xa1, 0x65, - 0x35, 0x9a, 0xae, 0xd9, 0xb2, 0x90, 0xed, 0x3a, 0x05, 0x07, 0xb7, 0x70, 0xdb, 0x30, 0x9b, 0x96, - 0x8d, 0x0a, 0xfd, 0xdd, 0xf0, 0x36, 0xdf, 0xe9, 0x62, 0x17, 0xb3, 0x39, 0xab, 0x66, 0xe6, 0xc3, - 0x26, 0xf9, 0xb0, 0x4e, 0x7f, 0x97, 0x7b, 0xcd, 0xe3, 0x34, 0x71, 0x17, 0x15, 0x4c, 0x6c, 0xdb, - 0xc8, 0x74, 0x2d, 0x6c, 0x17, 0xfa, 0x3b, 0xa1, 0x1d, 0x61, 0xe2, 0x5e, 0x0e, 0x14, 0x9b, 0x86, - 0x6d, 0xa3, 0x96, 0xaf, 0x45, 0x96, 0x54, 0x25, 0xdd, 0xc0, 0x0d, 0xec, 0x2f, 0x0b, 0xde, 0x8a, - 0x4a, 0xb7, 0x1a, 0x18, 0x37, 0x5a, 0xa8, 0xe0, 0xef, 0x6a, 0xbd, 0xa3, 0x82, 0x61, 0x0f, 0x09, - 0x24, 0xfc, 0x1c, 0x83, 0xa4, 0xe4, 0xc7, 0x55, 0x75, 0x0d, 0x17, 0xb1, 0x1c, 0xac, 0x39, 0xe8, - 0x51, 0x0f, 0xd9, 0x26, 0xca, 0x30, 0x3c, 0xb3, 0x1d, 0x57, 0xe7, 0x7b, 0x76, 0x07, 0x12, 0x96, - 0xa3, 0x1f, 0x75, 0xf1, 0x17, 0xc8, 0xce, 0xc4, 0x78, 0x66, 0x7b, 0xad, 0x98, 0x9e, 0x4e, 0x72, - 0xa9, 0xa1, 0xd1, 0x6e, 0xdd, 0x12, 0xe6, 0x90, 0xa0, 0xae, 0x59, 0xce, 0x6d, 0x7f, 0xc9, 0xba, - 0xb0, 0x69, 0x62, 0xdb, 0x41, 0xb6, 0xd3, 0x73, 0x74, 0xc7, 0xf3, 0x90, 0x39, 0xc3, 0x33, 0xdb, - 0xc9, 0xdd, 0x42, 0xfe, 0x94, 0xb4, 0xe4, 0xa5, 0x99, 0x9d, 0x1f, 0x58, 0x91, 0x9b, 0x4e, 0x72, - 0x97, 0x88, 0xa7, 0x05, 0x46, 0x41, 0xdd, 0x30, 0x23, 0xba, 0x2c, 0x82, 0x2b, 0x46, 0xab, 0x85, - 0x07, 0x7a, 0xaf, 0x53, 0x37, 0x5c, 0xa4, 0x1b, 0x47, 0x2e, 0xea, 0xea, 0x9d, 0x2e, 0xee, 0x60, - 0xc7, 0x68, 0x65, 0xe2, 0x7e, 0xe8, 0xd7, 0xa6, 0x93, 0x9c, 0x40, 0x08, 0x97, 0x28, 0x0b, 0x6a, - 0xc6, 0x47, 0x0f, 0x7d, 0x50, 0xf4, 0xb0, 0x0a, 0x85, 0x6e, 0xc5, 0x9f, 0x7c, 0x9f, 0x5b, 0x11, - 0x7e, 0x60, 0x60, 0x23, 0x1a, 0x2b, 0x7b, 0x17, 0xa0, 0xd3, 0xab, 0xb5, 0x2c, 0x53, 0x7f, 0x88, - 0x86, 0x7e, 0x1a, 0x93, 0xbb, 0xe9, 0x3c, 0x29, 0x42, 0x7e, 0x56, 0x84, 0xbc, 0x68, 0x0f, 0x8b, - 0x17, 0xa7, 0x93, 0xdc, 0x4b, 0x24, 0x88, 0xc0, 0x42, 0x50, 0x13, 0x64, 0x73, 0x0f, 0x0d, 0x59, - 0x1e, 0x92, 0x75, 0xab, 0x8f, 0xba, 0x8e, 0x75, 0x64, 0xa1, 0xae, 0x9f, 0xf6, 0x84, 0x1a, 0x16, - 0xb1, 0x57, 0x21, 0xe1, 0x5a, 0x6d, 0xe4, 0xb8, 0x46, 0xbb, 0xe3, 0x67, 0x37, 0xae, 0x06, 0x02, - 0x1a, 0xe4, 0xd7, 0x31, 0x58, 0xbd, 0x83, 0x8c, 0x3a, 0xea, 0x2e, 0xad, 0x70, 0x84, 0x2a, 0xb6, - 0x40, 0xe5, 0xa1, 0x8e, 0xd5, 0xb0, 0x0d, 0xb7, 0xd7, 0x25, 0x65, 0x5c, 0x57, 0x03, 0x01, 0x7b, - 0x08, 0x1b, 0x36, 0x1a, 0xe8, 0xa1, 0x83, 0xc7, 0x97, 0x1c, 0x7c, 0x6b, 0x3a, 0xc9, 0x5d, 0x24, - 0x07, 0x8f, 0x5a, 0x09, 0xea, 0xba, 0x8d, 0x06, 0x95, 0xf9, 0xf9, 0x25, 0xd8, 0xf4, 0x14, 0xc2, - 0x39, 0x38, 0xeb, 0xe5, 0x20, 0xdc, 0x10, 0x0b, 0x0a, 0x82, 0xea, 0x45, 0x52, 0x0a, 0x04, 0x34, - 0x09, 0xbf, 0xc6, 0x60, 0x7d, 0xdf, 0x72, 0x6a, 0xa8, 0x69, 0xf4, 0x2d, 0xdc, 0xeb, 0x7a, 0x0d, - 0x4d, 0x9a, 0x4f, 0xb7, 0xea, 0x7e, 0x2e, 0x12, 0xe1, 0x86, 0x9e, 0x43, 0x82, 0xba, 0x46, 0xd6, - 0xe5, 0x7a, 0x24, 0x7b, 0xb1, 0x85, 0xec, 0x75, 0xe0, 0xfc, 0x3c, 0x1d, 0x3a, 0xb6, 0x67, 0xad, - 0xbe, 0x73, 0x6a, 0xab, 0x57, 0x67, 0x56, 0xa2, 0x5d, 0x2f, 0x19, 0xae, 0x51, 0xcc, 0x4c, 0x27, - 0xb9, 0x34, 0x89, 0x22, 0xc2, 0x28, 0xa8, 0xeb, 0xf3, 0xfd, 0x81, 0xbd, 0xe0, 0xd1, 0x1d, 0x60, - 0x9a, 0xf2, 0xff, 0xca, 0xa3, 0x3b, 0xc0, 0x61, 0x8f, 0xda, 0x00, 0xd3, 0x4c, 0xfe, 0xc2, 0x40, - 0x6a, 0x91, 0x22, 0xda, 0x1e, 0xcc, 0x62, 0x7b, 0x7c, 0x06, 0x89, 0xba, 0xe1, 0x1a, 0xba, 0x3b, - 0xec, 0x90, 0xcc, 0x6d, 0xec, 0xbe, 0x7e, 0x6a, 0x98, 0x1e, 0xaf, 0x36, 0xec, 0xa0, 0x70, 0x59, - 0xe6, 0x2c, 0x82, 0xba, 0x56, 0xa7, 0x38, 0xcb, 0x42, 0xdc, 0x5b, 0xd3, 0xae, 0xf4, 0xd7, 0xd1, - 0x66, 0x8e, 0xbf, 0xf8, 0xbb, 0xf8, 0x8a, 0x81, 0x8c, 0x36, 0x93, 0xa1, 0xfa, 0xfc, 0x4c, 0xfe, - 0x81, 0x3e, 0x80, 0x8d, 0x20, 0x17, 0x3e, 0xbd, 0x7f, 0xaa, 0x70, 0xef, 0x46, 0x71, 0x41, 0x0d, - 0xca, 0x51, 0x3a, 0x16, 0x42, 0xec, 0xc5, 0x21, 0xfc, 0xc1, 0x40, 0xc2, 0xf3, 0x5b, 0x1c, 0xba, - 0xc8, 0xf9, 0x17, 0x5f, 0xe7, 0xc2, 0xa0, 0x38, 0x73, 0x7c, 0x50, 0x44, 0x4a, 0x10, 0xff, 0xbf, - 0x4a, 0x70, 0x36, 0x28, 0x01, 0x3d, 0xe1, 0x4f, 0x0c, 0x00, 0x19, 0x3e, 0x7e, 0x52, 0xf6, 0x20, - 0x49, 0x3f, 0xf9, 0x53, 0xc7, 0xe3, 0xa5, 0xe9, 0x24, 0xc7, 0x46, 0xa6, 0x04, 0x9d, 0x8f, 0x64, - 0x44, 0x9c, 0x30, 0x1f, 0x62, 0xff, 0x70, 0x3e, 0x7c, 0x09, 0x9b, 0xa1, 0xab, 0xd0, 0x8f, 0x95, - 0x85, 0x78, 0xc7, 0x70, 0x9b, 0xb4, 0x9d, 0xfd, 0x35, 0x5b, 0x81, 0x75, 0x3a, 0x1a, 0xc8, 0x85, - 0x16, 0x5b, 0x72, 0x80, 0xcb, 0xd3, 0x49, 0xee, 0x42, 0x64, 0x9c, 0xd0, 0x2b, 0x2b, 0x69, 0x06, - 0x9e, 0xa8, 0xfb, 0x6f, 0x18, 0x60, 0xa3, 0x17, 0xc9, 0x89, 0x21, 0xdc, 0x3f, 0x7e, 0xad, 0x2e, - 0x8b, 0xe2, 0x6f, 0xdc, 0x9d, 0x34, 0x96, 0x3e, 0x5c, 0x90, 0xe6, 0xcf, 0x8f, 0xe5, 0xb1, 0xc8, - 0x00, 0xc1, 0x4b, 0x85, 0x86, 0xf1, 0xaa, 0xdf, 0x56, 0xde, 0x53, 0x25, 0x1f, 0x7a, 0xc5, 0xf4, - 0x77, 0xf2, 0x01, 0xa9, 0x6c, 0xd7, 0xd5, 0x90, 0x21, 0xf5, 0x5b, 0x87, 0x94, 0x44, 0x1e, 0x34, - 0xcb, 0x9d, 0xde, 0x84, 0x73, 0xf4, 0xe1, 0x43, 0x3d, 0x5e, 0x0d, 0x79, 0xa4, 0x2f, 0x22, 0xcf, - 0x1d, 0x59, 0xaa, 0x33, 0x65, 0xea, 0xe5, 0x2e, 0xa4, 0x2b, 0x86, 0xf9, 0x10, 0xb9, 0x12, 0x6e, - 0xb7, 0x2d, 0xb7, 0x8d, 0x6c, 0xf7, 0x44, 0x4f, 0x59, 0xef, 0x78, 0x33, 0x2d, 0xdf, 0xd9, 0xba, - 0x1a, 0x92, 0x08, 0xf7, 0x61, 0x8b, 0x70, 0x89, 0xe6, 0x43, 0x1b, 0x0f, 0x5a, 0xa8, 0xde, 0x40, - 0x4b, 0x09, 0xb7, 0x61, 0xd3, 0x88, 0xaa, 0x52, 0xd6, 0x45, 0xb1, 0x90, 0x87, 0x0c, 0xa1, 0x56, - 0x91, 0x89, 0xac, 0x8e, 0x2b, 0xd6, 0x1c, 0x6f, 0x0e, 0x9c, 0xc4, 0x2c, 0x34, 0x21, 0xad, 0xa0, - 0xc7, 0x6e, 0x95, 0xce, 0x0b, 0x15, 0x99, 0xfd, 0x13, 0xa3, 0x78, 0x17, 0xce, 0xdb, 0xe8, 0xb1, - 0xab, 0x3b, 0xe8, 0x91, 0xde, 0x45, 0x66, 0x9f, 0xcc, 0x93, 0xf0, 0x35, 0x10, 0x81, 0x05, 0x35, - 0x69, 0x13, 0x6a, 0x8f, 0xf5, 0x8d, 0x6f, 0xe3, 0xb0, 0x36, 0x1b, 0x0c, 0xec, 0x3b, 0xf0, 0x4a, - 0x49, 0xd4, 0x44, 0x5d, 0xbb, 0x5f, 0x91, 0xf5, 0x43, 0xa5, 0xac, 0x94, 0xb5, 0xb2, 0xb8, 0x57, - 0x7e, 0x20, 0x97, 0xf4, 0x43, 0xa5, 0x5a, 0x91, 0xa5, 0xf2, 0xed, 0xb2, 0x5c, 0x4a, 0xad, 0x70, - 0x9b, 0xa3, 0x31, 0x9f, 0x0c, 0x89, 0xd8, 0x6b, 0x70, 0x29, 0xb0, 0x94, 0xf6, 0xca, 0xb2, 0xa2, - 0xe9, 0x55, 0x4d, 0xd4, 0xe4, 0x14, 0xc3, 0xc1, 0x68, 0xcc, 0xaf, 0x12, 0x19, 0xfb, 0x26, 0x6c, - 0x85, 0xf4, 0x0e, 0x94, 0xaa, 0xac, 0x54, 0x0f, 0xab, 0x54, 0x35, 0xc6, 0x9d, 0x1f, 0x8d, 0xf9, - 0xc4, 0x5c, 0xcc, 0xe6, 0x81, 0x8b, 0x68, 0x2b, 0xb2, 0xa4, 0x95, 0x0f, 0x14, 0xaa, 0x7e, 0x86, - 0xdb, 0x18, 0x8d, 0x79, 0x08, 0xe4, 0xec, 0x36, 0x5c, 0x0e, 0xe9, 0xdf, 0x11, 0x15, 0x45, 0xde, - 0xa3, 0xca, 0x71, 0x2e, 0x39, 0x1a, 0xf3, 0xe7, 0xa8, 0x90, 0x7d, 0x1b, 0xae, 0x04, 0x9a, 0x15, - 0x51, 0xba, 0x27, 0x6b, 0xba, 0x74, 0xb0, 0xbf, 0x5f, 0xd6, 0xf6, 0x65, 0x45, 0x4b, 0x9d, 0xe5, - 0xd2, 0xa3, 0x31, 0x9f, 0x22, 0x40, 0x20, 0x67, 0xdf, 0x07, 0xfe, 0x98, 0x99, 0x28, 0xdd, 0x53, - 0x0e, 0x3e, 0xd9, 0x93, 0x4b, 0x1f, 0xca, 0xbe, 0xed, 0x2a, 0xb7, 0x35, 0x1a, 0xf3, 0x17, 0x09, - 0xba, 0x00, 0xb2, 0xef, 0xbd, 0x80, 0x40, 0x95, 0x25, 0xb9, 0x5c, 0xd1, 0x74, 0xb1, 0x58, 0x95, - 0x15, 0x49, 0x4e, 0x9d, 0xe3, 0x32, 0xa3, 0x31, 0x9f, 0x26, 0x28, 0x05, 0x29, 0xc6, 0xde, 0x84, - 0xab, 0x81, 0xbd, 0x22, 0x7f, 0xaa, 0xe9, 0x55, 0xf9, 0xa3, 0x43, 0x0f, 0xf2, 0x68, 0x3e, 0x4e, - 0xad, 0x91, 0xc0, 0x3d, 0x64, 0x06, 0x78, 0x72, 0x96, 0x87, 0x54, 0x60, 0x77, 0x47, 0x16, 0x4b, - 0xb2, 0x9a, 0x4a, 0x90, 0xca, 0x90, 0x1d, 0x17, 0x7f, 0xf2, 0x63, 0x76, 0xa5, 0xf8, 0xf9, 0x6f, - 0xcf, 0xb2, 0xcc, 0xd3, 0x67, 0x59, 0xe6, 0xcf, 0x67, 0x59, 0xe6, 0xbb, 0xe7, 0xd9, 0x95, 0xa7, - 0xcf, 0xb3, 0x2b, 0xbf, 0x3f, 0xcf, 0xae, 0x3c, 0xb8, 0xdd, 0xb0, 0xdc, 0x66, 0xaf, 0x96, 0x37, - 0x71, 0xbb, 0x60, 0x62, 0xa7, 0x8d, 0x9d, 0x82, 0x55, 0x33, 0xaf, 0x37, 0x70, 0xa1, 0x7f, 0xa3, - 0xd0, 0xc6, 0xf5, 0x5e, 0x0b, 0x39, 0xe4, 0x7f, 0xea, 0xfa, 0xec, 0x87, 0xea, 0xad, 0x9b, 0xd7, - 0xc3, 0xff, 0x54, 0xde, 0x35, 0xe3, 0xd4, 0x56, 0xfd, 0x79, 0x76, 0xe3, 0xaf, 0x00, 0x00, 0x00, - 0xff, 0xff, 0x5d, 0xd4, 0x6c, 0xfb, 0x80, 0x0d, 0x00, 0x00, + // 1371 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0xdd, 0x8e, 0xdb, 0x44, + 0x14, 0x5e, 0xa7, 0xe9, 0x36, 0x99, 0x6c, 0x77, 0x83, 0x9b, 0xb6, 0x59, 0xb7, 0x4a, 0x8c, 0x11, + 0x65, 0x41, 0x34, 0x61, 0xb7, 0xa2, 0x42, 0x15, 0x02, 0x1c, 0xc7, 0xa5, 0x69, 0xb7, 0xde, 0xe0, + 0x78, 0x81, 0x56, 0x48, 0xc6, 0xb1, 0x67, 0xb3, 0x56, 0x1d, 0x4f, 0x1a, 0x4f, 0x92, 0x06, 0x09, + 0x09, 0x71, 0x55, 0x22, 0x2e, 0x78, 0x81, 0x48, 0x08, 0xc4, 0x73, 0x70, 0x87, 0xb8, 0x42, 0xbd, + 0xe4, 0x2a, 0xa0, 0xf6, 0x0d, 0xf2, 0x04, 0xc8, 0x9e, 0x49, 0x6c, 0x67, 0x77, 0xb3, 0xe2, 0xef, + 0x6e, 0xe6, 0x7c, 0x67, 0xbe, 0xf3, 0xcd, 0x99, 0xe3, 0x33, 0x63, 0xb0, 0x6d, 0x37, 0xcd, 0xb2, + 0x63, 0xb7, 0x0e, 0xb1, 0xe9, 0xd8, 0xd0, 0xc5, 0x5e, 0xd9, 0x43, 0x0e, 0x6a, 0x1b, 0xe6, 0xa1, + 0xed, 0xc2, 0x72, 0x7f, 0x27, 0x3a, 0x2d, 0x75, 0xba, 0x08, 0x23, 0xb6, 0x68, 0x37, 0xcd, 0x52, + 0x74, 0x49, 0x29, 0xea, 0xd3, 0xdf, 0xe1, 0x5e, 0xf3, 0x39, 0x4d, 0xd4, 0x85, 0x65, 0x13, 0xb9, + 0x2e, 0x34, 0xb1, 0x8d, 0xdc, 0x72, 0x7f, 0x3b, 0x32, 0x23, 0x4c, 0xdc, 0xcb, 0xa1, 0xe3, 0xa1, + 0xe1, 0xba, 0xd0, 0x09, 0xbc, 0xc8, 0x90, 0xba, 0xe4, 0x5a, 0xa8, 0x85, 0x82, 0x61, 0xd9, 0x1f, + 0x51, 0xeb, 0x66, 0x0b, 0xa1, 0x96, 0x03, 0xcb, 0xc1, 0xac, 0xd9, 0x3b, 0x28, 0x1b, 0xee, 0x90, + 0x40, 0xc2, 0xcf, 0x09, 0x90, 0x91, 0x02, 0x5d, 0x0d, 0x6c, 0x60, 0xc8, 0x72, 0x20, 0xe5, 0xc1, + 0xc7, 0x3d, 0xe8, 0x9a, 0x30, 0xcf, 0xf0, 0xcc, 0x56, 0x52, 0x9d, 0xcf, 0xd9, 0x6d, 0x90, 0xb6, + 0x3d, 0xfd, 0xa0, 0x8b, 0xbe, 0x80, 0x6e, 0x3e, 0xc1, 0x33, 0x5b, 0xa9, 0x4a, 0x6e, 0x3a, 0x29, + 0x66, 0x87, 0x46, 0xdb, 0xb9, 0x25, 0xcc, 0x21, 0x41, 0x4d, 0xd9, 0xde, 0xed, 0x60, 0xc8, 0x62, + 0xb0, 0x61, 0x22, 0xd7, 0x83, 0xae, 0xd7, 0xf3, 0x74, 0xcf, 0x8f, 0x90, 0x3f, 0xc3, 0x33, 0x5b, + 0x99, 0x9d, 0x72, 0xe9, 0x94, 0xb4, 0x94, 0xa4, 0xd9, 0xba, 0x40, 0x58, 0x85, 0x9b, 0x4e, 0x8a, + 0x97, 0x48, 0xa4, 0x05, 0x46, 0x41, 0x5d, 0x37, 0x63, 0xbe, 0x2c, 0x04, 0x57, 0x0c, 0xc7, 0x41, + 0x03, 0xbd, 0xd7, 0xb1, 0x0c, 0x0c, 0x75, 0xe3, 0x00, 0xc3, 0xae, 0xde, 0xe9, 0xa2, 0x0e, 0xf2, + 0x0c, 0x27, 0x9f, 0x0c, 0xa4, 0x5f, 0x9b, 0x4e, 0x8a, 0x02, 0x21, 0x5c, 0xe2, 0x2c, 0xa8, 0xf9, + 0x00, 0xdd, 0x0f, 0x40, 0xd1, 0xc7, 0xea, 0x14, 0xba, 0x95, 0x7c, 0xfa, 0x7d, 0x71, 0x45, 0xf8, + 0x81, 0x01, 0xeb, 0x71, 0xad, 0xec, 0x5d, 0x00, 0x3a, 0xbd, 0xa6, 0x63, 0x9b, 0xfa, 0x23, 0x38, + 0x0c, 0xd2, 0x98, 0xd9, 0xc9, 0x95, 0xc8, 0x21, 0x94, 0x66, 0x87, 0x50, 0x12, 0xdd, 0x61, 0xe5, + 0xe2, 0x74, 0x52, 0x7c, 0x89, 0x88, 0x08, 0x57, 0x08, 0x6a, 0x9a, 0x4c, 0xee, 0xc1, 0x21, 0xcb, + 0x83, 0x8c, 0x65, 0xf7, 0x61, 0xd7, 0xb3, 0x0f, 0x6c, 0xd8, 0x0d, 0xd2, 0x9e, 0x56, 0xa3, 0x26, + 0xf6, 0x2a, 0x48, 0x63, 0xbb, 0x0d, 0x3d, 0x6c, 0xb4, 0x3b, 0x41, 0x76, 0x93, 0x6a, 0x68, 0xa0, + 0x22, 0xbf, 0x4e, 0x80, 0xd5, 0x3b, 0xd0, 0xb0, 0x60, 0x77, 0xe9, 0x09, 0xc7, 0xa8, 0x12, 0x0b, + 0x54, 0x3e, 0xea, 0xd9, 0x2d, 0xd7, 0xc0, 0xbd, 0x2e, 0x39, 0xc6, 0x35, 0x35, 0x34, 0xb0, 0xfb, + 0x60, 0xdd, 0x85, 0x03, 0x3d, 0xb2, 0xf1, 0xe4, 0x92, 0x8d, 0x6f, 0x4e, 0x27, 0xc5, 0x8b, 0x64, + 0xe3, 0xf1, 0x55, 0x82, 0xba, 0xe6, 0xc2, 0x41, 0x7d, 0xbe, 0x7f, 0x09, 0x6c, 0xf8, 0x0e, 0xd1, + 0x1c, 0x9c, 0xf5, 0x73, 0x10, 0x2d, 0x88, 0x05, 0x07, 0x41, 0xf5, 0x95, 0x54, 0x43, 0x03, 0x4d, + 0xc2, 0x6f, 0x09, 0x70, 0xa1, 0xda, 0xeb, 0x38, 0xb6, 0x69, 0x60, 0xd8, 0x98, 0x09, 0xf7, 0xfc, + 0xba, 0x26, 0x35, 0xa8, 0xdb, 0x56, 0x90, 0x92, 0x74, 0xb4, 0xae, 0xe7, 0x90, 0xa0, 0xa6, 0xc8, + 0xb8, 0x66, 0xc5, 0x92, 0x98, 0x58, 0x48, 0x62, 0x07, 0x9c, 0x9f, 0x67, 0x45, 0x47, 0xee, 0xac, + 0xe2, 0xb7, 0x4f, 0xad, 0xf8, 0xb9, 0x24, 0xd1, 0xb5, 0xaa, 0x06, 0x36, 0x2a, 0xf9, 0xe9, 0xa4, + 0x98, 0x23, 0x2a, 0x62, 0x8c, 0x82, 0xba, 0x36, 0x9f, 0xef, 0xb9, 0x0b, 0x11, 0xf1, 0x00, 0xd1, + 0xcc, 0xff, 0x57, 0x11, 0xf1, 0x00, 0x45, 0x23, 0x6a, 0x03, 0x44, 0x13, 0xfa, 0x0b, 0x03, 0xb2, + 0x8b, 0x14, 0xf1, 0x2a, 0x61, 0x16, 0xab, 0xe4, 0x33, 0x90, 0xb6, 0x0c, 0x6c, 0xe8, 0x78, 0xd8, + 0x21, 0x99, 0x5b, 0xdf, 0x79, 0xfd, 0x54, 0x99, 0x3e, 0xaf, 0x36, 0xec, 0xc0, 0xe8, 0xb1, 0xcc, + 0x59, 0x04, 0x35, 0x65, 0x51, 0x9c, 0x65, 0x41, 0xd2, 0x1f, 0xd3, 0xe2, 0x0c, 0xc6, 0xf1, 0x9a, + 0x4e, 0x1e, 0xff, 0x79, 0x7c, 0xc5, 0x80, 0xbc, 0x36, 0xb3, 0x41, 0x6b, 0xbe, 0xa7, 0x60, 0x43, + 0x1f, 0x80, 0xf5, 0x30, 0x17, 0x01, 0x7d, 0xb0, 0xab, 0x68, 0x09, 0xc7, 0x71, 0x41, 0x0d, 0x8f, + 0xa3, 0x7a, 0x44, 0x42, 0xe2, 0x78, 0x09, 0x7f, 0x30, 0x20, 0xed, 0xc7, 0xad, 0x0c, 0x31, 0xf4, + 0xfe, 0xc5, 0x47, 0xba, 0xd0, 0x2f, 0xce, 0x1c, 0xed, 0x17, 0xb1, 0x23, 0x48, 0xfe, 0x5f, 0x47, + 0x70, 0x36, 0x3c, 0x02, 0xba, 0xc3, 0x9f, 0x18, 0x00, 0x48, 0x0f, 0x0a, 0x92, 0xb2, 0x0b, 0x32, + 0xf4, 0xcb, 0x3f, 0xb5, 0x4b, 0x5e, 0x9a, 0x4e, 0x8a, 0x6c, 0xac, 0x59, 0xd0, 0x36, 0x49, 0x3a, + 0xc5, 0x09, 0x6d, 0x22, 0xf1, 0x0f, 0xdb, 0xc4, 0x97, 0x60, 0x23, 0x72, 0x23, 0x06, 0x5a, 0x59, + 0x90, 0xec, 0x18, 0xf8, 0x90, 0x96, 0x73, 0x30, 0x66, 0xeb, 0x60, 0x8d, 0xb6, 0x06, 0x72, 0xaf, + 0x25, 0x96, 0x6c, 0xe0, 0xf2, 0x74, 0x52, 0xbc, 0x10, 0x6b, 0x27, 0xf4, 0xe6, 0xca, 0x98, 0x61, + 0x24, 0x1a, 0xfe, 0x1b, 0x06, 0xb0, 0xf1, 0xfb, 0xe4, 0x44, 0x09, 0x0f, 0x8e, 0xde, 0xae, 0xcb, + 0x54, 0xfc, 0x8d, 0x2b, 0x94, 0x6a, 0xe9, 0x83, 0x0b, 0xd2, 0xfc, 0x15, 0xb2, 0x5c, 0x8b, 0x0c, + 0x40, 0xf8, 0x60, 0xa1, 0x32, 0x5e, 0x0d, 0xca, 0xca, 0x7f, 0xb1, 0x94, 0x22, 0x8f, 0x99, 0xfe, + 0x76, 0x29, 0x24, 0x95, 0x5d, 0x4b, 0x8d, 0x2c, 0xa4, 0x71, 0x2d, 0x90, 0x95, 0xc8, 0xbb, 0x66, + 0x79, 0xd0, 0x9b, 0xe0, 0x1c, 0x7d, 0xff, 0xd0, 0x88, 0x57, 0x23, 0x11, 0xe9, 0xc3, 0xc8, 0x0f, + 0x47, 0x86, 0xea, 0xcc, 0x99, 0x46, 0xb9, 0x0b, 0x72, 0x75, 0xc3, 0x7c, 0x04, 0xb1, 0x84, 0xda, + 0x6d, 0x1b, 0xb7, 0xa1, 0x8b, 0x4f, 0x8c, 0x54, 0xf0, 0xb7, 0x37, 0xf3, 0x0a, 0x82, 0xad, 0xa9, + 0x11, 0x8b, 0xf0, 0x00, 0x6c, 0x12, 0x2e, 0xd1, 0x7c, 0xe4, 0xa2, 0x81, 0x03, 0xad, 0x16, 0x5c, + 0x4a, 0xb8, 0x05, 0x36, 0x8c, 0xb8, 0x2b, 0x65, 0x5d, 0x34, 0x0b, 0x25, 0x90, 0x27, 0xd4, 0x2a, + 0x34, 0xa1, 0xdd, 0xc1, 0x62, 0xd3, 0xf3, 0xfb, 0xc0, 0x49, 0xcc, 0xc2, 0x21, 0xc8, 0x29, 0xf0, + 0x09, 0x6e, 0xd0, 0x7e, 0xa1, 0x42, 0xb3, 0x7f, 0xa2, 0x8a, 0x77, 0xc1, 0x79, 0x17, 0x3e, 0xc1, + 0xba, 0x07, 0x1f, 0xeb, 0x5d, 0x68, 0xf6, 0x49, 0x3f, 0x89, 0x5e, 0x03, 0x31, 0x58, 0x50, 0x33, + 0x2e, 0xa1, 0xf6, 0x59, 0xdf, 0xf8, 0x36, 0x09, 0x52, 0xb3, 0xc6, 0xc0, 0xbe, 0x03, 0x5e, 0xa9, + 0x8a, 0x9a, 0xa8, 0x6b, 0x0f, 0xea, 0xb2, 0xbe, 0xaf, 0xd4, 0x94, 0x9a, 0x56, 0x13, 0x77, 0x6b, + 0x0f, 0xe5, 0xaa, 0xbe, 0xaf, 0x34, 0xea, 0xb2, 0x54, 0xbb, 0x5d, 0x93, 0xab, 0xd9, 0x15, 0x6e, + 0x63, 0x34, 0xe6, 0x33, 0x11, 0x13, 0x7b, 0x0d, 0x5c, 0x0a, 0x57, 0x4a, 0xbb, 0x35, 0x59, 0xd1, + 0xf4, 0x86, 0x26, 0x6a, 0x72, 0x96, 0xe1, 0xc0, 0x68, 0xcc, 0xaf, 0x12, 0x1b, 0xfb, 0x26, 0xd8, + 0x8c, 0xf8, 0xed, 0x29, 0x0d, 0x59, 0x69, 0xec, 0x37, 0xa8, 0x6b, 0x82, 0x3b, 0x3f, 0x1a, 0xf3, + 0xe9, 0xb9, 0x99, 0x2d, 0x01, 0x2e, 0xe6, 0xad, 0xc8, 0x92, 0x56, 0xdb, 0x53, 0xa8, 0xfb, 0x19, + 0x6e, 0x7d, 0x34, 0xe6, 0x41, 0x68, 0x67, 0xb7, 0xc0, 0xe5, 0x88, 0xff, 0x1d, 0x51, 0x51, 0xe4, + 0x5d, 0xea, 0x9c, 0xe4, 0x32, 0xa3, 0x31, 0x7f, 0x8e, 0x1a, 0xd9, 0xb7, 0xc1, 0x95, 0xd0, 0xb3, + 0x2e, 0x4a, 0xf7, 0x64, 0x4d, 0x97, 0xf6, 0xee, 0xdf, 0xaf, 0x69, 0xf7, 0x65, 0x45, 0xcb, 0x9e, + 0xe5, 0x72, 0xa3, 0x31, 0x9f, 0x25, 0x40, 0x68, 0x67, 0xdf, 0x07, 0xfc, 0x91, 0x65, 0xa2, 0x74, + 0x4f, 0xd9, 0xfb, 0x64, 0x57, 0xae, 0x7e, 0x28, 0x07, 0x6b, 0x57, 0xb9, 0xcd, 0xd1, 0x98, 0xbf, + 0x48, 0xd0, 0x05, 0x90, 0x7d, 0xef, 0x18, 0x02, 0x55, 0x96, 0xe4, 0x5a, 0x5d, 0xd3, 0xc5, 0x4a, + 0x43, 0x56, 0x24, 0x39, 0x7b, 0x8e, 0xcb, 0x8f, 0xc6, 0x7c, 0x8e, 0xa0, 0x14, 0xa4, 0x18, 0x7b, + 0x13, 0x5c, 0x0d, 0xd7, 0x2b, 0xf2, 0xa7, 0x9a, 0xde, 0x90, 0x3f, 0xda, 0xf7, 0x21, 0x9f, 0xe6, + 0xe3, 0x6c, 0x8a, 0x08, 0xf7, 0x91, 0x19, 0xe0, 0xdb, 0x59, 0x1e, 0x64, 0xc3, 0x75, 0x77, 0x64, + 0xb1, 0x2a, 0xab, 0xd9, 0x34, 0x39, 0x19, 0x32, 0xe3, 0x92, 0x4f, 0x7f, 0x2c, 0xac, 0x54, 0x3e, + 0xff, 0xf5, 0x79, 0x81, 0x79, 0xf6, 0xbc, 0xc0, 0xfc, 0xf9, 0xbc, 0xc0, 0x7c, 0xf7, 0xa2, 0xb0, + 0xf2, 0xec, 0x45, 0x61, 0xe5, 0xf7, 0x17, 0x85, 0x95, 0x87, 0xb7, 0x5b, 0x36, 0x3e, 0xec, 0x35, + 0x4b, 0x26, 0x6a, 0x97, 0x4d, 0xe4, 0xb5, 0x91, 0x57, 0xb6, 0x9b, 0xe6, 0xf5, 0x16, 0x2a, 0xf7, + 0x6f, 0x94, 0xdb, 0xc8, 0xea, 0x39, 0xd0, 0x23, 0xbf, 0x55, 0xd7, 0x67, 0xff, 0x55, 0x6f, 0xdd, + 0xbc, 0x1e, 0xfd, 0xb5, 0xf2, 0xaf, 0x19, 0xaf, 0xb9, 0x1a, 0xf4, 0xb3, 0x1b, 0x7f, 0x05, 0x00, + 0x00, 0xff, 0xff, 0xe8, 0xd6, 0xdc, 0x98, 0x87, 0x0d, 0x00, 0x00, } func (m *ClientState) Marshal() (dAtA []byte, err error) { @@ -1078,7 +1078,7 @@ func (m *Header) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *Misbehaviour) Marshal() (dAtA []byte, err error) { +func (m *DuplicateSignatures) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -1088,12 +1088,12 @@ func (m *Misbehaviour) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *Misbehaviour) MarshalTo(dAtA []byte) (int, error) { +func (m *DuplicateSignatures) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *Misbehaviour) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *DuplicateSignatures) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -1700,7 +1700,7 @@ func (m *Header) Size() (n int) { return n } -func (m *Misbehaviour) Size() (n int) { +func (m *DuplicateSignatures) Size() (n int) { if m == nil { return 0 } @@ -2415,7 +2415,7 @@ func (m *Header) Unmarshal(dAtA []byte) error { } return nil } -func (m *Misbehaviour) Unmarshal(dAtA []byte) error { +func (m *DuplicateSignatures) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -2438,10 +2438,10 @@ func (m *Misbehaviour) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: Misbehaviour: wiretype end group for non-group") + return fmt.Errorf("proto: DuplicateSignatures: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: Misbehaviour: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: DuplicateSignatures: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: diff --git a/proto/ibc/lightclients/solomachine/v2/solomachine.proto b/proto/ibc/lightclients/solomachine/v2/solomachine.proto index e626c18ac66..90fb7c61d61 100644 --- a/proto/ibc/lightclients/solomachine/v2/solomachine.proto +++ b/proto/ibc/lightclients/solomachine/v2/solomachine.proto @@ -48,9 +48,9 @@ message Header { string new_diversifier = 5 [(gogoproto.moretags) = "yaml:\"new_diversifier\""]; } -// Misbehaviour defines misbehaviour for a solo machine which consists +// DuplicateSignatures defines misbehaviour for a solo machine which consists // of a sequence and two signatures over different messages at that sequence. -message Misbehaviour { +message DuplicateSignatures { option (gogoproto.goproto_getters) = false; string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; uint64 sequence = 2; diff --git a/testing/solomachine.go b/testing/solomachine.go index 485fb75be0e..928fb441510 100644 --- a/testing/solomachine.go +++ b/testing/solomachine.go @@ -154,7 +154,7 @@ func (solo *Solomachine) CreateHeader() *solomachinetypes.Header { // CreateMisbehaviour constructs testing misbehaviour for the solo machine client // by signing over two different data bytes at the same sequence. -func (solo *Solomachine) CreateMisbehaviour() *solomachinetypes.Misbehaviour { +func (solo *Solomachine) CreateMisbehaviour() *solomachinetypes.DuplicateSignatures { path := solo.GetClientStatePath("counterparty") dataOne, err := solomachinetypes.ClientStateDataBytes(solo.cdc, path, solo.ClientState()) require.NoError(solo.t, err) @@ -204,7 +204,7 @@ func (solo *Solomachine) CreateMisbehaviour() *solomachinetypes.Misbehaviour { Timestamp: solo.Time, } - return &solomachinetypes.Misbehaviour{ + return &solomachinetypes.DuplicateSignatures{ ClientId: solo.ClientID, Sequence: solo.Sequence, SignatureOne: &signatureOne, From a333a73059c53b01e277a3082573dfe077c86b3b Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Fri, 11 Mar 2022 12:04:45 +0100 Subject: [PATCH 04/71] chore: reverting renaming of Misbehaviour (#1099) --- CHANGELOG.md | 1 - docs/ibc/proto-docs.md | 40 ++-- modules/core/02-client/types/msgs_test.go | 2 +- .../06-solomachine/types/codec.go | 2 +- .../06-solomachine/types/misbehaviour.go | 24 +- .../types/misbehaviour_handle.go | 6 +- .../06-solomachine/types/misbehaviour_test.go | 44 ++-- .../06-solomachine/types/solomachine.pb.go | 220 +++++++++--------- .../solomachine/v2/solomachine.proto | 4 +- testing/solomachine.go | 4 +- 10 files changed, 173 insertions(+), 174 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e0d59752b5..2e0015683ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,7 +54,6 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (channel) [\#644](https://github.com/cosmos/ibc-go/pull/644) Removes `CounterpartyHops` function from the ChannelKeeper. * (testing) [\#776](https://github.com/cosmos/ibc-go/pull/776) Adding helper fn to generate capability name for testing callbacks * (channel) [\#882](https://github.com/cosmos/ibc-go/pull/882) The `WriteAcknowledgement` API now takes `exported.Acknowledgement` instead of a byte array -* (06-solomachine) [\#1093](https://github.com/cosmos/ibc-go/pull/1093) Renaming `06-solomachine` type `Misbehaviour` to `DuplicateSignatures` ### State Machine Breaking diff --git a/docs/ibc/proto-docs.md b/docs/ibc/proto-docs.md index 9972e06fcb2..79d56a6a541 100644 --- a/docs/ibc/proto-docs.md +++ b/docs/ibc/proto-docs.md @@ -245,9 +245,9 @@ - [ConnectionStateData](#ibc.lightclients.solomachine.v2.ConnectionStateData) - [ConsensusState](#ibc.lightclients.solomachine.v2.ConsensusState) - [ConsensusStateData](#ibc.lightclients.solomachine.v2.ConsensusStateData) - - [DuplicateSignatures](#ibc.lightclients.solomachine.v2.DuplicateSignatures) - [Header](#ibc.lightclients.solomachine.v2.Header) - [HeaderData](#ibc.lightclients.solomachine.v2.HeaderData) + - [Misbehaviour](#ibc.lightclients.solomachine.v2.Misbehaviour) - [NextSequenceRecvData](#ibc.lightclients.solomachine.v2.NextSequenceRecvData) - [PacketAcknowledgementData](#ibc.lightclients.solomachine.v2.PacketAcknowledgementData) - [PacketCommitmentData](#ibc.lightclients.solomachine.v2.PacketCommitmentData) @@ -3680,25 +3680,6 @@ verification. - - -### DuplicateSignatures -DuplicateSignatures defines misbehaviour for a solo machine which consists -of a sequence and two signatures over different messages at that sequence. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `client_id` | [string](#string) | | | -| `sequence` | [uint64](#uint64) | | | -| `signature_one` | [SignatureAndData](#ibc.lightclients.solomachine.v2.SignatureAndData) | | | -| `signature_two` | [SignatureAndData](#ibc.lightclients.solomachine.v2.SignatureAndData) | | | - - - - - - ### Header @@ -3734,6 +3715,25 @@ HeaderData returns the SignBytes data for update verification. + + +### Misbehaviour +Misbehaviour defines misbehaviour for a solo machine which consists +of a sequence and two signatures over different messages at that sequence. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `client_id` | [string](#string) | | | +| `sequence` | [uint64](#uint64) | | | +| `signature_one` | [SignatureAndData](#ibc.lightclients.solomachine.v2.SignatureAndData) | | | +| `signature_two` | [SignatureAndData](#ibc.lightclients.solomachine.v2.SignatureAndData) | | | + + + + + + ### NextSequenceRecvData diff --git a/modules/core/02-client/types/msgs_test.go b/modules/core/02-client/types/msgs_test.go index cede52147af..35dd08aedba 100644 --- a/modules/core/02-client/types/msgs_test.go +++ b/modules/core/02-client/types/msgs_test.go @@ -591,7 +591,7 @@ func (suite *TypesTestSuite) TestMsgSubmitMisbehaviour_ValidateBasic() { { "invalid solomachine misbehaviour", func() { - msg, err = types.NewMsgSubmitMisbehaviour("solomachine", &solomachinetypes.DuplicateSignatures{}, suite.chainA.SenderAccount.GetAddress().String()) + msg, err = types.NewMsgSubmitMisbehaviour("solomachine", &solomachinetypes.Misbehaviour{}, suite.chainA.SenderAccount.GetAddress().String()) suite.Require().NoError(err) }, false, diff --git a/modules/light-clients/06-solomachine/types/codec.go b/modules/light-clients/06-solomachine/types/codec.go index 67f89394f39..1db36165157 100644 --- a/modules/light-clients/06-solomachine/types/codec.go +++ b/modules/light-clients/06-solomachine/types/codec.go @@ -27,7 +27,7 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) { ) registry.RegisterImplementations( (*exported.Misbehaviour)(nil), - &DuplicateSignatures{}, + &Misbehaviour{}, ) } diff --git a/modules/light-clients/06-solomachine/types/misbehaviour.go b/modules/light-clients/06-solomachine/types/misbehaviour.go index d07503b6f17..b673d7e6cd6 100644 --- a/modules/light-clients/06-solomachine/types/misbehaviour.go +++ b/modules/light-clients/06-solomachine/types/misbehaviour.go @@ -10,48 +10,48 @@ import ( "github.com/cosmos/ibc-go/v3/modules/core/exported" ) -var _ exported.Misbehaviour = &DuplicateSignatures{} +var _ exported.Misbehaviour = &Misbehaviour{} // ClientType is a Solo Machine light client. -func (ds DuplicateSignatures) ClientType() string { +func (misbehaviour Misbehaviour) ClientType() string { return exported.Solomachine } // GetClientID returns the ID of the client that committed a misbehaviour. -func (ds DuplicateSignatures) GetClientID() string { - return ds.ClientId +func (misbehaviour Misbehaviour) GetClientID() string { + return misbehaviour.ClientId } // Type implements Misbehaviour interface. -func (ds DuplicateSignatures) Type() string { +func (misbehaviour Misbehaviour) Type() string { return exported.TypeClientMisbehaviour } // ValidateBasic implements Misbehaviour interface. -func (ds DuplicateSignatures) ValidateBasic() error { - if err := host.ClientIdentifierValidator(ds.ClientId); err != nil { +func (misbehaviour Misbehaviour) ValidateBasic() error { + if err := host.ClientIdentifierValidator(misbehaviour.ClientId); err != nil { return sdkerrors.Wrap(err, "invalid client identifier for solo machine") } - if ds.Sequence == 0 { + if misbehaviour.Sequence == 0 { return sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "sequence cannot be 0") } - if err := ds.SignatureOne.ValidateBasic(); err != nil { + if err := misbehaviour.SignatureOne.ValidateBasic(); err != nil { return sdkerrors.Wrap(err, "signature one failed basic validation") } - if err := ds.SignatureTwo.ValidateBasic(); err != nil { + if err := misbehaviour.SignatureTwo.ValidateBasic(); err != nil { return sdkerrors.Wrap(err, "signature two failed basic validation") } // misbehaviour signatures cannot be identical - if bytes.Equal(ds.SignatureOne.Signature, ds.SignatureTwo.Signature) { + if bytes.Equal(misbehaviour.SignatureOne.Signature, misbehaviour.SignatureTwo.Signature) { return sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "misbehaviour signatures cannot be equal") } // message data signed cannot be identical - if bytes.Equal(ds.SignatureOne.Data, ds.SignatureTwo.Data) { + if bytes.Equal(misbehaviour.SignatureOne.Data, misbehaviour.SignatureTwo.Data) { return sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "misbehaviour signature data must be signed over different messages") } diff --git a/modules/light-clients/06-solomachine/types/misbehaviour_handle.go b/modules/light-clients/06-solomachine/types/misbehaviour_handle.go index 7b8a7157ff4..d5a1d57cb57 100644 --- a/modules/light-clients/06-solomachine/types/misbehaviour_handle.go +++ b/modules/light-clients/06-solomachine/types/misbehaviour_handle.go @@ -22,11 +22,11 @@ func (cs ClientState) CheckMisbehaviourAndUpdateState( misbehaviour exported.Misbehaviour, ) (exported.ClientState, error) { - soloMisbehaviour, ok := misbehaviour.(*DuplicateSignatures) + soloMisbehaviour, ok := misbehaviour.(*Misbehaviour) if !ok { return nil, sdkerrors.Wrapf( clienttypes.ErrInvalidClientType, - "misbehaviour type %T, expected %T", misbehaviour, &DuplicateSignatures{}, + "misbehaviour type %T, expected %T", misbehaviour, &Misbehaviour{}, ) } @@ -50,7 +50,7 @@ func (cs ClientState) CheckMisbehaviourAndUpdateState( // verifySignatureAndData verifies that the currently registered public key has signed // over the provided data and that the data is valid. The data is valid if it can be // unmarshaled into the specified data type. -func verifySignatureAndData(cdc codec.BinaryCodec, clientState ClientState, misbehaviour *DuplicateSignatures, sigAndData *SignatureAndData) error { +func verifySignatureAndData(cdc codec.BinaryCodec, clientState ClientState, misbehaviour *Misbehaviour, sigAndData *SignatureAndData) error { // do not check misbehaviour timestamp since we want to allow processing of past misbehaviour diff --git a/modules/light-clients/06-solomachine/types/misbehaviour_test.go b/modules/light-clients/06-solomachine/types/misbehaviour_test.go index e8590bb9f21..813a8520ee7 100644 --- a/modules/light-clients/06-solomachine/types/misbehaviour_test.go +++ b/modules/light-clients/06-solomachine/types/misbehaviour_test.go @@ -19,91 +19,91 @@ func (suite *SoloMachineTestSuite) TestMisbehaviourValidateBasic() { testCases := []struct { name string - malleateMisbehaviour func(duplicateSigHeader *types.DuplicateSignatures) + malleateMisbehaviour func(misbehaviour *types.Misbehaviour) expPass bool }{ { "valid misbehaviour", - func(*types.DuplicateSignatures) {}, + func(*types.Misbehaviour) {}, true, }, { "invalid client ID", - func(duplicateSigHeader *types.DuplicateSignatures) { - duplicateSigHeader.ClientId = "(badclientid)" + func(misbehaviour *types.Misbehaviour) { + misbehaviour.ClientId = "(badclientid)" }, false, }, { "sequence is zero", - func(duplicateSigHeader *types.DuplicateSignatures) { - duplicateSigHeader.Sequence = 0 + func(misbehaviour *types.Misbehaviour) { + misbehaviour.Sequence = 0 }, false, }, { "signature one sig is empty", - func(duplicateSigHeader *types.DuplicateSignatures) { - duplicateSigHeader.SignatureOne.Signature = []byte{} + func(misbehaviour *types.Misbehaviour) { + misbehaviour.SignatureOne.Signature = []byte{} }, false, }, { "signature two sig is empty", - func(duplicateSigHeader *types.DuplicateSignatures) { - duplicateSigHeader.SignatureTwo.Signature = []byte{} + func(misbehaviour *types.Misbehaviour) { + misbehaviour.SignatureTwo.Signature = []byte{} }, false, }, { "signature one data is empty", - func(duplicateSigHeader *types.DuplicateSignatures) { - duplicateSigHeader.SignatureOne.Data = nil + func(misbehaviour *types.Misbehaviour) { + misbehaviour.SignatureOne.Data = nil }, false, }, { "signature two data is empty", - func(duplicateSigHeader *types.DuplicateSignatures) { - duplicateSigHeader.SignatureTwo.Data = []byte{} + func(misbehaviour *types.Misbehaviour) { + misbehaviour.SignatureTwo.Data = []byte{} }, false, }, { "signatures are identical", - func(duplicateSigHeader *types.DuplicateSignatures) { - duplicateSigHeader.SignatureTwo.Signature = duplicateSigHeader.SignatureOne.Signature + func(misbehaviour *types.Misbehaviour) { + misbehaviour.SignatureTwo.Signature = misbehaviour.SignatureOne.Signature }, false, }, { "data signed is identical", - func(duplicateSigHeader *types.DuplicateSignatures) { - duplicateSigHeader.SignatureTwo.Data = duplicateSigHeader.SignatureOne.Data + func(misbehaviour *types.Misbehaviour) { + misbehaviour.SignatureTwo.Data = misbehaviour.SignatureOne.Data }, false, }, { "data type for SignatureOne is unspecified", - func(misbehaviour *types.DuplicateSignatures) { + func(misbehaviour *types.Misbehaviour) { misbehaviour.SignatureOne.DataType = types.UNSPECIFIED }, false, }, { "data type for SignatureTwo is unspecified", - func(misbehaviour *types.DuplicateSignatures) { + func(misbehaviour *types.Misbehaviour) { misbehaviour.SignatureTwo.DataType = types.UNSPECIFIED }, false, }, { "timestamp for SignatureOne is zero", - func(misbehaviour *types.DuplicateSignatures) { + func(misbehaviour *types.Misbehaviour) { misbehaviour.SignatureOne.Timestamp = 0 }, false, }, { "timestamp for SignatureTwo is zero", - func(misbehaviour *types.DuplicateSignatures) { + func(misbehaviour *types.Misbehaviour) { misbehaviour.SignatureTwo.Timestamp = 0 }, false, }, diff --git a/modules/light-clients/06-solomachine/types/solomachine.pb.go b/modules/light-clients/06-solomachine/types/solomachine.pb.go index 8b280dcba72..441a7030402 100644 --- a/modules/light-clients/06-solomachine/types/solomachine.pb.go +++ b/modules/light-clients/06-solomachine/types/solomachine.pb.go @@ -222,27 +222,27 @@ func (m *Header) XXX_DiscardUnknown() { var xxx_messageInfo_Header proto.InternalMessageInfo -// DuplicateSignatures defines misbehaviour for a solo machine which consists +// Misbehaviour defines misbehaviour for a solo machine which consists // of a sequence and two signatures over different messages at that sequence. -type DuplicateSignatures struct { +type Misbehaviour struct { ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` Sequence uint64 `protobuf:"varint,2,opt,name=sequence,proto3" json:"sequence,omitempty"` SignatureOne *SignatureAndData `protobuf:"bytes,3,opt,name=signature_one,json=signatureOne,proto3" json:"signature_one,omitempty" yaml:"signature_one"` SignatureTwo *SignatureAndData `protobuf:"bytes,4,opt,name=signature_two,json=signatureTwo,proto3" json:"signature_two,omitempty" yaml:"signature_two"` } -func (m *DuplicateSignatures) Reset() { *m = DuplicateSignatures{} } -func (m *DuplicateSignatures) String() string { return proto.CompactTextString(m) } -func (*DuplicateSignatures) ProtoMessage() {} -func (*DuplicateSignatures) Descriptor() ([]byte, []int) { +func (m *Misbehaviour) Reset() { *m = Misbehaviour{} } +func (m *Misbehaviour) String() string { return proto.CompactTextString(m) } +func (*Misbehaviour) ProtoMessage() {} +func (*Misbehaviour) Descriptor() ([]byte, []int) { return fileDescriptor_141333b361aae010, []int{3} } -func (m *DuplicateSignatures) XXX_Unmarshal(b []byte) error { +func (m *Misbehaviour) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *DuplicateSignatures) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *Misbehaviour) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_DuplicateSignatures.Marshal(b, m, deterministic) + return xxx_messageInfo_Misbehaviour.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -252,17 +252,17 @@ func (m *DuplicateSignatures) XXX_Marshal(b []byte, deterministic bool) ([]byte, return b[:n], nil } } -func (m *DuplicateSignatures) XXX_Merge(src proto.Message) { - xxx_messageInfo_DuplicateSignatures.Merge(m, src) +func (m *Misbehaviour) XXX_Merge(src proto.Message) { + xxx_messageInfo_Misbehaviour.Merge(m, src) } -func (m *DuplicateSignatures) XXX_Size() int { +func (m *Misbehaviour) XXX_Size() int { return m.Size() } -func (m *DuplicateSignatures) XXX_DiscardUnknown() { - xxx_messageInfo_DuplicateSignatures.DiscardUnknown(m) +func (m *Misbehaviour) XXX_DiscardUnknown() { + xxx_messageInfo_Misbehaviour.DiscardUnknown(m) } -var xxx_messageInfo_DuplicateSignatures proto.InternalMessageInfo +var xxx_messageInfo_Misbehaviour proto.InternalMessageInfo // SignatureAndData contains a signature and the data signed over to create that // signature. @@ -803,7 +803,7 @@ func init() { proto.RegisterType((*ClientState)(nil), "ibc.lightclients.solomachine.v2.ClientState") proto.RegisterType((*ConsensusState)(nil), "ibc.lightclients.solomachine.v2.ConsensusState") proto.RegisterType((*Header)(nil), "ibc.lightclients.solomachine.v2.Header") - proto.RegisterType((*DuplicateSignatures)(nil), "ibc.lightclients.solomachine.v2.DuplicateSignatures") + proto.RegisterType((*Misbehaviour)(nil), "ibc.lightclients.solomachine.v2.Misbehaviour") proto.RegisterType((*SignatureAndData)(nil), "ibc.lightclients.solomachine.v2.SignatureAndData") proto.RegisterType((*TimestampedSignatureData)(nil), "ibc.lightclients.solomachine.v2.TimestampedSignatureData") proto.RegisterType((*SignBytes)(nil), "ibc.lightclients.solomachine.v2.SignBytes") @@ -823,93 +823,93 @@ func init() { } var fileDescriptor_141333b361aae010 = []byte{ - // 1371 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0xdd, 0x8e, 0xdb, 0x44, - 0x14, 0x5e, 0xa7, 0xe9, 0x36, 0x99, 0x6c, 0x77, 0x83, 0x9b, 0xb6, 0x59, 0xb7, 0x4a, 0x8c, 0x11, - 0x65, 0x41, 0x34, 0x61, 0xb7, 0xa2, 0x42, 0x15, 0x02, 0x1c, 0xc7, 0xa5, 0x69, 0xb7, 0xde, 0xe0, - 0x78, 0x81, 0x56, 0x48, 0xc6, 0xb1, 0x67, 0xb3, 0x56, 0x1d, 0x4f, 0x1a, 0x4f, 0x92, 0x06, 0x09, - 0x09, 0x71, 0x55, 0x22, 0x2e, 0x78, 0x81, 0x48, 0x08, 0xc4, 0x73, 0x70, 0x87, 0xb8, 0x42, 0xbd, - 0xe4, 0x2a, 0xa0, 0xf6, 0x0d, 0xf2, 0x04, 0xc8, 0x9e, 0x49, 0x6c, 0x67, 0x77, 0xb3, 0xe2, 0xef, - 0x6e, 0xe6, 0x7c, 0x67, 0xbe, 0xf3, 0xcd, 0x99, 0xe3, 0x33, 0x63, 0xb0, 0x6d, 0x37, 0xcd, 0xb2, - 0x63, 0xb7, 0x0e, 0xb1, 0xe9, 0xd8, 0xd0, 0xc5, 0x5e, 0xd9, 0x43, 0x0e, 0x6a, 0x1b, 0xe6, 0xa1, - 0xed, 0xc2, 0x72, 0x7f, 0x27, 0x3a, 0x2d, 0x75, 0xba, 0x08, 0x23, 0xb6, 0x68, 0x37, 0xcd, 0x52, - 0x74, 0x49, 0x29, 0xea, 0xd3, 0xdf, 0xe1, 0x5e, 0xf3, 0x39, 0x4d, 0xd4, 0x85, 0x65, 0x13, 0xb9, - 0x2e, 0x34, 0xb1, 0x8d, 0xdc, 0x72, 0x7f, 0x3b, 0x32, 0x23, 0x4c, 0xdc, 0xcb, 0xa1, 0xe3, 0xa1, - 0xe1, 0xba, 0xd0, 0x09, 0xbc, 0xc8, 0x90, 0xba, 0xe4, 0x5a, 0xa8, 0x85, 0x82, 0x61, 0xd9, 0x1f, - 0x51, 0xeb, 0x66, 0x0b, 0xa1, 0x96, 0x03, 0xcb, 0xc1, 0xac, 0xd9, 0x3b, 0x28, 0x1b, 0xee, 0x90, - 0x40, 0xc2, 0xcf, 0x09, 0x90, 0x91, 0x02, 0x5d, 0x0d, 0x6c, 0x60, 0xc8, 0x72, 0x20, 0xe5, 0xc1, - 0xc7, 0x3d, 0xe8, 0x9a, 0x30, 0xcf, 0xf0, 0xcc, 0x56, 0x52, 0x9d, 0xcf, 0xd9, 0x6d, 0x90, 0xb6, - 0x3d, 0xfd, 0xa0, 0x8b, 0xbe, 0x80, 0x6e, 0x3e, 0xc1, 0x33, 0x5b, 0xa9, 0x4a, 0x6e, 0x3a, 0x29, - 0x66, 0x87, 0x46, 0xdb, 0xb9, 0x25, 0xcc, 0x21, 0x41, 0x4d, 0xd9, 0xde, 0xed, 0x60, 0xc8, 0x62, - 0xb0, 0x61, 0x22, 0xd7, 0x83, 0xae, 0xd7, 0xf3, 0x74, 0xcf, 0x8f, 0x90, 0x3f, 0xc3, 0x33, 0x5b, - 0x99, 0x9d, 0x72, 0xe9, 0x94, 0xb4, 0x94, 0xa4, 0xd9, 0xba, 0x40, 0x58, 0x85, 0x9b, 0x4e, 0x8a, - 0x97, 0x48, 0xa4, 0x05, 0x46, 0x41, 0x5d, 0x37, 0x63, 0xbe, 0x2c, 0x04, 0x57, 0x0c, 0xc7, 0x41, - 0x03, 0xbd, 0xd7, 0xb1, 0x0c, 0x0c, 0x75, 0xe3, 0x00, 0xc3, 0xae, 0xde, 0xe9, 0xa2, 0x0e, 0xf2, - 0x0c, 0x27, 0x9f, 0x0c, 0xa4, 0x5f, 0x9b, 0x4e, 0x8a, 0x02, 0x21, 0x5c, 0xe2, 0x2c, 0xa8, 0xf9, - 0x00, 0xdd, 0x0f, 0x40, 0xd1, 0xc7, 0xea, 0x14, 0xba, 0x95, 0x7c, 0xfa, 0x7d, 0x71, 0x45, 0xf8, - 0x81, 0x01, 0xeb, 0x71, 0xad, 0xec, 0x5d, 0x00, 0x3a, 0xbd, 0xa6, 0x63, 0x9b, 0xfa, 0x23, 0x38, - 0x0c, 0xd2, 0x98, 0xd9, 0xc9, 0x95, 0xc8, 0x21, 0x94, 0x66, 0x87, 0x50, 0x12, 0xdd, 0x61, 0xe5, - 0xe2, 0x74, 0x52, 0x7c, 0x89, 0x88, 0x08, 0x57, 0x08, 0x6a, 0x9a, 0x4c, 0xee, 0xc1, 0x21, 0xcb, - 0x83, 0x8c, 0x65, 0xf7, 0x61, 0xd7, 0xb3, 0x0f, 0x6c, 0xd8, 0x0d, 0xd2, 0x9e, 0x56, 0xa3, 0x26, - 0xf6, 0x2a, 0x48, 0x63, 0xbb, 0x0d, 0x3d, 0x6c, 0xb4, 0x3b, 0x41, 0x76, 0x93, 0x6a, 0x68, 0xa0, - 0x22, 0xbf, 0x4e, 0x80, 0xd5, 0x3b, 0xd0, 0xb0, 0x60, 0x77, 0xe9, 0x09, 0xc7, 0xa8, 0x12, 0x0b, - 0x54, 0x3e, 0xea, 0xd9, 0x2d, 0xd7, 0xc0, 0xbd, 0x2e, 0x39, 0xc6, 0x35, 0x35, 0x34, 0xb0, 0xfb, - 0x60, 0xdd, 0x85, 0x03, 0x3d, 0xb2, 0xf1, 0xe4, 0x92, 0x8d, 0x6f, 0x4e, 0x27, 0xc5, 0x8b, 0x64, - 0xe3, 0xf1, 0x55, 0x82, 0xba, 0xe6, 0xc2, 0x41, 0x7d, 0xbe, 0x7f, 0x09, 0x6c, 0xf8, 0x0e, 0xd1, - 0x1c, 0x9c, 0xf5, 0x73, 0x10, 0x2d, 0x88, 0x05, 0x07, 0x41, 0xf5, 0x95, 0x54, 0x43, 0x03, 0x4d, - 0xc2, 0x6f, 0x09, 0x70, 0xa1, 0xda, 0xeb, 0x38, 0xb6, 0x69, 0x60, 0xd8, 0x98, 0x09, 0xf7, 0xfc, - 0xba, 0x26, 0x35, 0xa8, 0xdb, 0x56, 0x90, 0x92, 0x74, 0xb4, 0xae, 0xe7, 0x90, 0xa0, 0xa6, 0xc8, - 0xb8, 0x66, 0xc5, 0x92, 0x98, 0x58, 0x48, 0x62, 0x07, 0x9c, 0x9f, 0x67, 0x45, 0x47, 0xee, 0xac, - 0xe2, 0xb7, 0x4f, 0xad, 0xf8, 0xb9, 0x24, 0xd1, 0xb5, 0xaa, 0x06, 0x36, 0x2a, 0xf9, 0xe9, 0xa4, - 0x98, 0x23, 0x2a, 0x62, 0x8c, 0x82, 0xba, 0x36, 0x9f, 0xef, 0xb9, 0x0b, 0x11, 0xf1, 0x00, 0xd1, - 0xcc, 0xff, 0x57, 0x11, 0xf1, 0x00, 0x45, 0x23, 0x6a, 0x03, 0x44, 0x13, 0xfa, 0x0b, 0x03, 0xb2, - 0x8b, 0x14, 0xf1, 0x2a, 0x61, 0x16, 0xab, 0xe4, 0x33, 0x90, 0xb6, 0x0c, 0x6c, 0xe8, 0x78, 0xd8, - 0x21, 0x99, 0x5b, 0xdf, 0x79, 0xfd, 0x54, 0x99, 0x3e, 0xaf, 0x36, 0xec, 0xc0, 0xe8, 0xb1, 0xcc, - 0x59, 0x04, 0x35, 0x65, 0x51, 0x9c, 0x65, 0x41, 0xd2, 0x1f, 0xd3, 0xe2, 0x0c, 0xc6, 0xf1, 0x9a, - 0x4e, 0x1e, 0xff, 0x79, 0x7c, 0xc5, 0x80, 0xbc, 0x36, 0xb3, 0x41, 0x6b, 0xbe, 0xa7, 0x60, 0x43, - 0x1f, 0x80, 0xf5, 0x30, 0x17, 0x01, 0x7d, 0xb0, 0xab, 0x68, 0x09, 0xc7, 0x71, 0x41, 0x0d, 0x8f, - 0xa3, 0x7a, 0x44, 0x42, 0xe2, 0x78, 0x09, 0x7f, 0x30, 0x20, 0xed, 0xc7, 0xad, 0x0c, 0x31, 0xf4, - 0xfe, 0xc5, 0x47, 0xba, 0xd0, 0x2f, 0xce, 0x1c, 0xed, 0x17, 0xb1, 0x23, 0x48, 0xfe, 0x5f, 0x47, - 0x70, 0x36, 0x3c, 0x02, 0xba, 0xc3, 0x9f, 0x18, 0x00, 0x48, 0x0f, 0x0a, 0x92, 0xb2, 0x0b, 0x32, - 0xf4, 0xcb, 0x3f, 0xb5, 0x4b, 0x5e, 0x9a, 0x4e, 0x8a, 0x6c, 0xac, 0x59, 0xd0, 0x36, 0x49, 0x3a, - 0xc5, 0x09, 0x6d, 0x22, 0xf1, 0x0f, 0xdb, 0xc4, 0x97, 0x60, 0x23, 0x72, 0x23, 0x06, 0x5a, 0x59, - 0x90, 0xec, 0x18, 0xf8, 0x90, 0x96, 0x73, 0x30, 0x66, 0xeb, 0x60, 0x8d, 0xb6, 0x06, 0x72, 0xaf, - 0x25, 0x96, 0x6c, 0xe0, 0xf2, 0x74, 0x52, 0xbc, 0x10, 0x6b, 0x27, 0xf4, 0xe6, 0xca, 0x98, 0x61, - 0x24, 0x1a, 0xfe, 0x1b, 0x06, 0xb0, 0xf1, 0xfb, 0xe4, 0x44, 0x09, 0x0f, 0x8e, 0xde, 0xae, 0xcb, - 0x54, 0xfc, 0x8d, 0x2b, 0x94, 0x6a, 0xe9, 0x83, 0x0b, 0xd2, 0xfc, 0x15, 0xb2, 0x5c, 0x8b, 0x0c, - 0x40, 0xf8, 0x60, 0xa1, 0x32, 0x5e, 0x0d, 0xca, 0xca, 0x7f, 0xb1, 0x94, 0x22, 0x8f, 0x99, 0xfe, - 0x76, 0x29, 0x24, 0x95, 0x5d, 0x4b, 0x8d, 0x2c, 0xa4, 0x71, 0x2d, 0x90, 0x95, 0xc8, 0xbb, 0x66, - 0x79, 0xd0, 0x9b, 0xe0, 0x1c, 0x7d, 0xff, 0xd0, 0x88, 0x57, 0x23, 0x11, 0xe9, 0xc3, 0xc8, 0x0f, - 0x47, 0x86, 0xea, 0xcc, 0x99, 0x46, 0xb9, 0x0b, 0x72, 0x75, 0xc3, 0x7c, 0x04, 0xb1, 0x84, 0xda, - 0x6d, 0x1b, 0xb7, 0xa1, 0x8b, 0x4f, 0x8c, 0x54, 0xf0, 0xb7, 0x37, 0xf3, 0x0a, 0x82, 0xad, 0xa9, - 0x11, 0x8b, 0xf0, 0x00, 0x6c, 0x12, 0x2e, 0xd1, 0x7c, 0xe4, 0xa2, 0x81, 0x03, 0xad, 0x16, 0x5c, - 0x4a, 0xb8, 0x05, 0x36, 0x8c, 0xb8, 0x2b, 0x65, 0x5d, 0x34, 0x0b, 0x25, 0x90, 0x27, 0xd4, 0x2a, - 0x34, 0xa1, 0xdd, 0xc1, 0x62, 0xd3, 0xf3, 0xfb, 0xc0, 0x49, 0xcc, 0xc2, 0x21, 0xc8, 0x29, 0xf0, - 0x09, 0x6e, 0xd0, 0x7e, 0xa1, 0x42, 0xb3, 0x7f, 0xa2, 0x8a, 0x77, 0xc1, 0x79, 0x17, 0x3e, 0xc1, - 0xba, 0x07, 0x1f, 0xeb, 0x5d, 0x68, 0xf6, 0x49, 0x3f, 0x89, 0x5e, 0x03, 0x31, 0x58, 0x50, 0x33, - 0x2e, 0xa1, 0xf6, 0x59, 0xdf, 0xf8, 0x36, 0x09, 0x52, 0xb3, 0xc6, 0xc0, 0xbe, 0x03, 0x5e, 0xa9, - 0x8a, 0x9a, 0xa8, 0x6b, 0x0f, 0xea, 0xb2, 0xbe, 0xaf, 0xd4, 0x94, 0x9a, 0x56, 0x13, 0x77, 0x6b, - 0x0f, 0xe5, 0xaa, 0xbe, 0xaf, 0x34, 0xea, 0xb2, 0x54, 0xbb, 0x5d, 0x93, 0xab, 0xd9, 0x15, 0x6e, - 0x63, 0x34, 0xe6, 0x33, 0x11, 0x13, 0x7b, 0x0d, 0x5c, 0x0a, 0x57, 0x4a, 0xbb, 0x35, 0x59, 0xd1, - 0xf4, 0x86, 0x26, 0x6a, 0x72, 0x96, 0xe1, 0xc0, 0x68, 0xcc, 0xaf, 0x12, 0x1b, 0xfb, 0x26, 0xd8, - 0x8c, 0xf8, 0xed, 0x29, 0x0d, 0x59, 0x69, 0xec, 0x37, 0xa8, 0x6b, 0x82, 0x3b, 0x3f, 0x1a, 0xf3, - 0xe9, 0xb9, 0x99, 0x2d, 0x01, 0x2e, 0xe6, 0xad, 0xc8, 0x92, 0x56, 0xdb, 0x53, 0xa8, 0xfb, 0x19, - 0x6e, 0x7d, 0x34, 0xe6, 0x41, 0x68, 0x67, 0xb7, 0xc0, 0xe5, 0x88, 0xff, 0x1d, 0x51, 0x51, 0xe4, - 0x5d, 0xea, 0x9c, 0xe4, 0x32, 0xa3, 0x31, 0x7f, 0x8e, 0x1a, 0xd9, 0xb7, 0xc1, 0x95, 0xd0, 0xb3, - 0x2e, 0x4a, 0xf7, 0x64, 0x4d, 0x97, 0xf6, 0xee, 0xdf, 0xaf, 0x69, 0xf7, 0x65, 0x45, 0xcb, 0x9e, - 0xe5, 0x72, 0xa3, 0x31, 0x9f, 0x25, 0x40, 0x68, 0x67, 0xdf, 0x07, 0xfc, 0x91, 0x65, 0xa2, 0x74, - 0x4f, 0xd9, 0xfb, 0x64, 0x57, 0xae, 0x7e, 0x28, 0x07, 0x6b, 0x57, 0xb9, 0xcd, 0xd1, 0x98, 0xbf, - 0x48, 0xd0, 0x05, 0x90, 0x7d, 0xef, 0x18, 0x02, 0x55, 0x96, 0xe4, 0x5a, 0x5d, 0xd3, 0xc5, 0x4a, - 0x43, 0x56, 0x24, 0x39, 0x7b, 0x8e, 0xcb, 0x8f, 0xc6, 0x7c, 0x8e, 0xa0, 0x14, 0xa4, 0x18, 0x7b, - 0x13, 0x5c, 0x0d, 0xd7, 0x2b, 0xf2, 0xa7, 0x9a, 0xde, 0x90, 0x3f, 0xda, 0xf7, 0x21, 0x9f, 0xe6, - 0xe3, 0x6c, 0x8a, 0x08, 0xf7, 0x91, 0x19, 0xe0, 0xdb, 0x59, 0x1e, 0x64, 0xc3, 0x75, 0x77, 0x64, - 0xb1, 0x2a, 0xab, 0xd9, 0x34, 0x39, 0x19, 0x32, 0xe3, 0x92, 0x4f, 0x7f, 0x2c, 0xac, 0x54, 0x3e, - 0xff, 0xf5, 0x79, 0x81, 0x79, 0xf6, 0xbc, 0xc0, 0xfc, 0xf9, 0xbc, 0xc0, 0x7c, 0xf7, 0xa2, 0xb0, - 0xf2, 0xec, 0x45, 0x61, 0xe5, 0xf7, 0x17, 0x85, 0x95, 0x87, 0xb7, 0x5b, 0x36, 0x3e, 0xec, 0x35, - 0x4b, 0x26, 0x6a, 0x97, 0x4d, 0xe4, 0xb5, 0x91, 0x57, 0xb6, 0x9b, 0xe6, 0xf5, 0x16, 0x2a, 0xf7, - 0x6f, 0x94, 0xdb, 0xc8, 0xea, 0x39, 0xd0, 0x23, 0xbf, 0x55, 0xd7, 0x67, 0xff, 0x55, 0x6f, 0xdd, - 0xbc, 0x1e, 0xfd, 0xb5, 0xf2, 0xaf, 0x19, 0xaf, 0xb9, 0x1a, 0xf4, 0xb3, 0x1b, 0x7f, 0x05, 0x00, - 0x00, 0xff, 0xff, 0xe8, 0xd6, 0xdc, 0x98, 0x87, 0x0d, 0x00, 0x00, + // 1370 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0x5f, 0x8f, 0xdb, 0x44, + 0x10, 0x3f, 0xa7, 0xe9, 0xf5, 0x32, 0xb9, 0xde, 0x05, 0x37, 0x6d, 0x73, 0x6e, 0x95, 0x18, 0x23, + 0xca, 0x81, 0x68, 0xc2, 0x5d, 0x45, 0x85, 0x2a, 0x04, 0x38, 0x8e, 0x4b, 0xd3, 0xde, 0xf9, 0x82, + 0xe3, 0x03, 0x5a, 0x21, 0x19, 0xc7, 0xd9, 0x4b, 0xac, 0x26, 0xde, 0x34, 0x76, 0x92, 0x06, 0x09, + 0x09, 0xf1, 0x54, 0x22, 0x1e, 0xf8, 0x02, 0x91, 0x10, 0x88, 0xcf, 0xc1, 0x1b, 0xf0, 0xd8, 0x47, + 0x9e, 0x02, 0x6a, 0xbf, 0x41, 0x3e, 0x01, 0xb2, 0x77, 0x13, 0xdb, 0xb9, 0x5e, 0x4e, 0xfc, 0x7b, + 0xdb, 0x9d, 0xdf, 0xcc, 0x6f, 0x66, 0x67, 0xc6, 0xb3, 0x6b, 0xd8, 0xb1, 0x6a, 0x66, 0xa1, 0x65, + 0x35, 0x9a, 0xae, 0xd9, 0xb2, 0x90, 0xed, 0x3a, 0x05, 0x07, 0xb7, 0x70, 0xdb, 0x30, 0x9b, 0x96, + 0x8d, 0x0a, 0xfd, 0xdd, 0xf0, 0x36, 0xdf, 0xe9, 0x62, 0x17, 0xb3, 0x39, 0xab, 0x66, 0xe6, 0xc3, + 0x26, 0xf9, 0xb0, 0x4e, 0x7f, 0x97, 0x7b, 0xcd, 0xe3, 0x34, 0x71, 0x17, 0x15, 0x4c, 0x6c, 0xdb, + 0xc8, 0x74, 0x2d, 0x6c, 0x17, 0xfa, 0x3b, 0xa1, 0x1d, 0x61, 0xe2, 0x5e, 0x0e, 0x14, 0x9b, 0x86, + 0x6d, 0xa3, 0x96, 0xaf, 0x45, 0x96, 0x54, 0x25, 0xdd, 0xc0, 0x0d, 0xec, 0x2f, 0x0b, 0xde, 0x8a, + 0x4a, 0xb7, 0x1a, 0x18, 0x37, 0x5a, 0xa8, 0xe0, 0xef, 0x6a, 0xbd, 0xa3, 0x82, 0x61, 0x0f, 0x09, + 0x24, 0xfc, 0x1c, 0x83, 0xa4, 0xe4, 0xc7, 0x55, 0x75, 0x0d, 0x17, 0xb1, 0x1c, 0xac, 0x39, 0xe8, + 0x51, 0x0f, 0xd9, 0x26, 0xca, 0x30, 0x3c, 0xb3, 0x1d, 0x57, 0xe7, 0x7b, 0x76, 0x07, 0x12, 0x96, + 0xa3, 0x1f, 0x75, 0xf1, 0x17, 0xc8, 0xce, 0xc4, 0x78, 0x66, 0x7b, 0xad, 0x98, 0x9e, 0x4e, 0x72, + 0xa9, 0xa1, 0xd1, 0x6e, 0xdd, 0x12, 0xe6, 0x90, 0xa0, 0xae, 0x59, 0xce, 0x6d, 0x7f, 0xc9, 0xba, + 0xb0, 0x69, 0x62, 0xdb, 0x41, 0xb6, 0xd3, 0x73, 0x74, 0xc7, 0xf3, 0x90, 0x39, 0xc3, 0x33, 0xdb, + 0xc9, 0xdd, 0x42, 0xfe, 0x94, 0xb4, 0xe4, 0xa5, 0x99, 0x9d, 0x1f, 0x58, 0x91, 0x9b, 0x4e, 0x72, + 0x97, 0x88, 0xa7, 0x05, 0x46, 0x41, 0xdd, 0x30, 0x23, 0xba, 0x2c, 0x82, 0x2b, 0x46, 0xab, 0x85, + 0x07, 0x7a, 0xaf, 0x53, 0x37, 0x5c, 0xa4, 0x1b, 0x47, 0x2e, 0xea, 0xea, 0x9d, 0x2e, 0xee, 0x60, + 0xc7, 0x68, 0x65, 0xe2, 0x7e, 0xe8, 0xd7, 0xa6, 0x93, 0x9c, 0x40, 0x08, 0x97, 0x28, 0x0b, 0x6a, + 0xc6, 0x47, 0x0f, 0x7d, 0x50, 0xf4, 0xb0, 0x0a, 0x85, 0x6e, 0xc5, 0x9f, 0x7c, 0x9f, 0x5b, 0x11, + 0x7e, 0x60, 0x60, 0x23, 0x1a, 0x2b, 0x7b, 0x17, 0xa0, 0xd3, 0xab, 0xb5, 0x2c, 0x53, 0x7f, 0x88, + 0x86, 0x7e, 0x1a, 0x93, 0xbb, 0xe9, 0x3c, 0x29, 0x42, 0x7e, 0x56, 0x84, 0xbc, 0x68, 0x0f, 0x8b, + 0x17, 0xa7, 0x93, 0xdc, 0x4b, 0x24, 0x88, 0xc0, 0x42, 0x50, 0x13, 0x64, 0x73, 0x0f, 0x0d, 0x59, + 0x1e, 0x92, 0x75, 0xab, 0x8f, 0xba, 0x8e, 0x75, 0x64, 0xa1, 0xae, 0x9f, 0xf6, 0x84, 0x1a, 0x16, + 0xb1, 0x57, 0x21, 0xe1, 0x5a, 0x6d, 0xe4, 0xb8, 0x46, 0xbb, 0xe3, 0x67, 0x37, 0xae, 0x06, 0x02, + 0x1a, 0xe4, 0xd7, 0x31, 0x58, 0xbd, 0x83, 0x8c, 0x3a, 0xea, 0x2e, 0xad, 0x70, 0x84, 0x2a, 0xb6, + 0x40, 0xe5, 0xa1, 0x8e, 0xd5, 0xb0, 0x0d, 0xb7, 0xd7, 0x25, 0x65, 0x5c, 0x57, 0x03, 0x01, 0x7b, + 0x08, 0x1b, 0x36, 0x1a, 0xe8, 0xa1, 0x83, 0xc7, 0x97, 0x1c, 0x7c, 0x6b, 0x3a, 0xc9, 0x5d, 0x24, + 0x07, 0x8f, 0x5a, 0x09, 0xea, 0xba, 0x8d, 0x06, 0x95, 0xf9, 0xf9, 0x25, 0xd8, 0xf4, 0x14, 0xc2, + 0x39, 0x38, 0xeb, 0xe5, 0x20, 0xdc, 0x10, 0x0b, 0x0a, 0x82, 0xea, 0x45, 0x52, 0x0a, 0x04, 0x34, + 0x09, 0xbf, 0xc6, 0x60, 0x7d, 0xdf, 0x72, 0x6a, 0xa8, 0x69, 0xf4, 0x2d, 0xdc, 0xeb, 0x7a, 0x0d, + 0x4d, 0x9a, 0x4f, 0xb7, 0xea, 0x7e, 0x2e, 0x12, 0xe1, 0x86, 0x9e, 0x43, 0x82, 0xba, 0x46, 0xd6, + 0xe5, 0x7a, 0x24, 0x7b, 0xb1, 0x85, 0xec, 0x75, 0xe0, 0xfc, 0x3c, 0x1d, 0x3a, 0xb6, 0x67, 0xad, + 0xbe, 0x73, 0x6a, 0xab, 0x57, 0x67, 0x56, 0xa2, 0x5d, 0x2f, 0x19, 0xae, 0x51, 0xcc, 0x4c, 0x27, + 0xb9, 0x34, 0x89, 0x22, 0xc2, 0x28, 0xa8, 0xeb, 0xf3, 0xfd, 0x81, 0xbd, 0xe0, 0xd1, 0x1d, 0x60, + 0x9a, 0xf2, 0xff, 0xca, 0xa3, 0x3b, 0xc0, 0x61, 0x8f, 0xda, 0x00, 0xd3, 0x4c, 0xfe, 0xc2, 0x40, + 0x6a, 0x91, 0x22, 0xda, 0x1e, 0xcc, 0x62, 0x7b, 0x7c, 0x06, 0x89, 0xba, 0xe1, 0x1a, 0xba, 0x3b, + 0xec, 0x90, 0xcc, 0x6d, 0xec, 0xbe, 0x7e, 0x6a, 0x98, 0x1e, 0xaf, 0x36, 0xec, 0xa0, 0x70, 0x59, + 0xe6, 0x2c, 0x82, 0xba, 0x56, 0xa7, 0x38, 0xcb, 0x42, 0xdc, 0x5b, 0xd3, 0xae, 0xf4, 0xd7, 0xd1, + 0x66, 0x8e, 0xbf, 0xf8, 0xbb, 0xf8, 0x8a, 0x81, 0x8c, 0x36, 0x93, 0xa1, 0xfa, 0xfc, 0x4c, 0xfe, + 0x81, 0x3e, 0x80, 0x8d, 0x20, 0x17, 0x3e, 0xbd, 0x7f, 0xaa, 0x70, 0xef, 0x46, 0x71, 0x41, 0x0d, + 0xca, 0x51, 0x3a, 0x16, 0x42, 0xec, 0xc5, 0x21, 0xfc, 0xc1, 0x40, 0xc2, 0xf3, 0x5b, 0x1c, 0xba, + 0xc8, 0xf9, 0x17, 0x5f, 0xe7, 0xc2, 0xa0, 0x38, 0x73, 0x7c, 0x50, 0x44, 0x4a, 0x10, 0xff, 0xbf, + 0x4a, 0x70, 0x36, 0x28, 0x01, 0x3d, 0xe1, 0x4f, 0x0c, 0x00, 0x19, 0x3e, 0x7e, 0x52, 0xf6, 0x20, + 0x49, 0x3f, 0xf9, 0x53, 0xc7, 0xe3, 0xa5, 0xe9, 0x24, 0xc7, 0x46, 0xa6, 0x04, 0x9d, 0x8f, 0x64, + 0x44, 0x9c, 0x30, 0x1f, 0x62, 0xff, 0x70, 0x3e, 0x7c, 0x09, 0x9b, 0xa1, 0xab, 0xd0, 0x8f, 0x95, + 0x85, 0x78, 0xc7, 0x70, 0x9b, 0xb4, 0x9d, 0xfd, 0x35, 0x5b, 0x81, 0x75, 0x3a, 0x1a, 0xc8, 0x85, + 0x16, 0x5b, 0x72, 0x80, 0xcb, 0xd3, 0x49, 0xee, 0x42, 0x64, 0x9c, 0xd0, 0x2b, 0x2b, 0x69, 0x06, + 0x9e, 0xa8, 0xfb, 0x6f, 0x18, 0x60, 0xa3, 0x17, 0xc9, 0x89, 0x21, 0xdc, 0x3f, 0x7e, 0xad, 0x2e, + 0x8b, 0xe2, 0x6f, 0xdc, 0x9d, 0x34, 0x96, 0x3e, 0x5c, 0x90, 0xe6, 0xcf, 0x8f, 0xe5, 0xb1, 0xc8, + 0x00, 0xc1, 0x4b, 0x85, 0x86, 0xf1, 0xaa, 0xdf, 0x56, 0xde, 0x53, 0x25, 0x1f, 0x7a, 0xc5, 0xf4, + 0x77, 0xf2, 0x01, 0xa9, 0x6c, 0xd7, 0xd5, 0x90, 0x21, 0xf5, 0x5b, 0x87, 0x94, 0x44, 0x1e, 0x34, + 0xcb, 0x9d, 0xde, 0x84, 0x73, 0xf4, 0xe1, 0x43, 0x3d, 0x5e, 0x0d, 0x79, 0xa4, 0x2f, 0x22, 0xcf, + 0x1d, 0x59, 0xaa, 0x33, 0x65, 0xea, 0xe5, 0x2e, 0xa4, 0x2b, 0x86, 0xf9, 0x10, 0xb9, 0x12, 0x6e, + 0xb7, 0x2d, 0xb7, 0x8d, 0x6c, 0xf7, 0x44, 0x4f, 0x59, 0xef, 0x78, 0x33, 0x2d, 0xdf, 0xd9, 0xba, + 0x1a, 0x92, 0x08, 0xf7, 0x61, 0x8b, 0x70, 0x89, 0xe6, 0x43, 0x1b, 0x0f, 0x5a, 0xa8, 0xde, 0x40, + 0x4b, 0x09, 0xb7, 0x61, 0xd3, 0x88, 0xaa, 0x52, 0xd6, 0x45, 0xb1, 0x90, 0x87, 0x0c, 0xa1, 0x56, + 0x91, 0x89, 0xac, 0x8e, 0x2b, 0xd6, 0x1c, 0x6f, 0x0e, 0x9c, 0xc4, 0x2c, 0x34, 0x21, 0xad, 0xa0, + 0xc7, 0x6e, 0x95, 0xce, 0x0b, 0x15, 0x99, 0xfd, 0x13, 0xa3, 0x78, 0x17, 0xce, 0xdb, 0xe8, 0xb1, + 0xab, 0x3b, 0xe8, 0x91, 0xde, 0x45, 0x66, 0x9f, 0xcc, 0x93, 0xf0, 0x35, 0x10, 0x81, 0x05, 0x35, + 0x69, 0x13, 0x6a, 0x8f, 0xf5, 0x8d, 0x6f, 0xe3, 0xb0, 0x36, 0x1b, 0x0c, 0xec, 0x3b, 0xf0, 0x4a, + 0x49, 0xd4, 0x44, 0x5d, 0xbb, 0x5f, 0x91, 0xf5, 0x43, 0xa5, 0xac, 0x94, 0xb5, 0xb2, 0xb8, 0x57, + 0x7e, 0x20, 0x97, 0xf4, 0x43, 0xa5, 0x5a, 0x91, 0xa5, 0xf2, 0xed, 0xb2, 0x5c, 0x4a, 0xad, 0x70, + 0x9b, 0xa3, 0x31, 0x9f, 0x0c, 0x89, 0xd8, 0x6b, 0x70, 0x29, 0xb0, 0x94, 0xf6, 0xca, 0xb2, 0xa2, + 0xe9, 0x55, 0x4d, 0xd4, 0xe4, 0x14, 0xc3, 0xc1, 0x68, 0xcc, 0xaf, 0x12, 0x19, 0xfb, 0x26, 0x6c, + 0x85, 0xf4, 0x0e, 0x94, 0xaa, 0xac, 0x54, 0x0f, 0xab, 0x54, 0x35, 0xc6, 0x9d, 0x1f, 0x8d, 0xf9, + 0xc4, 0x5c, 0xcc, 0xe6, 0x81, 0x8b, 0x68, 0x2b, 0xb2, 0xa4, 0x95, 0x0f, 0x14, 0xaa, 0x7e, 0x86, + 0xdb, 0x18, 0x8d, 0x79, 0x08, 0xe4, 0xec, 0x36, 0x5c, 0x0e, 0xe9, 0xdf, 0x11, 0x15, 0x45, 0xde, + 0xa3, 0xca, 0x71, 0x2e, 0x39, 0x1a, 0xf3, 0xe7, 0xa8, 0x90, 0x7d, 0x1b, 0xae, 0x04, 0x9a, 0x15, + 0x51, 0xba, 0x27, 0x6b, 0xba, 0x74, 0xb0, 0xbf, 0x5f, 0xd6, 0xf6, 0x65, 0x45, 0x4b, 0x9d, 0xe5, + 0xd2, 0xa3, 0x31, 0x9f, 0x22, 0x40, 0x20, 0x67, 0xdf, 0x07, 0xfe, 0x98, 0x99, 0x28, 0xdd, 0x53, + 0x0e, 0x3e, 0xd9, 0x93, 0x4b, 0x1f, 0xca, 0xbe, 0xed, 0x2a, 0xb7, 0x35, 0x1a, 0xf3, 0x17, 0x09, + 0xba, 0x00, 0xb2, 0xef, 0xbd, 0x80, 0x40, 0x95, 0x25, 0xb9, 0x5c, 0xd1, 0x74, 0xb1, 0x58, 0x95, + 0x15, 0x49, 0x4e, 0x9d, 0xe3, 0x32, 0xa3, 0x31, 0x9f, 0x26, 0x28, 0x05, 0x29, 0xc6, 0xde, 0x84, + 0xab, 0x81, 0xbd, 0x22, 0x7f, 0xaa, 0xe9, 0x55, 0xf9, 0xa3, 0x43, 0x0f, 0xf2, 0x68, 0x3e, 0x4e, + 0xad, 0x91, 0xc0, 0x3d, 0x64, 0x06, 0x78, 0x72, 0x96, 0x87, 0x54, 0x60, 0x77, 0x47, 0x16, 0x4b, + 0xb2, 0x9a, 0x4a, 0x90, 0xca, 0x90, 0x1d, 0x17, 0x7f, 0xf2, 0x63, 0x76, 0xa5, 0xf8, 0xf9, 0x6f, + 0xcf, 0xb2, 0xcc, 0xd3, 0x67, 0x59, 0xe6, 0xcf, 0x67, 0x59, 0xe6, 0xbb, 0xe7, 0xd9, 0x95, 0xa7, + 0xcf, 0xb3, 0x2b, 0xbf, 0x3f, 0xcf, 0xae, 0x3c, 0xb8, 0xdd, 0xb0, 0xdc, 0x66, 0xaf, 0x96, 0x37, + 0x71, 0xbb, 0x60, 0x62, 0xa7, 0x8d, 0x9d, 0x82, 0x55, 0x33, 0xaf, 0x37, 0x70, 0xa1, 0x7f, 0xa3, + 0xd0, 0xc6, 0xf5, 0x5e, 0x0b, 0x39, 0xe4, 0x7f, 0xea, 0xfa, 0xec, 0x87, 0xea, 0xad, 0x9b, 0xd7, + 0xc3, 0xff, 0x54, 0xde, 0x35, 0xe3, 0xd4, 0x56, 0xfd, 0x79, 0x76, 0xe3, 0xaf, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x5d, 0xd4, 0x6c, 0xfb, 0x80, 0x0d, 0x00, 0x00, } func (m *ClientState) Marshal() (dAtA []byte, err error) { @@ -1078,7 +1078,7 @@ func (m *Header) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *DuplicateSignatures) Marshal() (dAtA []byte, err error) { +func (m *Misbehaviour) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -1088,12 +1088,12 @@ func (m *DuplicateSignatures) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *DuplicateSignatures) MarshalTo(dAtA []byte) (int, error) { +func (m *Misbehaviour) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *DuplicateSignatures) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *Misbehaviour) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -1700,7 +1700,7 @@ func (m *Header) Size() (n int) { return n } -func (m *DuplicateSignatures) Size() (n int) { +func (m *Misbehaviour) Size() (n int) { if m == nil { return 0 } @@ -2415,7 +2415,7 @@ func (m *Header) Unmarshal(dAtA []byte) error { } return nil } -func (m *DuplicateSignatures) Unmarshal(dAtA []byte) error { +func (m *Misbehaviour) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -2438,10 +2438,10 @@ func (m *DuplicateSignatures) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: DuplicateSignatures: wiretype end group for non-group") + return fmt.Errorf("proto: Misbehaviour: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: DuplicateSignatures: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: Misbehaviour: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: diff --git a/proto/ibc/lightclients/solomachine/v2/solomachine.proto b/proto/ibc/lightclients/solomachine/v2/solomachine.proto index 90fb7c61d61..e626c18ac66 100644 --- a/proto/ibc/lightclients/solomachine/v2/solomachine.proto +++ b/proto/ibc/lightclients/solomachine/v2/solomachine.proto @@ -48,9 +48,9 @@ message Header { string new_diversifier = 5 [(gogoproto.moretags) = "yaml:\"new_diversifier\""]; } -// DuplicateSignatures defines misbehaviour for a solo machine which consists +// Misbehaviour defines misbehaviour for a solo machine which consists // of a sequence and two signatures over different messages at that sequence. -message DuplicateSignatures { +message Misbehaviour { option (gogoproto.goproto_getters) = false; string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; uint64 sequence = 2; diff --git a/testing/solomachine.go b/testing/solomachine.go index 928fb441510..485fb75be0e 100644 --- a/testing/solomachine.go +++ b/testing/solomachine.go @@ -154,7 +154,7 @@ func (solo *Solomachine) CreateHeader() *solomachinetypes.Header { // CreateMisbehaviour constructs testing misbehaviour for the solo machine client // by signing over two different data bytes at the same sequence. -func (solo *Solomachine) CreateMisbehaviour() *solomachinetypes.DuplicateSignatures { +func (solo *Solomachine) CreateMisbehaviour() *solomachinetypes.Misbehaviour { path := solo.GetClientStatePath("counterparty") dataOne, err := solomachinetypes.ClientStateDataBytes(solo.cdc, path, solo.ClientState()) require.NoError(solo.t, err) @@ -204,7 +204,7 @@ func (solo *Solomachine) CreateMisbehaviour() *solomachinetypes.DuplicateSignatu Timestamp: solo.Time, } - return &solomachinetypes.DuplicateSignatures{ + return &solomachinetypes.Misbehaviour{ ClientId: solo.ClientID, Sequence: solo.Sequence, SignatureOne: &signatureOne, From daa01db76aa9e28f7174f6a5a70413a719eacfbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?colin=20axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Fri, 11 Mar 2022 12:22:46 +0100 Subject: [PATCH 05/71] remove GetClientID from 07-tendermint misbehaviour (#1097) * remove GetClientID from 07-tendermint misbehaviour and deprecate the ClientId field in 07-tendermint Misbehaviour * add changelog entry Co-authored-by: Damian Nolan --- CHANGELOG.md | 2 + docs/ibc/proto-docs.md | 2 +- .../07-tendermint/types/misbehaviour.go | 5 - .../07-tendermint/types/misbehaviour_test.go | 1 - .../07-tendermint/types/tendermint.pb.go | 133 +++++++++--------- .../tendermint/v1/tendermint.proto | 3 +- 6 files changed, 72 insertions(+), 74 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e0015683ae..32648765517 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,8 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### API Breaking +* (07-tendermint) [\#1097](https://github.com/cosmos/ibc-go/pull/1097) Remove `GetClientID` function from 07-tendermint `Misbehaviour` type. +* (07-tendermint) [\#1097](https://github.com/cosmos/ibc-go/pull/1097) Deprecate `ClientId` field in 07-tendermint `Misbehaviour` type. * (channel( [\#848](https://github.com/cosmos/ibc-go/pull/848) Added `ChannelId` to MsgChannelOpenInitResponse * (testing( [\#813](https://github.com/cosmos/ibc-go/pull/813) The `ack` argument to the testing function `RelayPacket` has been removed as it is no longer needed. * (testing) [\#774](https://github.com/cosmos/ibc-go/pull/774) Added `ChainID` arg to `SetupWithGenesisValSet` on the testing app. `Coordinator` generated ChainIDs now starts at index 1 diff --git a/docs/ibc/proto-docs.md b/docs/ibc/proto-docs.md index 79d56a6a541..b18857d9fbf 100644 --- a/docs/ibc/proto-docs.md +++ b/docs/ibc/proto-docs.md @@ -3991,7 +3991,7 @@ that implements Misbehaviour interface expected by ICS-02 | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `client_id` | [string](#string) | | | +| `client_id` | [string](#string) | | **Deprecated.** ClientID is deprecated | | `header_1` | [Header](#ibc.lightclients.tendermint.v1.Header) | | | | `header_2` | [Header](#ibc.lightclients.tendermint.v1.Header) | | | diff --git a/modules/light-clients/07-tendermint/types/misbehaviour.go b/modules/light-clients/07-tendermint/types/misbehaviour.go index 28ea7aa3666..f03ab08f7a0 100644 --- a/modules/light-clients/07-tendermint/types/misbehaviour.go +++ b/modules/light-clients/07-tendermint/types/misbehaviour.go @@ -31,11 +31,6 @@ func (misbehaviour Misbehaviour) ClientType() string { return exported.Tendermint } -// GetClientID returns the ID of the client that committed a misbehaviour. -func (misbehaviour Misbehaviour) GetClientID() string { - return misbehaviour.ClientId -} - // GetTime returns the timestamp at which misbehaviour occurred. It uses the // maximum value from both headers to prevent producing an invalid header outside // of the misbehaviour age range. diff --git a/modules/light-clients/07-tendermint/types/misbehaviour_test.go b/modules/light-clients/07-tendermint/types/misbehaviour_test.go index bba616bc5e4..6b67d1b2945 100644 --- a/modules/light-clients/07-tendermint/types/misbehaviour_test.go +++ b/modules/light-clients/07-tendermint/types/misbehaviour_test.go @@ -25,7 +25,6 @@ func (suite *TendermintTestSuite) TestMisbehaviour() { } suite.Require().Equal(exported.Tendermint, misbehaviour.ClientType()) - suite.Require().Equal(clientID, misbehaviour.GetClientID()) } func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { diff --git a/modules/light-clients/07-tendermint/types/tendermint.pb.go b/modules/light-clients/07-tendermint/types/tendermint.pb.go index 6436578b2ae..7257f37e5a5 100644 --- a/modules/light-clients/07-tendermint/types/tendermint.pb.go +++ b/modules/light-clients/07-tendermint/types/tendermint.pb.go @@ -146,7 +146,8 @@ var xxx_messageInfo_ConsensusState proto.InternalMessageInfo // Misbehaviour is a wrapper over two conflicting Headers // that implements Misbehaviour interface expected by ICS-02 type Misbehaviour struct { - ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` + // ClientID is deprecated + ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` // Deprecated: Do not use. Header1 *Header `protobuf:"bytes,2,opt,name=header_1,json=header1,proto3" json:"header_1,omitempty" yaml:"header_1"` Header2 *Header `protobuf:"bytes,3,opt,name=header_2,json=header2,proto3" json:"header_2,omitempty" yaml:"header_2"` } @@ -324,75 +325,75 @@ func init() { } var fileDescriptor_c6d6cf2b288949be = []byte{ - // 1080 bytes of a gzipped FileDescriptorProto + // 1079 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xcf, 0x6f, 0xe3, 0x44, 0x14, 0x6e, 0xda, 0xb2, 0x4d, 0x26, 0xe9, 0x76, 0x31, 0xa5, 0x9b, 0x96, 0x6e, 0x1c, 0x19, 0xb4, 0x44, 0x48, 0xb5, 0x49, 0x8a, 0x84, 0x54, 0x71, 0xc1, 0xdd, 0x45, 0x2d, 0x62, 0xa5, 0xca, 0xe5, 0x87, 0x84, 0x84, 0xcc, 0xc4, 0x9e, 0x24, 0xa3, 0xb5, 0x3d, 0xc6, 0x33, 0x09, 0x2d, 0x7f, 0x01, - 0x1c, 0x90, 0xf6, 0x88, 0x38, 0x71, 0xe0, 0x8f, 0xd9, 0x63, 0x8f, 0x9c, 0x0c, 0x6a, 0x2f, 0x9c, - 0x73, 0xe4, 0x84, 0xe6, 0x87, 0xed, 0x69, 0xb6, 0x4b, 0xb5, 0x5c, 0xa2, 0x79, 0xef, 0x7d, 0xef, - 0xfb, 0x32, 0x6f, 0xde, 0xbc, 0x31, 0x70, 0xf0, 0x30, 0x70, 0x22, 0x3c, 0x9e, 0xb0, 0x20, 0xc2, - 0x28, 0x61, 0xd4, 0x61, 0x28, 0x09, 0x51, 0x16, 0xe3, 0x84, 0x39, 0xb3, 0xbe, 0x66, 0xd9, 0x69, - 0x46, 0x18, 0x31, 0x3a, 0x78, 0x18, 0xd8, 0x7a, 0x82, 0xad, 0x41, 0x66, 0xfd, 0x9d, 0xae, 0x96, - 0xcf, 0xce, 0x53, 0x44, 0x9d, 0x19, 0x8c, 0x70, 0x08, 0x19, 0xc9, 0x24, 0xc3, 0xce, 0xee, 0x0b, - 0x08, 0xf1, 0xab, 0xa2, 0xad, 0x34, 0x23, 0x64, 0x54, 0x58, 0x9d, 0x31, 0x21, 0xe3, 0x08, 0x39, - 0xc2, 0x1a, 0x4e, 0x47, 0x4e, 0x38, 0xcd, 0x20, 0xc3, 0x24, 0x51, 0x71, 0x73, 0x31, 0xce, 0x70, - 0x8c, 0x28, 0x83, 0x71, 0x5a, 0x00, 0xf8, 0xfe, 0x02, 0x92, 0x21, 0x47, 0xfe, 0x5d, 0xbe, 0x27, - 0xb9, 0x52, 0x80, 0x77, 0x2b, 0x00, 0x89, 0x63, 0xcc, 0xe2, 0x02, 0x54, 0x5a, 0x0a, 0xb8, 0x39, - 0x26, 0x63, 0x22, 0x96, 0x0e, 0x5f, 0x49, 0xaf, 0xf5, 0xf7, 0x1a, 0x68, 0x1e, 0x0a, 0xbe, 0x53, - 0x06, 0x19, 0x32, 0xb6, 0x41, 0x3d, 0x98, 0x40, 0x9c, 0xf8, 0x38, 0x6c, 0xd7, 0xba, 0xb5, 0x5e, - 0xc3, 0x5b, 0x13, 0xf6, 0x71, 0x68, 0x20, 0xd0, 0x64, 0xd9, 0x94, 0x32, 0x3f, 0x42, 0x33, 0x14, - 0xb5, 0x97, 0xbb, 0xb5, 0x5e, 0x73, 0xd0, 0xb3, 0xff, 0xbb, 0x9e, 0xf6, 0x27, 0x19, 0x0c, 0xf8, - 0x86, 0xdd, 0x9d, 0xe7, 0xb9, 0xb9, 0x34, 0xcf, 0x4d, 0xe3, 0x1c, 0xc6, 0xd1, 0x81, 0xa5, 0x51, - 0x59, 0x1e, 0x10, 0xd6, 0x67, 0xdc, 0x30, 0x46, 0x60, 0x43, 0x58, 0x38, 0x19, 0xfb, 0x29, 0xca, - 0x30, 0x09, 0xdb, 0x2b, 0x42, 0x6a, 0xdb, 0x96, 0xc5, 0xb2, 0x8b, 0x62, 0xd9, 0x8f, 0x54, 0x31, - 0x5d, 0x4b, 0x71, 0x6f, 0x69, 0xdc, 0x55, 0xbe, 0xf5, 0xcb, 0x9f, 0x66, 0xcd, 0xbb, 0x5b, 0x78, - 0x4f, 0x84, 0xd3, 0xc0, 0xe0, 0xde, 0x34, 0x19, 0x92, 0x24, 0xd4, 0x84, 0x56, 0x6f, 0x13, 0x7a, - 0x5b, 0x09, 0xdd, 0x97, 0x42, 0x8b, 0x04, 0x52, 0x69, 0xa3, 0x74, 0x2b, 0x29, 0x04, 0x36, 0x62, - 0x78, 0xe6, 0x07, 0x11, 0x09, 0x9e, 0xfa, 0x61, 0x86, 0x47, 0xac, 0xfd, 0xda, 0x2b, 0x6e, 0x69, - 0x21, 0x5f, 0x0a, 0xad, 0xc7, 0xf0, 0xec, 0x90, 0x3b, 0x1f, 0x71, 0x9f, 0xf1, 0x0d, 0x58, 0x1f, - 0x65, 0xe4, 0x07, 0x94, 0xf8, 0x13, 0xc4, 0x0f, 0xa4, 0x7d, 0x47, 0x88, 0xec, 0x88, 0x23, 0xe2, - 0x2d, 0x62, 0xab, 0xce, 0x99, 0xf5, 0xed, 0x23, 0x81, 0x70, 0x77, 0x95, 0xca, 0xa6, 0x54, 0xb9, - 0x96, 0x6e, 0x79, 0x2d, 0x69, 0x4b, 0x2c, 0xa7, 0x8f, 0x20, 0x43, 0x94, 0x15, 0xf4, 0x6b, 0xaf, - 0x4a, 0x7f, 0x2d, 0xdd, 0xf2, 0x5a, 0xd2, 0x56, 0xf4, 0xc7, 0xa0, 0x29, 0xae, 0x8e, 0x4f, 0x53, - 0x14, 0xd0, 0x76, 0xbd, 0xbb, 0xd2, 0x6b, 0x0e, 0xee, 0xd9, 0x38, 0xa0, 0x83, 0x7d, 0xfb, 0x84, - 0x47, 0x4e, 0x53, 0x14, 0xb8, 0x5b, 0x55, 0x0b, 0x69, 0x70, 0xcb, 0x03, 0x69, 0x01, 0xa1, 0xc6, - 0x01, 0x68, 0x4d, 0xd3, 0x71, 0x06, 0x43, 0xe4, 0xa7, 0x90, 0x4d, 0xda, 0x8d, 0xee, 0x4a, 0xaf, - 0xe1, 0xde, 0x9f, 0xe7, 0xe6, 0x1b, 0xea, 0xdc, 0xb4, 0xa8, 0xe5, 0x35, 0x95, 0x79, 0x02, 0xd9, - 0xc4, 0xf0, 0xc1, 0x36, 0x8c, 0x22, 0xf2, 0xbd, 0x3f, 0x4d, 0x43, 0xc8, 0x90, 0x0f, 0x47, 0x0c, - 0x65, 0x3e, 0x3a, 0x4b, 0x71, 0x76, 0xde, 0x06, 0xdd, 0x5a, 0xaf, 0xee, 0xbe, 0x33, 0xcf, 0xcd, - 0xae, 0x24, 0x7a, 0x29, 0xd4, 0xf2, 0xb6, 0x44, 0xec, 0x0b, 0x11, 0xfa, 0x98, 0x47, 0x1e, 0x8b, - 0x80, 0xf1, 0x1d, 0x30, 0x6f, 0xc8, 0x8a, 0x31, 0x1d, 0xa2, 0x09, 0x9c, 0x61, 0x32, 0xcd, 0xda, - 0x4d, 0x21, 0xf3, 0xde, 0x3c, 0x37, 0x1f, 0xbe, 0x54, 0x46, 0x4f, 0xb0, 0xbc, 0xdd, 0x45, 0xb1, - 0x27, 0x5a, 0xf8, 0x60, 0xf5, 0xc7, 0xdf, 0xcc, 0x25, 0xeb, 0xf7, 0x65, 0x70, 0xf7, 0x90, 0x24, - 0x14, 0x25, 0x74, 0x4a, 0xe5, 0x6d, 0x77, 0x41, 0xa3, 0x1c, 0x38, 0xe2, 0xba, 0xf3, 0xe3, 0x5c, - 0x6c, 0xc9, 0xcf, 0x0b, 0x84, 0x5b, 0xe7, 0xc7, 0xf9, 0x8c, 0x77, 0x5e, 0x95, 0x66, 0x7c, 0x04, - 0x56, 0x33, 0x42, 0x98, 0x9a, 0x07, 0x96, 0xd6, 0x0d, 0xd5, 0x04, 0x9a, 0xf5, 0xed, 0x27, 0x28, - 0x7b, 0x1a, 0x21, 0x8f, 0x10, 0xe6, 0xae, 0x72, 0x1a, 0x4f, 0x64, 0x19, 0x3f, 0xd5, 0xc0, 0x66, - 0x82, 0xce, 0x98, 0x5f, 0x4e, 0x59, 0xea, 0x4f, 0x20, 0x9d, 0x88, 0x3b, 0xdf, 0x72, 0xbf, 0x9a, - 0xe7, 0xe6, 0x5b, 0xb2, 0x06, 0x37, 0xa1, 0xac, 0x7f, 0x72, 0xf3, 0x83, 0x31, 0x66, 0x93, 0xe9, - 0x90, 0xcb, 0xe9, 0xb3, 0x5f, 0x5b, 0x46, 0x78, 0x48, 0x9d, 0xe1, 0x39, 0x43, 0xd4, 0x3e, 0x42, - 0x67, 0x2e, 0x5f, 0x78, 0x06, 0xa7, 0xfb, 0xb2, 0x64, 0x3b, 0x82, 0x74, 0xa2, 0xca, 0xf4, 0xf3, - 0x32, 0x68, 0xe9, 0xd5, 0x33, 0xfa, 0xa0, 0x21, 0x1b, 0xbb, 0x9c, 0x89, 0xee, 0xe6, 0x3c, 0x37, - 0xef, 0xc9, 0xbf, 0x55, 0x86, 0x2c, 0xaf, 0x2e, 0xd7, 0xc7, 0xa1, 0x01, 0x41, 0x7d, 0x82, 0x60, - 0x88, 0x32, 0xbf, 0xaf, 0xea, 0xf2, 0xf0, 0xb6, 0x39, 0x79, 0x24, 0xf0, 0x6e, 0xe7, 0x32, 0x37, - 0xd7, 0xe4, 0xba, 0x3f, 0xcf, 0xcd, 0x0d, 0x29, 0x52, 0x90, 0x59, 0xde, 0x9a, 0x5c, 0xf6, 0x35, - 0x89, 0x81, 0x9a, 0x8f, 0xff, 0x43, 0x62, 0xf0, 0x82, 0xc4, 0xa0, 0x94, 0x18, 0xa8, 0x7a, 0xfc, - 0xba, 0x02, 0xee, 0x48, 0xb4, 0x01, 0xc1, 0x3a, 0xc5, 0xe3, 0x04, 0x85, 0xbe, 0x84, 0xa8, 0x96, - 0xe9, 0xe8, 0x3a, 0xf2, 0x2d, 0x3c, 0x15, 0x30, 0x25, 0xb8, 0x7b, 0x91, 0x9b, 0xb5, 0x6a, 0x0a, - 0x5c, 0xa3, 0xb0, 0xbc, 0x16, 0xd5, 0xb0, 0x7c, 0xc8, 0x94, 0x67, 0xec, 0x53, 0x54, 0xb4, 0xd5, - 0x0d, 0x12, 0xe5, 0xe1, 0x9d, 0x22, 0xe6, 0xb6, 0x2b, 0xfa, 0x6b, 0xe9, 0x96, 0xd7, 0x9a, 0x69, - 0x38, 0xe3, 0x5b, 0x20, 0x9f, 0x01, 0xa1, 0x2f, 0x86, 0xd8, 0xca, 0xad, 0x43, 0xec, 0x81, 0x1a, - 0x62, 0x6f, 0x6a, 0x8f, 0x4b, 0x99, 0x6f, 0x79, 0xeb, 0xca, 0xa1, 0xc6, 0x58, 0x04, 0x8c, 0x02, - 0x51, 0x35, 0xab, 0x7a, 0x58, 0x6e, 0xdb, 0xc5, 0x83, 0x79, 0x6e, 0x6e, 0x5f, 0x57, 0xa9, 0x38, - 0x2c, 0xef, 0x75, 0xe5, 0xac, 0xda, 0xd6, 0xfa, 0x14, 0xd4, 0x8b, 0x07, 0xd6, 0xd8, 0x05, 0x8d, - 0x64, 0x1a, 0xa3, 0x8c, 0x47, 0xc4, 0xc9, 0xac, 0x7a, 0x95, 0xc3, 0xe8, 0x82, 0x66, 0x88, 0x12, - 0x12, 0xe3, 0x44, 0xc4, 0x97, 0x45, 0x5c, 0x77, 0xb9, 0xfe, 0xf3, 0xcb, 0x4e, 0xed, 0xe2, 0xb2, - 0x53, 0xfb, 0xeb, 0xb2, 0x53, 0x7b, 0x76, 0xd5, 0x59, 0xba, 0xb8, 0xea, 0x2c, 0xfd, 0x71, 0xd5, - 0x59, 0xfa, 0xfa, 0xb1, 0x76, 0xc5, 0x02, 0x42, 0x63, 0x42, 0xf9, 0x67, 0xd7, 0xde, 0x98, 0x38, - 0xb3, 0x7d, 0x27, 0x26, 0xe1, 0x34, 0x42, 0x54, 0x7e, 0x84, 0xed, 0x15, 0x5f, 0x61, 0xef, 0x7f, - 0xb8, 0xb7, 0xf8, 0x99, 0x34, 0xbc, 0x23, 0x46, 0xca, 0xfe, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, - 0xc4, 0x2b, 0x98, 0x08, 0xb4, 0x09, 0x00, 0x00, + 0x9c, 0xd8, 0x23, 0xe2, 0xc4, 0x81, 0x3f, 0x66, 0x8f, 0x3d, 0x72, 0x32, 0xa8, 0xbd, 0x70, 0xce, + 0x91, 0x13, 0x9a, 0x1f, 0xb6, 0xa7, 0xd9, 0x2e, 0xd5, 0x72, 0x89, 0xe6, 0xbd, 0xf7, 0xbd, 0xef, + 0xcb, 0xbc, 0x79, 0xf3, 0xc6, 0xc0, 0xc1, 0xc3, 0xc0, 0x89, 0xf0, 0x78, 0xc2, 0x82, 0x08, 0xa3, + 0x84, 0x51, 0x87, 0xa1, 0x24, 0x44, 0x59, 0x8c, 0x13, 0xe6, 0xcc, 0xfa, 0x9a, 0x65, 0xa7, 0x19, + 0x61, 0xc4, 0xe8, 0xe0, 0x61, 0x60, 0xeb, 0x09, 0xb6, 0x06, 0x99, 0xf5, 0x77, 0xba, 0x5a, 0x3e, + 0x3b, 0x4f, 0x11, 0x75, 0x66, 0x30, 0xc2, 0x21, 0x64, 0x24, 0x93, 0x0c, 0x3b, 0xbb, 0x2f, 0x20, + 0xc4, 0xaf, 0x8a, 0xb6, 0xd2, 0x8c, 0x90, 0x51, 0x61, 0x75, 0xc6, 0x84, 0x8c, 0x23, 0xe4, 0x08, + 0x6b, 0x38, 0x1d, 0x39, 0xe1, 0x34, 0x83, 0x0c, 0x93, 0x44, 0xc5, 0xcd, 0xc5, 0x38, 0xc3, 0x31, + 0xa2, 0x0c, 0xc6, 0x69, 0x01, 0xe0, 0xfb, 0x0b, 0x48, 0x86, 0x1c, 0xf9, 0x77, 0xf9, 0x9e, 0xe4, + 0x4a, 0x01, 0xde, 0xad, 0x00, 0x24, 0x8e, 0x31, 0x8b, 0x0b, 0x50, 0x69, 0x29, 0xe0, 0xe6, 0x98, + 0x8c, 0x89, 0x58, 0x3a, 0x7c, 0x25, 0xbd, 0xd6, 0xdf, 0x6b, 0xa0, 0x79, 0x28, 0xf8, 0x4e, 0x19, + 0x64, 0xc8, 0xd8, 0x06, 0xf5, 0x60, 0x02, 0x71, 0xe2, 0xe3, 0xb0, 0x5d, 0xeb, 0xd6, 0x7a, 0x0d, + 0x6f, 0x4d, 0xd8, 0xc7, 0xa1, 0x81, 0x40, 0x93, 0x65, 0x53, 0xca, 0xfc, 0x08, 0xcd, 0x50, 0xd4, + 0x5e, 0xee, 0xd6, 0x7a, 0xcd, 0x41, 0xcf, 0xfe, 0xef, 0x7a, 0xda, 0x9f, 0x64, 0x30, 0xe0, 0x1b, + 0x76, 0x77, 0x9e, 0xe7, 0xe6, 0xd2, 0x3c, 0x37, 0x8d, 0x73, 0x18, 0x47, 0x07, 0x96, 0x46, 0x65, + 0x79, 0x40, 0x58, 0x9f, 0x71, 0xc3, 0x18, 0x81, 0x0d, 0x61, 0xe1, 0x64, 0xec, 0xa7, 0x28, 0xc3, + 0x24, 0x6c, 0xaf, 0x08, 0xa9, 0x6d, 0x5b, 0x16, 0xcb, 0x2e, 0x8a, 0x65, 0x3f, 0x52, 0xc5, 0x74, + 0x2d, 0xc5, 0xbd, 0xa5, 0x71, 0x57, 0xf9, 0xd6, 0x2f, 0x7f, 0x9a, 0x35, 0xef, 0x6e, 0xe1, 0x3d, + 0x11, 0x4e, 0x03, 0x83, 0x7b, 0xd3, 0x64, 0x48, 0x92, 0x50, 0x13, 0x5a, 0xbd, 0x4d, 0xe8, 0x6d, + 0x25, 0x74, 0x5f, 0x0a, 0x2d, 0x12, 0x48, 0xa5, 0x8d, 0xd2, 0xad, 0xa4, 0x10, 0xd8, 0x88, 0xe1, + 0x99, 0x1f, 0x44, 0x24, 0x78, 0xea, 0x87, 0x19, 0x1e, 0xb1, 0xf6, 0x6b, 0xaf, 0xb8, 0xa5, 0x85, + 0x7c, 0x29, 0xb4, 0x1e, 0xc3, 0xb3, 0x43, 0xee, 0x7c, 0xc4, 0x7d, 0xc6, 0x37, 0x60, 0x7d, 0x94, + 0x91, 0x1f, 0x50, 0xe2, 0x4f, 0x10, 0x3f, 0x90, 0xf6, 0x1d, 0x21, 0xb2, 0x23, 0x8e, 0x88, 0xb7, + 0x88, 0xad, 0x3a, 0x67, 0xd6, 0xb7, 0x8f, 0x04, 0xc2, 0xdd, 0x55, 0x2a, 0x9b, 0x52, 0xe5, 0x5a, + 0xba, 0xe5, 0xb5, 0xa4, 0x2d, 0xb1, 0x9c, 0x3e, 0x82, 0x0c, 0x51, 0x56, 0xd0, 0xaf, 0xbd, 0x2a, + 0xfd, 0xb5, 0x74, 0xcb, 0x6b, 0x49, 0x5b, 0xd1, 0x1f, 0x83, 0xa6, 0xb8, 0x3a, 0x3e, 0x4d, 0x51, + 0x40, 0xdb, 0xf5, 0xee, 0x4a, 0xaf, 0x39, 0xb8, 0x67, 0xe3, 0x80, 0x0e, 0xf6, 0xed, 0x13, 0x1e, + 0x39, 0x4d, 0x51, 0xe0, 0x6e, 0x55, 0x2d, 0xa4, 0xc1, 0x2d, 0x0f, 0xa4, 0x05, 0x84, 0x1a, 0x07, + 0xa0, 0x35, 0x4d, 0xc7, 0x19, 0x0c, 0x91, 0x9f, 0x42, 0x36, 0x69, 0x37, 0xba, 0x2b, 0xbd, 0x86, + 0x7b, 0x7f, 0x9e, 0x9b, 0x6f, 0xa8, 0x73, 0xd3, 0xa2, 0x96, 0xd7, 0x54, 0xe6, 0x09, 0x64, 0x13, + 0xc3, 0x07, 0xdb, 0x30, 0x8a, 0xc8, 0xf7, 0xfe, 0x34, 0x0d, 0x21, 0x43, 0x3e, 0x1c, 0x31, 0x94, + 0xf9, 0xe8, 0x2c, 0xc5, 0xd9, 0x79, 0x1b, 0x74, 0x6b, 0xbd, 0xba, 0xfb, 0xce, 0x3c, 0x37, 0xbb, + 0x92, 0xe8, 0xa5, 0x50, 0xcb, 0xdb, 0x12, 0xb1, 0x2f, 0x44, 0xe8, 0x63, 0x1e, 0x79, 0x2c, 0x02, + 0xc6, 0x77, 0xc0, 0xbc, 0x21, 0x2b, 0xc6, 0x74, 0x88, 0x26, 0x70, 0x86, 0xc9, 0x34, 0x6b, 0x37, + 0x85, 0xcc, 0x7b, 0xf3, 0xdc, 0x7c, 0xf8, 0x52, 0x19, 0x3d, 0xc1, 0xf2, 0x76, 0x17, 0xc5, 0x9e, + 0x68, 0xe1, 0x83, 0xd5, 0x1f, 0x7f, 0x33, 0x97, 0xac, 0xdf, 0x97, 0xc1, 0xdd, 0x43, 0x92, 0x50, + 0x94, 0xd0, 0x29, 0x95, 0xb7, 0xdd, 0x05, 0x8d, 0x72, 0xe0, 0x88, 0xeb, 0xce, 0x8f, 0x73, 0xb1, + 0x25, 0x3f, 0x2f, 0x10, 0x6e, 0x9d, 0x1f, 0xe7, 0x33, 0xde, 0x79, 0x55, 0x9a, 0xf1, 0x11, 0x58, + 0xcd, 0x08, 0x61, 0x6a, 0x1e, 0x58, 0x5a, 0x37, 0x54, 0x13, 0x68, 0xd6, 0xb7, 0x9f, 0xa0, 0xec, + 0x69, 0x84, 0x3c, 0x42, 0x98, 0xbb, 0xca, 0x69, 0x3c, 0x91, 0x65, 0xfc, 0x54, 0x03, 0x9b, 0x09, + 0x3a, 0x63, 0x7e, 0x39, 0x65, 0xa9, 0x3f, 0x81, 0x74, 0x22, 0xee, 0x7c, 0xcb, 0xfd, 0x6a, 0x9e, + 0x9b, 0x6f, 0xc9, 0x1a, 0xdc, 0x84, 0xb2, 0xfe, 0xc9, 0xcd, 0x0f, 0xc6, 0x98, 0x4d, 0xa6, 0x43, + 0x2e, 0xa7, 0xcf, 0x7e, 0x6d, 0x19, 0xe1, 0x21, 0x75, 0x86, 0xe7, 0x0c, 0x51, 0xfb, 0x08, 0x9d, + 0xb9, 0x7c, 0xe1, 0x19, 0x9c, 0xee, 0xcb, 0x92, 0xed, 0x08, 0xd2, 0x89, 0x2a, 0xd3, 0xcf, 0xcb, + 0xa0, 0xa5, 0x57, 0xcf, 0xd8, 0x07, 0x0d, 0xd9, 0xd8, 0xe5, 0x4c, 0x14, 0x4d, 0x78, 0x4f, 0xfe, + 0xad, 0x32, 0x64, 0xb5, 0x6b, 0x5e, 0x5d, 0x5a, 0xc7, 0xa1, 0x01, 0x41, 0x7d, 0x82, 0x60, 0x88, + 0x32, 0xbf, 0xaf, 0x2a, 0xf3, 0xf0, 0xb6, 0x49, 0x79, 0x24, 0xf0, 0x6e, 0xe7, 0x32, 0x37, 0xd7, + 0xe4, 0xba, 0x3f, 0xcf, 0xcd, 0x0d, 0x29, 0x53, 0x90, 0x59, 0xde, 0x9a, 0x5c, 0xf6, 0x35, 0x89, + 0x81, 0x9a, 0x90, 0xff, 0x43, 0x62, 0xf0, 0x82, 0xc4, 0xa0, 0x94, 0x18, 0xa8, 0x8a, 0xfc, 0xba, + 0x02, 0xee, 0x48, 0xb4, 0x01, 0xc1, 0x3a, 0xc5, 0xe3, 0x04, 0x85, 0xbe, 0x84, 0xa8, 0xa6, 0xe9, + 0xe8, 0x3a, 0xf2, 0x35, 0x3c, 0x15, 0x30, 0x25, 0xb8, 0x7b, 0x91, 0x9b, 0xb5, 0x6a, 0x0e, 0x5c, + 0xa3, 0xb0, 0xbc, 0x16, 0xd5, 0xb0, 0x7c, 0xcc, 0x94, 0xa7, 0xec, 0x53, 0x54, 0x34, 0xd6, 0x0d, + 0x12, 0xe5, 0xf1, 0x9d, 0x22, 0xe6, 0xb6, 0x2b, 0xfa, 0x6b, 0xe9, 0x96, 0xd7, 0x9a, 0x69, 0x38, + 0xe3, 0x5b, 0x20, 0x1f, 0x02, 0xa1, 0x2f, 0xc6, 0xd8, 0xca, 0xad, 0x63, 0xec, 0x81, 0x1a, 0x63, + 0x6f, 0x6a, 0xcf, 0x4b, 0x99, 0x6f, 0x79, 0xeb, 0xca, 0xa1, 0x06, 0x59, 0x04, 0x8c, 0x02, 0x51, + 0xb5, 0xab, 0x7a, 0x5a, 0x6e, 0xdb, 0xc5, 0x83, 0x79, 0x6e, 0x6e, 0x5f, 0x57, 0xa9, 0x38, 0x2c, + 0xef, 0x75, 0xe5, 0xac, 0x1a, 0xd7, 0xfa, 0x14, 0xd4, 0x8b, 0x27, 0xd6, 0xd8, 0x05, 0x8d, 0x64, + 0x1a, 0xa3, 0x8c, 0x47, 0xc4, 0xc9, 0xac, 0x7a, 0x95, 0xc3, 0xe8, 0x82, 0x66, 0x88, 0x12, 0x12, + 0xe3, 0x44, 0xc4, 0x97, 0x45, 0x5c, 0x77, 0xb9, 0xfe, 0xf3, 0xcb, 0x4e, 0xed, 0xe2, 0xb2, 0x53, + 0xfb, 0xeb, 0xb2, 0x53, 0x7b, 0x76, 0xd5, 0x59, 0xba, 0xb8, 0xea, 0x2c, 0xfd, 0x71, 0xd5, 0x59, + 0xfa, 0xfa, 0xb1, 0x76, 0xc9, 0x02, 0x42, 0x63, 0x42, 0xf9, 0x87, 0xd7, 0xde, 0x98, 0x38, 0xb3, + 0x7d, 0x27, 0x26, 0xe1, 0x34, 0x42, 0x54, 0x7e, 0x86, 0xed, 0x15, 0xdf, 0x61, 0xef, 0x7f, 0xb8, + 0xb7, 0xf8, 0xa1, 0x34, 0xbc, 0x23, 0x86, 0xca, 0xfe, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xfb, + 0x37, 0x2a, 0xdd, 0xb6, 0x09, 0x00, 0x00, } func (m *ClientState) Marshal() (dAtA []byte, err error) { diff --git a/proto/ibc/lightclients/tendermint/v1/tendermint.proto b/proto/ibc/lightclients/tendermint/v1/tendermint.proto index 322574fa5bf..0b55a20e433 100644 --- a/proto/ibc/lightclients/tendermint/v1/tendermint.proto +++ b/proto/ibc/lightclients/tendermint/v1/tendermint.proto @@ -80,7 +80,8 @@ message ConsensusState { message Misbehaviour { option (gogoproto.goproto_getters) = false; - string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + // ClientID is deprecated + string client_id = 1 [deprecated = true, (gogoproto.moretags) = "yaml:\"client_id\""]; Header header_1 = 2 [(gogoproto.customname) = "Header1", (gogoproto.moretags) = "yaml:\"header_1\""]; Header header_2 = 3 [(gogoproto.customname) = "Header2", (gogoproto.moretags) = "yaml:\"header_2\""]; } From 2de5f1ded2760e43a1d4aa6c8d16247c2c11fa47 Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Fri, 11 Mar 2022 12:39:28 +0100 Subject: [PATCH 06/71] chore: remove GetClientID from 06-solomachine type Misbehaviour (#1100) * reverting renaming of Misbehaviour * removing client id from 06-solomachine type Misbehaviour * adding changelog --- CHANGELOG.md | 4 +- docs/ibc/proto-docs.md | 2 +- .../06-solomachine/types/misbehaviour.go | 5 - .../06-solomachine/types/misbehaviour_test.go | 1 - .../06-solomachine/types/solomachine.pb.go | 175 +++++++++--------- .../solomachine/v2/solomachine.proto | 10 +- 6 files changed, 98 insertions(+), 99 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32648765517..18f8c23903c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,7 +42,9 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (core) [\#709](https://github.com/cosmos/ibc-go/pull/709) Replace github.com/pkg/errors with stdlib errors ### API Breaking - + +* (06-solomachine) [\#1100](https://github.com/cosmos/ibc-go/pull/1100) Remove `GetClientID` function from 06-solomachine `Misbehaviour` type. +* (06-solomachine) [\#1100](https://github.com/cosmos/ibc-go/pull/1100) Deprecate `ClientId` field in 06-solomachine `Misbehaviour` type. * (07-tendermint) [\#1097](https://github.com/cosmos/ibc-go/pull/1097) Remove `GetClientID` function from 07-tendermint `Misbehaviour` type. * (07-tendermint) [\#1097](https://github.com/cosmos/ibc-go/pull/1097) Deprecate `ClientId` field in 07-tendermint `Misbehaviour` type. * (channel( [\#848](https://github.com/cosmos/ibc-go/pull/848) Added `ChannelId` to MsgChannelOpenInitResponse diff --git a/docs/ibc/proto-docs.md b/docs/ibc/proto-docs.md index b18857d9fbf..cedc9fac02e 100644 --- a/docs/ibc/proto-docs.md +++ b/docs/ibc/proto-docs.md @@ -3724,7 +3724,7 @@ of a sequence and two signatures over different messages at that sequence. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `client_id` | [string](#string) | | | +| `client_id` | [string](#string) | | **Deprecated.** ClientID is deprecated | | `sequence` | [uint64](#uint64) | | | | `signature_one` | [SignatureAndData](#ibc.lightclients.solomachine.v2.SignatureAndData) | | | | `signature_two` | [SignatureAndData](#ibc.lightclients.solomachine.v2.SignatureAndData) | | | diff --git a/modules/light-clients/06-solomachine/types/misbehaviour.go b/modules/light-clients/06-solomachine/types/misbehaviour.go index b673d7e6cd6..31b9b1dc97c 100644 --- a/modules/light-clients/06-solomachine/types/misbehaviour.go +++ b/modules/light-clients/06-solomachine/types/misbehaviour.go @@ -17,11 +17,6 @@ func (misbehaviour Misbehaviour) ClientType() string { return exported.Solomachine } -// GetClientID returns the ID of the client that committed a misbehaviour. -func (misbehaviour Misbehaviour) GetClientID() string { - return misbehaviour.ClientId -} - // Type implements Misbehaviour interface. func (misbehaviour Misbehaviour) Type() string { return exported.TypeClientMisbehaviour diff --git a/modules/light-clients/06-solomachine/types/misbehaviour_test.go b/modules/light-clients/06-solomachine/types/misbehaviour_test.go index 813a8520ee7..d7c41996e19 100644 --- a/modules/light-clients/06-solomachine/types/misbehaviour_test.go +++ b/modules/light-clients/06-solomachine/types/misbehaviour_test.go @@ -10,7 +10,6 @@ func (suite *SoloMachineTestSuite) TestMisbehaviour() { misbehaviour := suite.solomachine.CreateMisbehaviour() suite.Require().Equal(exported.Solomachine, misbehaviour.ClientType()) - suite.Require().Equal(suite.solomachine.ClientID, misbehaviour.GetClientID()) } func (suite *SoloMachineTestSuite) TestMisbehaviourValidateBasic() { diff --git a/modules/light-clients/06-solomachine/types/solomachine.pb.go b/modules/light-clients/06-solomachine/types/solomachine.pb.go index 441a7030402..6eafde55d0c 100644 --- a/modules/light-clients/06-solomachine/types/solomachine.pb.go +++ b/modules/light-clients/06-solomachine/types/solomachine.pb.go @@ -225,7 +225,8 @@ var xxx_messageInfo_Header proto.InternalMessageInfo // Misbehaviour defines misbehaviour for a solo machine which consists // of a sequence and two signatures over different messages at that sequence. type Misbehaviour struct { - ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` + // ClientID is deprecated + ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` // Deprecated: Do not use. Sequence uint64 `protobuf:"varint,2,opt,name=sequence,proto3" json:"sequence,omitempty"` SignatureOne *SignatureAndData `protobuf:"bytes,3,opt,name=signature_one,json=signatureOne,proto3" json:"signature_one,omitempty" yaml:"signature_one"` SignatureTwo *SignatureAndData `protobuf:"bytes,4,opt,name=signature_two,json=signatureTwo,proto3" json:"signature_two,omitempty" yaml:"signature_two"` @@ -823,93 +824,93 @@ func init() { } var fileDescriptor_141333b361aae010 = []byte{ - // 1370 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0x5f, 0x8f, 0xdb, 0x44, - 0x10, 0x3f, 0xa7, 0xe9, 0xf5, 0x32, 0xb9, 0xde, 0x05, 0x37, 0x6d, 0x73, 0x6e, 0x95, 0x18, 0x23, - 0xca, 0x81, 0x68, 0xc2, 0x5d, 0x45, 0x85, 0x2a, 0x04, 0x38, 0x8e, 0x4b, 0xd3, 0xde, 0xf9, 0x82, - 0xe3, 0x03, 0x5a, 0x21, 0x19, 0xc7, 0xd9, 0x4b, 0xac, 0x26, 0xde, 0x34, 0x76, 0x92, 0x06, 0x09, - 0x09, 0xf1, 0x54, 0x22, 0x1e, 0xf8, 0x02, 0x91, 0x10, 0x88, 0xcf, 0xc1, 0x1b, 0xf0, 0xd8, 0x47, - 0x9e, 0x02, 0x6a, 0xbf, 0x41, 0x3e, 0x01, 0xb2, 0x77, 0x13, 0xdb, 0xb9, 0x5e, 0x4e, 0xfc, 0x7b, - 0xdb, 0x9d, 0xdf, 0xcc, 0x6f, 0x66, 0x67, 0xc6, 0xb3, 0x6b, 0xd8, 0xb1, 0x6a, 0x66, 0xa1, 0x65, - 0x35, 0x9a, 0xae, 0xd9, 0xb2, 0x90, 0xed, 0x3a, 0x05, 0x07, 0xb7, 0x70, 0xdb, 0x30, 0x9b, 0x96, - 0x8d, 0x0a, 0xfd, 0xdd, 0xf0, 0x36, 0xdf, 0xe9, 0x62, 0x17, 0xb3, 0x39, 0xab, 0x66, 0xe6, 0xc3, - 0x26, 0xf9, 0xb0, 0x4e, 0x7f, 0x97, 0x7b, 0xcd, 0xe3, 0x34, 0x71, 0x17, 0x15, 0x4c, 0x6c, 0xdb, - 0xc8, 0x74, 0x2d, 0x6c, 0x17, 0xfa, 0x3b, 0xa1, 0x1d, 0x61, 0xe2, 0x5e, 0x0e, 0x14, 0x9b, 0x86, - 0x6d, 0xa3, 0x96, 0xaf, 0x45, 0x96, 0x54, 0x25, 0xdd, 0xc0, 0x0d, 0xec, 0x2f, 0x0b, 0xde, 0x8a, - 0x4a, 0xb7, 0x1a, 0x18, 0x37, 0x5a, 0xa8, 0xe0, 0xef, 0x6a, 0xbd, 0xa3, 0x82, 0x61, 0x0f, 0x09, - 0x24, 0xfc, 0x1c, 0x83, 0xa4, 0xe4, 0xc7, 0x55, 0x75, 0x0d, 0x17, 0xb1, 0x1c, 0xac, 0x39, 0xe8, - 0x51, 0x0f, 0xd9, 0x26, 0xca, 0x30, 0x3c, 0xb3, 0x1d, 0x57, 0xe7, 0x7b, 0x76, 0x07, 0x12, 0x96, - 0xa3, 0x1f, 0x75, 0xf1, 0x17, 0xc8, 0xce, 0xc4, 0x78, 0x66, 0x7b, 0xad, 0x98, 0x9e, 0x4e, 0x72, - 0xa9, 0xa1, 0xd1, 0x6e, 0xdd, 0x12, 0xe6, 0x90, 0xa0, 0xae, 0x59, 0xce, 0x6d, 0x7f, 0xc9, 0xba, - 0xb0, 0x69, 0x62, 0xdb, 0x41, 0xb6, 0xd3, 0x73, 0x74, 0xc7, 0xf3, 0x90, 0x39, 0xc3, 0x33, 0xdb, - 0xc9, 0xdd, 0x42, 0xfe, 0x94, 0xb4, 0xe4, 0xa5, 0x99, 0x9d, 0x1f, 0x58, 0x91, 0x9b, 0x4e, 0x72, - 0x97, 0x88, 0xa7, 0x05, 0x46, 0x41, 0xdd, 0x30, 0x23, 0xba, 0x2c, 0x82, 0x2b, 0x46, 0xab, 0x85, - 0x07, 0x7a, 0xaf, 0x53, 0x37, 0x5c, 0xa4, 0x1b, 0x47, 0x2e, 0xea, 0xea, 0x9d, 0x2e, 0xee, 0x60, - 0xc7, 0x68, 0x65, 0xe2, 0x7e, 0xe8, 0xd7, 0xa6, 0x93, 0x9c, 0x40, 0x08, 0x97, 0x28, 0x0b, 0x6a, - 0xc6, 0x47, 0x0f, 0x7d, 0x50, 0xf4, 0xb0, 0x0a, 0x85, 0x6e, 0xc5, 0x9f, 0x7c, 0x9f, 0x5b, 0x11, - 0x7e, 0x60, 0x60, 0x23, 0x1a, 0x2b, 0x7b, 0x17, 0xa0, 0xd3, 0xab, 0xb5, 0x2c, 0x53, 0x7f, 0x88, - 0x86, 0x7e, 0x1a, 0x93, 0xbb, 0xe9, 0x3c, 0x29, 0x42, 0x7e, 0x56, 0x84, 0xbc, 0x68, 0x0f, 0x8b, + // 1373 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0x5f, 0x6f, 0xdb, 0x54, + 0x14, 0xaf, 0xb3, 0xac, 0x6b, 0x4e, 0xba, 0x36, 0x78, 0xd9, 0x96, 0x7a, 0x53, 0x62, 0x8c, 0x18, + 0x05, 0xb1, 0x84, 0x76, 0x62, 0x42, 0x13, 0x02, 0x1c, 0xc7, 0x63, 0xd9, 0x5a, 0x37, 0x38, 0x2e, + 0xb0, 0x09, 0xc9, 0x38, 0xce, 0x6d, 0x62, 0x2d, 0xf1, 0xcd, 0x62, 0x27, 0x59, 0x90, 0x90, 0x10, + 0x4f, 0x23, 0xe2, 0x81, 0x2f, 0x10, 0x09, 0x81, 0xf8, 0x1c, 0xbc, 0x21, 0x78, 0xdb, 0x23, 0x4f, + 0x01, 0x6d, 0xdf, 0x20, 0x9f, 0x00, 0xd9, 0xf7, 0x26, 0xb6, 0xd3, 0x35, 0x15, 0xff, 0xde, 0xee, + 0x3d, 0xbf, 0x73, 0x7e, 0xe7, 0xcf, 0x3d, 0x3e, 0xf7, 0x1a, 0x76, 0xac, 0x9a, 0x59, 0x68, 0x59, + 0x8d, 0xa6, 0x6b, 0xb6, 0x2c, 0x64, 0xbb, 0x4e, 0xc1, 0xc1, 0x2d, 0xdc, 0x36, 0xcc, 0xa6, 0x65, + 0xa3, 0x42, 0x7f, 0x37, 0xbc, 0xcd, 0x77, 0xba, 0xd8, 0xc5, 0x6c, 0xce, 0xaa, 0x99, 0xf9, 0xb0, + 0x49, 0x3e, 0xac, 0xd3, 0xdf, 0xe5, 0x5e, 0xf3, 0x38, 0x4d, 0xdc, 0x45, 0x05, 0x13, 0xdb, 0x36, + 0x32, 0x5d, 0x0b, 0xdb, 0x85, 0xfe, 0x4e, 0x68, 0x47, 0x98, 0xb8, 0x97, 0x03, 0xc5, 0xa6, 0x61, + 0xdb, 0xa8, 0xe5, 0x6b, 0x91, 0x25, 0x55, 0x49, 0x37, 0x70, 0x03, 0xfb, 0xcb, 0x82, 0xb7, 0xa2, + 0xd2, 0xad, 0x06, 0xc6, 0x8d, 0x16, 0x2a, 0xf8, 0xbb, 0x5a, 0xef, 0xa8, 0x60, 0xd8, 0x43, 0x02, + 0x09, 0x3f, 0xc7, 0x20, 0x29, 0xf9, 0x71, 0x55, 0x5d, 0xc3, 0x45, 0x2c, 0x07, 0x6b, 0x0e, 0x7a, + 0xd4, 0x43, 0xb6, 0x89, 0x32, 0x0c, 0xcf, 0x6c, 0xc7, 0xd5, 0xf9, 0x9e, 0xdd, 0x81, 0x84, 0xe5, + 0xe8, 0x47, 0x5d, 0xfc, 0x05, 0xb2, 0x33, 0x31, 0x9e, 0xd9, 0x5e, 0x2b, 0xa6, 0xa7, 0x93, 0x5c, + 0x6a, 0x68, 0xb4, 0x5b, 0xb7, 0x84, 0x39, 0x24, 0xa8, 0x6b, 0x96, 0x73, 0xdb, 0x5f, 0xb2, 0x2e, + 0x6c, 0x9a, 0xd8, 0x76, 0x90, 0xed, 0xf4, 0x1c, 0xdd, 0xf1, 0x3c, 0x64, 0xce, 0xf0, 0xcc, 0x76, + 0x72, 0xb7, 0x90, 0x3f, 0xa5, 0x2c, 0x79, 0x69, 0x66, 0xe7, 0x07, 0x56, 0xe4, 0xa6, 0x93, 0xdc, + 0x25, 0xe2, 0x69, 0x81, 0x51, 0x50, 0x37, 0xcc, 0x88, 0x2e, 0x8b, 0xe0, 0x8a, 0xd1, 0x6a, 0xe1, + 0x81, 0xde, 0xeb, 0xd4, 0x0d, 0x17, 0xe9, 0xc6, 0x91, 0x8b, 0xba, 0x7a, 0xa7, 0x8b, 0x3b, 0xd8, + 0x31, 0x5a, 0x99, 0xb8, 0x1f, 0xfa, 0xb5, 0xe9, 0x24, 0x27, 0x10, 0xc2, 0x25, 0xca, 0x82, 0x9a, + 0xf1, 0xd1, 0x43, 0x1f, 0x14, 0x3d, 0xac, 0x42, 0xa1, 0x5b, 0xf1, 0x27, 0xdf, 0xe7, 0x56, 0x84, + 0x1f, 0x18, 0xd8, 0x88, 0xc6, 0xca, 0xde, 0x05, 0xe8, 0xf4, 0x6a, 0x2d, 0xcb, 0xd4, 0x1f, 0xa2, + 0xa1, 0x5f, 0xc6, 0xe4, 0x6e, 0x3a, 0x4f, 0x0e, 0x21, 0x3f, 0x3b, 0x84, 0xbc, 0x68, 0x0f, 0x8b, 0x17, 0xa7, 0x93, 0xdc, 0x4b, 0x24, 0x88, 0xc0, 0x42, 0x50, 0x13, 0x64, 0x73, 0x0f, 0x0d, 0x59, - 0x1e, 0x92, 0x75, 0xab, 0x8f, 0xba, 0x8e, 0x75, 0x64, 0xa1, 0xae, 0x9f, 0xf6, 0x84, 0x1a, 0x16, - 0xb1, 0x57, 0x21, 0xe1, 0x5a, 0x6d, 0xe4, 0xb8, 0x46, 0xbb, 0xe3, 0x67, 0x37, 0xae, 0x06, 0x02, - 0x1a, 0xe4, 0xd7, 0x31, 0x58, 0xbd, 0x83, 0x8c, 0x3a, 0xea, 0x2e, 0xad, 0x70, 0x84, 0x2a, 0xb6, - 0x40, 0xe5, 0xa1, 0x8e, 0xd5, 0xb0, 0x0d, 0xb7, 0xd7, 0x25, 0x65, 0x5c, 0x57, 0x03, 0x01, 0x7b, - 0x08, 0x1b, 0x36, 0x1a, 0xe8, 0xa1, 0x83, 0xc7, 0x97, 0x1c, 0x7c, 0x6b, 0x3a, 0xc9, 0x5d, 0x24, - 0x07, 0x8f, 0x5a, 0x09, 0xea, 0xba, 0x8d, 0x06, 0x95, 0xf9, 0xf9, 0x25, 0xd8, 0xf4, 0x14, 0xc2, - 0x39, 0x38, 0xeb, 0xe5, 0x20, 0xdc, 0x10, 0x0b, 0x0a, 0x82, 0xea, 0x45, 0x52, 0x0a, 0x04, 0x34, - 0x09, 0xbf, 0xc6, 0x60, 0x7d, 0xdf, 0x72, 0x6a, 0xa8, 0x69, 0xf4, 0x2d, 0xdc, 0xeb, 0x7a, 0x0d, - 0x4d, 0x9a, 0x4f, 0xb7, 0xea, 0x7e, 0x2e, 0x12, 0xe1, 0x86, 0x9e, 0x43, 0x82, 0xba, 0x46, 0xd6, - 0xe5, 0x7a, 0x24, 0x7b, 0xb1, 0x85, 0xec, 0x75, 0xe0, 0xfc, 0x3c, 0x1d, 0x3a, 0xb6, 0x67, 0xad, - 0xbe, 0x73, 0x6a, 0xab, 0x57, 0x67, 0x56, 0xa2, 0x5d, 0x2f, 0x19, 0xae, 0x51, 0xcc, 0x4c, 0x27, - 0xb9, 0x34, 0x89, 0x22, 0xc2, 0x28, 0xa8, 0xeb, 0xf3, 0xfd, 0x81, 0xbd, 0xe0, 0xd1, 0x1d, 0x60, - 0x9a, 0xf2, 0xff, 0xca, 0xa3, 0x3b, 0xc0, 0x61, 0x8f, 0xda, 0x00, 0xd3, 0x4c, 0xfe, 0xc2, 0x40, - 0x6a, 0x91, 0x22, 0xda, 0x1e, 0xcc, 0x62, 0x7b, 0x7c, 0x06, 0x89, 0xba, 0xe1, 0x1a, 0xba, 0x3b, - 0xec, 0x90, 0xcc, 0x6d, 0xec, 0xbe, 0x7e, 0x6a, 0x98, 0x1e, 0xaf, 0x36, 0xec, 0xa0, 0x70, 0x59, - 0xe6, 0x2c, 0x82, 0xba, 0x56, 0xa7, 0x38, 0xcb, 0x42, 0xdc, 0x5b, 0xd3, 0xae, 0xf4, 0xd7, 0xd1, - 0x66, 0x8e, 0xbf, 0xf8, 0xbb, 0xf8, 0x8a, 0x81, 0x8c, 0x36, 0x93, 0xa1, 0xfa, 0xfc, 0x4c, 0xfe, - 0x81, 0x3e, 0x80, 0x8d, 0x20, 0x17, 0x3e, 0xbd, 0x7f, 0xaa, 0x70, 0xef, 0x46, 0x71, 0x41, 0x0d, - 0xca, 0x51, 0x3a, 0x16, 0x42, 0xec, 0xc5, 0x21, 0xfc, 0xc1, 0x40, 0xc2, 0xf3, 0x5b, 0x1c, 0xba, - 0xc8, 0xf9, 0x17, 0x5f, 0xe7, 0xc2, 0xa0, 0x38, 0x73, 0x7c, 0x50, 0x44, 0x4a, 0x10, 0xff, 0xbf, - 0x4a, 0x70, 0x36, 0x28, 0x01, 0x3d, 0xe1, 0x4f, 0x0c, 0x00, 0x19, 0x3e, 0x7e, 0x52, 0xf6, 0x20, - 0x49, 0x3f, 0xf9, 0x53, 0xc7, 0xe3, 0xa5, 0xe9, 0x24, 0xc7, 0x46, 0xa6, 0x04, 0x9d, 0x8f, 0x64, - 0x44, 0x9c, 0x30, 0x1f, 0x62, 0xff, 0x70, 0x3e, 0x7c, 0x09, 0x9b, 0xa1, 0xab, 0xd0, 0x8f, 0x95, - 0x85, 0x78, 0xc7, 0x70, 0x9b, 0xb4, 0x9d, 0xfd, 0x35, 0x5b, 0x81, 0x75, 0x3a, 0x1a, 0xc8, 0x85, - 0x16, 0x5b, 0x72, 0x80, 0xcb, 0xd3, 0x49, 0xee, 0x42, 0x64, 0x9c, 0xd0, 0x2b, 0x2b, 0x69, 0x06, - 0x9e, 0xa8, 0xfb, 0x6f, 0x18, 0x60, 0xa3, 0x17, 0xc9, 0x89, 0x21, 0xdc, 0x3f, 0x7e, 0xad, 0x2e, - 0x8b, 0xe2, 0x6f, 0xdc, 0x9d, 0x34, 0x96, 0x3e, 0x5c, 0x90, 0xe6, 0xcf, 0x8f, 0xe5, 0xb1, 0xc8, - 0x00, 0xc1, 0x4b, 0x85, 0x86, 0xf1, 0xaa, 0xdf, 0x56, 0xde, 0x53, 0x25, 0x1f, 0x7a, 0xc5, 0xf4, - 0x77, 0xf2, 0x01, 0xa9, 0x6c, 0xd7, 0xd5, 0x90, 0x21, 0xf5, 0x5b, 0x87, 0x94, 0x44, 0x1e, 0x34, - 0xcb, 0x9d, 0xde, 0x84, 0x73, 0xf4, 0xe1, 0x43, 0x3d, 0x5e, 0x0d, 0x79, 0xa4, 0x2f, 0x22, 0xcf, - 0x1d, 0x59, 0xaa, 0x33, 0x65, 0xea, 0xe5, 0x2e, 0xa4, 0x2b, 0x86, 0xf9, 0x10, 0xb9, 0x12, 0x6e, - 0xb7, 0x2d, 0xb7, 0x8d, 0x6c, 0xf7, 0x44, 0x4f, 0x59, 0xef, 0x78, 0x33, 0x2d, 0xdf, 0xd9, 0xba, - 0x1a, 0x92, 0x08, 0xf7, 0x61, 0x8b, 0x70, 0x89, 0xe6, 0x43, 0x1b, 0x0f, 0x5a, 0xa8, 0xde, 0x40, - 0x4b, 0x09, 0xb7, 0x61, 0xd3, 0x88, 0xaa, 0x52, 0xd6, 0x45, 0xb1, 0x90, 0x87, 0x0c, 0xa1, 0x56, - 0x91, 0x89, 0xac, 0x8e, 0x2b, 0xd6, 0x1c, 0x6f, 0x0e, 0x9c, 0xc4, 0x2c, 0x34, 0x21, 0xad, 0xa0, - 0xc7, 0x6e, 0x95, 0xce, 0x0b, 0x15, 0x99, 0xfd, 0x13, 0xa3, 0x78, 0x17, 0xce, 0xdb, 0xe8, 0xb1, - 0xab, 0x3b, 0xe8, 0x91, 0xde, 0x45, 0x66, 0x9f, 0xcc, 0x93, 0xf0, 0x35, 0x10, 0x81, 0x05, 0x35, - 0x69, 0x13, 0x6a, 0x8f, 0xf5, 0x8d, 0x6f, 0xe3, 0xb0, 0x36, 0x1b, 0x0c, 0xec, 0x3b, 0xf0, 0x4a, - 0x49, 0xd4, 0x44, 0x5d, 0xbb, 0x5f, 0x91, 0xf5, 0x43, 0xa5, 0xac, 0x94, 0xb5, 0xb2, 0xb8, 0x57, - 0x7e, 0x20, 0x97, 0xf4, 0x43, 0xa5, 0x5a, 0x91, 0xa5, 0xf2, 0xed, 0xb2, 0x5c, 0x4a, 0xad, 0x70, - 0x9b, 0xa3, 0x31, 0x9f, 0x0c, 0x89, 0xd8, 0x6b, 0x70, 0x29, 0xb0, 0x94, 0xf6, 0xca, 0xb2, 0xa2, - 0xe9, 0x55, 0x4d, 0xd4, 0xe4, 0x14, 0xc3, 0xc1, 0x68, 0xcc, 0xaf, 0x12, 0x19, 0xfb, 0x26, 0x6c, - 0x85, 0xf4, 0x0e, 0x94, 0xaa, 0xac, 0x54, 0x0f, 0xab, 0x54, 0x35, 0xc6, 0x9d, 0x1f, 0x8d, 0xf9, - 0xc4, 0x5c, 0xcc, 0xe6, 0x81, 0x8b, 0x68, 0x2b, 0xb2, 0xa4, 0x95, 0x0f, 0x14, 0xaa, 0x7e, 0x86, - 0xdb, 0x18, 0x8d, 0x79, 0x08, 0xe4, 0xec, 0x36, 0x5c, 0x0e, 0xe9, 0xdf, 0x11, 0x15, 0x45, 0xde, - 0xa3, 0xca, 0x71, 0x2e, 0x39, 0x1a, 0xf3, 0xe7, 0xa8, 0x90, 0x7d, 0x1b, 0xae, 0x04, 0x9a, 0x15, - 0x51, 0xba, 0x27, 0x6b, 0xba, 0x74, 0xb0, 0xbf, 0x5f, 0xd6, 0xf6, 0x65, 0x45, 0x4b, 0x9d, 0xe5, - 0xd2, 0xa3, 0x31, 0x9f, 0x22, 0x40, 0x20, 0x67, 0xdf, 0x07, 0xfe, 0x98, 0x99, 0x28, 0xdd, 0x53, - 0x0e, 0x3e, 0xd9, 0x93, 0x4b, 0x1f, 0xca, 0xbe, 0xed, 0x2a, 0xb7, 0x35, 0x1a, 0xf3, 0x17, 0x09, - 0xba, 0x00, 0xb2, 0xef, 0xbd, 0x80, 0x40, 0x95, 0x25, 0xb9, 0x5c, 0xd1, 0x74, 0xb1, 0x58, 0x95, - 0x15, 0x49, 0x4e, 0x9d, 0xe3, 0x32, 0xa3, 0x31, 0x9f, 0x26, 0x28, 0x05, 0x29, 0xc6, 0xde, 0x84, - 0xab, 0x81, 0xbd, 0x22, 0x7f, 0xaa, 0xe9, 0x55, 0xf9, 0xa3, 0x43, 0x0f, 0xf2, 0x68, 0x3e, 0x4e, - 0xad, 0x91, 0xc0, 0x3d, 0x64, 0x06, 0x78, 0x72, 0x96, 0x87, 0x54, 0x60, 0x77, 0x47, 0x16, 0x4b, - 0xb2, 0x9a, 0x4a, 0x90, 0xca, 0x90, 0x1d, 0x17, 0x7f, 0xf2, 0x63, 0x76, 0xa5, 0xf8, 0xf9, 0x6f, - 0xcf, 0xb2, 0xcc, 0xd3, 0x67, 0x59, 0xe6, 0xcf, 0x67, 0x59, 0xe6, 0xbb, 0xe7, 0xd9, 0x95, 0xa7, - 0xcf, 0xb3, 0x2b, 0xbf, 0x3f, 0xcf, 0xae, 0x3c, 0xb8, 0xdd, 0xb0, 0xdc, 0x66, 0xaf, 0x96, 0x37, - 0x71, 0xbb, 0x60, 0x62, 0xa7, 0x8d, 0x9d, 0x82, 0x55, 0x33, 0xaf, 0x37, 0x70, 0xa1, 0x7f, 0xa3, - 0xd0, 0xc6, 0xf5, 0x5e, 0x0b, 0x39, 0xe4, 0x7f, 0xea, 0xfa, 0xec, 0x87, 0xea, 0xad, 0x9b, 0xd7, - 0xc3, 0xff, 0x54, 0xde, 0x35, 0xe3, 0xd4, 0x56, 0xfd, 0x79, 0x76, 0xe3, 0xaf, 0x00, 0x00, 0x00, - 0xff, 0xff, 0x5d, 0xd4, 0x6c, 0xfb, 0x80, 0x0d, 0x00, 0x00, + 0x1e, 0x92, 0x75, 0xab, 0x8f, 0xba, 0x8e, 0x75, 0x64, 0xa1, 0xae, 0x5f, 0xf6, 0x84, 0x1a, 0x16, + 0xb1, 0x57, 0x21, 0xe1, 0x5a, 0x6d, 0xe4, 0xb8, 0x46, 0xbb, 0xe3, 0x57, 0x37, 0xae, 0x06, 0x02, + 0x1a, 0xe4, 0xd7, 0x31, 0x58, 0xbd, 0x83, 0x8c, 0x3a, 0xea, 0x2e, 0x3d, 0xe1, 0x08, 0x55, 0x6c, + 0x81, 0xca, 0x43, 0x1d, 0xab, 0x61, 0x1b, 0x6e, 0xaf, 0x4b, 0x8e, 0x71, 0x5d, 0x0d, 0x04, 0xec, + 0x21, 0x6c, 0xd8, 0x68, 0xa0, 0x87, 0x12, 0x8f, 0x2f, 0x49, 0x7c, 0x6b, 0x3a, 0xc9, 0x5d, 0x24, + 0x89, 0x47, 0xad, 0x04, 0x75, 0xdd, 0x46, 0x83, 0xca, 0x3c, 0x7f, 0x09, 0x36, 0x3d, 0x85, 0x70, + 0x0d, 0xce, 0x7a, 0x35, 0x08, 0x37, 0xc4, 0x82, 0x82, 0xa0, 0x7a, 0x91, 0x94, 0x02, 0x01, 0x2d, + 0xc2, 0x6f, 0x31, 0x58, 0xdf, 0xb7, 0x9c, 0x1a, 0x6a, 0x1a, 0x7d, 0x0b, 0xf7, 0xba, 0xec, 0x0d, + 0x48, 0x90, 0xe6, 0xd3, 0xad, 0xba, 0x5f, 0x8b, 0x44, 0xf1, 0x52, 0xd0, 0xd0, 0x73, 0x48, 0xc8, + 0x30, 0xea, 0x1a, 0xd9, 0x95, 0xeb, 0x91, 0xfa, 0xc5, 0x16, 0xea, 0xd7, 0x81, 0xf3, 0xf3, 0x82, + 0xe8, 0xd8, 0x9e, 0x35, 0xfb, 0xce, 0xa9, 0xcd, 0x5e, 0x9d, 0x59, 0x89, 0x76, 0xbd, 0x64, 0xb8, + 0x46, 0x31, 0x33, 0x9d, 0xe4, 0xd2, 0x24, 0x8e, 0x08, 0xa3, 0xa0, 0xae, 0xcf, 0xf7, 0x07, 0xf6, + 0x82, 0x47, 0x77, 0x80, 0x69, 0xd1, 0xff, 0x2b, 0x8f, 0xee, 0x00, 0x87, 0x3d, 0x6a, 0x03, 0x4c, + 0x6b, 0xf9, 0x0b, 0x03, 0xa9, 0x45, 0x8a, 0x68, 0x83, 0x30, 0x8b, 0x0d, 0xf2, 0x19, 0x24, 0xea, + 0x86, 0x6b, 0xe8, 0xee, 0xb0, 0x43, 0x2a, 0xb7, 0xb1, 0xfb, 0xfa, 0xa9, 0x61, 0x7a, 0xbc, 0xda, + 0xb0, 0x83, 0xc2, 0x93, 0x66, 0xce, 0x22, 0xa8, 0x6b, 0x75, 0x8a, 0xb3, 0x2c, 0xc4, 0xbd, 0x35, + 0xed, 0x4b, 0x7f, 0x1d, 0x6d, 0xe7, 0xf8, 0x8b, 0xbf, 0x8c, 0xaf, 0x18, 0xc8, 0x68, 0x33, 0x19, + 0xaa, 0xcf, 0x73, 0xf2, 0x13, 0xfa, 0x00, 0x36, 0x82, 0x5a, 0xf8, 0xf4, 0x7e, 0x56, 0xe1, 0xee, + 0x8d, 0xe2, 0x82, 0x1a, 0x1c, 0x47, 0xe9, 0x58, 0x08, 0xb1, 0x17, 0x87, 0xf0, 0x07, 0x03, 0x09, + 0xcf, 0x6f, 0x71, 0xe8, 0x22, 0xe7, 0x5f, 0x7c, 0x9f, 0x0b, 0xa3, 0xe2, 0xcc, 0xf1, 0x51, 0x11, + 0x39, 0x82, 0xf8, 0xff, 0x75, 0x04, 0x67, 0x83, 0x23, 0xa0, 0x19, 0xfe, 0xc4, 0x00, 0x90, 0xf1, + 0xe3, 0x17, 0x65, 0x0f, 0x92, 0xf4, 0xa3, 0x3f, 0x75, 0x40, 0x7a, 0xdf, 0x23, 0x1b, 0x99, 0x13, + 0x74, 0x42, 0x92, 0x21, 0x71, 0xc2, 0x84, 0x88, 0xfd, 0xc3, 0x09, 0xf1, 0x25, 0x6c, 0x86, 0x2e, + 0x43, 0x3f, 0x56, 0x16, 0xe2, 0x1d, 0xc3, 0x6d, 0xd2, 0x76, 0xf6, 0xd7, 0x6c, 0x05, 0xd6, 0xe9, + 0x70, 0x20, 0x57, 0x5a, 0x6c, 0x49, 0x02, 0x97, 0xa7, 0x93, 0xdc, 0x85, 0xc8, 0x40, 0xa1, 0x97, + 0x56, 0xd2, 0x0c, 0x3c, 0x51, 0xf7, 0xdf, 0x30, 0xc0, 0x46, 0xaf, 0x92, 0x13, 0x43, 0xb8, 0x7f, + 0xfc, 0x62, 0x5d, 0x16, 0xc5, 0xdf, 0xb8, 0x3d, 0x69, 0x2c, 0x7d, 0xb8, 0x20, 0xcd, 0x1f, 0x20, + 0xcb, 0x63, 0x91, 0x01, 0x82, 0xb7, 0x0a, 0x0d, 0xe3, 0x55, 0xbf, 0xad, 0xbc, 0xc7, 0x4a, 0x3e, + 0xf4, 0x8e, 0xe9, 0xef, 0xe4, 0x03, 0x52, 0xd9, 0xae, 0xab, 0x21, 0x43, 0xea, 0xb7, 0x0e, 0x29, + 0x89, 0x3c, 0x69, 0x96, 0x3b, 0xbd, 0x09, 0xe7, 0xe8, 0xd3, 0x87, 0x7a, 0xbc, 0x1a, 0xf2, 0x48, + 0xdf, 0x44, 0x9e, 0x3b, 0xb2, 0x54, 0x67, 0xca, 0xd4, 0xcb, 0x5d, 0x48, 0x57, 0x0c, 0xf3, 0x21, + 0x72, 0x25, 0xdc, 0x6e, 0x5b, 0x6e, 0x1b, 0xd9, 0xee, 0x89, 0x9e, 0xb2, 0x5e, 0x7a, 0x33, 0x2d, + 0xdf, 0xd9, 0xba, 0x1a, 0x92, 0x08, 0xf7, 0x61, 0x8b, 0x70, 0x89, 0xe6, 0x43, 0x1b, 0x0f, 0x5a, + 0xa8, 0xde, 0x40, 0x4b, 0x09, 0xb7, 0x61, 0xd3, 0x88, 0xaa, 0x52, 0xd6, 0x45, 0xb1, 0x90, 0x87, + 0x0c, 0xa1, 0x56, 0x91, 0x89, 0xac, 0x8e, 0x2b, 0xd6, 0x1c, 0x6f, 0x0e, 0x9c, 0xc4, 0x2c, 0x34, + 0x21, 0xad, 0xa0, 0xc7, 0x6e, 0x95, 0xce, 0x0b, 0x15, 0x99, 0xfd, 0x13, 0xa3, 0x78, 0x17, 0xce, + 0xdb, 0xe8, 0xb1, 0xab, 0x3b, 0xe8, 0x91, 0xde, 0x45, 0x66, 0x9f, 0xcc, 0x93, 0xf0, 0x35, 0x10, + 0x81, 0x05, 0x35, 0x69, 0x13, 0x6a, 0x8f, 0xf5, 0x8d, 0x6f, 0xe3, 0xb0, 0x36, 0x1b, 0x0c, 0xec, + 0x3b, 0xf0, 0x4a, 0x49, 0xd4, 0x44, 0x5d, 0xbb, 0x5f, 0x91, 0xf5, 0x43, 0xa5, 0xac, 0x94, 0xb5, + 0xb2, 0xb8, 0x57, 0x7e, 0x20, 0x97, 0xf4, 0x43, 0xa5, 0x5a, 0x91, 0xa5, 0xf2, 0xed, 0xb2, 0x5c, + 0x4a, 0xad, 0x70, 0x9b, 0xa3, 0x31, 0x9f, 0x0c, 0x89, 0xd8, 0x6b, 0x70, 0x29, 0xb0, 0x94, 0xf6, + 0xca, 0xb2, 0xa2, 0xe9, 0x55, 0x4d, 0xd4, 0xe4, 0x14, 0xc3, 0xc1, 0x68, 0xcc, 0xaf, 0x12, 0x19, + 0xfb, 0x26, 0x6c, 0x85, 0xf4, 0x0e, 0x94, 0xaa, 0xac, 0x54, 0x0f, 0xab, 0x54, 0x35, 0xc6, 0x9d, + 0x1f, 0x8d, 0xf9, 0xc4, 0x5c, 0xcc, 0xe6, 0x81, 0x8b, 0x68, 0x2b, 0xb2, 0xa4, 0x95, 0x0f, 0x14, + 0xaa, 0x7e, 0x86, 0xdb, 0x18, 0x8d, 0x79, 0x08, 0xe4, 0xec, 0x36, 0x5c, 0x0e, 0xe9, 0xdf, 0x11, + 0x15, 0x45, 0xde, 0xa3, 0xca, 0x71, 0x2e, 0x39, 0x1a, 0xf3, 0xe7, 0xa8, 0x90, 0x7d, 0x1b, 0xae, + 0x04, 0x9a, 0x15, 0x51, 0xba, 0x27, 0x6b, 0xba, 0x74, 0xb0, 0xbf, 0x5f, 0xd6, 0xf6, 0x65, 0x45, + 0x4b, 0x9d, 0xe5, 0xd2, 0xa3, 0x31, 0x9f, 0x22, 0x40, 0x20, 0x67, 0xdf, 0x07, 0xfe, 0x98, 0x99, + 0x28, 0xdd, 0x53, 0x0e, 0x3e, 0xd9, 0x93, 0x4b, 0x1f, 0xca, 0xbe, 0xed, 0x2a, 0xb7, 0x35, 0x1a, + 0xf3, 0x17, 0x09, 0xba, 0x00, 0xb2, 0xef, 0xbd, 0x80, 0x40, 0x95, 0x25, 0xb9, 0x5c, 0xd1, 0x74, + 0xb1, 0x58, 0x95, 0x15, 0x49, 0x4e, 0x9d, 0xe3, 0x32, 0xa3, 0x31, 0x9f, 0x26, 0x28, 0x05, 0x29, + 0xc6, 0xde, 0x84, 0xab, 0x81, 0xbd, 0x22, 0x7f, 0xaa, 0xe9, 0x55, 0xf9, 0xa3, 0x43, 0x0f, 0xf2, + 0x68, 0x3e, 0x4e, 0xad, 0x91, 0xc0, 0x3d, 0x64, 0x06, 0x78, 0x72, 0x96, 0x87, 0x54, 0x60, 0x77, + 0x47, 0x16, 0x4b, 0xb2, 0x9a, 0x4a, 0x90, 0x93, 0x21, 0x3b, 0x2e, 0xfe, 0xe4, 0xc7, 0xec, 0x4a, + 0xf1, 0xf3, 0x5f, 0x9f, 0x65, 0x99, 0xa7, 0xcf, 0xb2, 0xcc, 0x9f, 0xcf, 0xb2, 0xcc, 0x77, 0xcf, + 0xb3, 0x2b, 0x4f, 0x9f, 0x67, 0x57, 0x7e, 0x7f, 0x9e, 0x5d, 0x79, 0x70, 0xbb, 0x61, 0xb9, 0xcd, + 0x5e, 0x2d, 0x6f, 0xe2, 0x76, 0xc1, 0xc4, 0x4e, 0x1b, 0x3b, 0x05, 0xab, 0x66, 0x5e, 0x6f, 0xe0, + 0x42, 0xff, 0x46, 0xa1, 0x8d, 0xeb, 0xbd, 0x16, 0x72, 0xc8, 0x1f, 0xd5, 0xf5, 0xd9, 0x2f, 0xd5, + 0x5b, 0x37, 0xaf, 0x87, 0xff, 0xaa, 0xbc, 0x6b, 0xc6, 0xa9, 0xad, 0xfa, 0xf3, 0xec, 0xc6, 0x5f, + 0x01, 0x00, 0x00, 0xff, 0xff, 0xe7, 0x08, 0x62, 0xbe, 0x82, 0x0d, 0x00, 0x00, } func (m *ClientState) Marshal() (dAtA []byte, err error) { diff --git a/proto/ibc/lightclients/solomachine/v2/solomachine.proto b/proto/ibc/lightclients/solomachine/v2/solomachine.proto index e626c18ac66..f4f36910eb2 100644 --- a/proto/ibc/lightclients/solomachine/v2/solomachine.proto +++ b/proto/ibc/lightclients/solomachine/v2/solomachine.proto @@ -52,10 +52,12 @@ message Header { // of a sequence and two signatures over different messages at that sequence. message Misbehaviour { option (gogoproto.goproto_getters) = false; - string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; - uint64 sequence = 2; - SignatureAndData signature_one = 3 [(gogoproto.moretags) = "yaml:\"signature_one\""]; - SignatureAndData signature_two = 4 [(gogoproto.moretags) = "yaml:\"signature_two\""]; + + // ClientID is deprecated + string client_id = 1 [deprecated = true, (gogoproto.moretags) = "yaml:\"client_id\""]; + uint64 sequence = 2; + SignatureAndData signature_one = 3 [(gogoproto.moretags) = "yaml:\"signature_one\""]; + SignatureAndData signature_two = 4 [(gogoproto.moretags) = "yaml:\"signature_two\""]; } // SignatureAndData contains a signature and the data signed over to create that From 12f4ed28b3f4208ac9946d1e564d274aebfe858a Mon Sep 17 00:00:00 2001 From: Sean King Date: Fri, 11 Mar 2022 15:54:12 +0100 Subject: [PATCH 07/71] client-02 update to latest from main (#1106) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: allow the mock module to be used multiple times as base ibc application in middleware stack (#892) ## Description Currently the `AppModule` assumes a single scoped keeper. This doesn't allow the mock module to be used as a base application for different middleware stack (ica stack, fee stack, etc) I broke the API because I think it is cleaner. If we want this to be non API breaking, I can try to readjust ref: #891 --- 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. - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * feat: adding Pack/Unpack acknowledgement helper fns (#895) * feat: adding Pack/Unpack acknowledgement helper fns * chore: changelog * fix: docs * Update modules/core/04-channel/types/codec.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * imp: support custom keys for testing (#893) * chore: add ParsePacketFromEvents testing helper function (#904) ## Description ref: #891 --- 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. - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * fix: correctly claim capability for mock module, handle genesis exports (#921) ## Description This contains two fixes: - the capability being claimed by the scoped keeper was incorrect (mock.ModuleName -> port ID) - the mock module wasn't accounting for non empty genesis state in capabilities (after genesis export, capability will create the bound ports so rebinding doesn't need to happen) closes: #XXXX --- 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. - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * docs: update migration docs for upgrade proposal in relation to ICS27 (#920) ## Description closes: #XXXX --- 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. - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * chore(ica): add trail of bits audit report (#903) * chore(ica): add trail of bits audit report * relocate the audit report for ICA Co-authored-by: Carlos Rodriguez * testing: adding multiple sender accounts for testing purposes (#935) * testing: adding multiple sender accounts for testing puproses * fix genesis setup (#936) * Update testing/chain.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * refactor: code hygiene * Update testing/chain.go Co-authored-by: Aditya * fix: setting totalySupply to empty * nit: CamelCase not UPPERCASE Co-authored-by: Aditya Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * Create test chain with multiple validators (#942) * testing: adding multiple sender accounts for testing puproses * fix genesis setup (#936) * Update testing/chain.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * refactor: code hygiene * Update testing/chain.go Co-authored-by: Aditya * multi validator commit taken from @saione * add function to pass custom valset * add changelog Co-authored-by: Sean King Co-authored-by: Sean King Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * add changelog entry for SDK bump * fix: classify client states without consensus states as expired (#941) ## Description closes: #850 --- 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. - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * chore: fix broken link (#972) * add backport actions for v1.3.x and v2.1.x (#958) * Revert "feat: adding Pack/Unpack acknowledgement helper fns (#895)" (#973) This reverts commit 843b459635da8cedd92945141c4efe3a762f305d. Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> * chore: update migration docs (#985) * chore: update migration docs * Update docs/migrations/v2-to-v3.md Co-authored-by: Damian Nolan Co-authored-by: Damian Nolan * chore: fix mispelled words (#991) * fix: remove go mod tidy from proto-gen script (#989) * bug: support base denoms with slashes (#978) * bug: support base denoms with slashes * add changelog entry Co-authored-by: Carlos Rodriguez * upgrade ics23 to v0.7 (#948) * upgrade ics23 to v0.7-rc * add changelog entry * update ics23 to final 0.7 Co-authored-by: Carlos Rodriguez * ibctesting: make `testing.T` public (#1020) * add changelog entry for #941 * fix package import (#1007) * feat: Add a function to initialize the ICS27 module via an upgrade proposal (#1037) ## Description closes: #1034 --- 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. - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * docs: add missing args to NewKeeper in integration docs (#1038) ## Description This add some missing arguments to `ibckeeper.NewKeeper` and `ibctransferkeeper.NewKeeper` in integration docs --- 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)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [x] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * small fixes for v2 to v3 migration (#1016) * small fixes for v2 to v3 migration * review comment * Update v2-to-v3.md * add store upgrade documentation Co-authored-by: Carlos Rodriguez * add missing slash * build(deps): bump actions/checkout from 2.4.0 to 3 (#1045) Bumps [actions/checkout](https://github.com/actions/checkout) from 2.4.0 to 3.
Release notes

Sourced from actions/checkout's releases.

v3.0.0

  • Update default runtime to node16
Changelog

Sourced from actions/checkout's changelog.

Changelog

v2.3.1

v2.3.0

v2.2.0

v2.1.1

  • Changes to support GHES (here and here)

v2.1.0

v2.0.0

v2 (beta)

  • Improved fetch performance
    • The default behavior now fetches only the SHA being checked-out
  • Script authenticated git commands
    • Persists with.token in the local git config
    • Enables your scripts to run authenticated git commands
    • Post-job cleanup removes the token
    • Coming soon: Opt out by setting with.persist-credentials to false
  • Creates a local branch
    • No longer detached HEAD when checking out a branch
    • A local branch is created with the corresponding upstream branch set
  • Improved layout

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=2.4.0&new-version=3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
* call packet.GetSequence() rather than passing the func as argument (#995) * Add counterpartyChannelID param to IBCModule.OnChanOpenAck (#1086) * add counterpartyChannelID param to IBCModule OnChanOpenAck() * change testing mock * change ica IBCModules ChannelOpenAck * change transfer IBCModules ChannelOpenAck * change core keeper ChannelOpenAck() * CHANGELOG.md * update v2-to-v3 migration doc * Update docs/migrations/v2-to-v3.md Co-authored-by: Carlos Rodriguez Co-authored-by: Carlos Rodriguez * fix mirgation docs (#1091) * fix: handle testing update client errors (#1094) * replace channel keeper with IBC keeper in AnteDecorator (#950) * replace channel keeper with IBC keeper in AnteDecorator and pass message to rpc handler * fix error checking condition * fix for proper way of getting go context * refactor tests for ante handler * review comment * review comments and some fixes * review comments * execute message for update client as well * add migration Co-authored-by: Carlos Rodriguez * add backport rules for v1.4.x and v2.2.x (#1085) * ibctesting: custom voting power reduction for testing (#939) * ibctesting: custom voting power reduction for testing * changelog * fix * make T public * fix * revert changes * fix test Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> Co-authored-by: Carlos Rodriguez Co-authored-by: Carlos Rodriguez Co-authored-by: Aditya Co-authored-by: Tim Lind Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Damian Nolan Co-authored-by: daeMOn Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Joe Bowman Co-authored-by: khanh <50263489+catShaark@users.noreply.github.com> --- .github/mergify.yml | 36 +- .github/workflows/check-docs.yml | 2 +- .github/workflows/link-check.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/test.yml | 8 +- CHANGELOG.md | 16 +- docs/.vuepress/config.js | 14 +- .../interchain-accounts/active-channels.md | 0 .../Trail of Bits audit - Final Report.pdf | Bin 0 -> 1147519 bytes .../interchain-accounts/auth-modules.md | 0 .../interchain-accounts/integration.md | 0 .../interchain-accounts/overview.md | 0 .../interchain-accounts/parameters.md | 0 .../interchain-accounts/transactions.md | 0 docs/ibc/integration.md | 6 +- docs/ibc/proto-docs.md | 35 ++ docs/ibc/relayer.md | 2 +- docs/migrations/v2-to-v3.md | 101 ++- go.mod | 2 +- go.sum | 7 +- .../controller/ibc_module.go | 3 +- .../controller/ibc_module_test.go | 4 +- .../27-interchain-accounts/host/ibc_module.go | 1 + modules/apps/27-interchain-accounts/module.go | 13 + .../27-interchain-accounts/module_test.go | 74 +++ modules/apps/transfer/ibc_module.go | 3 +- modules/apps/transfer/ibc_module_test.go | 2 +- modules/apps/transfer/keeper/relay_test.go | 8 +- modules/apps/transfer/transfer_test.go | 33 +- modules/apps/transfer/types/ack_test.go | 2 +- modules/apps/transfer/types/msgs_test.go | 2 +- modules/apps/transfer/types/trace.go | 18 +- modules/apps/transfer/types/trace_test.go | 5 +- .../core/02-client/keeper/grpc_query_test.go | 2 +- modules/core/04-channel/keeper/keeper.go | 2 +- modules/core/04-channel/keeper/packet.go | 2 +- modules/core/04-channel/types/tx.pb.go | 299 ++++++--- modules/core/05-port/types/module.go | 1 + modules/core/ante/ante.go | 45 +- modules/core/ante/ante_test.go | 595 +++++++++--------- modules/core/keeper/msg_server.go | 18 +- .../07-tendermint/types/client_state.go | 4 +- .../07-tendermint/types/client_state_test.go | 4 +- proto/ibc/core/channel/v1/tx.proto | 36 +- scripts/protocgen.sh | 2 - testing/README.md | 1 + testing/app.go | 40 +- testing/chain.go | 181 ++++-- testing/coordinator.go | 46 +- testing/endpoint.go | 81 ++- testing/events.go | 60 ++ testing/mock/ibc_app.go | 13 + testing/mock/ibc_module.go | 26 +- testing/mock/mock.go | 21 +- testing/path.go | 10 +- testing/simapp/ante_handler.go | 9 +- testing/simapp/app.go | 14 +- 57 files changed, 1274 insertions(+), 639 deletions(-) rename docs/{app-modules => apps}/interchain-accounts/active-channels.md (100%) create mode 100644 docs/apps/interchain-accounts/audits/Trail of Bits audit - Final Report.pdf rename docs/{app-modules => apps}/interchain-accounts/auth-modules.md (100%) rename docs/{app-modules => apps}/interchain-accounts/integration.md (100%) rename docs/{app-modules => apps}/interchain-accounts/overview.md (100%) rename docs/{app-modules => apps}/interchain-accounts/parameters.md (100%) rename docs/{app-modules => apps}/interchain-accounts/transactions.md (100%) create mode 100644 modules/apps/27-interchain-accounts/module_test.go diff --git a/.github/mergify.yml b/.github/mergify.yml index 83b6f2d771c..70c3828d03f 100644 --- a/.github/mergify.yml +++ b/.github/mergify.yml @@ -26,7 +26,7 @@ pull_request_rules: backport: branches: - release/v1.1.x - - name: backport patches to v1.2x branch + - name: backport patches to v1.2.x branch conditions: - base=main - label=backport-to-v1.2.x @@ -34,6 +34,22 @@ pull_request_rules: backport: branches: - release/v1.2.x + - name: backport patches to v1.3.x branch + conditions: + - base=main + - label=backport-to-v1.3.x + actions: + backport: + branches: + - release/v1.3.x + - name: backport patches to v1.4.x branch + conditions: + - base=main + - label=backport-to-v1.4.x + actions: + backport: + branches: + - release/v1.4.x - name: backport patches to v2.0.x branch conditions: - base=main @@ -42,6 +58,22 @@ pull_request_rules: backport: branches: - release/v2.0.x + - name: backport patches to v2.1.x branch + conditions: + - base=main + - label=backport-to-v2.1.x + actions: + backport: + branches: + - release/v2.1.x + - name: backport patches to v2.2.x branch + conditions: + - base=main + - label=backport-to-v2.2.x + actions: + backport: + branches: + - release/v2.2.x - name: backport patches to v3.0.x branch conditions: - base=main @@ -49,4 +81,4 @@ pull_request_rules: actions: backport: branches: - - release/v3.0.x \ No newline at end of file + - release/v3.0.x diff --git a/.github/workflows/check-docs.yml b/.github/workflows/check-docs.yml index abcaa066e2d..0060623ba15 100644 --- a/.github/workflows/check-docs.yml +++ b/.github/workflows/check-docs.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout 🛎️ - uses: actions/checkout@v2.4.0 + uses: actions/checkout@v3 with: persist-credentials: false fetch-depth: 0 diff --git a/.github/workflows/link-check.yml b/.github/workflows/link-check.yml index d40882e4fde..fdf8b184259 100644 --- a/.github/workflows/link-check.yml +++ b/.github/workflows/link-check.yml @@ -4,7 +4,7 @@ jobs: markdown-link-check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2.4.0 + - uses: actions/checkout@v3 - uses: gaurav-nelson/github-action-markdown-link-check@v1 with: config-file: '.github/workflows/link-check-config.json' \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a1a1da8a340..ffffe3a4dae 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2.4.0 + uses: actions/checkout@v3 with: fetch-depth: 0 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 585a3653270..fdcc086ae4f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -37,7 +37,7 @@ jobs: matrix: go-arch: ["amd64", "arm", "arm64"] steps: - - uses: actions/checkout@v2.4.0 + - uses: actions/checkout@v3 - uses: actions/setup-go@v2.1.5 with: go-version: 1.17 @@ -54,7 +54,7 @@ jobs: split-test-files: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2.4.0 + - uses: actions/checkout@v3 - name: Create a file with all the pkgs run: go list ./... > pkgs.txt - name: Split pkgs into 4 files @@ -85,7 +85,7 @@ jobs: matrix: part: ["00", "01", "02", "03"] steps: - - uses: actions/checkout@v2.4.0 + - uses: actions/checkout@v3 - uses: actions/setup-go@v2.1.5 with: go-version: 1.17 @@ -112,7 +112,7 @@ jobs: runs-on: ubuntu-latest needs: tests steps: - - uses: actions/checkout@v2.4.0 + - uses: actions/checkout@v3 - uses: technote-space/get-diff-action@v6.0.1 with: PATTERNS: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 18f8c23903c..66296a985a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,10 +39,13 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Dependencies * [\#404](https://github.com/cosmos/ibc-go/pull/404) Bump Go version to 1.17 +* [\#851](https://github.com/cosmos/ibc-go/pull/851) Bump SDK version to v0.45.1 +* [\#948](https://github.com/cosmos/ibc-go/pull/948) Bump ics23/go to v0.7 * (core) [\#709](https://github.com/cosmos/ibc-go/pull/709) Replace github.com/pkg/errors with stdlib errors ### API Breaking - +* (testing) [\#939](https://github.com/cosmos/ibc-go/pull/939) Support custom power reduction for testing. +* (modules/core/05-port) [\#1086](https://github.com/cosmos/ibc-go/pull/1086) Added `counterpartyChannelID` argument to IBCModule.OnChanOpenAck * (06-solomachine) [\#1100](https://github.com/cosmos/ibc-go/pull/1100) Remove `GetClientID` function from 06-solomachine `Misbehaviour` type. * (06-solomachine) [\#1100](https://github.com/cosmos/ibc-go/pull/1100) Deprecate `ClientId` field in 06-solomachine `Misbehaviour` type. * (07-tendermint) [\#1097](https://github.com/cosmos/ibc-go/pull/1097) Remove `GetClientID` function from 07-tendermint `Misbehaviour` type. @@ -57,7 +60,9 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (modules/core/02-client) [\#536](https://github.com/cosmos/ibc-go/pull/536) `GetSelfConsensusState` return type changed from bool to error. * (channel) [\#644](https://github.com/cosmos/ibc-go/pull/644) Removes `CounterpartyHops` function from the ChannelKeeper. * (testing) [\#776](https://github.com/cosmos/ibc-go/pull/776) Adding helper fn to generate capability name for testing callbacks +* (testing) [\#892](https://github.com/cosmos/ibc-go/pull/892) IBC Mock modules store the scoped keeper and portID within the IBCMockApp. They also maintain reference to the AppModule to update the AppModule's list of IBC applications it references. Allows for the mock module to be reused as a base application in middleware stacks. * (channel) [\#882](https://github.com/cosmos/ibc-go/pull/882) The `WriteAcknowledgement` API now takes `exported.Acknowledgement` instead of a byte array +* (modules/core/ante) [\#950](https://github.com/cosmos/ibc-go/pull/950) Replaces the channel keeper with the IBC keeper in the IBC `AnteDecorator` in order to execute the entire message and be able to reject redundant messages that are in the same block as the non-redundant messages. ### State Machine Breaking @@ -65,13 +70,17 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Improvements +* (interchain-accounts) [\#1037](https://github.com/cosmos/ibc-go/pull/1037) Add a function `InitModule` to the interchain accounts `AppModule`. This function should be called within the upgrade handler when adding the interchain accounts module to a chain. It should be called in place of InitGenesis (set the consensus version in the version map). +* (testing) [\#942](https://github.com/cosmos/ibc-go/pull/942) `NewTestChain` will create 4 validators in validator set by default. A new constructor function `NewTestChainWithValSet` is provided for test writers who want custom control over the validator set of test chains. +* (testing) [\#904](https://github.com/cosmos/ibc-go/pull/904) Add `ParsePacketFromEvents` function to the testing package. Useful when sending/relaying packets via the testing package. +* (testing) [\#893](https://github.com/cosmos/ibc-go/pull/893) Support custom private keys for testing. * (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. * (connection) [\#721](https://github.com/cosmos/ibc-go/pull/721) Simplify connection handshake error messages when unpacking client state. * (channel) [\#692](https://github.com/cosmos/ibc-go/pull/692) Minimize channel logging by only emitting the packet sequence, source port/channel, destination port/channel upon packet receives, acknowledgements and timeouts. * [\#383](https://github.com/cosmos/ibc-go/pull/383) Adds helper functions for merging and splitting middleware versions from the underlying app version. * (modules/core/05-port) [\#288](https://github.com/cosmos/ibc-go/issues/288) Making the 05-port keeper function IsBound public. The IsBound function checks if the provided portID is already binded to a module. * (channel) [\#644](https://github.com/cosmos/ibc-go/pull/644) Adds `GetChannelConnection` to the ChannelKeeper. This function returns the connectionID and connection state associated with a channel. -* (channel) [\647](https://github.com/cosmos/ibc-go/pull/647) Reorganizes channel handshake handling to set channel state after IBC application callbacks. +* (channel) [\#647](https://github.com/cosmos/ibc-go/pull/647) Reorganizes channel handshake handling to set channel state after IBC application callbacks. * (client) [\#724](https://github.com/cosmos/ibc-go/pull/724) `IsRevisionFormat` and `IsClientIDFormat` have been updated to disallow newlines before the dash used to separate the chainID and revision number, and the client type and client sequence. ### Features @@ -83,6 +92,9 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Bug Fixes * (testing) [\#884](https://github.com/cosmos/ibc-go/pull/884) Add and use in simapp a custom ante handler that rejects redundant transactions +* (transfer) [\#978](https://github.com/cosmos/ibc-go/pull/978) Support base denoms with slashes in denom validation +* (client) [\#941](https://github.com/cosmos/ibc-go/pull/941) Classify client states without consensus states as expired +* (modules/core/04-channel) [\#994](https://github.com/cosmos/ibc-go/pull/944) Call `packet.GetSequence()` rather than passing func in `AcknowledgePacket` log output ## [v2.0.2](https://github.com/cosmos/ibc-go/releases/tag/v2.0.2) - 2021-12-15 diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 8a69e377fe3..8e83c7c7cd3 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -109,37 +109,37 @@ module.exports = { { title: "Interchain Accounts", directory: true, - path: "/app-modules", + path: "/apps", children: [ { title: "Overview", directory: false, - path: "/app-modules/interchain-accounts/overview.html" + path: "/apps/interchain-accounts/overview.html" }, { title: "Authentication Modules", directory: false, - path: "/app-modules/interchain-accounts/auth-modules.html" + path: "/apps/interchain-accounts/auth-modules.html" }, { title: "Active Channels", directory: false, - path: "/app-modules/interchain-accounts/active-channels.html" + path: "/apps/interchain-accounts/active-channels.html" }, { title: "Integration", directory: false, - path: "/app-modules/interchain-accounts/integration.html" + path: "/apps/interchain-accounts/integration.html" }, { title: "Parameters", directory: false, - path: "/app-modules/interchain-accounts/parameters.html" + path: "/apps/interchain-accounts/parameters.html" }, { title: "Transactions", directory: false, - path: "/app-modules/interchain-accounts/transactions.html" + path: "/apps/interchain-accounts/transactions.html" }, ] }, diff --git a/docs/app-modules/interchain-accounts/active-channels.md b/docs/apps/interchain-accounts/active-channels.md similarity index 100% rename from docs/app-modules/interchain-accounts/active-channels.md rename to docs/apps/interchain-accounts/active-channels.md diff --git a/docs/apps/interchain-accounts/audits/Trail of Bits audit - Final Report.pdf b/docs/apps/interchain-accounts/audits/Trail of Bits audit - Final Report.pdf new file mode 100644 index 0000000000000000000000000000000000000000..3f6182e4e98e6b4fc587a8e165f734299f69adba GIT binary patch literal 1147519 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�GV4R$6(FD7{Y^myIi|Qa_@PSLe+9lU6iD_7{x}$ttM_j_#maS- zH}<{l##m`hIb>@mkoVW#qqlV`sG70#Zl}3Yu0*2gJiDxn{_!qjHr_xcU!b*Sy|HfF z^{Q6|Z5}3A-{3821ozLqsySCmjk++e3_%Wehm8Ek61^If;QPhCP3GYWQI8>$oNCy_ z<-~2?DvnQeQ8C|OQS|>9A3b9+t;Ei6*#MV|vXJllyaSF7*nc)|S`272rng{ODxy&% zqQDJx0Ap?s*uHY>FQB^tv)h~dqMkeVr*S`%QPA=sAPxH>qjEfFxbObuO5dzgQ%M`>OTM(=_Jq z&u1=qz$qvyGNwUS+CeH{2wLB`68t+C`DSRuvu$u`cX2zfXnXk$7d9$Um|!W~bgzhs zTm4YI8_FBXFh(&j*WLM-{g%1)nJ|+^W%5tDBqX6*0XDC zLdogz5evh z-Ig7X>~>7iR2*39O+%~JD+`#4X2srPU1x=7i_YxqV)ucfI7R+Jen+Y$HfHCFa zPalq7+41Y&qIDDdxuS=A*rsa_^b6&O@Nb1AG>Olk zSoxnwyFU)R6zCT)Wi&&!l0!pT_+;b1*!v1{hxTl$337kf?#s-HpUvC~Jj{6#wqddK zn{vMYa4Sa&M&<(UCahG**h(K0gTyw10_0b5n;kMo?I7{wO9gsF&@4otFvTg&&%cU@ z3Fto)K)QCgcc7$cj=M6Je^SNaN4@nTg>$vXchBy@p<%D8YCc1)Y+>(0J-eKwjNxzK z=H}IQ!JeCtbuzGi6YI>7YrzVdI?p;l*HvMH5_C_48POgN!Wa0vm^Rm<_{v z*a+d51^rRDt!4h7A}-$2H#wX;`s`iaYGKBe%c zWBn4yxze1-))7B;PluuKE5DL>Dz4c2I;6>bXiHu;=|=-8S&GLh(;Zj;2j<>=>Fs%u zEw$Yi=yQb*-9~fF=zC`RQhe7OKNvdh0khlY7raiM^Z>gUQ(~a)3_pOWmw;B9T=}e? zQ?dnrH1PVwU~l4F``kOn1JdFB{V)yOm+M9^3D2fL)Frm!tyI{ioicj?y_=Z1>o0$? z@r1Z~xJ(ajbbKu$u%)`f@Fss!l8Qw-H z>!zRRZ)C>;$LIP?QeX}&W+EJ|K(Sn^1Kt1MU#1v|t#)Tz`qaaXYfV2h>aJ1Ik1;7= zlRMI&I!+7TTmWVlJ(PbCfx`z3==zrj4z2Pk#Yhjnj*aRBU%vr1u49?54o<}4J|a9h ztadq)*I(!EQF&)C8DmH|7(*owJwh7PHh-CNe{lc&wTCHtt7A`E;DDmcK(w%vgzBX5 zFC33&57bY9`mg+@O?|rn0_r-b>pjYUEmCMB^nn$tjocZ~NaeoE1bm(lk zlw4Yp@99VCUZH&2<9;oKXCo~r>lL=XpBVE?HAnZ3)upqsN(P`Y>>3!|lKfrK(UapI z`QRSGewJ-;x=6R<0PS%F?q$a!A2I)lW$KIjTc+PtvY4RK3(fS*wzAB_(vIN-YWE2X z*|!68)CwUN^GZYFz5~uPJXa=_rM`owqJ|>8&n*T$N@+Rj{SPE-h?t^}yC}E*ULw$+ zW_!oaR`R2O!qMz#NjupCf7yXJK3exB=yS~6E|#fQ_<`_Ah-V6E*lgDL3#Q4}?Dxa5 z4@Cs4Yt@`I9CjujZU~R2K(F%Zw~9c7E_xcFU!4t05P^`y&|BdU@I0zMy{_gSJ&P3M z3e)0T`VjsNgg?V2e-C&)2AqS2-F-s);K%mMGzd?-t$ZQd z>@4yvBHlp~4_738oY;CfxLP&^#o4sb2=J%Mxbh~(3WpgbiPuc!)x`w3yORIOAfBE{=L#pKnn{J(2CG z!+2=t&wO{}9C)zr3U(|3G~|H-7z3b#JoTs;_T|qb`2!7H@gz~=`pgIzJQx|!6@l!n z#CtzVO8*@#zg8NS7=}9o{J?GZ4-v!b434m?5sTc~i^$J^nqQp`9i?&~GpQDVBvW^d zBlqX6&keQjqLsKGU7HZD*D8p7d$g6>-x{LwG4Vej%dVYNFb##X#8)a3WBEvbPu@v? zYQ??;3E&(Xf4QX?I$}Rz(W|Z{*pj|TEd5VNLe!N;YW6;+@&m>8!_rHU`x(DB3EOYh zsS=ug*%-&Ry{U#%-~!Q8%|ZM%gjyn|=d<=+8BNC2;e%I@odo!jWs}$;<|43vXQepp zaJ|1T|K;bRZE-)H`u5XyrYS9rH~P*me&rR3#6FRLdV^c}6<9K8eBDC@NJ3hGdJza{ zd5{EF_@V5hG{(?!=govBi>h5y$Mm5E;Oc!ypWLfGpxj>ny2L$g!d>j~Ct)63))H~N zqI8+-H`~qMY*eTE*2A#^(M>I67vYn{bvMA5`0a+TYi*amTYpCU#5;@jN>3F*R$622 z_R_64>jDla$UebCpnvt_;K}@)CoSP~-jff>CdqhLrXN`wA=&8g;L*UFQ_%f& z%~}2rVecJJ_51&ipK}nBEwVBS$yQdz zAw{L4kdc{;tYeRib8MAWWJD+#AtSOk8JQW`E30s9hvOXQ`MbQ{pYeWwZ{OeV_fNN* zTmSHQKCbI}jQdEkhb?J1Dy)8UwPYYn+QRKu5Jz7h)UslfXmkivO`ubKobLFc0q(mS zDSFrR#v#Iu2S40;o#bY%nNA6=J@aBvb}E9%gm;){jY}Zkq$<{>e8C?%waiQ<_S8Odo?^bLz^A@U56P+_nFm zDahjHtrJi@N>9%*_nTP|KXScsgj@F4oD=?wXa_p?%Os&FWl-qKL6uFkH@TK~@Ab8?O)MR~U<`LGdu zd8bI$(rD*>v*O;w_gEOUqJi*Y5;% z9&SWM$-vEP2pIlsV3D8P{*Z=Ok6rHxd-x~W7-mu$=aP36;V^%vVdl6#1L=R=rU!=x z8^cFCfyEB^hWUP^&v`u_yS?c1?Gi6SIJq`-q$2O$bbdHr_(=uYv3}@IDf1z&He}VW zKG0_XHacSwZZ2T>c+Ao&dlxY2@-}x-1!SA~%!A6zYXZ#)5O!<>E zgUb9AsCNtM=y=7{q(p+Ywk#TB#76`$g~k}UW1-@mL5wf|Uu1e|#40ZE&-%ccBTXpDxylf4XP-C19r^?$$|-cTV+~_1Dkm&-$z$pQ)Z(LEW{#^U>zgj`deL z&Tnw<>?5;cp7U3m+Jrt4Mp(VwPY&&*kdpZl^fYU(SBFyjapEbNnb!}h|9U;TN+0n+ zTGTzHf@3pE8*aFa0PXfa6&^tW-_X-z2|G|^2wM~VovNo8e2SH{%5d7$ux z^Yc*1DaG|l_W&Qm_VS;c43nMbRDuhqs(5}~mjqdxs& zE-B-FH@WICrUdb*!c6*HZ>#2rJ6youJL|GWWvd1j2r0ByCP5u4PY)OUPJ!xNeMV#D1 zl-%Q_ktILOXOv^qIS|GGKsS=&pE?bLGs4ufBDN+7ut~X9K|HizRYDF%DN-bq1ibw5 z>Iy_s%N(RLy+~irNl-zcrL*bt6$`YzA3>YiYk03Ex6$CGN5@`0 z5#5iJc&#S>M29;Wp5ak@^n8lm;SK#~dgtx`2wTfVmPxZ|?h=uC)~VgzY6e)y6>_RH zZYl&r^23t%hqs<_HpoTxIZ1N=q=MvrQQ33G*b z4BCF4i9xwcDp)h&l-rhsP4b^8mn^3oH%raEl%k|ob^qM z6Ic#S6@djN46mz8$R<}k4G&_%U?S^@vz^i1ICv9k5W9_L>A+VXhwUz0%b42#0-fzX zymM5oMQuSgo$(C0x_yTDK@;@-42P}f9wwvj5hh3cx;GECQrYsDN!<-K=N(75o`yp1 zeN)LfpQUr6XLQEe+qM{=-n4igE*t1jn;~_SF&^0C$JIA0F6JcMW4UHyB)31ByB};9@5mLZ$W%v}zc{TQOX{S%}%`%bU@Q@go<4(l{;<>@WYEU%jY*9>z$ znFf~>QcZn9L?yu<&Y`-{vke1)VWD7(SlsL5etZot>fF;3m z4m8(Omo4F4e1hv4+kJ_R%JTHc%a{f@|GWx5qqQld?ygP)JkrX@+Eaks3+jBJaP&r&fjUXK{X$Kjj|!VHB}) z#bW#S9}J3*2@ru0eCir$du{ccZYxIkv3GMfirgLX6AF3=mXsC%mN{$V&`1 z&j~yd{*g9~5tw_KD~Eg}ej0=oYBw^hn5Ja-F_at8aD^ zR7awF4u94S$wNQDG^n}mu0A6=j0_zv_)rjr(;9K@Uf{XWn><>@ExB<>cLHQs`_+4I zrJVNKn`L>!V|hiI26fA~DN6|KSx^eLqpst@m7MHsc7AG6lBiSi`c#EJ*XckNWtOdD z&@M!p%=z%;1u2HXD7TAZ2!Dg#4iF}nA*`$H(3N>e11YIkI9pe@GJdJJxQw9gE62I+ z4Wrf982EDJ)wl8r?BQI~%zemVOT9N1yBD3#oA^0QB1hHSxZTza6nOmZE?n!|C0ooO zRuawp`0khO#*F_KZ~MlDmS7+1pT4OYDb15R&grkiVtndBdI zbyoI{1$_HyQxKS1<(wsEKo;@9-q^N>XT<;4`AhCg_ju}lN8NC&Y$7-pme6zU#s5ti z9vnQQ{Qegm=`YAV4pgMIg@^0Lj~a%7Q)?}iwS0#myX$hETT8$9H_|dLtp$v1iujyM za#LuWUg^L|{CJt#UWHWj|F6KJIptnnk~^rhq*dJ3xgB|0BQito2pYde1+g(jJMYd- zJf0*~)4g$s(l_V86--vW@*qiQFR1IF9X!+16FmLOb|UNBOd}l>NM@&Vl2Qw}Rij#C zqzc`G(rMn?-`YQQLp-kQWUF44f?gy*)kc^q0#siH`z8Qe5LGoL#R|YKXjf#-UD$#s zchAU@uS<|NNAyVat2VpK_dQMZQL*RT}_#cZ1yVwBdZ-~dm(SuKt;?e zTYJCk5PB%G=_~tVZMHp{|8g7Yo?&aGqTU&P-PgbRIgcSnP`dF6Jdj8nxE1wR{XfDs z7A1*W84ENj9hDA3AKaLKwsK$V(fQJ(DjHX@j#;gsVK+9<4u*viE9y0q!j+G?0{PFa z_}RMuDeWu@5nwgw4Nxz}C#aX{YRtq7OpP|YydNrtQ1p=o4`p+dR*{ot%^C}F{{q4+ywcr}aM zV%FW9)L32|)$l5Qv@x3Vre5T6YI68%&%84?H2A;#lYQ@+p|FHd6Sj@~@l@IelYP}O zasQowV`l(gnPBL=zIlHR`gNs%J{^DqYFM4zjVecHRy_4Wo{2t?r!{D;j3PBhidy}3oC>dW-pr)$m|l~-Co*9mLY z0js59XH+V^A+((GwxFUtM>dCBUxpuT-Rep;cT|wH)_kXP!q4kbinN<6DW6KTIrseh z2us-lw?Dm2Y&Vy)=qdgX|J#RV0ZA@O70Dpb{O9_gP}q@=IC=S9LP+=cCRT|^MMP@t zjHVbMwtjSDU$8>xW$7dF`(}`=Gs^?UJqe~W`d^zLWgq!wK^cIKYu>3=li8K(>Vo~^ zv|^&!DbtiMK}#5Matz;ZK%d&IJix_so=xxZUZ-EC82#uZzJ{A1)W!UGTW-wKSGBbD zMZ?P~ZHYgJLv_SQC<#W&75c!yTTG#o4{JFA@nr^_&isPt)P_@uAiOgW=Oo}J!VnrL zUQLZ=w9Ow3HV~otAOJgZ$`pBnVroInLvG8DE1M#09%V=eAwBfd1|#my&3+8L(3P}J z0-NLCmHGs+NjEk7%PV*O$-c^b-y#8Fx=UQYqNR@_TvO+dmEu`&&q4xGsK&bTr~1=7 zYgmC%am6~7YAz}Ics}(0yK-J zUqHGjcEA3AvM(Y2NcSBhgJxN@v&sIh284WU1nBZl`fniEDMx1}yf&~HDYC+&!4mW7 zc4HqrItkzSWoz>>_&Zp3dE`xBpyr>?3h4d9r@3P7eN*$MN*=V&fB#MSqom!4Lj3Tw zngW-)7@zhfxCae^PJ8tcX@h~AY(&}h&h{%9%7f%|Wm zNi~3xEG1Kb9{W3(ZDgzjTb=&uWfrE3Y2R%a&7XWRQ-ePZU#6(Kf~wcJcF z{fcAty8-s0YgVMZ4n42xcXH^>*n2~|tv>mu-#$$!m#VGwCk*a?4#;dEmCZ0; zqlyZWPgiFV0U8+sAyS1_pNg_@`n%AjN$wC?Bu<;vj|}|`~+J}RZvF*749g?D0YjHr10Zao0t|FVr|Bw z<98tyNdKC_+0MfTt-Qirg@uWco#ot$^+($M)zej5z10*}b+~W?s|1$cucAiR^)b-~ z`Z z_(H;RsU(WYwF7GGM6}#WtHR?VL67$GzGP&+Z^65j30pD9_@ zY7O+%GPhiW_T~6z?;pQg_2ulLsgn4keE%0g{KMD2l_&>;?n$~O%MYC@b)A8kXB<}P z-0E-|hE@rV3-4Y-1p;6`4zM-d4uH1RNT34EgyCW1F(Yi$t;v+7P6FaN0r*Kc4Bk*} zS?XSbymP6EoBi&G3m@wT7&8}Ck-_N{%0)dZATb|RMOm+?XTP3{hu=_qFB+Ls^VkqLFX0ndf!)2czq~*YGwzkHJVvbt)l-5G5UBW zeFNX2@_2LCBc-GjZ7JE|L%Gey@0P;-XWoRO3asHsM)5Id20-G4hvFOMNa9QOgLt`# zk{j%_pB{XXc(2E%LtC-ls-X1dt>75Fd~zeh_%5T&g3A)OlRJNJRQc|2t7@!<`Im`N zHS^0ih)y?h_MSD4W};TE5)RFr{3ntkZOqM4ehavRmr+8is%_Z*@2=usBNl8#Mf2g2 z3vN5cO=zcKz-a5ExL8av@Go|oDGlIhXM&*#kr(X56pCt77W=aT9VMW~#2QtwLCn{5HQaoPXx~nd2^Erz|^BL2&-KkQyrUD3>QpUHwaR zk9eP(j9M%|Y&^hJ``#hcN8zcG?DD9ok~HINe#v@XEt-z@f(j>w)gYtaJ7yXQ{^=g* zzGt}Y`iaAG3C7-rh8bLr7kSpi2aWTQHHtK@X|L8z{^%{($V>l#=TSJDl8V7qP6Mav zhIiP&?EzHO2qO>L0IIpA`rX%1*l_;^^!iSH70PKU@M>?+G56;cbLYz@fgPs5agBLy z4>D=hFG%d-RcD2Dj}q|yprcr$@GqMKEWG^LM*&ri-CCc(xlWXd=}(-KbBjejwltTs zp-+O$pR?vG(w#Av+)Hp3-+N_B7nBi@rpIAM!3c%V)3VG)1WPW`&MzfM`~=E&SL zbB^KlwKXTy6v@m}lhcyd{vcsNo^7i7&p2%kCjf-@d>K&kJ3BY2Ma>NV1{hG^Fo29) z19T{v;o}*Ire_BF>5lU?sa-88Vf`lP;*Eo)K6Tj=?OzusX5TI`xs1N*-V7DFe_v&0 zhCZ4r1-bW1IveO2$t$l6R zJpMX+ONrzd7BHQxY(hf+HOjlX|NK1P9~H?u^@N}%fYu6O4tI&Ar%>8Uq@FwU?S<&2 zHRH4{L+EX#J21cRL!#Cc4fTzhUmbf?bm2pnfaJ?2+2inp^ABUv z?dvW)(6|II*T*#O19^ixe1K*I@N9F5WP$+FCIFjPgIS7U-d&#b-E=gXE6Yz@t$e(Q z?bihWT&=0MVRB-X2kN>2W7N%jRlY#mk`#O^RuJrR4}K&&mtQCoS$i;a3DuCJ@v-o# zyQBo22-mo~%*NHIj*KrQJe+%;euSy)Y_W?6!K1lRWDanK!e+e?^;4W@NQo>6Gn2l0 za|@s2S`*C?( zc>7Y};r)g_GZN8`L#bYi(_s^L{p^N(HBSB9M%}hSOtL*pe}S9&DgvrxL7Zd+J%S2U z<|m8Gf6c}Qil~SYcc#N&Ro5|$cr&(tYNS^Xr?n&fK3zLagiv+i*sI=i@8qrqrnT~r z*Ppt^hN~TIy(wotoToDWc$|M?GtFfrw6asV9T4uY%td z_5IMBnP1lvO~q^T?EfbzzRemadnVQadO)b4P&v9t(Vb-U?Q;kzo~A9oip;B#%HOs4 zxNm30Pxz;F_Mho!`{LbdgIZ}^R*@*#tDiOrr7@EcG|ks<&~@l&brx9NOKR*Az+7h$ z6F~qGuTIcXo2W`iSPTCbYM%ptdd^z{Jfn<%?mM>z98~Ka=MO8RrZskc`nEtg>5*I( zV{X~|cI6oXICdahP)~;1>^SSUra-Jceyux+E=z`hu(4WW@3`Zu*@`OXk(ZTyQ~DG- zLrrNH-M7*0$WJ`zik3jf@NvIuX6H-Guc~K%Q@?)z^poop-@-~38i86^;0Xo926h+W zW3$y{;oYp2xl~n47=72c^Z~~5nPw`zMgZn~o=+<~a~VIg)BGEnwA`{jW87+V&v}WIYGaZw@Cp?r($zhb1;<{tUFm@79-p_La6n z-A8Kqz3;D|cw5TzIY4(>t%BV;bHQujV+mKn%3Ij2s`*PMch1hO-7y07_2|j8i>m;f zC=53q?M#g!{ATt&I{Tv&dUakquJP)Xv?iXF-DcbwF)q|jho>I^P45Q~u*$@JC&-ij zOU6LTJrWyKNpKo>dpzYcnkGRqXHliR(uR-NTj&S8m|jUg?T+pXiHRC*`w5k_!A*Vp z8*V8WgIrKk7tR588DEnYrWh6i7bO2F^>B?Nb{42d{}h2!5oh)PsmMP0Cc%1;7-Tl`qUkp#pW zI1g`CF{-ASApYDZ9|k(9|43m0Z0sj;i+KnLUhdR1v65yHGXZ#X72pe@?_P!^2ukS& z>taq!Dae^cbDK>e``oeo+kwg%$a&~#t>gtA=bIMWAXUT)xVPA#%{vqnlaaghfB(5n zQY~@mF-e-DV$pd{gW6|TB%B&9dLN3azTcSVB|};chaDn5FbAs~o;Hy_Cn{dV;B}TNFg>S+Hq*`ZTDo+Ac1kbZ~WA^kPiMspQ$}CG?{r_)!K}vBlv9s znIzc0F-#hd+5}G*xdj06T!3(3qe#-m_-|wVMkwU_P!i;wW9Sizn#%wRhW9^>l>pRw z0DrBicZ!f6^sD_U57`iB4e2`b*<8FP)>`$|H}1JawveXmM(YWFGHL9wKOE-VzvH8Q zQCDwULdyYsx%IT`gM&eO&wr)r@&Tp>*xwv9J7NxayUAH@kw8K6_H&SmyafN0^;WDH%1EWSl@4Wyz(Z}}zYmdMa0fmLGG8zdfVd<=p_E;yId)q13^~dA zyO~|k0PhEDeyoK1VWk|Nxen~=J{+Q5hs}73INVUd>MXbwjoOXr*@CD=pIc^R86wT7 zyU(*g*Ce+|qkwg?NJ^y3-O*_p`g{K-*Ea7ZRm>?$gfltMX;{aOOSRe7D`q^H=;pLD0dqoB>SKtF?gNhFc7FBvA||T1jwFRQP0T}dH5hea*U0Q-RW4)N53 zI0`xdZj#KFL{DQle)@j-Aq2-G-`N+_SDtf2#pr77uK-w>+{J3Q+iEWq-0m8+s|b#x zdVKyP6fu{iY1eCaTbhecc!d@%<3UYD823~3yMTLfjs`xR3ei9v2(@|0;5h_+Vp2_p zIL-Yy=QrhlT+4`0u&!MHnygXF3T3>G4b5ia(@M0wEnf9&MAorriIX5*p0|^2PW3E?$+&04XO{! zV+x?VMDloahg#K#0Ta?Bqh65!qXTr9!KQmVXy@xf_f3@l9IPl1OFbFqv~%`8b0Vv; zvn>FdH=7RS);cH6;_lqbYX2%1QF?HhPwu>~d|R&F)H9!1yEiR?qTO>?Cq0k}LVVwR zuN4auS52renbah5qHHp|q-@n)RU^@=25iO<9N$=2K^~{icsL-ulm8u(djuu~pFq`N zP-JRCgyd7Get^Spg{COzb$IVLe=BJJNPkm4?rIN^=PX~;c|_17y!Ou25r}Y;`8uhd zUqIgUU5&5&133vLOb*b?uP>|qgOD0{1Rn;oKmm-`%{io=y)B$+VEgAu&gDi<&Ct{4 zOwH4Op_{Nw8KInzG|H=Vg2dV@B>`&w;5YQ5{V{IRzyQ&C=gNgHm;A3$0e2T2bzozIkheGxaT*T65JxDfXlci}@ZI|1dBR~l-;#~-1dD}Wo3 z%A@(c$fU}b)?y4Zp~m!3=&>QHDB%z9z&n8jaShy+`_sLFE$zYgXcY5`qckn)NH$3XM&#xV{L z&DvgN#y-~Xr`Y>=prdNaHs(kHH>zYL#T0{)hfm5$wPmEi2uRh8ZgG#mPNrA5hV5FM zjE^GAgVCGxUNx^lE7gZR#yR%lu8g*f6fe0y|2BH?Pwu~|O)Z7o>dmzi^Kv zh81!+jG5{O7a$yuetEnRQ(jGMJh}bOKYH{|q4i_!)jung+1DcW&44b=+qn%)BnA554^^(C*z#UER{enV=eGzN`{ z{eJ;l0t`NS1cmwxRkb{}0k-DV~M zG!Hm4V{96$Exj&7C~iObkSh~D#WtLt`j$c&{3EiT8h>64J2p&g?v_r|(vssX(zWn< z)K3>W%}|kmTw_c8KRe8fP#v;hBZ)zo)+b;)RrZX*{&CL^TjNi0M+XuC2^) z+ia!MVzo|2{` zxhuts`sL3+=HO*0ya+f}Du||S^u936nQyL{0NEyyMQs*5K`*3VaS8Y(#UnCoKD!t@ zbQrHlWCZU5VYuL+VrTYm@s|HXj?UjiziqS?ABI!Ag@~>wz7Gf|oVy%Z57-b9q9}`p z2T2R2A_mu#ic01iA97z&W*?m)c|7~es%IM}!1+Aj`6W;_+<{j;l{6GiJDL2@{CR>q z=(~Ps4m}KA=ROdE-Wy2RfzPNeK5sCJ2S6P2dtcd&aP4b3?Vw!92am(M+;hw+sxwk% zh{F_fl61j^^2zGsrPlC{=<$Z<0~Se#4gQ{YYp)-@0E27evko}8>#=zE{ZO(E@U9EP z%RiNNL$|3j>!H~Ot`a4h0b5&7E!d8zY9f;BprdNZ5 zfdJ~#0Jvf`Y+?6`+DX%1iXjkpau<)>9Tn` z&RLwwk@Qn8-66czndD7)=(d;<)(+VEY*k538qjmBGtDLy$2>E*g?!+b zVNmd-SB1A|R$zkK^D6U`Jb_Qotv(7QdVVko3Jki80pK4~D&7iK7jFfN?jGbfc?RtD zjfme+Cr*selaB2mv4-K391O_Np8%N`t#-Uf2LbFwGocI*;TC}mBcu6yalXHjIkH}_ z?mr0?xVIaA(G#5&BzLsy5k?EWT}}SmRfWUlYH3yGRG{A?XVbdj3_qJpE%;zyn#No3kc~z!Vc@NS-mKc5lCit;bv=6YUUc^p4zd6Kqlbsp`)<^Q zVSYiCVhX>~hJkO9g^zzSwt$k>OD;zOnzBcoZ~TT+nSJ#AU4W`dWA``Or}C#MXT`K& z&|C(>t^g)8uLnM>G0cfNGk{@E+xGQkj4{HF2R@ew69?;w^@*4%`tOh!$09Tpid-ck zpWflAyK8-ihnxhoB*6EUguaFsOV-Eci%w|MeWN9UCE9wykiZIe`+ECYzo`I*k!MCj zT%g`@u{V$ht@yIQk<2fTu1;r1IE_mqe4w!Jm#aWG!gvDaLvU=Hhu_$R|F z)%@`*MY!ed#}1pjPWpx8lx_R99+OS9CNpYovrMW<@W2jH$B9SqhG$PRKscd zLUAh(5qBNdQF>rZWx=tTJK^}voSYP!1eT}YgJ~k%593pb-GwSZ&6Cs6TWZ$%9$?Ia zU**m{N(#a;m*53vLoj&)WRqX$Kev$4=Ky)KIYP{o?AXB$! zP4Df1FQsquxuYlVvz+I#A*_B>^Q%ijr}t*a-B|87HX0StD&64iw9|{K9U;?_uZrIu z;Iypgdl>Ctsydno(+L6HxtN9J%{i|J7I8->kec#um<>0W=h;la6}gP?+lE}aEaITY zIR`7wJH?||u~#_$U7_;XBIl5)ITG{;NX^=k`tdGS@3M~E`-%$i?;hCqNOSmPA+-(0 zw#+~0EM9y&cs!Q*Pv*;g%++<<^Ppy)F=Ivi*enLrtz(z!r!FbHe>TdwRF7|{en!Lz zz*euTMla3UwpkKipKhT%H7-I?y4p13YNcmVH%4$b`uAYm*#jr<3lsZ}=kTt@ZK;}P z6qTHn)01hpMD!IoP96Z29^A;y5Lq9I45d^dRAf`ex35J8iBG20;(q3x^S%IL(h%?3 z&-t|e0f-4#jsw0?hr;f&V=X`6C%`;!&H+TF*y)~oo&I<9A_P$t+Dfe1%(yN)3J#rDZ z7+<;-eud@aGzcr)&h|jc=G7nn9UK(<^OtES2^+`Ml40%m%#Vx2#@{y!wkRQCA|ez@ z>FG3wTdnlwX}_q%%eHCy5m8&vX93vP$=}e8c7J>cNBMs}0B@~X6t1el!fK}NFrIA^ z`QO$w{E&*;?027rN{qdIXYTTh!khdn@Ej;U1*{ZQd}Xy~7=n-v)~3ax-)lm6vmQuo z{Gt;rJ)DX0n~`6mO>i?|zGa#Op*sqxfENVT7`Dg=fJ7M3F)TstU?q3+glG`lJIU14 zKtyh}Y~#_VaVLqwOo+?&>poj7g!^2IWbD9Jkm9SC+gY9|Ykb9nSPknfv(lz4orQaS(2&XE-V2qi~AmI<8>w!p%}G(kO^ zhl^gyUWuox*L<;VA!3FIOa>CMP!YL8keovsX$vD@J;J-GYki{a8@nlP0%IVUzE|%u)49<0wqF~i{=0i2o(95 zKrVX#G;=O&wTP;)4{=cv?TIz6YwikLvK^)PAzD~^S=E&0&z-6+p3t%*mI**VYfdHB z9^je3X&u!J%PJMA-M>seb!Nre67oIG>6q2Ktbxp$t>AR~ov05vvaA&7=ZbZw)7w1b z6*~Wh?6gT)6duT?syLbW5r(p}v(d_$TdU;{n`Xfw3o0fPq3YY3-;-AjnwK%qET==x z`fb+Fl5mwWV?tWgaXZbQmdL19cov~BKnOaBq1R&j;l}nCr}0fYL{`+1>cvyy~Bcl~`cg4@QmB*a5+LP)_a5e5rbwQ753%SPT`E#VCIW8D$E!Lc%U5qpB zt0AN;jn2Q+w)P%QYr-_{z`XJrXrY5A=-`7|MGw?YF=|tYnJ}z%@CHv@dwk3W>?zFk z$1FIVDt_LN$Ubs@Y`y+_4sbHDD7gK`i^T*!CNUbyFpKGw8KeemG40XeeAPo zr{T!=%dcPu2ps)gMW64_Q(#X?pi6m)v??AVey9{&4kXW1q+$4saJd2*=EJ-O(;p*T>Wg{j{m$;p-e1?% z{u=_2XCpwr&poZtZ@*Tt?WDPtAkBLAd6|n5oQmaJ3y*BGDO! zz!q_V&h?m$Avj<)ED(8~f#sCJ2T||W&L};?RlC-f@VHy;jfj<%if&oM@6%jIC0j0P zAx2%DjEhR&yM3cNR{CKG?sc^OF9!M-)muH562dgLZPIWO=n2v0BZ?&!Tdm&8vgd*(}x2d_5)vy{R*!D5;q3` zL{_*B6g)|TfuEm4J8m-lUst4y653{ZqVU0`S+2*X-=CWuq*}+eN=%KO%9}{FmB5#K z`{9!WJ$Z^+pHrS5xqfcJQ@-W7`X{M!M)A?G&``*m1`fo2z@qjGA}$v;aT#;%ztP9L zfG@z?lU7CpNhnU4=g;J}lPE~c6JW5THesH#Eiu+8_RHT_IQU?sMzwZl^F+P7KYnss zGdt%Zd>wT#8N39ZGv^4=n{VdNjHfi|cZ0;~PR)J#4E6ZrbX7ckWpW^@n80=VBQ^Q& z#`MH9?cmPV5hZ8XGTVi2H-XWk2@P~WH)^!0_?Qz-`uBtxxFw|5*YA`xT^w<2u1bG4 zq?4mxk!d%nsk_#fRUt#PEsFadx7jdv)P})ybKVw>w>vCpg4R%RU+YZ_ zPQezPPCKFPO^DjCk~-IdD@iJ8d&0MqUpjJ5WCS)!;StKrssl(~9!X>jcVO++m|-T2 zBwL)rP4Kv33aEOPq1B_t>Dpr&94ZK{91kT0`hqbssLS+$NV3o~ks0l?yRD@Ly{znx zLqMWz%bOiFa*`-r8K+k|L&=KFPZDKDNkFSC`p@9to6F)N>w-~ZPV+)%bQlnUO3I8d zK}yGSRrg#z%xM7*r$qkkpiU=<$A7Dt5GDAQ>~s@^0j`%XCJuLPt6zEeWliL&ak9#W zYFzr@7U*2-7?2?bNk|Lowzs|XSJKh|2s@zI!P&dqZv=#UbDSo$YXcr0JfID)Q+5;@ zb`hmCm#O1u&E}Y^>Kp{NU#E4*CdWk(enN;h1uz^CJJM-7MQoh;Q2QKJ-Or_{;f@MN*69Ll>`hrsc8RmGV>3wy!#H#ijrpSWYK-V zmPg9zS70aZ_OzzVxf!g6=7(@A8B#vuuX9pMFrl~_rsttds;?|3!Ol5!{41*}z}+V$ z*`L&Knd0P4y);)-p;72^56`gp^s57Wl5WHSezd1iWIr4Ygu}j2fsx`NG>du<(U24b zWlE)UP&j`{tq+d!YO?2D_NA947Iv0}jovxkNJ>o9`&MmP8R4Y}X1i@_l2LWaV(eSH ze?Pz?pFr~34CL)+4Nk+dUeQxfZK1tSKmosq5KvqhWvo!Pd@N(v-=gs|QaFk!s^~SU zvnnKB>fWs$%}6Snt~*o1j<=3~PrX=TiWemn%lhzFn->_OtT;_5e-ucf++FG0y=Ao4Lp7+!1)Cu#rz-kd^8cug6MBAsSy2QoCB zvyn&UibnAS&bLc1Q&_q>E7UVsoMCR?>rLPA4XAZo7-DwOH>2a-L>q=Na~0RMi)#6? z{{$sT$!Z@>7Uh`|hEI~paPPgPXW1~9vZ{xw-6Q5{f;30qu_XYn+3-R-lTIa)5%LCu zIAxn4^!b}Nm!Y(9w`pG0!+;N?nzToyuIiTv+F8cvwIxnnJaQb*L&D!03B4res(XcZ z|5e8!GRd{PS9ogf#bV%>C-u=-@fRVZ4&DdL16l;TV%#!T!X8@klBDC!cc;=cyD+xh zuBoN7)-KHC4Tf(9u6%RArYX?~6IB2kvxT2dg2|JBE|clpEFjGfYdh~q@Dxui0`}+3 zN^DO?&uXktN#LjJRl6Cr05kxaLH+ULFVmU#-%3ePy}Hs9e~5m;Vv;Ng633%|ZBm~z zBNbDy;|BAES}&)~`7FuU7)^9Xii?TS<}bd{&h-tlNDg~V{;`!l7q~4XVqUktMF4eJ zdL*&L0{@W8rI`psdC0A+Y0ettOY9xxsUc;ZsjQ)AdTXe9e=9PQHqLvA+x~h&YSri4 zaYx^QL2atXe}uyM1!mOQ`yhKj%kSC0hk|wGq~M^^56O|bk*^m0O84U4uu^;vq)`;1R^Ihtn9{rH|O8o9)uD-qv)ZJMvs@1W1Hnne9~bi`y;q~Hci zORybMguZ<9_78`iujNUMpbFv8OMTFwm_(){xph60JOdU(mu71ueK=qDP!(*$1wy(e z;AZRKsP5_|*6O%sA6+kw<=C@Y2xqWvB^DNiK~Q`L$*m!4*}VD%Ctfcow6hbcF1+D% zfBuyA#RG@4Yfg^dTI6+JwYAKFW)nt!-zu8}M_;bZwZq%~aCL4iMYq`d>}%Y%($QUD zn}?hLoTXa;HgO%m+F@^O;nKjIf)ZY(4|`b!t{Ebf6|3@4h>AZy1)VaOOQqse-QQT> z)ffh0z1%pW`w^1v(54Pp{h~SXdG*Uz$Rwr=y)uyeGRo|Eu)SkhA*&;s*c(7nKzf(6 zvm3s6Okdysb6S%bK7Wxf8C{p&VJxUH0Sk%S2!B33wLEJ<{=Z~56g=7q_?S6-*|db z8GNs>DQw!M>Q*%~cO4slf6h5+Do$tietP=sbqQ=v{luh2qr-Pkmj&1pZ@P9L?wFDp z)n{j%?1|tmHKodXAa(!-C|&-b=fR`@87%|B>cd#u&&+V6Lu3$C(}$rc0H2B3QMBDW^bb#bLEDuyTFa0@|&HC6U!{lTQ}Sq zm)l3`*NYgYqrfLJnmSCnXdRHgl2f>wG7?zV!M^VOCF;~&85^t%OU(Y$roC`uRGZ#~ z_X443OKvwkqj3z&g@L!nTWk)<8~`;_F>)#zI1mJ^Jc?&(+PplkwVFx<_G7?TdP>FA z73jYlnd|*{M{_HWswy8g)yN9X8ps-N?xqy6Ig47E#qmpz}B$ zr~AJrUNGx$hsuaQ5q+7$h$CdW#?3Ya>`^6Y(= zx;t_<8osoPgM)*wZ|+F1FC>5bjjJKF;&O+W;S3ABCBbC~KkC49g`O$AR7upTbjaG1 z9Ml)-HVuuwHzrTZQ3Bo=X6h_s^|hPqx_I=H->t%Ot_v#Z%6wq@8a^%1ym9E|9jY)2 zeliD|K2NV&;;uym~@T*xu+RP=b&g9NFtdj5g$Y-#TwRWiFi zYq7W<+L&}vL~3(4$H0EVy2pIDTp$rMmRdS>uZmRgq(00Ik|cOBagZmGyLI87=*4hp{Z&w+xcf$Qh8!= z!|$m8DLwtCZ#1}oImm_DM(0LI|Vq&#LgBdPd4`Nb*XtQa}NghW5$A`3zu zguulUes;6KPn?9lQvmlm7R)ycf#BD&HE&z1Dp^S(=hcNYU>ozRGv|1i*h@8; zS`xrfZkgYEW#ak>nnU3frFhh2bV63D5&2Hi&WahH<3OzLML&Qzotv(2S#lKhH{1a3 zUPZFbl}5)!*uGDx?un{nYF>2noet!RlUoqegmKIG5){_<;{fU|gN-o5&z^TSqR6~u z2+A{Ym-;)X2Y!CYM_e@)XaI-6Ko?K`Q9EojKEVw=>RIZGPnwJ{|MRtZ;kW1>_)q?@ zced=i^7f?X^6!4>;7*{b2GTl0*lIL&0 z@oi(#Ms`3bgLNA>Ns(fE%##t99wDes44r}4aUtgW`^yrQ^o;}7AsyeV?Z)+Dw?I*0 zpKjf=phvr$s*#aVB+0PVu*J)v?O$>C)%wI38kAeyk35ZEE|5iyF?{0f3|@hAQaiKL zz^z9t>RKD;0>(hIK0a1-VPFUF$^TyxpoaGpD zOq);`L}L_Q&)z*9y4J{*qxkOUw6f0qL7S<`?)i*Vn+zy=(&inVht1W5YYZ?nzY;@$xOqC6Mqgy3%ACu+qh*KFnm?UC8=&n5u#8?ja9!s0oyZOCKP*b3^ZzzBg^bg zmQAl~sKW2af`rL9_1m(?yweLsl1VOhSbHh$Vtgs>n_>8<`O!cLq0(JFccaM!@L`3O`8Pd%mNtp zhANleCh*$rqZ)pn%@do&e{jSNP5GU(^dp%kqaQQIX~9<2;bmGdfDJhDQHkyPBP93V zXXM2XV9}dZFV`=PsRP)qNS!WJC znFgK z;*B5iHdgb!hr7-e$9Jw0i0Gongv-9+3pxUCqmWEzI?Ak7|&C;r8l2*5t@GZY{I0p~y% zrUnp&)_d`(UESDL36JqPlh@5&pH2^OFPi!$O2z8RB%?S20&7}Sn+Po-c1qhXgu6?8 zWxD=3GCEGb#&bUIv-TXCy$8K)H>J%tC6=w)+2bO=N{C>~w}KKu-1Lh*&{F^LmHoe} zt-IfRH6H)j`N(RoHujwgC};pxEUXd!)&GVA2n5P8OWDxQ#I)|4(+zR=4mfiQrs~Mw zPTgMN{&}Piw1HNxe*V|}(G3tBC*de(wwF%~DV9r~Xcc(DVL!${R`#Bz6YRQwRPamY zhp)04&kUlZ!(uMuqhp#N$ng?)i}D;blyts zhHBeWd^u+nia07nCrA1_Qd^mU8}Z>57eoY&*d8L z+&+ZNB}PvqnZ+RJjS&s7ht*IWH<3{bhtM@F&4*uNx`3@WY{Lpoo=MQb{L_JK1GD8R z72@?I+WQ8IJ>n$P_P@Q7LOG(X%Oe+|>hf|A!;xxRMY;s2rAn_N;Vye+qXJl4mY!RO zpX)>d-xt#1LM#stPbn*}1q8O&W()ZBO;1Cob?wM@r;$u$PLCH~u;q&SDK;)#7XYhB zSs&(hmwkw5BQ%eKO-B814yRM?Lrxb16O?|3$Yzx{LkP!mfn}g#XHZ!c!1DVGH<~dx zte9Ev|0uyeIEcE5+xEHE2}Qe>xZDgrrokIzAaqus*EcVf*VI4ODPcoQ_=cLqLs7sy zezEb`Y^DpnxI1N5Jmv;7TAWpZ8cpczQe6vG2XL>HEgIEpee}3gSCJ9Pf#Jh-7CKxw zdN#z<@u8yT8z{-%y(7rua}cS0u{WgUhnt<0)53L7V#SCW7#|(&>8J{mV+aBGoh6|R z|KKnTD6CD`s>CT1@WIYDQ-be~>*)%j1E_vhos5NC^l?hk#0m#1NE*(TyOjAl(z` z9=q-fe{p{2{Li_c@?bCC*C*be{tAjcN^Ry-0FSX~^+qy+OYARjMXTNmZ2Q!+s?86t zMmmyhIx{|=d=-dzOlA5@@1y6h2YMLFadwYB7b}j)JTQd7Zi5GcF|JzBA;skWM-1X_ z#}(jNThxaUK>pI{n-OQhmrJc;P6vCy`HSH_(u6_5Mvge>3{5_pH+bsmtWac4@YQ4+ z-xE~zkRs~Q)}oSy_4QB57`;BuSlRVdj2P0$8qFW0_eHteX?kOi; z3DxlbPRSPm0Pa=qbH1Z%W_=4}Onutxf}{r|^r#+c`wW7TY9186_DdKR zX)bRseJou`(>4g+qH@YodOl{I_ahXh$T)AG-EvW9ksQ1Po0TElt^<=6{T(RW_#G%< zkF^{2PK}-H^T(?fXKyEnW~?Xhi8J5xn&x}tDh9NhTYP)09AuX}3gl@}?MU%Ev!ByT zycuD~!UX2o?pEgqb;ROY@zih6Dw|4ZfRk#E!9n^?P}ca(c8`nGHf@e?zfIvvzK?~{ zMvb4=X14=Zoy3lxD4mxqeWTvXbWJPSUuTe2Y}4*?JTsslM*&`3Y2DTHsNH($NA@(^ zqVPgnIdtC0-Z`UgesFrhiQz9Dm;9CA=5_sH=-f)=Sw*Q;u3Lq4woIAj>E#+vtJuPx z-+=5(D~{+wR2Bh`Gg(FO4n9Z~E2o^Pg(%d8A+YhZj}i2JGWBisO>%#BL{;BCX15|%X8Ph{$bHK!`jw#58&@hrgZn+t8^Q2u4rFyDZ>p&JmUW9j0Kr}qOfMl-}H)kpJ^@bR4$(Q7|CW5H{^Ow-vm0ncu+pT6@lQ*V#Cb z2n|R_UFQQDK~**0dg=UIRaZ}`@~mTPQIhL?-OIg@D-R6nbUsU&^6*CAn5lG*%-dFn z7#~F3Q(>qe6oJQYWw*7A;2VjMS}mwC4^R;gSzSvEN6&}Ce)2&A0WYBDG%@=0{>52~ z07X5|Ox{Zai(9DSC~*R&i>2|uV9K|%lQ$l+6F=Y5MZFt~%Rcy9WEy2(pG+tON5{M! zIqmiLoGun{0?zyMTfuc@e8OUHK!IJ|G{6J!Q$4IZrMtIPpL~UkCULgTs=A@|mQ3Vjjo_U$zDtWgBv*2S*x^KcgY;9ONZQkVtUV z*#dkeP*d_9X>7$^-a|=)M&zsS7M>uf0#kP;H+t7lSN^Ygb*l~|*Tj2&Dgwt?;JDk4 zobzq-%Vrk)+dZzY`_$T}2K;Ev?g8h^AGE9lM?Z@HA?WHk)GZl#rP-8@llxQ$bsxIc zH@{{~09eE`#p!ztNOB(J(rgpqZD=U z-u;n{zw86kpbK-rl$6$Y-zb?*ioGwP6FS?e=LlxSS&~JzM%1H=Rd;9DIBL1;W5*S2 zrQ7H3B~L6qaYk#lB1`N6)3|f>zqT)wYfPHvd%9s}?jcpBE4!grSfzp=+J%Q0`_IFy zH8@ocu-dTX6HZ0wx_%=-u>sCJIG#~l1kA4DfYS=X+UzYNb>$=QFfuVlI!Hboa3dIB zT{X4ld$m%vJ^gf+cxH@zN#*v_Ghc!BNtSX$S5|`>AhX-r`+Vg2NXfS32H{DgJbtXnj3XBcXt(PE?w4>v)4}? zEwQ$XX1kc6E_EO5x>xM(#p@06k1ka$t>z`F;aP>~+HS@0%7O(7I zU~dV1UI5n4b9`mYq|~?|xOewOLTPovA->mS=K*!{tZkG$OfiFGLIqN>0Pw;T*@q z`?1nb?JpD6b6AAyF3Nu{p461JAK7p-btm{N(GCh>uN(%{dd41W{b)>OB?>3v=Rv=@ zyRY`v@<-H8l}`ls3NE!B-XHkr=~hhN-5IbGNl4Lo^gcdRAUM4h2D zL+Iec$Q$L!dw#kv{6l-iapjxGuEKYNvXIQ}x7(RpECvBWxKZfT?S}K!2iWK0l@^n% z(DYw%%B>ezYxz1C0F(_eCF|I$CW;H2Q3JTR5Vv~vBAyDd*Q8_jE0B-&hpGeV*vG_t z9_L)|tqURB=kg7{N+I@8E2moMflhu2b;QpS-)9N2E~g{&w+4m6=R2`IULLHeQeP%0b(i%Qcl2W-C+bYbIBdAgN|tu9ciQ&GEW?zvms}#DYh1# zHu5pc&O>hJj?2VB04zVnzL$r*&Ctq!pq*JSlNHy8Yyu%fqo#|$~? z&^lU5TZ5PxX+U2+&tMe(f^}=af;cM=GFgWX0XOdUVbuumwLR!%cmogkw2q<FwYRL^x|UCb^jHR~ekFpu!+t=| z_Fu*o>ak1xm(==vqF8?%dfn@PK1-B96hhE_%P0dZVQLP54d*)K^9szj3B{C)XV$et zGRrEEoJ9QQ2aS>(ru|{g@urzq(zkbQ*{VENt~BJz?6$oC=!3~X4`g6>7xX}1&1m$k z>>d!~@;`B=?S2|oKSS{2f#@W_qgc04LNIL|^obW#jt9WH5oc`x@$jqzAjdtkO4Stm znvbl$FJ{!)IRerXLVX{yTk{?2T(f$j~9ybRXm!uEUuzGLot4_ZA&76uHDFgjz z`^UW(0=`6iK|Gc^s~qsH=7jrfk_3`7Of-A+5kAw6vOh-3=NRs6R#p|*ggVK=o7vIE z_`DIU&7zwUtW&U)Uc!Bqam5YTp*1~LqZj}IavF!E+OmQ4MbqXvBwJzkz=uj~2K6C= z9UAkYC2&3ln4?3xGoil#KExYOw@{a15*);|bATMI{}Ave*2V)m)qTdin5i#k$$!Po z?~a2*6XOroX?(k-{t(KGPTb(Y=fAzfkJomV^&Uhh$HE!?V@5s>y=~`FY9(hXCIfps^jQIt54t zL1KWQXd+u?l!q8CGaJ0fH**-frApp>*E$U{-kWi*!}x()A6(9 zJ6;zL*VG8kjBw{;(k=%U%@q%~&l}X=ry{^XyRzh$s?JGGh)RPMLfL&``G_W@V5MB* z00sTSa#yXIMpf3>oxM9X)<;(`mxm64iY+%vzGh1Gb|!BgPN9O$lyj5i*&C>wpcuu( z?j?=66fG3eR8}z4Ota&-#zboz?v^!@jNSL6_PckBGxHjRxD2Ri1f_6xRCs zEOgscegF?CIv2sw^rK_@fWRRF3}6UT+5qS>hzN1`OcltaIG%o*W(y$2gGu5)fW^02 z2?2oKB;8%F^|)uWSm8RSV#Z0OW53O?5evpax|KDZ12QS_usi79AK$B_AxUc2@wI?; zkG$RPV+k=*^BvFqaicJGlvUW~vn2Ul2^XutEXtN8zqGKz$;Y>g%lU!<5zQ7_t1AEc zD;bA7qxyM^M28~#J8^Mx*Iw^WMrmLI5CyxUnU@9KCUUi6#y(wjjP&7s__(*LAW6U8 z;Y<8br>}4VP1&B>9oG(E7mjYQxD1Oahg`U{p-Aik?s#%TtSGWx(=}}5Lij-T!b*Yu z5If{nEWJcpb@U9q)Aqcut_lxqEp?d{(LO(T@K3>c{eJ|<_uzSO$in!L}^LD*Q;Lna4~FYTex$bDyk9 z)kymC#ML`_?i%u0OYTL5lSjd6;>JfuHY2(vO~u7|=E(RDXWL%|)<8nS9=?DWYeA=# z(C4A)v%s{)JX0eNM2Iei8zN5^^9;fcO%6vl0VZ^cn?O$a<&)=rRu=-C=QykEe>jBl zKuSx}aERHl$He7PFLtV{486(L)?0ZD+o@`&n+lS(H1w+h(FFCrw=NvjzCQj})y40a ze=~G<&7VN5wDXRzkt*t>4A$q)FeNZYpHJaoN8nr53>3yM~7Ke zUS|6IM+4)e%#t19tJmdvo~9`%Sq?I05>9{H+in$Ig8?C01@aFPThxS_6Pr(-1EY~sCc_T-EM_>S-Kkh#yTI$^c(nGA zxIy0|J5%T9QT9f;udSZ zU;X4_=S}a_H_Pm*kfp*@ zI`wpm!{4@7Rw;(C=*+=V$eh%_=MN3N?ldT%>hOJb5TWOC*p!2D9{A9aq*>ywzI$H? zBcue;>Gl=t8_SHVWRLa^H_6(T zKr`bjrfAZAVl5Ren*8l@mRA=xhXv~-09GY30Xd>z1Fki99MheEq&Zj(>yaj5WUN^;`r67#%fQV@zm}EG1*!!xwtkZRzgsYPQLOvBT@|-x(0(Pjuh+`0_VKE~*YnU`d~m)qJsmD6eX#+N<9AT8zQORZ3TyOG^XD z_^Z^Lfv=^*<5$#7X@>S6`s@m8Yi+8_6z19NMh>f(Ho?7jCgIa31k}N8aDKq~J+O$! zLIE{G9}na;kVeK8(oJew7Ks(jf%VRZA^+hgy(oD!V2WiWuFYlsQt!DlM}nVOYDzMqFK zrvWZQ;&ozchdgy+N(2c&;63ne0ceRo<+_v%4%pFMp(1r|kj;^TwevFq!JndRqFo-V zMV*;UsQL59>;n&(AA3Ys5R=Ag z9WX6o-|=wT$jRj|DiAoJ{~fAku%@MFq#Qt3MkaYok^cj7g5W@i1@(>+6UYq&OXH24 zN-OP|=h>~k0NodFH#sI3Dqb#E35wBobDw zdB0%Nt*7Bx3^Hm1uBc9&4Tt!%qN~JX&Ow*G$Xtdn*+lBJVALtU1|%N>Mu!2z9)Nc7 zo#yq1BX;nnBri2{m(f%AI$r|n%#}0ShM^RepkZ;XoG`64{Y#Sb7p2UhYrq-giNL7j zo3{5d1ZYSC&Y#x`dX~~jR>_o(pPASijW=6?hg2IKH=`)ZpkIwB1GuvHl)MgdXQbLM zrzlt%=4jz3T;p0znmPA5-Tt!2s_l-#uR5J0`V6&Vc}e$5(CT~)g?WLn)6}#?I&g>w z627digm&j?SpDs1yonp=q4Ka8C-NBfQ}UAlh5H-l6en5*bUV%s0M8QO9DMwM=|p7Z z(vNx?oNU4ufQSxVF|kbk5mm%iS)`aLk?&E-dHdoW_E(drSTRxj-*!0Z$}_APV1xXT zNVrxu7U{l9v_q)e(GUO9zT?VY$XQMw$Z)nX=6$f*wMqsKL-&&Cqzw;sXiHrxmnE(;UigAD+O`+LBTt_WLx70z#L`+Zk`NJRsw?=^f4^f zex4kbJ(>e=nBaj&LU_MB-zG})sn@p%>Jso6$v57*FnvC=(0kDxl&~|gxgLFzTnEP! zvR|V8!aW`K8nDNno$ki}A>()>uNV&&V4Oi_7wU^LJ{mV(di4D|y{2G?WtdR*s7Kh^ zEO4_0x<|K-)O0%LsYBHrkDZ62bHBTPUsS)8|9>SLM^A=nwKCWkG1xdLgb5GpB5z&2 zPHB+hLFju*&kkMoh;dV0H-YGoD>84D*FdPZfV`KGoitbo9(7O+=1o;fw9HUJyje7) zoF19kMCu{^uIx)lw>??_S!$I$`1u)7!wo5mubj^&c*?rC1v+twVlJLZ|Hd~Ml(TP^ zO+f*e=ie8mYfWs_sho&u{bpQzj9o(+Uv~(;i$qUyBvGq+DOA5Esv<_oCjoPUH+|WD z@+=;wPVA}p0w9;H$u`kJmfvgUX)fM6YU#Tu^&jq4{`%7f;X8>WB^|;Zx;lEh@vHe& z>8q?E;@x6|=uyow>a*1Or-51jX+}}XPV53MKf%pkf!-B;$cbh?_2?_RWq7me=CWV+ zg&eeVR?QXc+4K*fb-|2ruCESKvs}&phrI=%{Y0{#09B@L|C931k18kzvicji;O| zb#x6AB(__((Q1UL`jku-GQ6yABz{|#&1EZEjvDd}W~+LfU<3(in4*Nim)3#u#|LNk z$mSq%0Lq~XJ+Hy^+U$@?_gwjj_f4+r+cVF4n4Vw|0%9hJ9`{}FZa<_3PlEkDEuCYDU)IFC&x!&ygy9EHAz-Xj^;)Fl&)&Al`dE5 zEL0pMI9%KkKc##gbOkTvC*a01!@Gi5{Y_v-E`i_G!Q|BjKZ28=wIcQLL*f{w*Vs?@ zX9CGGEb8M!DR2W@)x{swyYmecLk;z%dYe=l-hlx@IML%D@aZ40FbI&g4JLtqrifsW z6LElgcQWtX>kO{n_U-0Ge8J!&gviGO+kd)SY}Gr~K~oKbsY_m-j6 z<!vma`Fb~dpvF*KDrr(61h zRCwh@wcX$B8N5uLQsT~o-rY(3 z(Fr=*2*?W{^J+ufi>edEoo!U-3%^4^N_3_*1!1{Z2TN0~m6f2d)w}i5F1FY9yN!e5 zse#6)Ni+Oypd{lR`T{Qb@xK0LpwkK;_^3BS@$vmjsa}?}8C+NrQ62ZJMrb*s(s|QT zu0ZM%Hu%1H+*;oT^3{H{7YA7or1RyoYYG2Q<~Kn@-OP;8^2PIv(D_pr#h?(-P%WBZ zm%mXYQmE05vwE;#JtUg%fA7|dKhc+kunsUl1%(u~UiHGK)uFtQUq7hBE*~DAYnTU4 zBq#UPUQw^Ofi=_9WHQQF3B6N&4w@_lU#3H=9rr%Ew(XCZX!x-Lx~oj!=DZp4NmVzZ zCP3SeCWpwK-UI9A*}RjO~(QjqQ<6=V0ooRll}V4I^?OgPl+u z1RX_dtjE<-dvcJ0q5r>skM2jw8hCU-1o&)MVD!MB2RN}~KPKEZK~txHkSz|$Y=q zKF_iRegl{G_s0WjFW+QEasf-HU!eAcg7Etb(66gFf#&Xc?owXH;Yo|ILZH5Q$4#*b>i@&q6NJ!MJi!3fk^F_`lM7XG)=BmGIv7GX7v@%3$39KoA zRmt0s#UO|!9`pd&qkUVbg$|zk98AlX>$Z-@4pCuvx7^F43%iGSvYg$181U%0NVMY! zm!+{^?FAz52Sr8VzWDz|o}TD6e8N7a;N?xgMX!<7B`@#no)I?I&hER6lQX?2jMFrI z^yuhH|EOB>jdmvK_Wn*%B}N*fU#%B%12Y{Ic>|HOOa~F($J8q~(1fX-zN~k9cMCR- zAjOD-qZO0TItuBpb`8c`E9>hFgr^JDX*`aluNJ$~JKU^2yj);4 zGA{y2(lJ9pAQii5-;pkB0e0J=$?2K3O;CarE$0HvbXuzefgObM0w)pjYf_zvKbhRD z{WyJlKXjMUj2u>5)ljy9QtM0(NXjM=_*qf*v(ATo1!7s-Okn+lv*DKbw7(4-zwKP( zYh-=+LhY3677MI%E83suP!2FS>2r+Z{n|ntDEX6tx|fE*Gs| zNgvjUGrIGL^uqNs+TM-9%9q{i-?&h|22hS`>GV#tiYZYvV`x>(wie{4cn@i#{YvM- zQ0PZ5!{AXemy^a(t#z57CJo{I+U_f8!laNVsq2qkXyOAkH|x9eRhNwLX7Q%yl?a&o zVlehPDcJlL3M>v9L8wk^y`V-=9rSIW7M?cD{1K=@u9LP9z$e1R>^`44UK90m&QP)A zy9Ta4k7;g+e>+wSbbMD~Qdd;!-cR43MQ{qgf5*&Jhq z*Z&K7F3zNams{i_Di&iX4_!7}G27pI>!xp`9{M5l;<|Jrh(6*cmF*HQ$1)!?t97!= z%^7BMatOY+4zo>R}g5_;N+= zPIZ0r)3E#RH;4^*-MIbqA#Zy7`Ztq!*% z6R>{D`Mn2dbVuV&y@@3#N#cL!%K^IRv(V*mNHin_1wL40uITc7rcaQ*%lUqy*4?CU zoXbdM89@JzEH`@jn0Tb-Tm8;8!!;?z-;tbH1THIwwd+<2D0W>Dq*C)}9U*$r(q2p# zc?|4hf!DR{BW)V7`SdwNue{O^KGd7xSYIZYqgik6=S+MHxfgQxn#;mzT17SHb=u3& zh0FT8qH=57iH|lyv%nU zf(HuIY@od#(##Keb|dIj!8AEcOr_=Ksh49#`f(8Zll@gfMsorB+>q^DbSJ!_RtTJD z@Ft#K0JNB(*>s0>>N}lPmuiTz6I7Rg7tW)>O32U#OR4er$=d}dq_z&VgvSHcPuV`j z-)wc!1!J1qIpVhAuSRm}KPM4h;8QJ3KjC48U*D`+bjy12{2EP`JQR3 zU?$7W!g)tD1_*KR;OS;4IIB(DC79IEk869?rUYM7*_%E5ec(9%u&Fr#UkPh*i~ zyBMOJhATXCWha;}Kl=oRyZFv#mv^B-G~WFB9P8Hb1W8FInEXJFIL#Gc{a|zlavKqN zJ5~ZVrnU7*-D*qLx>!Q2?6qL0c^a4}8XLycHFaWtEj3?~RjvcATJM;u|97Sq0lyCP4pTgzb~UMJ+|RlmOJzEn_lOZlF6x~NWdCfA=3T^gw_+M?YK z5!8ycO)MO)wH1=*%USZk3z~%g1V?f|=~Yf7nT}qz>l{+vzTT?S|Is7^+Scv$)j-mE ziB*Qn#%jB(`H=UZXWZLWfKS5#XO+9B;VWbv2027Y!z#}JOLPr%$sF3Ja&rsbJX#Y~ zBORAhl$!5)_VX15K_oyyv`+p?_x}}}xGh+w>=SX;5M;B-crNM{$GlXjXO>imvh}D- zF0^-ft(@>+SzrzHQg>u#au_H2SL;XTnayd@;|HGE6|vt2lT|T!+dy*P(%j|6`-jBw z{QT0fx{P(}+rP#yd0eo=vTv&FsLSqAMza#a3}`1@xR_SBbFTn!?=(wbnq`oZ-NXeAY2g45b^6%)2rLh(@X~R=soO;~x*;A-Hsr2sFRV$WxgS_EU zJ{*U75j&CQhoq&%DZTXIs ze;cK6CVHTSSirUG?LKl7c+8j#qg=R%nJ&4~zFmIfdVXOkGoew<2WL~6q2B%Tj^wb$ zQU+YHnoe;E=!$}9oN}=ooi{mQ3S8*{#R~>)y{CLE1@>UUY8KcxulkJYhjXs&?hs&%={yYf}u{MyX+SC z9zBDv$i`&cPzG-3(t7Tqpa)o|tN#!^cme?1my%1epL60mim|P&FgSuF)*#X>umEi27G!7E($=dlov4pJITX z15h#xhRYi?>R`i8g%kOL9*CBg6Q{MA+1T=5-!#uFF*G>2gs>>CZEKTr_zX3A!cXKW zRdDK>_G1gy{c0yM1I?|gGDR>WG5(UMZP9^M+^b!h1%@cTt;Bn}Gta2WKS^duZXYR% z*GpW#oebczn&XG4=RNrgaz;|%3U2p%gNM5?bw!qe8yN+pi3CYZv0j?PoTcHoM$z&z#! zVD`Otq3o)$#iYV@K)=Ul?33G+DYzqKG_^^;*k$j~1RlRc7i~mc{!8Xo#fVW0{%hBR z?e%~fQ)`o?Li)Q*F`r=tT*W)j?h=1z#z?CPc3BIU8Y zv=9T=uLE0RDK}R<)cz82_T7~^D28HXSK~q-huWK+@=QEg--sqHT99lRR|IxWd0_`n z;$ec)UMnULu(!qupjP3ESRqbY=rU360phEUG*)SV{IXqMFG(+6$SDSiz0 zum4SKW872B3V(^s?xc!PuEKXzfAXBvq4_F~(3)IYtTA$?Y_)?saF|x#S4jZo7&)4p zk_^i}pR1JM9jq*&*blAVdE+dQfym*Iw7#Y=dAoUBrNsejmz*=o$02zApQw-tD@+fO zjr!zJ>B-txUjR^NosT2^8Ht8JF(iR>Q96 zqogzp0Bs!~1F;h0RCVzk<*YhMsF?#}L^=tZ-<8Gd{hZXg=i_Y;c#wbrd-nK{d5Vjw`Q5->~L`9RNLhdq-| zic`Bghr7gS)q1Ef+uYzvH!GRf*%)5M&vg15w;?y8uY=AA4zWaq1L9pIJ!l9ofZuQJ z#Ad)$=Q*-Z_yLZ!6P{d1Zi6(Wk3yKKGGV}E2Y+5M#~d}-CPH!}>05u`!kI!}n>Xo& z*jJ^ivWPbU-`!D{?4A!$C1;m7r|)D|t(oTE|DNx~@oQrcttjLDO>|{nes|qQ2*;6X zyjNjYDtax2#CJddeXVWBgufCJWyuI)mrzpanG8J+7WJv2EO{zOSl2l^s(smz$pAa- z+&qG+gMwT-gwnJ8C)^UJ*#(_fOeHN{?{x(05?<0?KO+Y|aSVDP9ri60{x*a@8*vrv znxS3++i;M9ZHOg00j*n*R$x8fok<==s_l0Gj$Kg~v;tTWg7b;@7#h4=-lhhmjQ;{- zhLhPCUbb2X#)CAcW3NHX(7X?S(urfLXoRLsV8+gwtSCt&Qw=id3U1@cUVt`=BY&#zab-cD%eSS7$4AoPLUnfov0pdMjCBexY*{z@`6o71 zhcl_0NSlA95HuJYC4mxmsU1A+08n0YH4s%=mvq2y{hu?q(L`V*ugwkFcnYza&sG8D z<6JCyDxah~s9#q%s-xO-l2~fx0(ZZN#>egI>--C3f>Q%;Fmf&%=yBW!De>-A-UH3q$D=dh{M|~`{N0vu23Z0W^0tp$ruYIWF!x%D?6?7aTC(?@^^Z(H5^k(;8nZzy zSLkpeRO_NFoe4FpIhf|qF}y3)45|-&Gnl}SY6dPagYPnj^x~ycSWlG@^==MZ$&2D+ zHP*9Mih0?0_Mudxa5x;yP%$6@LIAsjZko=e4uBsHtGCq?7 z4EhEFE@4#+#C!>qrYnmRxJfXHs+*X0WPA9t=f-sd6rc65SHO*vPb&l~ofXnMI%86I zkNPG&G53Yn&NaeUYIEF$aK1U2F}9_w+4iNPv#FvxSy4|9#qM5&-M$yfe0Ic^`g#w* z&?5P+Gk~{XeHo?VEE2dwa$@aa;MIwGR_`9rwD?=Ro)TC!{{jwEIe z61ILi!W6)eYz%hrY5t@@V{vC%iAcyDg4RnR0?JMx&Wy`0zti>W_G5{@cPt(yuO$x= zk{grBisDk12Jvoy@Uv_I%t8?MHM z1MW)6YKaapV0y7@5quohS%341NQgRpfdG-Oq^e0Z62>0YGF*cVPbz%pHq znFWM)UuS9~9IFv2_y5CY`2u6zH@0F$PJFFj|GRiMkHjX(p0VEC%xM{X_7^rdW~ZVN zrR02bW)^rcGfGu*r8dVv0%75n^lr!F0G=|Hg+(6l0Ri{8nM@4y6Goli?#Pf3UP|Ew z#eCE!6esuGO4UPfxdt4~5aPl#b3T(60Dyo)qGZsP1F+NUep+mingQ0p{^(!DWyEO+ zn>l1DhczCMiCcKP)Cb8>xwL97tBXh6GWegsPMwk1>w3a_{%&FnQ{2)fjJ%dMZmujH z?c{t1vG|tpMPR&lu$`?^DOcUE*5mD{APw9RMV)wqc;>J1Ly18Z9*#+aHl5V<>Ym+x zV?-I_yyyk3$?Q)E6relc=67g!%;)aLt6wZkvPEr0;J<9dur-?Ch!}m{Rli@eJ3vyzUiVe#L)6^9y3aI%jlnJ%saHjA?t6 z%;sU*I5}Q5zlalC|4n>ZU;EPDt5y;=aS%1>KT)3}V632lnQX-=(=h8|zqnLZ_P*lr z0O|saY^(ucgGW4A7MY)+p7qG+hw(WmZE;6zegxR7NAoOnvOw>|5QhGJHi{Q;ehzNM zOrDOT#|aN3WuNcU<=z7~v*K@+G?9{T_E=a17W0T=>Shp?suokp*2*XE!9fH^9mZ=y zt?oxh@6^S~;+A4pO)9_GFu1G55!$U`ATUd&bO*Hh62Nq%sM_(O9EQ-b6Y0P>?@@H! zim$DkcU2nf4)uB)%KF92WYO2QIMaY)OKxpc7s9p+XihdD%6}%~CbRjIHXn~VB0`5p zU&A>6Zb#(b)%r|~;0a$MI%&#lz#1h-*(0S9R3oO5ZMP{OGS|L}#zqV@CV)LUw`Bx8suag=X2&+gpdl-NrP%yO-^i zGM0d5T`DR3A;xJ6T&+6sw@FRi@wjAfUQ7L_nr=J|qcpVc37QqjCYE6Y3n-DkI8O1K z#O-v|2en?%7|vU`sIYCD^aK+L(n@u~4*j%99su46H1^0xC0_TTw zxU8t171%-sdF7@W7rNyX4W~*OCw;yk@EItho#MW76VX!XPxc?gdGI>w?E;urgL7OO zQa*@w1ew}jw%8dN7LJKBM;hOgs<;Jl9tM|OAbKw#zL&sES(P~JC&~N|>g995IzWC$ z4D45f*$6S@7lm?hk`!Du9Bf5d6l81X_*e+_R!ypK+i5_fG&$%3uou za*n#;TO!*gX16!21(2`PS!oQaJCCmuuB#e?o_FIZ{H0L|b?WZd!sLPKRHIY)#Fs38Se}hYg|e}p_ogj(?U=>-jCi> z6qW?ukaK(Zu+rjXr@iw5jTjgc*_{7M>j>68W#fZ2)vLOyUmDEu*0J8n5UA5W^+i<# zGAUQFm*5)0tTSJ;gQ5LvKULnAaVGEa7oDWahqpq01EqQf0u2OZZ6|Z*NNs(~nw_)A z*??jlbc~s%HH>)Q)(eTao`cQi?xf;QYq`R-+RVAkAh68#xBdBJ;yn9sn z-brj0Kt0?$9f$g}&Ns~urj}p^@^7hSggfzf0rH*Um4UU2QuEZ8NI@n5ARXK5y@HQx zV!3JZzjQhr^h}0DplAC2Zg4JGxZnJ0dJ4#2vL)B zhcr3jHIH7ppx*40nEE*%nGT-OXlB2XIC+LbOnk9g;9|5;MA6PJ0qZff0f>xOqk?O@ zBdYwZ#pBvG#&S2LjgN}=?00vsF0g&f;$NaOlgNRoe|X~q$$vFjou7ornN&l++_)`7 z0$z#SVS&1DO>NZd9OWDTpA3`iI@Sj*LY>@4k|8fW*14gtYM3-qy?xh^S)vi}*njC3 zu#J8wW?t^6GdJxhQAgGB#QHoDW(u~WsI9sClQn(?X6@a8zm$eX@rObB;* ztu7vKKC`F3#jw3cdFA;lOE9q7(`4=k%a385H?LJNEJAG_bA2W<6~M?o7_3wWfyev> z{KOG!vj;IlgkUO85*5~Orya%|+I~8J&nJn=?NHr>XKm)A=FHQ|9h0@uRh)g7tfi9X z7aj$z(BU{HEJN3P%lw(Czt7~&0C&^q49?o%p%`Ohc ziOQ9CbC}diT>P%u>4&XfT03vc!#pmA#3EsaYY$mY#-*DI8c($N}$>wi^{+90+ z?~L>frcgSxD-%QudiMv33;5o29R=7_279Z2ERz0}#7fY0R8xTAlgJ=rT=AicNiJd( zd91CnQr}1a|{UA@M6%e+eQjZ|yogg$84jp^u5piVD}d-uT`+_U3OG4^=$)dajc@&7LC90&hd zcC7+2xzK7{KQO9>^mfqoW)(x~AAdeNO5J+-P&;lLj!83ZD1GlVyL*@0nOFi`>;t>Q z(n;IkBHuKzEF*Lx4xA_fOZH%$vRX(H%wc_W8?1=YVCDKWJNH-v`)r?Ev+Wfo;+?*D z48F`e9XUaTVDDYS=bv<4A~nTAzcXOizT5QOJseOg1o`J|0%@*U{s9kX&g-}Pw!0pE zGJw@z>TFw5FQxfC;mtaCK+nPy@5{irs9X3NX?>9y#FSkJfIzo;OOyxX*_Bv?Zuv&KKju<+xY6bb?+;$A76C-6L=UV zN>x)CLLn4CQys1h@$F>Us>qO}J_I(zAne4HJZQ55KPXj2>EIa&q#4`$!yvHpZFnhrgtEke-M~2w09Tfm38nrY<%f{qoo$n71GC0>(JC zu-oE4yjOlEFX+7_)+>vIvLD>9iarDLmM=qG4u~ zu7h*`7o;n!4?r$XG1Mr?H2d#J2{Ox5(v6&i!EQ-K9;z3|ZZXdbokV(wdd#fGw7tin zgNbRkf8bOF1PZ-Ubf|49s8d3w>ip}lQGGZ!J%xvMW7{+-sI9omhDlyNlA}4S zWM}&o@+wu)6+(i~XjB?9ldpLH=!@cuTc4d8 zS*g>;g$vAoC>Mx}6pvM2Wynx@)le-|=OiZn@kKb3ba)3{F`N{dzI>JwJmTDZ5pAi| zK#O(OAkLPCVCm5P0r>ESn=^kDenDE^k)h*o=G>5?8B3>dptx|&?Rba&S>Eo^p8F#X zd!tXLs-Kevps#lJdPTKNk5?b~UcdIagS2I`O{S>)aUP}c3UTI3d$8&J{-R6uC*!*MA&dh*4%lMrDC9Z+_($tXj zNGZ%ri-5!W<`K4oawwAQj3~Qw2$em%!Hj*FF`wT(o%224?Rh=V@4xyZntSg1yy52g8 zs9vDM%{!Ck(|rk%b5cG`Bl=RBCbFH1mqXrL#MiQQf=wa+k(Zx=ysSBbV@RGCN#Ixv z+T{Gu9T*Z+`L<8=vP;F0s=Y0pN*vprN<9JtmVVe2cD23DGdLo(cBI$k{qbW?XO1cc zD!;MXfBZ!XVy4SFL&6ZGtGl+j)_qMc4OT-MgZ$0YW+X_T*3?wZJ?f0YS6-;`w=@>I z3p!tn9yPqW#y!B`D^$GF3?r%dPhPj-$f~NfvGLW=6J&)|obEuU;ciZI%VoKc(k>2V z4f7$1RtBTy{;yjQ`2DFJT&DP+x8S*Ajk2&wwtHYq*7-Ks(j((3B);R^{^jBc#R3Vt z5K^7Ug(qJl{nfCVZipdrwoFY~e7A9ltq`!F!%oA-+eI`@XVEzp_Y$^AAv z>Dc}&r6&`KeSE5yAY7MYZNmc7C)&s3{?24Ev-IpO>ZbZn zHerRl{DbLWwfIb}wt>cB!)Lg!@1;cIF za`D4r>P4MDdPP2^fV!;pl2k0h%gaf)ApQEP(I%)yl;OeD?=v}lhINWx1c@M!*^Ne^G+Zw)rTylr3|2vF_tpp5xUOj999f7 zIS)CAPr?0<*vC&WGDA;#hH-g?8s~=`yRIddlt=F*$QZN=y4%&5s5HG4`A+!Oe<8PL zZLYxlcvFr^PW;*23LizoU$-AA@aEi1#O<>oL_aBq{B64p_??WdHr&WI|HP=~G?TZ; zH-b^^y!9*a_)}8(zq_IBEBwW-u?>K^#K0yc-#Y!Q|g@HWdFVg$JOk*Jru8}c?RM=Di+c5bFwm)POPv!j3!#TP6 z>UKy~$$@nDb4yQN!Ye<{7U_h&>B(QYebe*`PX)?KqnUPh5uhyxjOlDoY94g2zZ~*PEGUD<+T@i zL@&Fs6|g?qi*;LA8Jn%#?2GEvtY{ThYYJa^n09>kH!ou%Tspb0wEA(I&8D_uY*NQk zEwSK^kKpUtg7Pb4z2{q>&a#Ub)?8oe%KK{W5q;jI^Nil-wPeC67HujlYUP44q6>IR zu)&mgH5tNXX~%8<(qdx)ZZz^_6z+PsJ_^TtCbEzF%)~S@TsoGcj*_yChm|Y52WrUH zf{VoRjixripEhhJDpiww%EbtWgTRP z2Jss_%*urqX7EeB2a_1{^!w?Tx!2;^D+Bi=-nG!uA+40PdfYAB9w<7pIt93YRcBl( zA9^u2oY>&nzOk*_!)%SZ_!?XT=JE_%sxIEkIc$Er)?&?})WQ`XOG$wm27L^Hf>R*zidI5s^ZD(M3F_KP&eWp6u zml7Wd%aGeQHyWPec1(-qg!^DPEB!vX-C+veV!l~IZCZi&_hutJ<|e@=X3o6ZHOTkJ zE2bU?r&Zp{;}&cLqBOv=Z%)Dt8bIU|Q(`_ICQoSs;aO7c;7ZcH zAe#aCX^eS4<)B3tZgqdnTpFgld7JINjQw;ruw#_W7P2BkXR~(S~(L_L_3#rD(Hf<3`qMHD7JjCD`S! zY_7_5+$(jtok3l+z=TFHmr?z09%5Q>!0cekp^*=|O1=9!EZ~)h>BNeKipuRyHG>Bz zVp1KKb|fFOYv@+UG1>3aZP)nW&Z|^X;TD7FX-bKOv>7%uj4?u%$+ry=WC`gz zFfFcQnEE4sbFRi&oU0DJRu6|4Lf1}igmhH&g1EZ6@p52fkJ7fAOC=0O5=EgPphs14O#ct9J>yoU z=NCMAEQkgBj~+-fiB?y7d7hTdweMj1Ri6nmsQNUYi!qsCn5s4Hy9m?ufvo#~;A4Rj zAqEaloP5vGcx<3=++|}!Vc6ora$Lyt;SUX-$M$b@jSl~8{oQXQKxjC>Z`kt$Df;$2 zPv5WSAOfo-FZrbDrGB1XDSx4DW!)ag(c2`fal9?azh>9T!@zam@^0mUXWuB#Y;5L2 ztdFTG+Qh3e99uY)?VE-S*4fMsecPhSOa(Ju#QQO)KfUp-;y7`4TS=+-Tw#s5BrPv8 z=ndxWHNQ0TgffN1vMOHWI~3C_=acSd zmE2r2C3}inOB43~K5r8$$Npsl7DjBhT|ayZ3fsa5PR8FvlpJQ|M*e$Hq$c}50kD7? zh^N%QiY~$h?-Zp|TxeIY&ASibnH%eZ)+sIux`Ia=Zy3tqW|v+K6u%TY2B-(MHhHAc zJE4LTKg8aE%vf^vAQQKLf~+2;obkuZEkmEm>U&7?*z@8{CrtI)16rl(uvDhVEt2xj zXKV@dwtjgd$pc;J?XfG=Pfc|<;MP9M(Jq_8+O2rlp)NNU(V#*%Hn05d6jX&-4wsX0 zeYrf#0Z{ix8XaJQFKIvYVT^(U9E)n#=vMJJqcdZ#3LwRqhpI2n!I zJvGC?r{7_0efI`jW14T_+rE%mTNR;x$)vKzqk(M+JH(d+{`Zev*_t_lt&WSKX?G)L z_cX|g$uB~}%gedh4C%ldb(n9LWx#&lrB5q^+>Nj!nxU3 z#E-i$R^3DFQBon9BTYAaFZegSulQhF!u5nal(Z%~r0EIYndtiS0odEi+26ccZugk{ zz=1z!v)R<_0CGhCJkW!2Cb*-3Ulg*0sEGs|tp7nW1wJ`LEfqPuX7iqUv~OFcCPikrrodXcacFl-`eNGyY+UOgvtoTowprp3d-!B zFc7*RDM((W(g?#lYv0h6y2AxXnk!*DK4P0APwRL_9NY?Br5x=&1T3AdXZGh*$B~#b zm})o@rVQcV;&RpZAgLmuGU*JqLyONB&U|cqDpJi^g`pOd9uCU&c=u*?2rJ z7@gGi*nPGo*q#4^X7|MFvCF@y=Gg@`__UU~dHwE?1#l0u`KxV`?^xKqwP7`q?C4%I z+0?|?hPPigcsVE?8c)YY+xtI>jMFj;d&4Ym7F+Y>VT30u>(kpMuT}lX&*$6fXgjSd z8dWjf`f1N@vuS~_uXMg+e$4yd4aeux!CKs=?R2}NY8Jp{zMpZ__o?lVWIF)=zcc?N!P!{-&Rgs)v! zf{d>x#J)xUp6$%npa zcj9u}i4w>C=hJ8V+` z`;X+#vYX^swtPKZY8U8`v76XG8>8&-E8$JshzYl78HaYNCsf{A{oxpW+bC4*qtuXZ zozD-ZxXHl=P4PDr*p+q(-yMQ`1@DBlezZ@yQaN{=^+bK()+5xNC4Bvw-cdE=H^!3{ zYEM07Ciy1VpKQN_DF3$BIdn5uML)IL?M~H)qSNJWAJ&;8nd=oswyBB*1&c1Hqb_Ku z=gT@su0|N_e`I-LMMSr_GHsv>xL7fXz>5sr0HG1u7*vj3>croA${;He^qD7qP$u9V zQ>o3QM0ELW#GucJ$<$hUay$8n(lMWt;Y$r%$M7!V+fr`~#--J&+gpBLYQ2Q5;Syaa zdidEYrSoA)M^VP@-*fBE=dEygrzxdo>g6_0lnh46rLwqY9KfTrEGoJXgN zZ367LTh^}Ne50cS9r1`9sk?roL&g?`6-1*h2p$3h*366 zQoJ|W8PBs8IN7tfW@q9x?wXoiG3I9BJjuKtF|G0m?ja6Rbww4)ha!?SbUSAFl_H#@ zS9%SzuUEQCU!X^-n%{{6gYv7TddxyY=y+Q12CU*BVYOWOZ$vZT?SF`~fwEl)w&>fQ zWJnEK;A8`P%(FTT=O(^flWs!oajEpyi)s7+zXzhpt39Rqx6PGnpLIWQ$f9f5tAECd zw9LsoTK^tew6!?jb}kUVUi_rhLn0ed1tN|D)@#uRZ#| zE{0EX$os<&A0)+iNSTf8xK1#a;Xx)CaIFh@3^$0Jr1v}P!b}Prs>W#C0IRU^PMa&; z7CBmkI^ZKnTWW|xdJ$A**}6pGl~Q|;lEt`qJM_{~(~Q*b_rm6P41sR?oXG2w|9l5c zpA%>)zWe`qDeeT`^6oKoK`e^2g1v+a&;Qg^iSx1eYXm;zNb{C8OGD}}4Z!~{a|FXn zo``ox)kPt80_sm^k1L~gnn4qLeE!>p60g_v;TV``l~R~_{(p!DjhiYba)&zq@g_8F zmDzlPX@SrA9x_eV&t~!I>nOA{d4AsFV0uV+y+8O`CCK|@{N&~zk8$Z6D{i zC!YC^xHXrH*_!DbA}<==g_DG`;3YgDm*0XW@B7A@Iz3paxOgiUh11KXZE6bbnn_Kxlq z6+Z=$tk#jcyT*VoC58R2K6Km}113J-NU#zpl;x4m{J@z7913s{kVDGD3PW{fsQYm4 zK4LX~`h!RY%TnkJa&z6kIdtn0lgq`ztW(kUj7Q?#G|92aI;T^SoewM_&pAchORZ?Ry(@o=v1%tNz4iL%3#=si$LYe?H5eFf zhLd6M3~)5pFdHovp^Py}99*-KwC*ZE0L2dlPTIm}3lZL-0qi>@ zhO6S6E)Hs(HH8wH$5Kl>s08*4GhGruI)frEV;7<>&4cJ1KzH<&~gZ!(hnhi^!= zY$|TKE#f`;rw^82?l?9pwnuASXr3nwsHxmcqL67vTJG~ z&*r8}$D4lv+k-^$38e zeZ;C1#QV+T6P!~_o&aQHs_9EW&-xl73gn1j;;Wd(zL-MS_F>xAdbFrBx)1B9ygxfh;OO7uRcSmQ?x%h2_2I;7`-bu277Kri<8Aauk9?lg zA8HcQ((MQk$d7amS?M*8UNdEXRNx$3ari{9Tyf>?gndA1$VC`NONGF|A*GH3VamY6 z;s_SlaAcx^h}IE0&CEGzQA83#89l|c!>cUA0!L{n9y)NySI-#j3RcXK>6lXADE9S8L+SUd%%5j-x(;)Fto{U>>SgRMOK;

pI_H{;wN(xkbp$-~lXfI_X|q$jKVQ5i``2@#W(KCLE3&e%Q=Fz} zaN4r8HBZMTy4O6SF2U;h1-XD48T@z`FtED8EfweRns!>ctL&VHLPgSt6tB1Krz(>da}FS3lu&_D$Duj&tf`H3FavN#~hWlwge{u|%jt%Sq{hCqX)g#AR?UK^iDR%$+iEx1v5iu99PmpUah`#z-4`38#GHrz{ z>odpZyFtvtYzVMxcOj~GGx13*H8&W%KW5;8jOds6yNN*_gUQsO%_lV;fM-KwoV{4q zAmaVL+FB$p8mMP>Oy_@)eA<`2b>w|Vk=v11x&EL+eSlQ+ScJRIfJZi2eCI~7opaB+ zL8*SR#YfVUaY65NBk2*#!{c4Mw6+DP`;oS&(KVv;_~prt_hERre5jEj{KKLdW(T80 zU`q_1dxEzpgx>=oc3Ps_u`e&e_e!6sUDBMJ2JX|}oWkcFw2)u=)vE1V=QAf2Zue@- zwDgOcDW@~;u77=3ZD9l3KFp!T#-)f_K4d3Y}vVXj^Z&CY|KBVVrn1U=y)H*gjOhHI?4iCI<2u6AP_?H2W#pB~T z;I6DxF5l*49fG@RS_{bOONPefqw3-+*e$s4ET5XuaAt7N{3sp)l&s7fd~m3#ID}zZs-m+a{DyfN&>k{Ry@iSvQz!8U{}Yk3XHG7M+D}k2~Fp zT8$CfSFRR74*eL+mbN;aQo5_OW811w{Ql(3vr>})(b_%xwS3Ud+VkN47ekww(|XyY zL$4oQnX=1FqV+$cfTcWyt9C}*DLrFmpUF9( zkg+9?S42p5{l~@GTsbl6T&~45zQ^6N`%14#v2Vp4$Oc7Ct`z;k-)SNMO*NXJJaeEO z)4pyTfZqTcl*tJ!F#~*#ghM(k-#H+{kOQ`|KtbOmh_C_Dko`Y3bx$)@Y9tycNmEbo zY=HfpHF|()F}~V(_;%0MuEcDXIcy>f=ckp=+SBTLG2bVmy98 zqOaed8L?Q1Ir5{E1lT3oixh^}Na4LI}t` z?Pr+)6R9u$@w6qN=Je2l)Qt?CGOFHx1mKfI-I0&m&!4iCT*Vw?vZ>h|A zYUGOU@%QcpduG1BkHzQ@3xc)e!#tOpF!h^!Z7d`1QykDn*1vU;@8rDu_4 zP(m!>B1)B&q(;22pm%9u0jygXp@2-!sQT_6Bvy9;1ufy<3ADL-O-`*!gQQg8voui8 zH)A`L%j$+xjt`>z7^;%`&R&WDYewn1fB`IiJndK`li}bf-cH-g} z|91Nh1DNPe(+$jm=6$${N*@oHWIZGdt1VDi+C64G>Vp}n5%?>rxHN6@W&Av!)0LqS zjaL#ak9NPk_cYHKfCnF)7isVbC|(F(Yq;SdE9svD#7SJQ1i>i?-@K#kK)r@ z3s_0-OJ#tr7+1Bq(oV#brFx8vC9ZVfLuDX=y>RP0LPrwS0{mRDFBU%JxTuaV`pEx0~t_Ro{Jd*ct9`jkNdZy@(1H<=gh|c+K_UyvvWt z;m?~r=brw4zA{-pfMQtCD$0j(KqcHli$U~ zIByqdlB`tOz+5aHt{(205>@G(-vZdkaCJa8zJ&y5;^~2S60!3LC{Y2dQ#glQnF&&? zg7yNJFP=TSjXbfgQy~s-E)kGf&}O6DqAA7G#+$X3mP~CU+(Q_*<3gzRvxQ1ziZ>ScM)6hf(wiD zQ5NUi)W^uF9iV=R;uEF9t8H&|<5M;Q-6^A~5j*MEi`9 zWgh{4F#ry=Ku9S`I&9bbXeH53mkE0gO`ZgnzsEU=(n0%Yr(Sa_GyjjR;~oS^1{lyU#L;cGD+F^F7l2 z-6b8QjMe;HEvr0lCG(}BbjPVKbLEeDmxD{w_L&r>o95%crTEX=zKa-d9#Y%kd23@l z$_MXlB}&DYJda;kjo~AW!plUsnu95dAzcWxV49nQCCQ+ELyrg$9wDU!>h!w82`vuT zx8MOG1>4csjn?6!7odB&oXS%g^u~8C{-)TxXMHv(jIsA;Ia#eQ~ROpYXQW> zVr9WM27i_>*Lw|X?3OBx^{pjb>=RQoY*4D%zE?No$jfG^FB}LXU{1s84uSs^f1OP}54JtUNZII+^L)JCy<11W72>YL5!}jX&-bt5ducyfNFgn-0%)A$ z@r<#^O|dme%dKtWM@Ks+U|Ru#1d56#U<4lPaMN&@N1905o#O)jXhGTQQW{JXR94As z!znQet-5CCUY8hYf~Sm(dHd9?N>^W}moW5Kj^^xpzUcBf28>k6FD%u9jAJ-)zDGdS z!aoaaP~YvDmDWVAtQwF6MrEt&m8#wc=7nl(5a#liSL62bjn_W9;e%8xvu6ufY z6?RJ^N-(`bhq5_P(G0viGGR!^0@1F3To)jOa@PqX5`>@tX;sk0)sTkj)3>y~;c2*!;P7_&hfN^nK7PZNU9AAXfZk zY&XbGo#R7F6`HY@jU?Qs$-;b~FJj3MKf$6dd((!W19%>o(xW!r3PrwWzI0>G7vKh4 z0GQT4dq7$Fb?>5`)Q#ZNnF{%y{%zk&!FnLSE2krywufz7+}rVW6~9WuUs;oY;Z?dD zLnV>g1M&xmo9fv_FzaUR{+sL&D=zD#+Lp}+)IMGFqFK-?YGwGFmhQsWCXlua+-7h) z2V%Vn0{zEB3T!HdxV7&CX9K@fb})?blp&NMR(!9%jWj|Aj-G1N$!y1Y5Lu4yV;g+4 zCODgSv-Q#dV^L4`sWA)Skl5#$HqvdLM!IU7z?%@K*#gTYtZ6PzoaIAmsRD~TPI_4- z|0MhtcuQp%Pc0Kk*EzR3N8r0_V-~1hr$qQ119zL(h>I|e{OEFZ0@*3C=jttgNhf(drZA+; z(f-o)C*#0!Zdo780*--`DDVf4gqJBSAwICt^%sSvcIbUo{fG{uuzdCbA+s0p#7*EE z;c?S)-u$c~7;5=VAZ#jDd^GZv53pkBIorOrM}8w=$Xrg{!R(Fxl1IRs0d%4feFCbl zmSN!ewu)~C>u9o;;ksC`1{nTiib{ytqHCwu!Tr zKf!E?t(CX0zF}P&W1C2NUAV-mJwOEvU!J>AUSjwOR&f$WdwfqTNH4Eufz_}HMJdq3 zYvUC~dlH_fZ!uK5nLTt58jk13i?$&3MA2oE|LxzHZin&DOB>=_;17Q~pVSG4#jNEn z<5iIfq3=h}5%4G-c7hHX3#twhI~2@S2MFl3XX~zV0a~JwMPL*fa*0XU_EF~>Dp7GO z!tdx$%qyi3`;OnaV^$7Oy3hBn9-0vM&#H5|W1;E7TuU2;hu?tyMbgK?@LSer7_TtQ z4!wl5;bB2Ub`o6Me0l-yK8(N0EhP#A&&?z_TmUMDEv~S34?UI*Wqzf?z?He`JOjV) zot?CUwp3n$lHnw)I!*I-D(BOgK07Q7tNim`bmiSSWUG1W;P3RB{F$>2nuDjAB#Ojf zKLK8qgG=)*x>v2>MmS3GPv$&JM*!?!HfymQ6y$ZuJHV+^66HCQBs$ZHj0299!)Vp3 z?3R>x?-ffTZ)r}yG;p%J_x|AIL-_==8I}4rf%4Xz!u;G>5Ir<$t4{HhBTd)@T$C0Boz>hZxKph z(i8MPR=dZ~92VR51yfFg%}_<`jgntZs*?;HnE3so{$ST#vICQP+hy|_)xX7l?Em91 z!hC|KMI_I;U$VI?L3>Y-> zAwj!6Y7cdd5jF(FI${Z|{zb?U6c>kn6&F(PXZ^2LDj*@ceD?c4=%Pgw>YB{Z5d`Hx z^RLih(ZhZQq!4o_dzEuTVH{jf@=3lyX|79G z@~AE~%P_O-~gmIW*v5O2+x3b_4rjX@YLv zj0cLgQe!6oMeV>9*ATQc6Nr}DvraW^S@%o7Kj;c_4cAhK@nugjcYODS zhSycHTm|a5m_!K#gW52K3>QNzqO&Xz&9Tr>EMfO$sTJ%=lqIFGjB1Hln70HAbWqdm zoBy~WAFB3*8Ck8v;I&K9pZ^f3}^mrriCS8R2eI_fc&-xA;rMEpuUjY7m#55^%QObJlQ&6yXRgHvju#x zeVwyRwqlTg|6uj_o5lagCHLV}-9s7m^*P5ueL8dg#1jxisg}$^kX&s@cnylpjpg)I z4;m)0Cja}*qV+4xzI6t@iFP}(T@$XTox(h-l{pgwVOhqGz=L3J6bw~BvD0t~GD3a9 zaPO?(9(l*75&U!S6yH+`qkGudtq!LBY*S6_Y$m$yrdM_&2S;S!pEslaIrBmLub3thVtos@8YnTOgjG)V=fRgW*#eFp&>mUdWB1wX^ z9vbt1-UDyz&P;Cp2vMqmkN*B^d(KJqEZ@37x3zo} zMR<^}H)YoRSZ@#>!M8Ae$DDltFH1omnNqMO5$@-0 z=0dKl|271MExKJ`uM5gBP^8ZD7wUm_*!i4|mm}MQnXZYP-qIR+H5>BuyTuc6k#CcP z*m&gj-v*<~EEY@o(&=#l2lw;mnC7bx{i*{%NOigR1PW_kFFjzyVbr8KbG~UD*5AQQ zw6>|`C4<$1Tllz#-9T+e@)%;j7c(WGw$zD<8vTsXTPnnH1+F58THQqH?TCk9LxO_ZePc^f;m1&a$5l0LH8;Tea6yeSP)IP)(c-$>AK zLS>inrz*O0{?|V0xs?k33sGK9pTYq6cieqE3>@Y6=;u;FXvgh>J$eHEb{#5u0tPl`!6IMNEXFBe zm}6e)f_wYehvjRF0o^thnZrWYWo7AZ5=BNyYuneuIF7p(df&9#FaA}LaY;MBuL&mM z`Igx*#V0MlyA~$lGbAl|ujj1U`^nV3s4atC9C9{ZfgoG9?l<^GmlO;S42Awi1Q5Kw z7CgKMQC9ZdAW4tyi_0QOhSs)}>r-AiF>|TOXkknYhN)?2*JR?nLL?xu)T)Q4l z?w(n#HW%sg+H20!HMX7ps71MF)&C2;z1p>|232fxVo znbjg*G)419Y=DkG^MJ0$=4{JyIx_a|RZ<6=h)Y-IuUs>qY?N=3Go~GZH@&tx$G}X; z-UkYs^QCqgZ!ygM6Z7%-8GtZ}&%?7hedUia2qsT|smeyx2)+}3w}6ylZBHY_v#H8J zJ5Z0CGZc{eNep5(h$2!s(7fI>X4jGwVJ%E07Lt&DdLka8#X`<)h3hkl0)(zw<3SOy zm!u=^dn6&&c<1@kYoc5A@86Lg_g&q)Z{}jiM)g>Sl4qJx_kEJ?)6(XTl3X7TbRBxH zjFuv++U6vq3E5D{*%1aY7qVp;0*>6vuG-No)+hsIvh3I&p+SF>i8eX`g8eVKWu3YN zu&21#`5CTRx{4Hd4K&1u+LVk|Z~fOJxoG$lSXBLaBHA>V5g==x!kKsTNzS~=O;ci< z#7YFyf+9IcUkw_8zr48N@4YWxe%eN*pSEs6nSXNdS$SW#y-Hm_^OXXUsA`2uXa*jA z!14^@ME;nX{DE#3Lr@ivd`H<7IKhO^vQNclAy6UC?`4oQR6UIN_ZuP4eZshJwrxt> z!yybTFR%u_Ig^2iPjXJj_qN5Am>IXFl}OwXfN)V@mS^O|szV5S9VQuoZVwIw+DavI zw+nKq{_WdOJ*HwL;st7yYDMf%xWiEh?t35CP60)R9oS6yUsvNjS~J5t~N%p^$C86aqU%-FU4JRmOoPb=G%^1 zzL|27m*9)#%u5 z4v%4vv3Un!xX+3q>0OiscCP-{!!Y46&*?gmfhFZE?`Ia?C^j>4it&u5=mJB;XOoTi z^--^&OG@h-Q^fTGt1C>qt#w8YNrNIP#URxnS>OUN-(}St09n8|RtmFpp-*8x888vT z;st6LL<;ii5WA}EwuT|x?7BUR7?1fh=Y&m(R>;2|Z zKvTNSi2B2q!gzZkNh_=TV_)p0MYxQ~f#Bbv^w8n+{TAk7Nnseia{dk*%LK*0D3N8D z`OB~lOl}SX9j*ln>N~%P|5*F;fBI&X#MP5cID80<6ce-DMojtVbbxsm?T(oaVnhYa z9Ge&he1~q28808tK=O8Wk)TE&mldV;k~P(wjoie z-KR^%qA&Jc2|mw-pG!*RoA<~#NnaB+?oJ>nci=ovsH(-3D9>ma)t}LVNmr3Adl?=K zVjug-_!ib`I`HE53@{}B7}tb1rU-vKFbSI`)Ngdi=#QTtNjPNTox|v--_pr zgGb3>PNbUVqYYMBDq>z6IZ=++GKY>_ zK6~lyY17Jk@fM@;AxlfoLJH0~t`EwZ>oF_U@xL(u#5DyUF}~_@h*fC906+u3ln_2G z#KiZb9Yd#xLVu4@fI^u^ZxMfpM>GQ?+$ecw5kJ@~0>>-185vz1$EVfT@~%W(YR}UX zJ7bmi=uN}7vh#t3V`C)zS=-LlRd|qoXVrdvV5m*pzLB!7Qnjh`wRUuP#lVn$w1>n8 z#~VOHxe#c4usk(g4D9P;{$haat;8t^x(qR@K;j1-}+uj!hT;m7m7 zrz&;`bSXuo3!&IPuk7A)Gp(+>U;corQm@OUYm1e_%x?-R#4;i~yYH}h*Pii0(7o{OcKi!q`j_>gmzO-wtmUqA;f^@yg+!U*afu#_Ou>pg!~1*&EU2@;)} zGzw;`LRo{zFUsyj0h_Jf;L1D@p<_gAa$skjh$t{c~%UZW(gQ+w*Gb3jf0$L zvz)|dg?`_k?Ciaho?=4rab+y`&;v1jH@JXg53{NfmQNu%n7bYrdY|eFS=j99B zIJS}0MB5KgW5A3DCJ&Pwh?{Yc$Rf~$fE{#ShB;2qU{A1)7x@^)aS$+jYWCpyHU7{E*qi#> zS=K(hgf=++LxO_}@C@Wh9?oUq5>Xsx`~P|Y^7Bvf!c%-+$xwekpZ3*w^3wjA=@lJX zR5QJzca0QEn;ydW;Fd^xmKu7>7K%e!h${rpUny%8;qSviL^P;OfnlK$C+PTrg)dl1 z_lGam6!2|B%0&})pr$*K)|TKgjEFv*V+RQUfZM#g9)ShD7qGLNtDgtKI-Y~SYa-8F zJXq~gA0#rA>v>bFJNz;0K@4QSuJbAW?r-qxI&}7T-nm0xrKDx-{Hy_v!nlU;(?aqT zgijPic-j9DK?JWRmy7b_MvuajbA;ME;M%Ed1tCHL#^wn|I%*^p@U?(xoi~gBw2#F{ z2Jndr41Pc#ZBMNOu#(gUR+70i()#d0W?|bY_(Fh&`D%9csViN?+#6k`8sa4ddKq`E zb1ctK-Sfy>A!#4uexp$x#fA^L%)~Eb_S31RHuL}uG`zulR%0{u?P7iJx_t?}48G&= z=(-Oe*lDsSWL#bY9eEX$qATPt0C(PpA{C)gz(2SS9?U$en*b2SQv=W60hSpiRClwh z0c8d(d?SbqEXOp0doU?9UyIf47E`2LlyX>Hl9&3|LZPE?curpUuE~2|9%J0V8 zr#*AP-ikgT)wOixjVS;Hi_ZYZnQ28xQ4-8-bxgtSp?QN@%elH>6p-oYfgPBv&ugu$(KuZT1wVBRt4VV5j-iy7@J}>24D!%LS?TI)i|VuwwSzLRQEmhbau7W#<{`gE}YI2 z^Ig|$P1Hdmy+^m5?A@?vBJ?Y47T$>X6*l$93QZ4x<=&^f$gY3!avTsTJmWxk{)2Za z)d5WB#Nk8Szxxl_DQ9$XMTkY5-VgpUC#_;%MrUD-wAJ?nQtb8rZd3WBd7)!o3l(JKZgBOE^X=* z7_c$7UTVOGuxA_<))Nvye$mm=o3{ZK$v#{Knv zL-u@gxJ8(ByJysoJaWi(ejBfj0_Z^93@$X$sLuFDSpD8HLjr6uCn`VVf8osAyL(2!_AOlB;rMNWNevY;X|r+!sE`x z%~#&(WzBJsZob+@2a|Wa-!sFd&D>^UvDaxJ6pUr4v>W2?Rx?%Zd~j4@xmlkLg#_db zsrDff^Lk|Wi<*7w=G9Jz>E(SLV0Zx{TnDS(pow7Lg2G?~FUW*D0l~rsh0R0~mVtrA z&SEsD%DyVdm>=sg-*IfQV@+_bua31t0N>_Ih3Jnql-W?}>vJ+)$^GuA&6s~XsG$Wh z;xnmx2@X?aPFvq>pB8;RFeu^J*4nJy-g0e@4l}sNW86__55$TPgVZzqPZ9^HK3J5~ z^yHIc>!ufb+j}gQlBCvb9$a693D8<$a$nKn5pb^@ICd-_b&yPUUl#fPM^VRVY)MYn zA@Q(Eyb=>vFB@yM6j&vmHNz@IFr?uI*wqQ_Hot=+p%W)j)f1@~gIIJ? ztm{H{=I0)>hqW&9NJuPYK$#8k>p3_m8&z@7Y;tXxz5$umEA*uzHrrt|R!DT?UZGf({VSTy93LR37>P zOyVC#a{uTr`vk|pQ_VMIt9fN7y{dKh4^AE|esSFT{+45&w_b*0r6jfgoJW?oQb3ae zW@M0?3Y#`DBzeb+^*VguNx+!2={D3ZFR;H;d$JCb;E$d)eNtJ6#GdD9Dd81m78Sm~4h~9x)*t^9Huu`e z^xY5ak#t((^JpZ~e_Vygbk@S%!82lCl$$+FCEsPMeRmiqyV7BjuLY&%;^($A$LAIs z!f?DK;O4+HYTr*{LyF~hFo=dw1&hrIb_Mvq7O1miA;E_k_;LaMcxW>hvn-%d%}vOK zP>1&UAv_PL&TZzz4c)A(37KXA!1q#?U~r_s5}CA5AOZuokavo`d0G8V9zr?O;9!tH z0AGDuvFw{qecH^Rc zXy#rHWqStX89FPW+`^y;=b1ic#Tb?=j5jF@h?#qJ_Ze04K(hO*!zE=j{EXhB0^^AN z*!acMmu_t=fbTi=IBnC(hRD3+U_<{BsGfbSZT9QhGTccp4?{9YFn&qgLkC&j47dYe z$Dbrh3TzT6V@uQukVLK64Vs~>{{kfx(NcU!_byH3(kbYwj1=I(?P>&4-h>ks+=K=M zaAm7aS6J-}kyP+`IfTZ#yuWh%4_79|O>W`4VYX7m@Z&Q$Wunb!Cw@u0F*0A#V?EZ) zYztD!k`f>!q92nJcOm$n2-IEjQIU$XOco+|fsX&-pRh!&P$^rbo;21B4Aiy@A`Y`u zvw5vbtbb>8f}n&1CV-bwp0S;yPx8++M7`#fQARlWY696Rx)C<7)0bHA0 zE^rjC^Q%JxbJs4V%WLF1ik=#|a0LaeDGn*i^oFn=h|QHkwHMC$r%4ZO|9NW$HMP`| z*{TW8kLHK&s<1$1jWz8x@yUTuin(9|mjR?%p-8BHI~B|Fl9`ktK(OTV&>5eWP*(f$ z1bp&h8Xh=)MeF^0&InmOl5lB1yZ8Nd2|QcQrJ3E^DY_^opGFyVwM~ex{gIXm^Cedz~&l?&8|&i*hYo z*ZaAdPa;h(!ols-{N6a1-wJRF@Mb-uFy-CqgVz-|hQWkYtw>Uh)T}2aT-hOYyniG z<_UoR7(iS54vpgZB%5`5NO0$+(07G>{a)|hveS=w1WmYIy9ivkIM-C(F0gTAuFt7- zCHIE4&$t6q4C)l9Hx1YloBNYB!yZ8#LS47_zE*H3>fV!K%;e;k={lO-0vp?p4D4hu z@mlC|wt2(xh(8G%68qa>xgQ=&_-F>X1h?DSk*jViaz+oE<3%%es(EV=oGDh(1M}04 z6szyz@b?N+?Ibd8X%3eS+-^VZ1;Y%>6Q%nl#r|%^H<O~cX$WC5=ex-9^h(f}fV$~vK=gzqEiu@y0}(!u^rG^Eo$ zr&|~L@g{%Vy1?N<{pXX%;)Gb^Hen{C22*|=yCb6Clb?GZST4hj#S(r4-CeQbL$zon z6O`2y3?Kag4+{gMahMzfp{c}i_!qzQSJ_bs_{Bq~Qb5?anu1a3T$i9&eKYbGUOrlG zz6AD5PUg@%TbAc?DsmDnxWC-i2m1r0ms_g)9=k+i1#ORzf-W&ab1Zoh-I0x~%}B@iZP5L#e%_GxrVP<-zO}(>JlEQ~=Ozz)I&qx6 z&PkEK*SV!9>>ym{JJJ7MXA6c{Q*FSh9+nPe_b-^0+h8La>8oAro)2&{S2yLs*m-Grho5fo$AVA}tTb=o1>Hu6geo(EV!v^g*WA9I z5ZBz1k2bGW@)$s0KokACBQ|08*_9qte=$_9U7vSZ>aPKJftzhwPk;varpdgH14i(1 z*k1|kT1XroFY_1eL2X1aX$Rm#HP@N818hx%+H5w$pN@g8ol)DGWj!$`>ggu^#nWjS}wKY!zNI|vkCnpI=et)OTl>giSXz!bB*-%uS_j>6Z%Le)5e6qs@}(6#k9U;PwXS{ekSD! zJZgNuXi*KAXr>%)`PX!1wa|f{*Dq|ZpIiBTVm&J8lH${R21yN2uKqP;I&KkjG!{u@ z&%l5!Gjg0WZxCNBaT*O9A>gYh%*TrSbp&B0C=s@%bhkVb(E1kV8iTaW0V0PdT)8!` zszL4<#yo7ie}Xz9>RP9-B8-UOkwN8lb~%H(xds`9rGjEl)<9kV`D)`E$eJ3tOB5CO zNoRuuq*LVJC}3C+^J0@5@EM3(>?OmapAJqB2j%biWW~(>GIP|Udza@wuZ@lP#f0>o z=KU!6l|P38E^Yiqky9i&K&j^Ns&!B>co_i}1VD|wy!G9VgYa<>MKIR*+gI)g=cQL1 zr%%HU4`W!?hrOy;sKE^@kQ-#Tn56%J)rR(qzT(gzP@jNsNAL$p_sd!B#NV#Hs&xxfVe5Kf@HX6~PcPc>bqQr#>*iGBhm3<4T`FR>Ch;flYc#s7Kd_9nc%{0Rm4>&wp1JLQ8z6myGe zbJj@oZ}IV_xL@JFx8>&*}*# z{>q5oDXJ@uIs|A}`{{B`y@$Z=U-S(xHhvtib8<@te84^!kHdXV+&+R{ruSGrG9`R{G!jL_p*djn5<8gI8T zI>e>CAP@<-8KfY^j5jWr8J{ds)&OA7y)MKdTPDshz&Xe&y5kUE{EgRGWKxicCwucE zHW*^e4R0DPm;4Tc7DkQ?)B}%onhmGjCeJb{I}F|p)oE=$7!&slphHFbS^YwSd+{NG zkE`QF_;@(1f;S)n+7ceKfQ2R2$8HDUXn?%BIm$gBq7jPJC#&*3`ymzo0^ynk?wBG5 z5>?2jU@R(6$WQIbHuIg5ieP$^7p1JU2ohr!`Nz6Gy6S z0c)gq2pAtg_mliuM7RO1HR|PIvlV@57FhHb4{O^i&5nwy?c#qg?(LD>C9(5mhos!g zYo!AbUw-zT*W<5>u$Fiqn`r@Uqwj&EYhc#VGe+^$b{W@OxsAdi{pY?NU zsv1%u6#>^2nYO_~<)KZ}8M`q(aucfqDTF!F^_mFM9_vKr!tcOvaOuoto)w?@kNvF; z1XlW!%Yy(n=`#n;q9m5}3XobplofNp26auBhk*>E;NI z0(@^vO3*w8(%Z9hGGZe)_S@BN#smyWWVKbrJq>G$f1J6dn{t>gxO$$Z^WlO)t`1q; zub8Yp((J5{-(=nj|2;?!)xI`|ZQ9CYtDf8KMGfWM$_T>LW`BZb3q9Gi}>7ALk=qdEWJ9J{kilGkP*~ zJU{5stg5`oYHx>^=h@J&`2zuwnr8H|5~$gZX$Sh4nb^Y`a7q@U4ccEICM=2ku$P&6d*`kK`~bFX5JH&d1<_t}F2D)^he zp}gW^Q6Bawd*}Bju9G*o8?(3?CsnOI84rqkNoie^I;A^Z3mWV5I5I zQL6hq3Z*-2O-|Yjq=o>^)&l{T8&Dpj8rRbWUW(6L0Pch$24FEMFOa4~C>xfX1qQi8 z?lVJGq@jh?&<(TTn-cO7O_rygMv*s|d;0{GHsvFR4B|;lA~qufX%PsnyyAMiF_BX- zTYV3AxV>N-VLR`;Jr37a)VU&x{@R#E9p~TEMRHvIbt>NXh6eGmQUo!i&N7X2=Gx=7 z9bc3Y^;4nFy*4OBcV7^Ws?YhMXoZgU?z2I;M8^~Xh^M@-p2%N%j#{#m>nZs}e3jqEW0DVsV|M!caH5~h{%`ZpRtiU*Jeo|@YJGvIH{Vl6 z_JIVp{de%$M}6JdWfNok9F6GZ;Sj%*jZ8|~Q0F1kVOD!L$8~F>D~7tE`oIH9eZP!Z zP~aN2lx-HnlRgaSW7iiQu{>O`wJ*avxZU+2bg=~wwx)cpo+UB4ib8D^Mr}_%Y?q=9 z*o?$-9MkBLIiB!PyIAVG&W33$zV{*af{^ zdCC%ch;?d22`NKwRYks0dXfYVl@hg84hv?d#lP6cBX=0df%1G5T$2X3a7a=OpyY>u$ z)QQnwX3a9X$9gHZR(q*MKI`UME7r4tfK}b*Tn*m zKwJs=06I;P5ys|a;K&>%!|z?xlkB&P|C*e2ftQ!P{=Y%Tf3DxE*czvqQt3y2q!xA? z<1~iNXrtOaNn{5Ib>Znjkd>LZ=2+0qHUz#i49Zy)jV}>mk42qfhFyFI>~ zd|g2jU*g+zKG-Z0v}}JH6w2NVzJE5O@_!${fPYe=%)|FhOTs>KX>)D_0_(dWBWoIzirMIh)bpcgfB*Np*dC}JzP9+ zQnz;jX!W&p0k2srRdj{LShRUREwf?0{gpv#26kPv-@*Dv{X~&O4Z_833SKHNZ0t-3 z$h#Aw6YoxMwkK(84P81J^9Xmo$k~bq#_aTS_Qzgd2oS-)IT(2Mc;jkb6*Ikb2@M(_ znYf~~=IGU|0pC<|Jm9NOhy-3z-#BmSjxpYoXYa;K|6a448w$ECmmz7sAW&>W?EHC{ zx~j9~r(4`onCtJs@jKm*icUyfU~O&P{@P1}{&`*QzK_Ai&E*4)grWAK>5MtxACSJg z*|nrQa>vX-8(|<-ZH^#V#I=5IFWBvGa;8scM!O6$bW6=(3P<+;0W!joZAK{LWzwOw zQ1*FBEK^=%i8U;6;F>tzUGzLlD49!^H8rAO1#CBx0}rL^Joc*m_NXbUd8>@O;qmM( zf@RjZ()xcvTQ>Do&|Qk z7N+{H!T&I)!Qy(>%Tt?dd!g1>8KKSm7aECzI5(;uXzzNMFSNo^@zbc8UCX;{Isn1$ zvHtb1`y$4aF1gk2cD}WoxwGYG+kvWM$DV)oTP?eFan&v~&Mk+&jl{Z*W(}kj&VZHQ zz5VMn-D8~Ab4sd-fhBkqESUh0Bfkee1<2=RN-^3uJH8wa5khmCp-P#QtEl&$ID@@15j__#;qH|h7@If&IPXQ)0ue}HEMSwq_^%=*pdTa|l;8g}|?b_)s7 z&x>gvovnzouMp>Jnn%*M&Tw8hzhx63e2~*b7h}mO5vaO*%cNX{>bJ2M?cV>*;V7oi z@O7ikGB{w1A^Hbr=qMxifsYyD&9dchfLBZEeJ6L2-;*kd0Y-4=%RIMaxGTzJ=w0h4 zPkh@Vph3EKNwV)qAVg)KgQ;uC$|2EU(woONnnshV{=r_ukUo=)PZJgfmTNQJ5Apb4958l@!@lodUO+5(G5`g@BP>V zlbjv_ogKVu5A_G+UP#r&~S|2yq)ofyqf?_` zL8VX)dutXDK6-_WnIXe(DHq%X^B-SZW6WI$R9w2K-2WV~jF1^*T`=G#(t1hG z2-xvZQyz0y@HSmVBTF&)_y_Uz>)-Xtc-Tu-Kbnk)_yfcljHHO$K8APeER(rMct;E- z%}>jTFJAKJEd^ZK-~V*;;WNq1G?$H}WyPdf#tt;g!Dg+dvKEX>GTdBX820xJ)^+n> z^^ks$ktz?Ngn={AC?L4|h{a#Vu~`N!HtHB5iQDjG^z-Ccqmi@ZMfW^aL@P?eow|O% z_6A-Yas(OSDdci@liJmg@6J30t>aY_)jdLD2$S7-L|6j~&RqV@K@Qu>Zt0(G5^!{t zRX>SG>!F~nah`>FnL6AY@%iKG(V>1d6ii&#)OYLrPgi9_fZlVg-4fPL=r=*?QJu?H z>qgeqbV{2Vp+U1IeMgSLC>Q~|E(8X$w%6!!E^PP?ftO3%pP1nwX+mH6fQBCO#%S&G zhA@aSOolsSv)`-)1_WPlJHdT_QM`P>=9{eob$IlXxzUxb#RRKkJfeACvM-Ze+m za^{G{@_QWl1DIlcb<%=Tdb#A&Hur@&4TEm(ya)2x%#Xnri>erAk1i?I+9nE``1XGD z0rhY&tSRxgIVi5^PF8kOMe#?P`rom=udMM@7^+J{zg|+gl{im&U zL*K7?9INo8_%gWVIt)y`{PZm5A07JSX4KWm=l*)phL%vG#j+isUFEz`jnwc2iUCbE z&Ka9!w-GQgPx=yoT2&=C$&-6@N36+2K4WjoH|xV8;#gj2w@aoK+dku?F9542=3cK; zlBQ6t(n%GxFU=rYzd`yy#l9y)t!-8ADa20?_!13UzXw_L54Q$ny!Ut{ZQDQC9PQ(J zw#a^cOgqA!#}TRi?Or581+&ZOnTyBMD&>jZ**5l)qhP!~Bnc$I4+@f*EvkqC%YoHi zwohq!X@xuUElsd1a{=A^>r1_d=YoLd7Q)!l`^Yuz092XoqSWxoT*ZTgek09(qD`6{ z3m?}N5pcEBsdaZ%KHlP}@aP1jK8}TqE!I>XTqCq6EhUxkap)JF(Oz&>8+0yWp@}dQN^qDB;%Salp z4OpXSQ22OLTPO|qOR*=#*u;cOu*T>;R`15)6KxZ#v~kppx=)_(t~!04uA7r}>*p|y z>Fj092^yVmO8A1-Z1!CyZQh^C1P@@;V4G-E(i$WgM!_PM>LhBvxIOj1kp?7<7KHlw z_@QIGJv=xe?RxycAf>(rEf)Z#s|GAV-zi}FH0@ShgLG5NRGl2>+=_eQ3#k{~H+^Pd zLz1a%+JcVdU5eS-E_F~y-&Qx4J+H$g?c)G$?WL58mxPb1L57?!<|dv#f!FA@AImmb zuW~EC7|l4Ra0~-6n?C!_W2a#D*MdW8=7pS4)GemwC<$e`313Y`2}NkEneg zW~!MZiNaEC#8j9YM|F&UShPkH=ri{W zbkf8sByXnNxCjqP*p+hxOAfP^;pgN(GYX!fk&doTbQ}{jCu8xK2wrY79cNG$+dIW`$cD5V(eEOp0KEjZM00OhgN0$FIq-Y_}};3HLurf#c~ygie|YyFRa!B z(w8gj@h7Km2(|$05nBmChoXDDS;uW*zJC(cV>#L_$uLi_{ZO^$AKccchEjzZ3Y{5} z>nU5i$w2+Dmo#vra zcF-Zkx?u2{Xe$x?Jou{UyB7Kj&GF`@7pZ?x=FEY|D>!FJGN$BB+B?Dt@;21fZ41Y- zfn~F6=MPL@rwt0B{jSEIdedF?x5aEo2XCgQz_ZGyMyZ8I_vKQ-cr#P{m+1-ZqMx1N zcWqKjfAOuhuy4`o2!JdNY-pW~L|+g>r?r4eEKv>Zj+rr^y-m$v!?KwYvfL?6G`jS0 z>J61H?)Tp4m z$+NWdSDF!>T2F$lrN~U)E~O8jVL$(r-0us4!>e^SdoNSi*XFxyrgw>7i2xi+d5m|j z3K%0(0?pI!#-^pK(LqgR1h|CM=niydV^}ZhuCzMN=4UOJTIKFvVQP1=F{%WhTk_ex zmi!p8GDT*2=dvIV=GGMyVueErI3Rp~$|o$#J)`DjfVandRO3Y2L#az>v?>__nC=5V zI1+cU62sIkj=1k+^CJy!;j`PWSr_B-1qBUSM9Lpqz7c8(sPYps@pE#wf3Ll%8Kw4t zLrHe9zs?jve?D~%alL_=S8v`?iKQLez)?;(!{EjHmS zil-W;5>7svk_g=5I8&WUEKeP8p7Eqe8haN}-wl2FaFP-;`)ue|g#qzll{TdZCsg-`jdWGH| zY8B@ewEzq-+U0E$_tKc2l~krZ4L0m@sw0yMLK*N4Kj!!H!>r2(IyC5x2X`MfXUhfaSLciuj=3e=AcvAxLHC<+(rPQSMd zJ86*lO`30p{3MQdYzK`v#DJ`PWDA=vi3;A;=RREr(MtAw&K#^A=|hE=+$v`w$%09e zlbo$sS~Jhy_t|Y%P9#X+>iL=f|JVw#c%m$`(9>Zcc6+VzRPT}s&ad3jca@&`sTzrK)=`C_1!{6C#A#{_xp&Z`!XiKgkNO3Y z0x(Ypk2?7-LKW>QX8|Y-fOB+#M+qk_J(`m?BhmLnU z+#~U&-t!Kd6M1(_#|>}nj^z$}+Qt17Q``7xcCDcT#vFngZ4`AL9UsCQcND&j>!yNM zm*AY=(FmfG*7Cc+usqU%dKt7J)uP^SM8{My7J!^>Kf0+F z=ygt`4>phpfNslU_u+)g_Ls0eb6X}1bUDSP;RWxCn>hTqvDz_N?L%Nv+9YTO1OW6Q z9k~p4Oou=J;0(iEOB)$v+PebBJ=7x77ZUR;F36eLXmpxn1vO=IF_3J$dPg5(Q=p1*NsI2 zAFj4_KYM%il3I4CTieonw~BG`m-_mt!#*KB{PTZyfO$}_0`9=V9LX3J_}NJf@)YS- zqHz|etbLe_A0Si->E#h+kHi@Z2 z=DKrbzuK0%XBvA2Ao}VK;|OIer1hR3OzFqF$5mx0DU0`JU!qrQ4*`eIh~6`Eq(UT4 z8CmBYGG3mVe#GVd3$*ZA1WD+`l7BZGE_e2bUAtLgtAiu{nyNfr?1&+%VR=lDH6}$O zDs&#~egAUL+E#(u$@9D=mP1SQjCufHw(@Sdx->Yv^ZS-TA;Ht%GA`}upZ)1Ge$t^b zM97EpYvvlx--#}i9mz~jbseAY9ESil1@fEAKh_6S8@@{5;K-L8ZGY)deeUneG!9FG zf=D}tiX%EIz$?`I1PKtCdHZw)!ZbPV3^OEy$2eXaf~!^Z37F5O-=>-(z8i^sXUfny zZ)#-rO>MlGi_EM;)Hj|}LQ-a%FE1}zVW{C-_Hg^1iIOtXQom&@m#SN(Ps97^o0_Rr z7`65XXtsdnQ?U;36nd+&JY|nzX0G-tAulobZbAV9Q;QK`#Ej+mS(+qP_duR@$JAEf z6^CvRN63_OA)6vmMo;3%-+3UR_Yp0V0&J5S;uq~*U|+P%4h_do0U)3#1ebX+Fp^0+)x*`a zr}Fegem4+w4GtNxMbQpJV;N54t5PCcV4%nwl8h^Oq<{x+j-rWWSYtk#A$F>eT8PUT zEM8P0-$6&^BPw0v&1|$X()*50r@UK-#6JUF$Yd_mbzQm@DS&K;;8fk!8|r=6XAqGf<@(V-_o{&LPh`Ur-+W2 z3^gn)TEP-I->TwdYp1i&Xz-@ZXM$}a0lgV;G-zLJ&FII}j{qQD|YVK8|Z{bjtaP~q=0E+hz`6=2r>9vAzB^EJ3reZkC zLtt1kJ{u2fPTt5D&*x~ye#p5G?Lu zWwHNF5S)*zn}*FZ;kuBEaAsw`LhNidSEd`I;tnBPmB}Df3{m(=(rn*er`vs5SNp{l zN3AcAU?Gz`%}Z{cMY%_!!`P%ug*>qUsbcDxp(Y!^kqECtQrMm|2iu%e2VRAW0lD{} zvQTQGdiCG;YE5MJvjCs%jIChT9LY9QnfdEEYx0_2daI}CF{>cDV|D<;eE7ZJ{;tjP zFSr*U9_hY#({I<3&ap%1i|Bo<)t@{}aQ-4ZF`2hO$P@*CmyCl1lu;qlUmd7cG<%qc z1|SCx7<0;njJ*mcwQjL^{x+4JC_F3qjV1FDtezX&E^!IF@y~LFgpOcLEh}ldy8`Ha z7|>YAckM|DIxaWn1E5UVVB$K_QGd!slT;vA0Fy&;45t#5utJowYDaoIXchUgVg>K9 z{wxrW`O3Z|ddpXbqkQfh?%BkAtB;lLP-)bT#}WF{CJwO*A6;Ix-9-fv^L?x7X&=0< zQ-JgI7{`^oNiK)sBnN!7;EoUZm2^v`I|)H!ivtGSgMhJM#j>@Pd{p~?xOS>CSvy_b@YowNG2;r$z(x*n3;U>z>2q^)ZAN+qHMGz)mB zOt~u85wLIrhDh^5W@9<>a-Se9vRCjlL0v{-W3O&LPaH|^`bMNY2!_E~C9*sM@-NOA zr5)96tzZqj5vC@`unCfTRr*|6ys#jn4hu5XevfxJ$?P_O&VlcbbJ$vqDDIPWu%gwA zpw%}zWUZmi&|kJBThH?IZ*h@i@v)U5bC7W1n>l!nhxP*~^@HqZ$+l%F_Z&icO`>(g zx2i8EE$bny-d{3P=F56pK-Sf`fh`jJb~|FuaEc0WECwj^Rr%ccgFxbdq}X+`>Jy7?*nkpR9WdQe=r9T3nM-e_Qa^MVwI6$41h-Cc9{=l!ZI>Lrj*Eu zkT^kP%J*YF3buQ%i?IfDYaTnJcMbSgLZI--JBdGGL}=`m#MsCc@M-Qw-g7JW^oP|; z)8RnRaoUiaEsm>vr-Uc~(x@#DZ3x`whj1)H9PL{O^h7~9_v_aisFw!O^}4X;5_*zl zn|eSI6rQx71^QC&da9wp^wQVay3wzV3=tr(RW9GcLzC4AR+`XBU>%A1-bsG!Dq zU(Pm7_WNqxc%ts1dh0-sn)LRB(M!5n&LjISX6uQPUG7WsF}lM=0e_5E!(a1Yu5 z-@KwY#)1LbaMX|4$Ry;$*kGg12n7+fB`OX+T8s3qk!HHNqYTt}y>MiQ>kzUZGxQ0+ zU@ta*=}8)v`qviFn?+D<&E2fbAOCm*5kNYrb1yE%=CBDwERsoVsPW{;EhQ6Vg!jo0 zfDjU*9?m!9^^}?;dBrlVKyF2e{I$pH!INxs(L5N!szA#vS_@{`et@+Yg`GUFxHo;c zbbCPWq~xUS+-r#8J>QZd1%CdPPN~|x?fIu*!k=q&Wv1#H4;)1~Q&>~|!G0hWQi%)8 zx2x;xN^h2w-AlKD$q7Y8{5;o($B4<9i(_?Emz&EaS1jpf{Zo?ttX0@`B*IKTCz>IK zTKtQmw+obk;gc*+JTwj6lM_k>hjgJkqCdJy&fv+f-@-sL=@V!iB1YC0C=2~vY=*kW zXg|cdXNp+u-Q7_98%yoyX^0JVgfF0)|K8;IWWe};bK%%l!P?uKHyU&AG03^3-iDu zYb{yo7#tQAD>Zr2GFi{LrBid{{pEyOsi~z}u3as&ZCvfgB0kRFTqFh5JT*GcVqn3E zmjn6ar&jIH|%Sl;G(a|Sy-He0iD)5`DaX^YG?n?G=~o8~tztmL?SVV|y> zE6w(~=&MK>i9jNi#L0vyAZQ!uOBb(uOl? z7`R#Y^BHbMtKg__l6*Mjq1pnQVa_)|rL?NEHwl!3tgV&FJ-F^6^ zTNc(Jjkpi`NreB0O#J;_T0NxFe=v8?81S7ecTp6+AM)D-u~MMrA!&D((DE88<|sQ| zeytJ+Nj~rMYQ3P#A?QV7+2Ue+sS{ozb>}#n*!OS00-X2>%a_9-mCd}rED{*DN#E>w z@)z@!$62PW89zP$Zu_W3-v4|PaC|1r)5YgBPBK2ufUi{{Zoii9M1q0)XhXeMF#8w+ zYfNB_BU3@FF^c~O7>CPl^$J0OF)?(hHYA^PYXa5ObtMPq4YgJ6wWtoHRahhJH9M1? z(&n9ybOb)Gas41-=@DA8X3+8vHsWK&Mv!pHL=*mX1&F%>c^QmCe=ACVFPnj& zUb#JHs=jt(Su*qtL=Ws#(gS0+vlQ%~lWN(<1R;BOoeLAt6W>{dgUK%B@aln(nrmXy z&{XPQ*Z{hE2h6vcLv!6S$IAW>XD^VJB)D;Ukw@ZpWtfJ-?aiw}3cE{*O62I}Bg5LO zd%X9aq6mbSD;iI#f76F7#Mjo}=zcy{r!uINr#Ug@J_f^X?moVYI)ca*Ii?n}tVWc3 z(ukrd2oGR>rYAvyCgQv{ua|qq*b2#BB!kOHEyJVOX(!O+5)K`gYVikq)em19|ITA5ccZHs>UMgTy@7Y@$M)pzIJaWE(=gN@ zMdz)*97WF(H|`wQG>Kk3_C}JD8a;hQuzII~M65fp{b@be1IA5dw^O$LYvqw(X@+El z8u?4@hLOoLQpCN0LK1`hS7Wu!1cY>{&zW zK0>X8!t2lo4OOj86&k@k6_s8a-q%*dnc@rPS>vVS6Z;Df#7!bGec|DW? z?6`FQeNn0xFVFJ$lx0CxF-N&1S4VCK(_6GZjaXb~$F0g5Xa@E;^3pa4^yExXzUk;9 zX`uN>uE5FAgMz6=`v3`A9dCNa%$}zkqGDTNdAmA&NnYpJ)>j9iNNeGu{H|9>pkuRm zL$8LiZkXSst6#{MHnxBwfUe3i`q_j-Zd-)sm#L~E%|M^eZnjiGiQLCv%OmV=o9N)k zsX*ME_%Mu!u^R)PQ2h*ahqfe$E~*)(ef$ibKzEd0(yJiKy~e#`#`1eBW9rH^N3Wkx z_te^D)n6OL+Rc*ecGZ#n`E@D&|D0Arp`#AyVra32m@vxz(PnxJ_^T0t5_xLiq^o2V zCsK+mnj%n2WHh0V*lk3TZy4(053&xtK>v(7gzpL)=8ZtbipkaP^DeKVI*-A6ID}wg zARDvg$yy|$E);5cHU~jUuKE>v;E?8=5amQ`hYKP_ihxA)FuMN9E;J`s33BjETPKti zj$ABKREq2ufva?+{e(&u6^WY@jdeB^FNt245K8y1c zA~Q^`e~yhX!xocUix$m?B2_q~a;x25_)H4Y3l&jt&Ln8jM9Z6_$Pj0mPYoVTi)Gap zqI2L+XdUg=9od(>c8N=eT0s3{h?d*_L3P@vp##Z{(~06*Qz;6r$6V68lyO^2hAJT13Da<{}k!zB?Di z1KX}TZ4J=7akG{b8EehdJLU0_q|TB4v@dF)?Xb`82%Corl8q>DM>muh;NG%>KgX+h zxpixmPRNCbw}SB015%$OD$&FM0^ai!8P3{#%u{5JJjK8-`}rVsR3wfXhFblkCG@&G z$g(AMKi@}JqawlxmXshm}zjKia6WBu|tOp_G{2UVpF9jdNx*27BJ6{A7b8#E!-aSqcTR^r60s-4z zWyp5x<)iWEkh#M|SS1p|n88C0M;Oe{g2(smvDYG(P-F&IVJAW5-D5_c$#q9fBBdaj(68DFZQ2ff(!k|9*s~7bG$>www|3y!IE~E7`dcTETMaN$G?S7Jy%Xa zQE`!gH+(XBe*1$?)ES$ zJBh2skcIi^*nwvediIwIBD;O92yylK2D87S%T(>L(S1Mbp3Bqn9Wc2Ax;K z?RMP)UZt?z;BXrx>`hqh&I{4SPHK4H1e6uWVT_rBMVK>6Dir7wtG9}j-DpT)3qB`%UbDqL0dS!43_&l9iyys(^C#b_k zM+H*|i@bt1(5W0ZE`v_R6ly=J{qCRonlspt8$|ZAXw*$O>RViU!EI@+Thz?B0=)wd?czkXGi_FdFVK4 zfyGAda3W6Mg&TRPUhiJGe4YN)njiJ@uz=>tDUOs!k{92jF3GyuNSSkz{Ukno-j6|& zueJoJ<|D|%QLExl-is1VYw*h63CI)TmOdD5J_tKVAax0ID42>K2Lw(M@^C%{fv4=3^azFIf#@GlEuj=ssyOMJh0eS3v4vLoIw(@bnYi zU61lBza=-tSfX-#ja&cU_WD5onT+rE!O^Es|B)8)_Mqwvc*TRyC5(dm>h?QZCG9ik z`K6m6Qr`$Oe!1{4987Tzp;B?=pi|V3SUbfP3Vde9<4fvVD69lgROAM_E)rRH^v3$t z4MUlyJ6#*x97)=bj0n-D=kX0txXyV2A}NaoYhl>Mk_zMHkG>>`Sidam1q)R)^!zIU zy((e{+3F>4qKu+jBsnoZKbq>q0$+brlOZ`=ff4o{+`Am zC1g8m_?p|1p9NUrtwgJ9>+YIp(K+RB-{J23|DU#g+n(O4(sTb7HbvWxe|Bz@KDW>1 z1F>!BPvDF?+b<8v1|D`#ip*nlGMpSr*L#M(sB1lv($IRn!L3IhCgYw8!Kb)J3K?G0 z0-=8!`6cQ8ft`=e5q{)2;9(7kUkI&P!Jwp?6XIb2^;>d)fyslJ{+!)RP|MCeBiOkh z%)zdoq6)$a1Ipb9Wp)mM3?-aW`37sZUN?6zgxpT>y;(TVdmH1vn4}O0ln^y@&+#LI zX9lZ=Pi$6_HJPv1efze>-Y1eb?-KVd=}*u5x*nNJIz&skrhMnt(%jK>FZ7Xng`acz zpRj81uduq#9EFHWsmFa;26em4k+~=2Ix}QEC4K#vdlg;pc@_&+7;A(Vke|w zbMxErr22nz$C^DetY_W5{@V;{9pYfOVqoSci<|caKe!zqoZ3dOQBh^--2+`dH=Iwr zKv*&N*xEf=OEA6`AWyY7gLml846)b`2uYej84;-Hyjz>YVr?Yt~eZ4 zFzRYwF!DL!VYV1hyb3$(%FlswpRY_KWJNI;7xu9(wE`r zDEM#5JF#=qiRI1Vz+3^Y)PM#?c)!aBzycPG^l}BT{Y}=?;m|P>>+R}6X1LuO8lY^)U;N>M7CIj7BjZSht3GevN8Ee z$Y@iv!IM? zWkyRb8D4k#(Y2lurqe(yOyXt%lq8rVi{}tYIqqIRK0LylisgtgMIi6&f}vW&EB!NL6Y^7dMtdgidieD1R#?3|-sP)HuVO(yDQ~lxK=u_8(*dm{ z&=V@{&T=Vw|AS4)k=7`Yz&yJcHY-?C$on49G<&1OfRMIJlFab|g@uFd%U%nql~N-~ zd2Cb`HGPF#-SIS$Y^g+k?#MzHoYs*Y<0ygBUF%#@K%(5#s)o0O3QI2)*uvQ8C*}?b zo>U+WgQW;EvJRP3BVOhEm^RvEFPm=M)SYn>kMPC$*$U20)CSs*mK{_zUqnf$rkJG> zSbFD5hU#F*3Z8@^Yv#P1^e&eViU!q~Gdy2f;*}9kT}8I2ACVDbP>Qg$+f$a*L@aS3 z8e&m)-r8&yV1~Rr2QlV_O_NMYax{Ci;Yv?u^CrwIxf7N%q*;=w;4^x>|C2E$3|&@# zM=48~_E2pm*5U0y#l7!nBycP|gu1}1s}8e-f%_}vj?15FWYP^OGJ0lKC|H##)hGc;I6sjF(1xC6~sQF(D8>OpaSi5zR+PKawPjmgo<{1ky}d3 z9|1q(l%Yj2JG+KEJmWDm1opFe2~iJM_WE3ANtvfHcl@XQZVn-L zE0fXFo^(D4o1@GLW&g3jAuts{9l2D9pvyVdokapLc$kVkK^|kA;2dAr8wmT~jDwBl za-Fa6jw49_uC7uL&z8`UH|oDW5-H&~qKm(sGG zTrX1v35)-62dEO|JVpvKv&Ns^LkavDmJM-gU;8Y0g>f?4yvh5O334N7*V$H%Ig|E9tOrn#ONqO5%+q;uQpp+OI4C$dx?gcLxs7M6zo z)&6Hk5=MTasWun*U8`Ui=k zvn^)_&30YZTZHk8Q4||3r65MFv%>9(cK>+bsq!0B#O}$KWeTLurhs18x`{)AdE8?j_Qd6R#Wcq*p4gr?@ZV>CF}S)z z@jkhdq34(TKQ7jo6>F>6FaDDbOSlx6x5DOWm>744W9_yAk}`a&(ica3BL>NMUtZ!l z6m5O_kK0s8BudV!z}_6MX6q9_`td4b1ekR#-#Vu3az~>9YyXkDd?B4i`%(O+p7Z2? z`?A9-Gfx}09q9>CnYd# zbzYGDLOIb|-}9II-Ut(868e8ennXEDn9LF8Ntvz2n`OQg+mNNT2I-X=0=07`?@nY7 z?xi_Z6aDq$&T&f^z44Z8pYygCo|n%19KHUd6Qp?ngl)b7M??;vhnkda1kxyHje2a- zz{93i2k|AH30AAl6=+&N0B!+d7&e6Z&yfMJD`O$OeU{X61{MHO@e_#ZoHnS`Aj}HqS~j5JoxNr z17GGcSju6i&;v6<=tOa}-N6s4oUnz`1&;dhm0`KX22+=Z@yNZFzx8Pf=SM(4ya0LP zKrYLQscRhx0`9}Tp=?N;Re{c2gYsCZPYV^jJGf*GQYg{oNuu#vRg6)k?dZ1sH((h| zlP~bREP=8s{!xqCbU?@2qkwNJEcy=sGnatAHsHnXhND}dj*mg-8^_MBGfI(KSPMK) zvql@AR2$i6{7i5iJ8H+wyC{ioFHCv0qKvqeYTS&MlCSv7$+;7^Yijwvz2YxZ@ma>> z`up!5sT0w;h7;%M8whq4xp!x8q{0u6#t&ve-Ar8K2$aX;AC$oFAKciHe#3lIUuG&W zkKheHUi?xAhdjWL7oQ^+%V9!1$sB;ZSHv|%uoQe}HLd52JfttfDzD9gAco%f-~Jvy zs%i36S~25OgWD^xcA{i<@vgk&tv|Ud%$&PwdGP&#FDAL1xeFNyMCf~$z z>Cof>9-7S5$M1ytY)CxAct{nG!H<=1^z`bk4~l(F@)CMf z&wkN_f}YXkp`L@j2awtIUpZf4WGi|`37NA*VCRFbX8c%EV@JvVV(iW1p>Eqh?(YnP z2rafEq(#Y^ifl95i)fQ%Ev+hRWS^0wEKw@TS}JQOS;I_(WUCM&GxqF*j2Xs!pJQ}g z_wTyz-~Bwl=k>b&xUSdzy00sz^Lw7hc^se5`-3R0ii|RN(%Em9wb9loN9O{LG?}Dj z`+xnO2QeT<*@Dp5RddDl#)tX^lOEVS;EF4fFMm@Ms+vjvyFYz&%SrC#0`Bqo=SXxt z%x&d|`^NS+^`JH{>BJVN;PZO59pQ#`PN|2qObE&0)T`)x?8c_C4Tn2b)W9$KMbig& zgko*F$Y!AlH9@2LPVVGB7i#i2wX@>FB&cGXY{f#5XY%q}5TkW=C6Y&oT&sT`7kZF1 z!ez=d_+Y?U?kWz@0i@y55bV3zqQ3ETAotH)*kUBs^WdoItv4SR&-MT7_Zucjw+a|s z=IDKNa6=weKnybmPmxOxqTyd|*iuzzzzPs)M#U6QEQ(?JrXlU`^pZ7Z>y4yfU|H5BY{@Ry!}&Nlb$ox@hP^L@I`^wiW{ zKGUy%UF-WTK}4C{AeQ_e8~hsjVYVRIX&@hOI39mnP3bQR#R$W9v-YZNoOitTIjWp{ga9 z9N<%=s3wM_PN5NLLK{_1v#FctK}B`!$N%dYFm1E8TUlC zD*$N&R|s|mhZGhsPQGWv*P)V*yf3n_2%ET)hR*aI0G6E&U~Q?X9o@_O~D zl3PWGFM^)`%u=P`DR}lrI$Gpp@2l_v@N)bsvn3eJrjy(Wbs-iuzR5$A0q%(2#(f)mB3z~B0gXg5E4Gl z422V7Q2Qy-(@4&l5Ab}3j=~DwYEdu2iotbyjEk=63uaUdb4Bzhh zHJ|}G;C#europTG$4ke8-)qjDV5)1DQMF}8E2_2iK6nnk!kxildNwLN>AJ-Ntw7|5 z>*0gbhC-MF=Ei)2-@HDnE>Dywbtlbnaw2A4l~hM-O;#8ppSX9w(~E_8c4A4jSpF)9 zHIy}!@__hnLbvin!>d;>2%W>Q0!J>nlO}(=xR;W?`rFHmZy&btp|m?CtA?SN%1@zMY?NS6$3=HgsAAzV73-WC6 z%@!^PQVQG=11M;ZVbD{GFv)azbgwbs;li`EsWFd(pO8@0R@w8`i}?2bK0P zC7iS8QR{xMmdn$l9xj=pJFe_N%WjB2H>01Z%{;-OTEPE1+q+gQK&?)`zhLt}ZUpd| z6^Rq@DE`2|%yN|7KH%8zx_k@WyGtmk3wOWAs=k2Zb+Uqvm%(vc3$F?mD9N-(5GLI@Zgy4Ba3lR zS~ik17QOpBIOirM2v}6@Re1y2&mA*cB%_SA?-n9;R-@HrtuNQJiDY>CJO9@mFmb#< zw~HgcQXTFM2*Fzb7ed$8y)>aHJC<-rrClX>+sZp}$J)xmhGYESq4@b-3UC+cnUkt+;}8DVK3QNj5;^n=QK7QbbN2IU zOY~Q8pB*YyZ*AMsRPQTjMO$~yrp{~4<`ijgPysU}J`@XGZbz=@1=eC8$M(se;bgM{ zl8vRPAg6God6y%+@A##>60jF#TxB=q1~26$d@E4qJrb3tr7rs#TL_7SQZhO`H?`D^ zh4@-GyT5-qA)j0x`~+++NA2Vcx?G;tqQqvn-Tu@OdWlhN@(Sy)oBe{M7fo${)e(VY z>?TE*@mu%)bno8?B9i-5xqZ%CyAQysE?m&&QH~Q zGaoM04^U^bZczPZD@5^~VNHsOzTdv7tl|U;%6sU7&*8k$69sxuAdMw_=O256!9wptL;#nsVM$al&N`RK$P%9Z-DQ1^V(4r&SOIMHNc576Kudvk<+zf3A>m zr+j}lT-Y@hRamPm#ofh`A61^T#=cfQddFyAw}6`p8^1;OI^Tc$e0Ea|e6hRpf>EJx zl8#!MI3Y>k(%>1=rW#gUCL-9n;c^Zrnv- z+TZzOx5pBx?+xaDCD(UfeRXzPrNyef<`fYpKTQEn2CmF`nI0MQk7KlRJ;=jB^t7B! zyo8I)6NF;Z6UO0O?fEQ>xrC7XFzFaamxPpxlM3S?LJkJpFF?cUcZG6)d*_X6sQ? zIG!z$Zz@8JmR~!<+0+%tUyc4aQ)pDd{F6llVZgmUh(;6flyO#Y#Xl_E{9AyHe~JBd zlk?oo%wQZ?r``VN z>frv!dB-~$gJWUG>nH;n32_My?<|t`4=NBSwm~oQekYeu+XS5Tj^q((;#ZZAk0#1z zYi^s{*Q@(xtron{HpsiW&1{*?IT|F|YUmD_9UFv>Cd6WK-WXpwrUjc?2zZ)){J`M8 z22q=|!UP{b33M=D)C3iU{a>ILmzdQPK8lK}t00)|dF624jmX0fA{^ z!)1j0YzMZ=#_ptTWMy~u-azLC<)AmHO9&E8OlT338bN&L7^~g0qh^1&HY7?@A7}X| zZ|bP@8_UV6ImLE7F%aqCQXrQhxiHX-gU`loU2MSStjrZVkm}(rj{+Xb9dro(5KrQO z#H8!4a8WcqaKZwspk+>W6_qvvg~}8d=3Lf+lzef5G`tI$h$oJ=qP30a8n8JOAj92| z?jnj^mVgUuyuy6QTQy`qycw~gOERH1d&DhE>maCg8F31M2KJc(gLj<2#5@uTOIyqS z@bYAJyCI>ww;ryZO*s*21f1T;3Ma7YZ*E zn9G)78%Ae@i?~7&9Tss$9FG14P;9rpTm$3}za6z_ZN z8aRTO%wFyR)&C_WR!3xSb!4a}Qz|R*WKnXwY+T43N zAod`5v>UnXpq<(Wr-(&ZdH8eXApjNXI<-9x8YSFn1U-;}cxPs6jp$3T@Z4$y6j1B9 z)rjj$!x1eBl&m@yJTMFXR%ouCo7A+C>o*HA7aQ#vzjx~EIc0w>1^$U0Hl*GKPKjz# z+|z<6&VbzKsjA>6;hVDYiL0$nSX&69l6A74IO$Kay|ZwE{|xQ4TKKGg^#sfoZ=!CY z*wMWd{%D;BrNz4j?5E&~1D7$Vzy9{}Ctt+$okU`X+4h-8A06JZpWf95ZQf~lUL2#H zlG? zy5RxAO3%`4foy>S%yr z6t5SSP$GGYD2%2o$Y?=Ra z7`*zqr(A7Lq<7mSkGoIVSl-((zbZRoD!pl7;krlI6smvkg4?9e@{55ZBkp(f@zmu4 z53!X!jwzqU`a~pfzP_ggyN z1#wDJ2WAPEJ(R5iY{6xxN$~aPEycp)X5cZYTfpxwMg7-q0FDyU8!qZn|?Zt$mw(RADy@{wfAPV`sru|SQaJPh}A^mukeHQ13j|}j`GoK>FlY5CzZeR*OiJq zA57W6({YP2I`!d0B||T61Q9|DP9UJI73F$OC9VJGT!l;+GYrV{;SV$je+Znq!SX_D zth}C3q3lZ+~XT@5u6 z*ZUwxmQ97xJX!F(I`c`-#&28|k<$k0rODD>UI#c6bIp64j2*ZiNXup3rG1hq`;q3& z&(}CMs!fQTxP35hPVMO&0E;He6c4~Xa_aqVkNc3 zOd%u@N|ErbH~o^Y_cSt{vln-O#qE!9r3lXJ^v)}K+aJpPaU2 zLj}+14Bn76d_%_oCbYrw1>L!J7#GH-wh7sBL~Ukm=+GR8CIF1^CBac@>&J2OtAKV@ zfnW0o+L3}p0&m&MjA^@{*I-q^q`<4Q033R+!b^WZROLuDg=mo5+^WO-1)OZ?RkyCx zEaz|K=zdD2Pi`dFYYSp4s&(!9wJYis%lGU#OVs=7qPbR!c=NJEU4_(%&wG(-cNafB z(C2;CTK$G>ZQ=DLc7Tw6%m(S#$ z%jDTtfV^1QRU|-*^T-O*KB|BC#t(PpY|YJYrKJl33)<6j@Y_~@wYJLHo$JMVU85}< z&huDLzChA8&U~fsjd832p1LNrFzxg2(}mSfv>Gs~aM&7WKk#I|-n^&)HZ0D9CVdVl zEo}qf$#;y&9hDRyu9MJIky2w;hf~C0h(?m-nA(6`*LztGc;j@;LBozdBnw6M&jA+! zto+MNjJxD3K;ztlu+ARwr4NLjGXB+w>tZ^qcy-f$5cyY$8+7#*tlDpA$ModSl(|?~ z!)vN*=TUx+ugd9{(a_!#Jz3u1a);A>$FFrY5?G)*{kWtS0a%s;Emk%QK1N{XX zdp+NYc0||jB2Af>INr-LPf@k=8Qc-=crhy5qVEn-rfx;AFDe;{t65^S*{i0QO+LZ^ z!wuf!L&)bRF7QZckI7fA+dtNg9k22i5sk7qgfPR;`sQDaH@BOVwr&qz_6;QF@yAzDAd^(<**;w^82@jQU6M+dm`r%;%_FShB`A!ub$8mVxjQvh+F zB7VW1;-1*4(uTmw*+nYuh3?4pW9y@l!Dx@Tp36^4gqAfhxozvpO{xv4LwwAYJ4@Q! z_MiMe_n4lj^U6DVV+;;79}z z?5$e911RmZ*pUdWA|dGDGJh=jJdt$;Xx%Vv`#fSCGFnwNM!S{|mwDP#xU>#e^=qYM zPIid2NJeKMg{6!+nPWEbPRriao12>6;7{L6;htF$PY2iMZ=`?7ImUfi9!q15{gK`; zet7s@!by{51Pu;ZiS%1gK8P!^21TgVazYk#T}U~CN&3ajf+Rg#y@5m=edmU)pK8TP zPm@~oHtknbJ?K)iS9mKWF_l#*tADH~P|Nn?*>cCCq&fPP8lnGkJ{=^*>fahV5743| zFRU;~WbGuVwd|Ue!NK`;`|4VFSN=f(V9kG)WSJpT!z7L%C2tdLONCzm_bMCv&~p(S zmX#>a-)E0WY>LBkt&MqGWXL?{!)?`1kC2ptzd#Z?d%AIqD#REXId13WZX=d+*ov9} z3uAG=zEmvt#L^Bc^8Vs75LfN_BV+&O&cl`)S$QZP;60k8;%)_stD5}87^zT#;;Ng3 zw*^uVZwQQ77G;&UxJr!@qo2#~Sm8h+m$L%UeV z_$7S&jWQ4W?uK&YOY{S^6UsxvV)3ij@MmpMld+oTwx_CN**khS2`rVHAU$I7Xt_5R z1^I}YgKUzNA#h3Yl)7k?%!3T%2Mey=Lqdda0}llW&N0=4DDB*uJDyp6xCW+`B&D>$ z?2ps$MYOYuAZ(rCZ89H4CE8yaXvVZuqQ}=Kdc0XUNsZj_kFV^nwcC!lPknd$9@V=q z+Rr>9RqGT@TFMK*{LE(48a5lCSAi&G;UiW>NcN_SJ`v+5#$Hlmit4! zZ=XI4Gy}ZGD=B1dZaOGrLutVCX19It-I2#u&GMv$VN`KvB;F$o&y&$*I&gJL*qaa$Wg`nLIo`)h{AV@a9>Qmx_%o}DA9_@m^>+LRi)P-u1QJ!$i(m1VKMn3R_(0DnF6aLe$4;6 zFk6YV0byVX>YnGqJV;#coZa(#+^aLpe;G2UnaxEK7%`NOO}E;=nj9P#_*kks&28_9 zf>+S#*bu@0B6xP0_8PvXXa;gCsn&xQe6=J(Y1}g=h5my zibqu`c`17*r}3-*BUE}Pd3YO%nj`&Si;4hg-`05{`yF%h5t|)3?}*L?3WV>DZ0e<) z+`fKx*KBaV`$n5e_Jh`#mmQVYlRH@Y>)M$5ccde_0@MckB~!8V@wbeHY|~I|NCEZL z$7_$f8hT^%Hdzzi#8*BryOOf{0J|Xu4m;EH*9HxP4arb$XJ0=FD5^ggPvWBHvlW#K zLCLRGc;T@FVyXm`j7_9`HqCY(++^KE59K-hhaUY<;VFG>|Vx^=;xCeqkrU zUtvoeROx&H$}_3R~UHkX2Q z*5khJ*y%l>t54!tVkB(!SJwuO9vDQiWRQMlH4iC_%txd_2lxC((itzWve|_*Uh#L$ zHOPJ2FARBT{QTwNZ>l6lUgy&LRhO>f#oBNhhpZp1E{-M|q?MhzO_^>IzEh@;-|CsW z;rvmA5!I$3(OS)_iIIjMvFl-8JpUTL1?UPL!qNN$fq2MohGbbHV}Hm4P9Ogw4@gBL z4B!}Xoa@@${!GCj>t7I&kdm%0xD3UT8ufP0iKW>S3eX=s+lth#pXEN)qD zXy1}nRl4r%3FL9z%qp%q0AP{upUK*GiQCC4dY~AR30iP4jz3(^bGyHv_1|_0>m1wQ zWBC3FO7yg=Ut{enEaG#xD}sjR&o9gr;XK{TbieGFxORM2Yfup0eTJXG^u#`4;3C+ z-Xjy_7ob6s0x(nTkF~5*(D2%Y*!$lAr=<5-*inbL`%U3Lvpx+4PHQrfAe<>)!>}IL zB_H|H#E0m#x?sUxPD|GRjMs5Ztup#ZSBY99PgUW^dLyN*?t$Iv*IeHTa*raoB+Pka z`6*uZp*5pvi~FbJ_IYFCSX#+X1BX%Y$3Md%^R;KG{(pi~(*1zUT-M6u`#a|)PnEk{^TI<;|o2|*|f|tyqeY;yr zy=)WsZ?Sldb@|f}^~>wIu5vpuW3J)Yd1L!8O|uLb=l{hsm`d+x&*MrfzF1%i;iu>? zQUk-?{>R+)sYDM-mNLmo*_x9|1JNZ_%0E?KN&jq^SY=e>WNyJ@+fX~^GaQq?|AHO< zyF!xeuI#iH%z09Yo;!vbA3_>CG2g<1w|$Y4LGEiro4&%$4{rCnZ}X>uLpUzAp9DTfhO~5BX7}zaSLY$S;K=)%oV_8_^;biIc*ZkP5rx5!1}9zk1hu>ez>ryT`{Vp% zjP2Cw-0SaS9Ixw=FY-#0`QeEN`Y(=*!E!=Z1XFu+c61&>%Vql5XoX_^^{DcfbmyJ& zUud@}vWrPE?clS_`s+L@*5{y~;DhHXTcsp%@3_YkOsJA_xp^o}TSzxz2eCXHHDzAf zV}xdg46h8BWId06StEq)7h)IIGMay@RG)H67fg|DDV3@ZTfL4}$}HnOpQ>?qk3?l?I6 zB5+do=yyuNVE=GjVj@s(C)~)2g=3~IYGn4YEu^{p!?c;_a|I<131UY~1S>o9fqiZ9&iBz+|4tNxTIpqY~P0T|?Wd!-H*4Rh>Cj z8Lq}Ic8Tl3m`I{((i@D$;%z;Rxy2FD*k*Unf@htYSGYy7wXs%lJT)_xW5wgzarHFR7j~H%j@*!2M zdJ@mHW=v^?5C#&#q!(Vd+N8He-D>grfcLB%udC6P=T-InrWlP$?*kA6sDJv{`=-=n zQm4)@$&hruTtJSF%`WjRJD%(lV8v9sG^j(^-nK6-<>>gH%sJ=ws@W}9^}@EUeqrMC z_cpuk+B>9xs&rO3*NA?fElBipyFlx_Gr(79TKtd zNU)&_*xW7J!+Li`3|PAXBI~i+oog0xm9YIu&~u?2ig^NUC7l<9LJwAKyWOj4^B=1<#3nl8=+XQ3ry?Fu zQE*+`p?&X>Yw5a2!3g$MK*Y{iijrxgxU2}9li8!lP2tG7RkjYwpUk!M5_E6njj5ZH zG&jEB54i3QB;c=w_2Ub6-vbmg+?Xf2CpV?|JlS!Ig1ck%;ud%Cc4{k1+z1ygFT~<3 zlAV%p^2@hfe1Mw<S);uLMBMKghb+`8y%ka;(o0cYn& zl!vSC1$7L!!~(+e=%2rnKgq=a(K?htuYtXadbrloHXx9KD2nCdauneo?ASd!pM;@S z#qj8pDJW{r94PxONML9(^v>2vGG|U1A309AXna5vJ2d>8b>q29?9cfRnLMT^2kB|$ zy+=)E?X)vkK`Udt3cU{YYj*kyygFQ$cA(I_KOHGFL)Pnbf!0Zbg_lroOvm$g><_oL z(}kW+(Q;Y{w7|H{B~iV+sGc^=H0XUR9Cm$2R-xaBGUVr>I1q@6|shQhs)8 z!K$I58eV_*;-Y$w3+5M*_Aj^jm9^-$aTB$X^Tg88yd!@zPB)YMSb{BAb`8=4~xQ6hpJYcWMGKcXymAu<=VfU6mC7NZDNK{;1&|WH6 zr$ayIE|RFEC)N>^XB|6QwuZ9c-oHjao|PC8S^aNBHm-58#U|1aOK ztG4A-=mu4n=@l{W3onPqEwu)h5ls2|);9v8i-#ux!n_jVB#+lhH1{m7^QJ_HB3sIc zd&Tlb=?~3<6i-|EoaoZM$YN2e%t_4h<4Bm;3@5-e=zK=zA%lgqW2VXbLN+BPzfqYm zZ}XJOiy$8=2p!s2Fzs{f*JQ(6HmBtlS+9wsb0;5yo~Knt3Yr!Mwi&Scq9Q<11llWh z2bII<@|h@|AAj#eUSK40ey89$lM%yub{g*jFh(M59g|u)n*kNxWlS>LgiY>>Ydv3L zfZzS>>a^=tmG752MiXr$Fg6j;;Q7}ronDDnPZHlkVws?TIe3oc036OWB7@0WmyTwF zV;h5@{NejiBJL#U0^|ocd*kfGPKe z8Rip=^2JG4j=~da=*O5^Pakfg2nH04n)JS=M5UQ+4lnMMFL|mLv3T?TsJ8w4gk1RL zNzAiM;uOtwd&fyiv&H}fXd6F1c-*)3g6IzFF%mjIK}NZ&Io+Te6bH_xS(GyYe-Y7H zB05Oq42lw&72JQwm?nMw;>|r;$F#4Z6SYk%1LkMf6{2`f`BVWig|2=rbjcR&&z#E3 zHZc|M;;3|7=rmboYpp6shtvDtHh4ceBhD=sX^UPeE>#3{s~`>m{eLsam_R(sJK5|0KvYyH1=HN?oVf}&O@ljt8S z>#y3ae#k;|tz~%u%ut9;O;KvUlU}yGWQE&Z+Ir~7LyK>uqtp;Ux?)^yAU)t66QA~# z=~qC$6D3(P2qks9V#WhgVtR8^>U{-4#YJD@+lbuf~cJ7 z)uOOjCzF)?u@Vc_?bNVCXAa5v(m&XubFHpH#Ah3b>|Qxbn(go4sM8!LnE`m+t)bZ! zwgh6XZpoQhA-78A_)lBBjp|1v6svCV`g>MPje~cF(eKlHvIFYO6pY9l{+O%LDv=0h zHCi}6op8@+0*8zZ+GsiGpjrkZ@?fr&19?N~jmb5HrlhPR+d<ZeB=!)=ZbLY6s1!%o9dMUk$P&YE)&HQ6#EsBMs9AMC7_2X zOi4&TR>AVxD@!-T_!y7k=r|?96!;O}LOzNMLBQ&-e3VkQts~|{gVTDnJU}N^Qu}}| zWtGy5u!iv5X=V4ashe{q35k&NQA(PX*73n4$BSrRTPObe?Cy!nPc;#n^Q%^vXvB); zSn76Enouo^t}p}l-A}BHD=MC3h>q!YsHK^|hWkG^aRgFoZVn0D$dJ5;f zjDzOqFH0(Ag5@F=w3m+&7;cSYPu$1G@DGm~@)5Uw8ni*x*6-g*aJjorSi9Q}>l*wu zQObUA8+x&lw;@<=y@(xd*M~I@++V}+a)EnWdglX5dT&ZQc50hpu(RIcTBWJe$1<@L z(w$y&yE$C$wtevuz$PPo4lW8p;xtYQU!>^E2BY}606g<6^xpR*@u@!H7SaP~iy~%oBnG8;HVF zO25BnwB9Hy_C4$5;@x5VHvatQm%SYay>ug-PImV;?e-C>*nDmv{zjMA$n_u;jX#k! zt>%!;D4-MV&yCBpW`PvXTwi!bmWdgOQfjkdkF2|m;0a{?x*n!@jDN@Gr!V4xlaizgXek^* zf3mGAeeid2_3la3^=8%J2nZ`{tN@?p&ryTvSUXY$e^d|U>bCcpF4WFL@tGxru2in1 zW8&!JwcTR52Q}X3rC1xgkex2jiFAEhgQhsU%rQM6@##FPq_E_r=@Yw=neNzCZFNo_=spw*nuRMMr-@3U`nZd&rci)JC@HwsoRWyS~X$#|A|&u3{0_3=LBv z!bd2CDxR^HfC(ILf;@Y`SMObR>wZ2bG(-|Qyn4E?nQixi-)>4$6;8dK)3#$GPa!=( z=EYTK|88?sgt98gdT>eB4XXA< zW!Hy7tmp$l#du}>y7`qL;G9SO&X#|ar=(Gbseuqz^&L2-(;$@?pG~K3^~Z1~N@s1(bBJ?I zi9dXp_6w~q5qg0wU_lj>dv&&)blWW*mS*|To_$IS=w}O5CvfYK0jh(%G+Ch zNp!LZ=q*Z-BaAlvjh(JfC7zY-1_@M#^7~4^mP_y3?63PQ%?;}LK}>v$Nz|I1W@Qxx z6EP5bzJ+Z`6P5jOg&Yh{a1ubTiM7hl?v2ptiq~>=Oh&qY$Szg!k>h2AR~s)(4jQ#c zQsH69+eM#azaVXGA-bJj|JLGFV?)H!@xJ0Xb7ON6Rr1b%E;B&(c5fkNnju|+1R9Kz zf&1SUzzxxK6$0u9;6O~2Uw*NpghBa8T$?ZNvH@qS;+RhBEk?eHttx6MIR@iD7iP5|bKmo72vDK6? zMY@7LSm`br;Ud3boekPA9X~${P!p;LZW#?m%J2pRE09Uo>?~2;sMB~mUq{Miuzw;C zg+1B=q)nR?ns02Xjr4qUzM#-a`kJI}xMQ1ohlS&OYs)n=(e$47e9W=S*LJq+@BI2> ztXY(VCZg;?Z)ueA*r1<0b)MacLmzv<{W-j6aXAOCYVI`Yz_(W9$u3cGZG1WBfoZn2 z&1_`{&c2(-6sMThPh<0m1No~8R#Kx=f(;8qit;|h@6)g@O=9nBA?w#hFcwDi)k7rE zL~iKtV$n?H$RqZDKtYr}3rD!(JvzDe6ueo;+{`~zBafH%^*q<2rFc<5UeQmr$nBtB z73xwJ#dQ!K84Gc+3o8l?U1Gt(OYo1bhDf!bH5}{9|9J|hV|mBjWzK~WcHcYNsdqa? z_nzc^Hfk{&h!hHvc*uR>q;kNMA8pq+`B&L&?6-`v=tBbl)KnQ64YcTam1fv!0CW@| zOwwqUJ)^__>B*cji@*11H|){N*DG#Y;EHHY%I`;6tC+3#PsN!Q?V4}Wq@&e%&YSZL zmD=7=w$OC7IWt-kgH~U)ElnhwLzi~7dG82g6=4mnBa1bp$hb>k=qF?(PQP22Qfn~1 z#~lCti8khWCEji1R)#3=#-f?vm>xmCts5~~DtUkTl)zx6e9pz4^4msUnS$bXZuu@? z=l_2CIHJR4v9rzJ03I;B{FMI@NdMjl5aQE_^8yi+599K{N9jcp~kc8W}^AY?U>)9IV&nZqso+PPxL&5nC$@;N9;^FB73}Iq+g4Uq)Ym(gL zet(^7w-TQoK00cvFF@9Z5b!$Fq80C@?(T;qa7mi&ul>*XXNpQ0^Q_U#4jP*MWo3Y( zDkhaoh@eE5?8L95wG53DsEOZW^>p?3&V^2|3k+Sw_bz1vyK+W+oLUgAR$wRaUH0JO* z6RdHdLG6A^lxeYN%kl5CckEeF|2L~wTgo0c5UyLz+h8xJBJyI_r0%!T1rNw+*jb#M zwmJz$&+})*k(ienESb;f6_cY2>BErriMG0J`%?~+c(9RR-I*bE{ZW2!J%oYu_vv5B zh?Ygr&Jy79ssRDVF#mdp3ggBzU;?6)v?;MxE8Fo5h{k zkCA(#_i+<38PqBBt7seORg&U}WIg=l=kB$ptDBuLWE)Q1U7o{6gua2dq4Tot=xY0* zqq7y-oERFRFz$m7gw zOI|90O#lrV)uC=K5)rQMtSk2gCI)i6j+n=snFF5IfvLxIdPAzcDQ+b~k`j$4Rxo%0n{Uo<{6O z#akXaiz9LEA0Uvb)0lglV+7>d)hqdM!3ien*s2k1l**H{9wnp2d$83r;}H_5CdQ9Sa^5mzn?zHQehE|JfUtPzUJ zy_S@^MKufO;L1xDE`jxZ!-Q7b(P!ST9^*ZE*r-{xeaya&${2D#$9zO|%QVWFdRE$T zc_g<0F=;Rbr`%{_WPT{zO!&-Df2(ex zKzIXuByr>t2D(0$$OA>%o`Pwqqs>6cHZH#s)Ja1SchCI&F@PRd}J4OFJflKlh-+J|#RMCG zDM@KB(a(%6=PUkdVWzQTgl1c?z-saGs+~=d=FKxYePXsvjUH6dXf$01py!&PnyNEOLD4`yyf%Nc-X(K@ZRQm1 z=R}|rZXsx|DH4Fz&Ut^OEepZl^8eGj2$F5qcPEl7} zFa1Oe9mcJu4KngQ$z6^7>-?tX&|Z5q-HM}D(xgkbPo8468%0anZqGmti4;RaXoU29 zs_}zUDg`)z=Y<|BBq^_K+fkdZ>#&)@8p=0sv%3-U@sQD}KUd3k0ne4UK%@ek3J@qb zpL&gW_$-5{q{TpLQic$h5`&(Ei9A5)d=;R~cfNrs=XTSTxoX$NrOO!kZOyj*6yX~7 zb}%WX;?3awQ@#`q^$OG8g;so(oaA+GL1&u}s?0%s4sA#gax~DWQcZ#>>ZrOXs6OA? z*@oYqmggoncrFwzlY#tcz-KPXGOeR!fTZ?;anC1iIy%Pnd1;01fp=hKaTkUGeYb0OJ zvZ1m2`UA8e6nDFvO*-3zm1TkqmA>uXl?nRC-s`${{$N~q6xr*0-P)Z%KD54bM}ugJ zA`UbH%KV$oTTC`q>YSao?qa;~az-|yGT9qlnpQ|GUMm+bhL7W%!#)q=`sUH#H~4n! zy9Xl>d;npM|3Ge-kxgsK%Ip(3Zy&!p9Mv|5UuM3+=<7Z*JqRvjH(u#KpVFDNK2C*K+`XLm@?ziS-6 zcHiIz)xY~B-u!hNy_zS^Ctmt)m+u9Zw)t15jvtlUc;Q3$z&vHW^Fo6ad6hDfZK8fygN_Xb_q=gHr`! z8^#SOea8`Az^#%PNpHE5UZMhEEd)j(3M%=HMq&HP$g6hEl(WVX>GlUBmPmxPM+uzk za0=r|pCd!(E7|sSIm*tgmpwVEed47oW9k?EUK3$g_dt{w2VKbdL)-*8F-XI5;<&q( zG#$m?jm{Vg`$OQ7{Bo%IfzQBw(!uvM7Xq55!&7C%i-%Dhsl>M{bh1LuicL&CD|XbZ zuBEoh|JDhzSkA?%`66__$&^fuz!Cezi_6K{Pn=8(wzO~jc2+9E3X7_s5`?4LJcSvi ze%@+T9H}9Ay_kog(H{z>QKAb-!nfy%sba^&(OznL$uW=mg8Fruhscb+sk9GDW;Sf{ z@le!q+r34j3MCZI$2VAcNt|)!zv@YTC8V2!hh5sb4kKgUc^siDA?kJ=?SaM%=dz+L zk?pq9x#>@1OP=xdMJGUa_4GIUn;{xLD*{GYAYL&;wUB<-nJY(`iHUjb6(MVa)0-=7 zWY=`LtQOkb>3g;t1wGv-mQ^ZpVED>aOpg9|HlmI9lHu5zH2tHf)CFtpuEKAm-_iy3 zKqRKxHW4xMYKgOF*>rwMR4%=3$!RmnH_I@;;uGZN%Z-$joJL+J|DRSU`nD*Xxbykv zN%G;pO}jdTZxd58*<}Tu6x$99AosR8*VF{_Q`BJ_pZI15eeNGHEoM zn8ou<#$RRD-;c;9bE1fjva0kstGY)=StUbQ1E&N@E9LX7-R`|{gH{kC4WJAN)0W#V zj(EPry^bpnS;3xc;GxkyLKI6oI7Wg3oB_ z>YC;a|Jsn%egKBh4^iI}SYm*Yp7-0dAIdEsB`2@mp1*x8k+4;=JcvIET3=}Y;wAnMp0k?N2w zfy8;#MTz9cMDOrpA{()nSY+RI@<8HIK}btH`wU(I1wN1*_p8QEmH(R0+3f?#&XoJY zVfsZc_S_zR(-gQH#^r7Ka_DU`W>a*AZRme~)>U4*n`m9~rW>)lG_b224Yr*`;N9Mo z2hZ2L8#a#I||J6fHJ&C;Fr?APVvF=An5K3c?grbtdJi{T} z^K-~ow(yG!wS-BG&>Q`-Hrw<-!Q?(+R|^SIMvt#BV|u!OA!viY8)K<$!nhWv@A~CQ zk6mo0ZxAZsihl0=#*cp!4Ksn-u@9&NHJ6E(<#cZ@*KUJlZKU+s5Zg~6?=K7XNdj2u zYc`p4+E$I|?{<1Qefn|@C4b0YPd~7C2p;UQ`twWm1(-EG=qf1R9<@@o$a`!U&}LiF z5(G&lS37*5Yx=Jdz`sx?AeTAOcW-3TOM_mVxMn$8cyDGRdyPodN8Ytz%X+GAIjQfF>yR0(J-4V1e5A*KYCv>rM%mTs2kpzl80JE$Zn;EN>D>;t?#{|{Sl9uMXF zwvXR4jZ9QbSt@BJlqE|$QD&5-Qi$x!sCZL$Aqg``kq{LviWb?8P(;j-EGbLb_fYm_ ztTT+ce%HL8&+~l0&+Gg9r&sk##(7`&c^&6*9OrrBsO5B7{q_2tSVFO~5e|sMRpVF- zEym+nO3;aM&}&ypL0(M<+F3Mt(QImf^=Gf!bEeNnuxn90!eJ`g-rV`Bu(%=UM4(OY z+T)L&gV9ae&8mx2WAU04&E7)sZ>E}+c!kT^&t82K6J)u(q%k!|y4xsM=Tu@0xifM< z53v%R^7u!CKLp!Dp2s$HzP%dc#MTTUXZZ(EkNA#fA42!39JI?6b&A^Nv+JqDbug~Pp z-+Hlht@Xo~?ED{nboI0KY-1z)6N*t9fh$`1>V?MVWJ)Z1g`#5AB~TIFTJg(sB7{!@ zO~sZxuS5Gzl*N?RPFe(_{2W-fzX@hkcdf0%`i{(3QetlXsRIvHs|Ey*8hr`v4zQV8 zUw(SXM(pW!ZdqXCdiE9iy&~7*Qx(0N&Nla_w$0XDll(S5(wIE7gR9;9a36b~{KAWS zS(hp_3x`8SU;g+8Yd2G|D`hqHEU~}ur6R`1je}1m?&h9L3ztF^mGB9Cp}|(Sg{ncQ ziNLbR>@kY9-vlzG%BH;83u58gC^CMg9%M;V2_Owis0H1sXki@b0WiTpn#xeGi%szp zqYeNm^uR5YMggL@ld13(kdqBi{$IXP(iq~R0XPt=hrobN*d3ie7{)I>(y3SEZ=WJm8ri)&Tp=!=h+ZGi5Y48BuppThW*YCE}9l0{R(VS zeJwHUCrQzjKD)O+6qzu*$t_Dajh!sp1qqRLOIDYB@+Zd+XaZuQltbB+rSt4(@R$#h^sCu zf*a-Nnk_yfAb}<)=^=c7U4ycCUT)3gP+I)B&djYcgN(CmyNpW>D^3$A9lwC2LDf;I zDfc1CE7x6~1O{v*df!loi%`CC-k+fqxOYVj1@~t&@CxTqoD6>uI@KMOMEvAI+q>kO z@6Y|<6|7&u-EDo)NoCd^V-(Q2Ud5sQ;TKv*%ljQtiiV5L!=NH<2(1;XFGWq=nPlxO z)UT+)$8qd!mUwYYy_J~L%^zkvyksiT;J$6m^$0x8@J-t~+a5{6fks_Ki<|rcYg7W4 zjbb=hk}O*9FEGQTIH4kFUw#9+eh&IkxX_^b`8kU1jP(Q7uKZ;}PpSB|Y-c)SL4LZ4lxOR(41`r%qy z-&gr?&*|4;A670k+w#LfF)ZM<71a<5=vE_C4lkkmI4M`7R^;ClohOFzJ- z`*SWDmlX%m+Tj-8I-$gpKW}foL5{VsQJEiZEx?aTR&4*}-&y2-T8@-m|b z+;+;ExJZsg*L={eIJV#Jf&c znr4cNwpUmkv6kC3cL;tfsp{89ZQi(0|AMyV=@Kk8Sv6~Y=RtJD zJY_+&?hGoXzu1*aUSY}W|5re3K(kPymE%;BEC8PNT#rwLVBsLaK+BHcKBcUiB=Wb; z-o$I$Aqz%M54ttvPrlBo19!ib^i2pjW#LDITU~DL{Jbkic5u%u^Dtj=@(@^O_%@kM zzTLWqd-gf@%h*>tzWX)(?Mh+I>DOhG3scB1+?9KDe>ECD1gh7BfjE6juE#mdIE#Kn zp0EqUY0QW@BnXe9js(h~op1arnD_@3AwrtrgX``=&Jwx_hj={Qq2+v^pxohrA!G*I zcGWncEKL3KY&Iq6PX$*W+|Sfb@hEF5^GyGeDMoi2Vf@f$le;iQc!N$Ny?tydwdAk3 z`K&^oa@NnR{6`XMd6W7wM$r%&(P~+m1le@?Fr3V)ISLY+(ValXsqbw zh;eC)H71Th#cCIrSi%?vf(1iNHi0w`PVW?c%IBHEpB*X*e5Pv27j6a`aK|u^Dh*Ty=av3`NVG zq?Eb$>N5bJbz`#7Tx>xAT=)Szr0EoAm8V0ZKD~|-pC5eK-LB$)efG$s~T{pyCV-~6(x>8Bik^?)oxlowtFl(%24oBv|Ni9a>&=N9RV}m zII8Z8U!V&^ zP;noqNd&?!)I|(R2S<%rS27R8f4HD7@G@zYH1dAC_Cd68j;aLev!<-JZ`ccx7>g_hZd7O>KDx{x9k+3h}8HhwVl z=O1d7t)>L#K|YSkd(lub-FKW1b&3qAvx&B`URyte#4O!ZTpU~~vrD@eSAOD_bOhtX z@93-_*L(EEdocxfKGWdgjd@>zpEhXc+{F)EITdy{c-yq3k&@JG&D_qbM9~I|dG#Sq zoYGcRwgp`y% zd6E<;Ks}A}zy4Z;bl85AaMk1I7<9>O(fIN}`sZ;h!9w-O%G2?D9ah3RvH!WIBKID0 z3+)z4n@sMXSXeT)osZgQw^vpLW`l!Rfh2wzjR(dnT3gmE#8xBRe^r{4;H$ZvBj7k% zxTEbUAH#$?<%F871+-)6yd8JcG~LA*PvwUo*EB_Z2QNdnGOO@~gM!J5ike0I_7%9<`}&HO$B_Ok zK6t<)c(kwlAt=gvFVSppu!Crk=rp_1x}@fRqTH#Q>z!yo{7jZb4>%rVQ)b7AFYb9@ z?$b3DSLH3{(Wiwb-cs=i;kDfNxhI$IOmQ^W08Uiz3J1U9v^aEiGx{JRl%iW5M5RG)qPbv5!aZW>BUpfiK4X z2RhI7dRv5U&(JjQL&+_Ul#piYx4Il0sOFIMkcS{@qPfWM`IvKZjj@V*|Ag;Z(aDar zQj*xjRdXAs=R>+B3rmSdhJ!woMi(u_MPDl?SNHTO8i}k(-3CS%PyVjkXNl%oB~!S^ z8qp6R#x{CVGE0sca?UrwHX*H#e#|WF##{_=L z1pY+_@r{6W&qa_Rk!LsExXs^X^{MTylDnrpB>XuSeBQy)*+<2(Z7!Ya20O=0e=H7y zP8*m_7YVah<|vXF%zMfM;Is^;UJ4$D*jty;ra+S7W6#ipm>J^wmZn8NCa2hfob^*HT2m8W+m$Qx$Y zk6M;%ktU=KmM5%}IByhB?tyShe}a2_J7$#GojL?^2!zq&1?BHG;AB^K@k!d0SF&)(gh*kOB3iZKkHk6qeelSd1? zv*4a_gs~d(G_Y(^KW0g^_njxV&WZ{=PTVBk*$Y&DeqcfnqK=ar1QLpgCrm#PRMm4Yu!BI_3WFPzx-{ihQW7a zSh_I+qHzdOXhks+OE@S<(nc<;49LVaT?`>-eg+Htt7D-jo}e#kBRV=JIv2Jw4=MeX z88nSA{~AE$Q_$bwBuW0z5+zi0W#mBM+9d^NJp&r@$a0;|-$N6-O7##)HnA^zu}q^_ps7NdstClVE7jfE z(e-~pYHUB`TBs@NQfpGCDO{d9mgjX9x- zERb#POo?UYo#Xd+utW~a6iWj)OG&I7@snl!yYgeU;-i7El8Vdc(BJ3R&w*++(xhNh zu^{O%?%>CsH1&NscLsy2X(pMrXLrh23FVO-<$q@nWhDeKfBQ`yM{<)x@+sUUvbHhv z&c*i4`@HDJOX?q#yHm0^S1CNo4z3%o$yYmFJ8rK-%+?t9$B;NjBSU>L4>qE28^)|Q zrbK5Sy|-dELATctNSWzn5=bq4Oe~4>3|^<&3;}c?w=pN2sFCt$DH-}aTK5W{c-2cJ z8NKifR+Wqfi3b!b_)ld^vzC7bzRi)6vgP(5 z&c*KJN~5^W06hX3ZEwcGr(`QKqZ_(*YW8d_nRUySUT3`G-YG~Wd+jtad|>SbyC3*Ngf`UvEEKi5Kfo4!tB_a1M-P8C~N31-y4{M1nWVQN>#V zwGfR24^3lj#8IOnRC3;Dm2YCJaj09JE9w^S6U5F&2UpK2SxT7vK9v-(`>IuZO8z6y zHm0e58Ou|{&wbC>NXBF`eN8;>;^*Cv#u8{reng2=PjoXfoB8ddVn%mZykaROINf-}a;{?>&<%)zfrg%}~G)@!n!vytUlwFy>cs$F2Qq=xF7g@TJqjs4uH(+YhL z#=9=k>cGN`~Gz8e1@te^3|1gYoFbr#*gM8`Rygm1Dh&SEt{Y%ACQ&!?uH5Eu8 zw>^6HM7>0joXgAbaOK4grI(+h;@6XI`MRYC8q}V6uGXL>Mch0i*e>ofV6^_EZ{!Cj z+_+FRocWY8eH{gJtyGpo-I79u`|b{XtjP|#0X()9PLUHLw1W*<8oyx{IkYmCmb|O| z1C=#4`22Z~l75ob^%cKP`2mmmw^Uj}88WzcY2X=$ei{eMo89P$S&+C1M~Vj?+ffV8 z_&hs!P<93;+BC#h11-xY3Zb>Xv>_qnkMMD1_OD&I$vk+>;@#ttXDP{%?J5bj?*nUx z!I=+NM%+)YWj|V*F9K5W_f+4O_;rM>CEJ*3%z@f3x3hy4_3z&H;orwY1lDUeX(9uY z|DZ7BntWR7(T@Di!I!y)EzR&@?#lKXoCgV5`;Aq|HV>Cuzs?CjUQ%-_Nxw9-%dl#tdXj;fuE#~4<{1^r-ZY=_8XYC=aw2>Wz+4_WE$Pc@nTws1hZ@~Z6RC`Y9ALG*G4Q>B- zAagC&T>n=fH*)_teoz6i$Bk=YE~Ua^O`DjFdefQ=+oNNU(Ip&IBQ{>|iCDkpka0oe z9^K$)xtSiauxz#L<7MUZdmvFZVf(f!aAdp)viBagPX8?I;Yl|e~lOM9bjN)rc?r21&3YnAVh z9&kE+vxBd+SxtOSl)i>QeauR`yscTM;IZ_QeJg%Xu?&?Y3R4lAMOIQ-C@PSd;)pZ zVBWj@^{)OGfWKbM_6BB3ukFF3_-bRShQH6*gNz!7yp8t6H`r$~aLYT$CKXY>&LqQc_c^6bRcnl%v;IF`Dk zVY%0t>(R`G^HLo5XAeb3Qr#Q1@+gJWl#5!fb}Os`6XNUsw?EYg6SoUCA9H@9#5=hK zLU|i+-sV=gwFkT{R?(YO?^`DRRlD?J*74!zC=(U`ZAtq^+Xf6!t$2|q*zt*)!q2nK0OPPtNUfx}%J5NEc4zlccWlj*A zsKaAnqmgPQ)T3d5;i&!vBB;VyP|Kn?iBtR2s(}+5MnjYUx7@ed0a-|bOf>!6lF`h- zp4zf3%M5*eI@?Hu{wOMvon6hGx!RbyGLh?UP*J05x0hMp6hQTTYQ0A7 ze>&=3GyX2y{m1zF9Cwq2+!Fl9+Dq{eyr#6XKuUA;>i2>H;6oRqS4$SBi$EHDnj6il z6rq|x3-$lJz;sFr#?sF$_VnjfSl-&{Q=f2C*|)uuU#8bT2{W!i1OCD7y5@=-H+JRd zZcKM9`@6P7phxAcU<0z?+cb5`4OY7%Z)=AqbXl%D-{m^QTzOhB{2(AU17vf8;W z6F;+x83pEEaqh+i$Z4Oq%!B0YNGYzS!|R$6k5507`9YX1UWJZl&~db9o6%4+JM&pAKg?_Nbyq zYXMp5JDFo8iIQ=-t18cpT@TTBN(5NNk-UwmTT!gsH|mhtM5Zi??BmWSGNDwV!3)Iq$Gyj!dwhj zN#LUxLBLCSp9>05P^(JW!}o+kH-L2H1zDpDTC458RikYg=v8YRm9c26JhkZz+wq%v z>S5`DOHcLr`lbC?O>-5FAx}BiECsQ)%G+AnGmh*h8$$Ls3!<%ctLRBsz6%qo?|Kjg z3B{76qiA6*lGvvSZBlw1h!P=eL0QO?yv3;wm=sxblX#jmby$%q6DPbeg#5CE7^#hj z%2Uak6V03|q~zSM&M#j($T&P{l8wCF1FkDCWXcdeO!XyvR~*4AN424 z$mo|VhJel6ikc-b=IY9N{#nMtywtujl###mJn?v4_X+x##~aMAwWcXFMd785_7EBd-QpqY_tTO67hicWb$2F^2JV&9`?i0#Yk~Z+edP zl;d4+%^bL{hVzvMeL?DIh#a>S;ux2tsZiO2@46}){y4*m2t^8kcTwq2K~&lCXe3^I zF4_AQieu@G5guzah61dhslBIMs}a^bYfW!w#epM z+E6eIS;1@mUuD}o`g=64lG5G>im!}BX$r;d;~by8`f3kJTtIZ^#ZV*t%iC9zBAq$T zT-w(gn7x`rHX!(pfY${GrCn zlT^51&_{_Ni;d7m=x1}VkCONv^x&QtBb40)BHG^(HX^6B_#v?5RqT$EqSDs6ppG4} zQduWG{-`8gd#L=i2xEn2ocGjJ{>cw?w{qs+?rx)HFNL+zE`#gu|NH)eF+C32^-k+( zK|^Xz@RQ}%&q7d8tMz!;(tZ}DcJpJlciVL{MgEr2Em>?zy$#ja@MX4iUy?53F_{_? z$i?4BlC%j=(b|8p7vI8XCQD=LrU&beZ(vFc4-}^;zXU^WEeC(ArVVjsrW}{*^qL!! z2wt?Wu{U0YE^OCS&RQenm5{|acPYQEsSR5d)Z~y?NyCg}T3x{#=VJs(l*P_lb3i?M z0DM-J8$Tyirai0hF}HtmYw0Xy>Zb?%`E7HV&HkJ&hZh!oZz}19mI#N+&up7_x0D>6 zsZXeY3Bib$Q`;E;25I;$b>DqNcNXdf44T0*UyLF~8%MQZMp{KV+jmNaJZ$$qh)&7u z-`?OPIs}ap76}P$=UX9Xcsg{QQ6NiKu-#x7(*B> zt^?z?`ps@2+GfqBC3>K!uEq;zy8(>cuy%KDmb`k-0i}Le!XZm7iH<)mI1rp_%3=-} z`qQ(?n|k*?@>ZMcmjx;v?+}Uol%U%*zQb&QovH^fg5NAuikrXJ%ohaoW6?<7x-tJ~ zkBDj&6XL<&xS-4IE6z#;rl^q^(yMql?MD4LX<%E85hsB6Chz+V86eUuMSaUK{0$@n zPIpXUZVhO-}y5GCeZeZ-yH?`HFC#;zl@6wiWX8tYc}OkdNOHQz6AytM$F)l z-lq$v6JNek-JASP-*x-OqS)t-&mZ5IFhIR7CtUgdW52%@ht{5PA%8*JyM6Htvbs@J zpiQqtST2WP)M-N!znpri;NC*2aUMzVT0u}qfi&O)jWL{}P-w8?mqk11aTDh#TB~84 z{>r)DWxnYTlGG3Kd3@lj7!nF5IbwSBNB6ICb z0%?61l`va)@!Qn`&PQ72r{(?jM6*`P>DY%a{QJk#mamy>@mb#r=Vav$YdfiuAr4HCM8Er}Y|-aptOc<_>&8OHVLI5)2!<8d5wI`Bt^AlCRZ)q?JbbKcMbl z#Rha$Z$DJthKZ+F^^egtr>}E=QVCB4EfT+U{y`^X2d79x5T87Y492(0p!Ni-?b8r= z@OS&nkv8dqN&M~T`#yX+sfDA$p*01IzTxg2+DLGQX1?z~J4(ZY z&ahhVz1wg+n>jI*Kif5>E{`N_duFJ3pjIi$YTa5v?%90bZ#+r?PPbG?kJf_%XxZRl z+Riwj!$~#*S}Y*`DH5^dsz@O#<=U`XM|&8+Qur*a)Pf?WeHrSP)PREvK0js^Ku*nS0$Ek54`Tzk1;oS0`!eHLfA}vyKCyrh_lq)_xV$ zdS|LM1IN|lc*a@#-HvT9QUaPff1fHI=<^E~x?1Jl9o4jz8_<}K9Ors%XA3E9Qn!}t zN$Cm8oc&so2Lro$DcB_N#OpM}WX+p%+8eneu9QPuLzV9DV|rtG9HPZRJ@wn;#w%-= zSU^c%V#ZLFubf;oHTqNOoSXFVkw7w%B*+jKFCqKcv-229MKIrrV2m7JO>EBp)pJNy zi(=aHpR3LnM{?KzX)v6h>%)C?}Q()?@PX-5+{=yPE zX;z-+^0bn{70d&NPi=~bEMH!acU7Qn&lRzOc6PNI5Yk0fHrt8+i(i-i($#UJ5-1s2 zKDF}KEgtZr%4ThKuA01SDp)v^uFJw7_@ZNG*>fX*W77(`q$19;>`EACLvIp|l}t4g zpm2)`+ALA`HBUClP*7)QOBgxZg-fsafEcqIB|c=I4uRcNf$;BbRf6189Dt7fbjr9Kzj!M@q*X!P_pn3EVX-UVaRg*Uq#PPA>wQEC47wM zzj9Ew_qfZz7rkh-5-w`NJa{3J$J9kqd9@2;nuk&!xnoFa2M*$>{HV)H5Mwa}L&$1% z?iH_5B|n84&`rKKK1yvCb50F@*Hz@REDQP4I`DOCqyGQSrymY*oA`WT&v9uNmbCRG z5uHbMi(gUKqB<4~860sYtgckEeyPSFRkU7d>f~E$HS}Nb7xwe2n=5jwUajXKZxuSU z$IY{}&{|AYyaCj0c}U+A6;Xb&Y5(T$>@S7t`Nhp8POBWv{M}WaRs)rHmBllUrmyCm zJzwe9<-e?!zeNCgI;4>(4oqn;2y3LNufTdG;WRNN*5dKvO(sRe*QPAsWe#(>RiDJI zS+tvnyAdA>t)}Vyw7nB^T=C&YO-20^pCNa>{_ciq(QI1v-{GgSKwJoZ#0W5-e5^fM z1|6uBGW-^C`WK(-!Hx@*rT=kw9Cz`?&8Zl)qrG|63&9u(x}zgN_=^@%!Ys@kMmL#z z<(p?jA)NT**5dpwg~mjaDQhcECrT9KY@`S`m3H9xC463ogrd^3X2RV2gu#5`H#bubOAFUgEkX38dlv4 zJemm|!6Y5jn*HN>u5eY>gW@4LbaZI=ACGn|?&u!1TQ`lY2d#egWDACOpIdf4Caosq zX`rcC&;4u%TZP;B068y}%g!hACkYM&%G^WLTJb+XqYnQth@1X8#$Q}Exec}R9xyRq zdT)Z+zsuk7#c2j*;s9}N;TKW7IcUVob_ezfnzhXI@g)eL^Ay-ypX+yHp;$v}+rtD7 z7~v!l1{+vvLn;uuAK@4cX(>eg&>5ao=QD9{66AtDe4T~=H(}-9_|f*>hOhM;Wgj}?Ez;)D zof4T7gM1tYDni1jhyd-RSuY%wD4!AouJc~wH|v>8Z{)Q{YO;iv?qcm;R7vtv{PBH7 z%gy27S-fRWwq%7{#;uQVVj=%#s6(Z)RzEdeSW?)2`a)lD*c;X{p8-wPU-_8Slh zNyDG!DYjnopkzt}b-%L5bY{;ayg+kH z6ont{d=6nP)W|r=F<9PZ3BOAEU%KgE{T~X`u9&<7JSU(Rmn@DFVS>bKqJ#;nzd`!> zeeL0aIMBZ6n>Ri=?ey_CSn(IzZ@?ulSIrwQ;(rb~$o3VjjFokn8vS_E-=YHvTAPK3 zW*^dT**9NM<^9J4^B_AmdbJTk-*Ie8OCxmqLCF?&H}iOuu*2w&cq2>qkI|VADAmli zGd*5X7Ux)7IlC(>k`k{CdsyG=mQ^#KwI!*dPaG3*F=AelzxRD-u>Z#F=byoEp3T?nnT8+plvnq8 zE^RX*+b|2{8gr8sw&Qe$^P_zFnc1oKw#|Ze2?x z&yHNaU4Xnie)BRD7zf*}RYkW(&Eu1Rxp;$96$p<`%M}t-?OWHQIvjs%-DHMlr6%yR z{5`dnU#8*18n~r%;H22IC&aIqGifK-6meS#(2k=v0uAN=(()fjvf&AY!(1tG&#@_u zhTa+jYGA}oz7mjAq5uD`&vpP`Wln&JMzR}=A~wm7Kgy|o!NUTQ4&ptXP6_W0w& zpXQjvWfGpS0YE{n5+O!Q)Z`Vr2tyv)D0;_p^{ zudRD2c3i(WR)(vRq^p(%EOfc2ey`uNtA*P5a?NG;TGTL@O*>)R>hb`-gV7xFf=~m+ zQU7m3*Ri=La=%nAKzGJvR7usX4;eRn`SZ8tz8sfcS^rh{3%Sr-pGZ4 z)q=$^d8&z^-uGCOAy+Y!_O1d)jM2hC_quryn|8nK&nKnv#T7TF!HpNY49-eeJYv-- z-`%m0T)7ILCX`gpJQ>qQ&Lm7|BcVrSO{mZ;>##%r#3iHS=sd3eP9`yrRca-LWNm47 zdRcvNQ#7*EB+f^?d;)^8v)j3_H)4E~Leq+6?aUTwk=)ZbpXAB+4<$7yTJAv^0yS;=kTJp;C&7PnK(Zo_UE?{T8%yB`5?D(_-vj!)b z%&iF4wW#_j&~?Gq(KbhYAo+OkEp;4X+Iwq~k)5_OW|!lfMp-L%KsEjSeew$dId|}r z`~KF;YspaYpSaAT40)4aef}>3{BP#+C^}sbUCpG#p|zzTSR;Idqrwp%3}G)C97IRfj%dzT7qifEban@TgW z-mCxTrN*v0!SO^Z6irV58PvT{q#MSIPOPcd_Ky`E%|q(a1Adl*n{$ilkcKOJIsqWk zZval1rsADV1m4Y>9gVP#V=Dj;HWwh;n3NlswkdK`m_t!YX~yt2x@*Ps#lg;dY7>+d zUBt3Kp6^ELowtujZ74(i)NM!WeM_@qk`w3H#yo$QvTo|s+rp5?@f6@K_m`WMZ~k6w zm!kE0CV-w?^OV<=C7$BfRBJv@*@bZiJAZ?kNF<)Wn+>ucUgUJ0;BlwU_mTYIH!x;< z*bf2u&R|kbRQ9wvkug`|qrvFr|5O^AHhj-S(A~hQsqF~%snGUMtG=Dd-HyY=g)2r* zTB++r@dgIk0m^Z%atR zA8NJ;A%ABR<)y+dl$t!8DvU9QmkeDE`a?IMIc3xrBGhDSrY#G{nCb9isH0DxyaP#K zS#+(~oDz=F4!f07_ZWYOj`WW;K6Tkr zy$fO^kaDkf^jYr~*!c4dYp;VGE~#ElPu-Q@B4s9553K*9A3b9`NHTSmE?Mos!1Ko) zC+UpGqb+*M<#rS!+}O+%W2a2v4^F*TILz`Ji?J?-`oVL5eYR7yl56jdN)HwzW=gda z@e8$W2%T((3b61)-Ju~Rd!hFx;T6|tqg@9Tr0f^>pBvub#&7hk6{5DkAkMCK+^|{) zIWrWnliQfAGi^)X?3)AQ+2RPj>h}>A>{-1!U~IRqD8s_ShB;e%3% z3^(~c5(@W+Nh5cG5KKS3Kj%M0A02?*g}k!M274Mhm`nyH6_EotLgeQ7wXnZPDij~? zI86+uG=K$-FWx1^zWd+?-qtgzCg`%$!< zZ+xoq`-@s!vLx!!3LAFM@YH&W4UcFn(nGpQt1+4DO-51)MJhpoZ6AI%YOk_$P#%YG zxXLI<@kKw^%oPhC#h?XV3n#gT#n6|CmE`Ik~>5!jow{2aC$g-srt{v zu=OX#%`vtD$J7-=t@&-9#P{#1@)!nYM6qE`XMp9!AD!yzE;eg(pAyerN7eUTsXMne zzOGyBb2@SCe%cpk&a?Tnt3yd*M!HGuw>M)us>^;tzR{vy<|$n{;w=B!v6Crt|2IT8ZctwIb zdRrOBC~%lK0$_XQf2hR3S&9tWGXare@@Pf491UF?1JZu>(S8*iViqiVvu_13zKq!3k@Rw)<9J>2N8ZIB|vY2@}j%YD4s`$ zqc~xl*PnE7~f2q(u zB>&@Z#3zZHj^eu!z7kl8?0p31pq zJ5M2^BSfB?J@vbdW#r>=r;P7O!I;PF1wtS1mZTYS`{6Y5dr18LUy+Df+9I2%i_{%) zQlq)wM+$wlX&o;B9SV=xV2fTLZ6CKV0p29!q~l=l*0GbAf!GsJY~2gQVWc*XUNb*7 z+&?3F1Q;&8cR)A01le5p*-{>GF=8^fcV0Y(k;q3r;Pcq%n%4UzSIeLLSBY_+Q5>=? zr~#?xLpEg2=r0bz?B2Ng+Fy!Mc3B2Dt3T%N)vGF={+j!;tmav0z#W;f#nq>_g{%C< z-pxy>K{e9}AEC+}?6rLLCRKg0*>>Ohfp!<+S=m(8YdHoBOAmo{xM>swb=)jS!|?jt zs=TANg#5IGrcC&`wS^x6`yAAZY(09oGGO(X*Ba$ojZ$>0cz6vMy?!gEV4S zP}GRd%dBt4Q9Y~%m!zFfpS?Of4EURyS(F=#yjK@Clt=@-2cC9rGa`YtG9Vb81cusPIRdFN~_F-j$ra>Pd>zy|I7 zdDUKc_QppOWV`Re@RQ9Eae(!t4y=V{SwZh?jXs-rfbs(C$wPi;Rnc-haf!mf+Aowd zq9ssj9HIt}s)vYRFtF!kC#rc}v#c7#(#P;mu>+4fpmFcd^#C|4zJE(0-s_aHfDAl+ zq5tRfB?1q)<0M$K?rH%J>2-}z?Xu??HzP97*Lgj0AsgsgRyE#+&rqP$~M`FwF;=CNSa1v>fu zl_>&uqU{NGVByf4V+EcgEWt^f%5<4I=D;sMm|*; zO4G?OnD3A?7hQCaBpF#Ycs^@I59STD#UcE3RKV zOr<)0ba5D-nIT6qRHTiX&Lm%kmJ~2@Cjm%l3xW)CJfxGc})4UTm$(*Vb{g zvu^S=FQ!q+H?0ICz5xDea3E5QAK&=|4@%SnZ>c~2K`q?-4+xCLQIpY!r?HCvBy#Mgut^XOE^maIfA;9wj-C-9 zE{t+Uo?yaoLlskZzsN#xqw^5(cc1G0B?~;@FjHxynFxg=m?O}VFm6qjNKnw5R4nM< zgU75Jrz9okH>`_L$SCPCre5WwvxWOyk(U zj%rBr5!Kxt=D-v*tL2?&IT17mlzA`?@+q5+LVG2IaoFXd(W>^do+A!WT=+^5*=+ zippb81gkb(hy68zeq@K$ewyWMue#vCr81SK#@AmAGi6=eAAC4J)lw0@tXMqFIK_SJ zwsm&I(FIYA$R5N_1>N|IZ~o^KBk|6Ti4TgPU4w7=4`}1@di;<7fQm@uITNHDP8J^* z#W)Znzk-K6Llay)MNUI5Zy;RLf##|7-;cYn1B+J$QtAV3GtGq<^RTAqZoZS&QZxzi zb@dl>vD@ogPX~R_y>dViv; zA+LBLSl}z0ej267r8Bju*%u}q2?gbaF~9}bd;@$-Lhk%2^h5%-yGYTrW+OdxaOD0dI~au zzF{~jkP`bf4qOz~6q=lOiPT%adYK&*?nKf<-Zq-CEx0|$=&3(9AMIwke*qbuaEBF# zS^4VpLjXgI;k?*Gy(a}t@{nlO;`YN7k9hbk zs#|qOEu7J?>J8Q3GHcF`SOhaGG)M79i|QkbP%VZ^@Db?ruq@Ng;Fw?UGhZ+_TmA}r zMpdJF57MK0RX5VpDnGA?zSE5;ZyOe0@CpY_%#%02{)P;mq-mM3uos-Kp=0Rm`xrXv z_M;)WS~0ld%mE2gf`Qjdgp)?CIo%b0Z#!Ct!N@*tZ?8&0M~YUZD9T)U+v(Xdilv_b zsg z3dd$7rU|02A7)j4pD3#r2<4t}@WmMKV?$u^$h66Mex_y0w8tSkBN%sE$;VE0cloGWpy_x_|2 zlrlDW%>ldok_bERfZYMqXimYIg)CkZQjyu*^r!55tg~{3)?IE z+04ia(M00jNw-}n#*fXEV#_dORBmAX-94@5u4#xu#KQhewePeh*eCctLSru|+RFz! zZEyWRW7nhT$4zfxnxepJ6z}0rXgivtUVAj%L-M@1njatn_6(ERDZGOUl2U;vX5_0x zdG)@=>1h=Z&fE*zBryyfJVQxNR=XYSfH}uT$NHIQQQGPY)l?5KOxxSnZCkC=HM)Gy z%!nq}z2)zvTb@%=qJvj;p+WvO@aXPmog78#gWl-&3d?5I_a8ne zlC5|OY5PLKa@L0hUaBdO1(bMGA$c`y&ID)2sP+mpb9~muM!0Q9I8jDjE>wUMg&)YAVm= z?A4!{C!$O}#`2^08y5mQr4irt2eYh`Dg({JvT@_WcHZo4nNrEgMvivo+Bm%%J4-+= zf*$loT!#MyXH8duZw_H4)Dv|aac6m^@DdeQbuduq-QW$xrN{(O0~N$VI`&`%Zk$4L z!y?ZN6r9FA^xT{JzZP?{vM_D#-^Jt}TyFiu-u&-kithNM>1pyjgyt#d_ucK3Wx|`@ z_*XmE+KrnsW?-jFaarjspxlR(qKQ-#guyGef@feYGc!IlUt?#qx6O|$y$MsF{=P12 zsxLb}VbEy4R`d47Nt|{FN{0DX(GAbgW)~7(b4Xme{38E(z+5y5w`A)H+eje;Ltros z{5F2Nn@NU$z!9{bJDC?<{z~k<$G9hTiF))tudw|bdSq*WP1*5XOTtI6JB8VL?glxA zp@lrcV&+Ko%TJA8jcgfd@kSYYHEjm2m={yvk3pqv41U ziqgLqP&7+d0_=B5oZEWzP~Q$x$Ks{+Rw{f%la01tqle9ckL``<@uz?5@iF;E%a4j* zs;YV&FuG)TzK2RLNn5sNC0!mvjo>XWNEz*Y!a_gi|M}f_N^q*Vm@MAwS_sQHZI2-* zc)V;RJ<{YQLC-1o)u88Su$$jM@-8`U!!DCQkBwB8Pntl@|K8bwc?dF(S#T7A=cSc` z1N)VLzG^JI_@(Y}3c^{=<>|$71ExGeRqrlUGSSHrRG~b0KeuyS!LNtK13r!!7ggZ- zL-fAch335Jjczy^M3x`m|MdjhcaI<^l%+jtz%I<%xX?D7g-Y$zzf2ap!>O}^Id&!q z)P+ufe5M8`9o9Dv2lpOI9W8mf9mN{8+PzAHc?Z|mtyUe4dnsO6FHhC!M%V=D=`$OEJ+IAhP zP|JRT@|sSdef7yKa&}b5fhUlGeS%(RCVa&Q7q-LE-|BzYz}Thjx3{CVb&u5XqHs%- z;tNNFi#NS^9=sy<510*$X_fw0EtW<+pCgSUo;MLi{Fc2Y*co38A2GWyVdW$>kgiM; z_PBJ#=<=iWT`ppDA1kD4RS0c508L(cI1&l2LG}Ql^T>ox{FW3ONs>Q{YKi~cxe>T9 zyTL2*24QgO;8f2QJ9aW3HdsEjCE0Gn-vt8dfwHl+Ox+-RMRE18Dc*wR5wXD-0z zIFZ|dOPI(5B0&sK3&A2|BZEWh1UVTh`>NtP0BB^sziN zyYr_z-<1D!^k?l~o}5g@d_Mlw{S>t&c4F`_-<>5=*|p7oHGi%YET(I4;}r#CjLUgW zMEHTAN@@+fRJBMRwLOc05p0`zyOPCz6fM-hE%A5<-8bL9Q*6D|h1hxUVy&K?k#Am} zx&-Z{Y#skpiOjDc`*W}O_8k6uFyP(Zgs%Hv_yTYa4|xYs+)^z^zQ%xX0QLeOT(g5D zDc%BFY$=;D3od^=@9Epn-x2NMckKZuW$lW(G ze?1kdSHS0_{_QPXJZU#aHe6gbS<%!@< z`S(PPU`Q^!OLYr^ZIoIU-g>Z^N|OEuVjSZ`%qZPfdM9Yh@D0AP>OJ{0YT@${!-tqzC!dQ7QN$p zn%+%&WRmdn@NcsAYpd7QbL8Jo5+6g)FU+_dEMmk=C`*2R^s$XK%wD|=a53tEmJ-tI z&^-pBH#Wp?FrRDgXRUb{&@#3TzM2%q-)$slzAaEih4+~0iq&;iC_@-o`&V+pmK?C^ z4Wl6(z>vfwNX5pQ$V$z90vfbMc{p-t;14yqDbia`i9jn1Md_yIZ zY8_A?SPFczW!_{?ZY7SRXv)TA&qSsU{6n={FL8qPMg^O*gjM&!J#@VG1g$fD;u&e? zf5zv?xKgvtTFN>m?`OR>yFC3wn5!JdMedF3omyzzy2NFJe{Tz9f*p!*3byj}`< zcn_L)W&yWKGj)#d#MErY5V6JE*b0^-QMH|qL^b}iY@19cAG++Jw5fcsFFyD}kKy}t z3&lqG60TCEs^tbNnMdV9fnvK`=`^oEtBc;&XyjP$#y*q?!?@;|R?Pf9KHSJ%HO-x01acHsVZ z@z`T9UifCd(zBDZm7;b>Qp8TiBFj-;7vR= zE{y}D3ETA=TW>m@NP3IOia&>3W3OYqCJ~Ak1=i`O#zf&T~-uR!)q%eC~?iy*pxeC?Axu<7}Qo= z;1IeH^ZdIQCZW4>bk#y1`pe-pMVV$2F6;RnEs>XqUALy?b!`^7m;j{$DVb9 z3^e2W@z$P+;>bTK5XdkEQ=nxf4KLvT{zwhm>_$>B`y9XRz-8XgE2WZtnBBG*h9Jf6 zh6*;!ysXiS7Ko#?`4&!Xj4KiJdvf&1)>C`JdnA)~ClMe4ujC|1Qec_AOUzm6w9B8T zPv9q>Srzhmer&+h?fM)x>QhT>F#^w29{V>y2?KcM9oWf zJd8rdgDLWYltG!t4~e1z;0^2H3bs!f(xs{^8PnxLSS~-t*Ou)ks(0%NM2F8jtVnn* zsA~IXjn%WN9l~=o9PY%eUlTPXS!Fh`JrovXO%v$8EK7kv1wocDs5&ATwcB&JeOQSj z$tRf_0574YkIgZpZStuUSh{pi9ocI@T39vu{rD(r> z`ZGB@^OWPHydS~^WkzYX6I0d&qR~jg3tqujSFaSMxYU26$_6?biUpJ>ve5Ld->hZd z%93C4PPB6QhrjXL4xx{NtG>-L$%lbj@k0L?JeGt=H*u~wHaf>6i8N?2DkK3|$*KP0 zV!xS>njLFIt3%1z z7kA=2DkCz^Wu{1&E^BV>$JU)+zjj>cYFXDa78&<%8#b9Ezx=mq{MR7q@j4N;wFMiw zix$+_!h)nbDD}ogB=cMEGpfthK2H|?kXq;f>Lz}*zwT69OGkG-8vX8TBRN|*(;={P z-OmYZy(PtTL|k^iY$Bdx<{FKBA85w)ZTG*%E>E0(i6H49wz;o7-X-(NQf}(`VqVT4 z=01byEof@>Xb5fO%1S_ashaV`#lTUFdR7SQ_V&@{2R@PlZw*v$g-=Ozsc^lb+wqvy zP97E#6gjwXQ(s)8r6wcCwxO}C{GA#^syo7)2q^^=`M*)`=^{Dk{h}I;$;0R+F|q7S zUA`P6H+~4kc&|=x~s*_-eAMa zk4?Y+ebM-)=e0BOx&afV#QithFURu|phztGUtmh|+bu8F8duO)?wE-XrYOeD_KX4! zu3k(0>7x&Jvqp@Aid~Q5nrkbJ8nr*6+#!@=L7phvQQ%t2OODFOvP`}nmn*a4x#4%E z-(d1icj$!O$iCH6seCs+1Wyv}26b&LrY{wxM90SwFX;`ZAtm&Ny;x4@jq@5>hIC6V zb(mjU=A%M%v7*Cj2?nr~YfEB50qkj6Obj%nN1DFmN^ZqyC>AMPWe;qfSvhb^d*I4+ z`<{T_Y^iNyr)ZCAha>raZn)%vM#b;?nJus^d54^f4_>38ccq%I_P@ImC3ruDubr7| zn2RSp^7^zeSZj-+$o|TfLWahK#arce*w zS;w)kRa}FG6`~x$rccoZBEB55ba3t6w&Ts#R`9Z(T!Y684H#Bh{{c~XNs`9C5NBu6 zGi<%6S-{#s7*Q6P^<SgR&p3@jYn-Gb)-V`Gl^a%=NNa}kF?CFqBc^3UC0iG;M5#UZp zo@#`AGlx1!4C5R!s7Jwe<-eW`K+7sWz^`S9mGg~lcC0Gd1ZVXPoxRn;{o}#4 zKMPmUq;Q_tPs4N_gciBli+kfn1x9ute+N|D7s9>_M^1Mo&T@S7^QeX(sVuL;5oSYp zdc${6?ev`9TJI$#+S|9jdqzCJeO-ar9U;qfR5GOrt99U&c*#E!vI%Q6`fc1ip!Fy;2JGO@-47pDu zXqEkaLdxf(?YFB`xW?4(J^!(^hcYfniQ7XtrHKGmQc4n7J=qaRrc7;#|L3K$6FnKm znq1d?W~@fH_bU8@o{!Y}@a|(=n_pD+SLLOpX`k&SX7^Tz@r zFYPmo`!Bt#N4smKD7FpXNFStT(qvG2#Jm}pBW#T-(c3p2Ptf#uWV^RVBmQQ7{_N?A zdrsVa^zg#6_2&8(6?usf2xgC|JbVx1a8|BAbQYneq ztnJ~{mg@ig19_nW(O6qD1n34owlEq0=UVT!og?dxRH6Kj59{M7)VWDy2Z4G`-t`JAS?5|Z8XdvmFH>!wW3Lt6 zSoACH5lnl7{dVqX=e>{DYW$WbH{n=0j+>F5EjKXRa5I24&WI)ZlwSht!9S?gJC4M$ zc?O&}z-}r(h8^9Car_^gWH^E3AMU$LLJ+h2c!{fFCe2>H?skp zTh~XpXepI|&FGe*Sai;lK&msmwmOzok9z;{=sqzZVw>%h>N&opCNz38hYZ{EtTx*! z&od}Eu2>$MvjJ{mh^4rGAkgf~!R(&!!1JkzIyXJ|`Hr&mTzAUO^(8gY6Fy@pS$CN3 zd@tKKHg*0MQ~1l{Tl${rb32}?ENp8&Bgsm8_s+Pj{Q{Jt@Ow08L$)gb3~R}R6h$W0 z^Z93?+CU*NdwtlDC0dC>M!-bEdMrr1Z|yLif4b2>@kGnmxpQKd;)peCz8<>mRG_DF zRTdz^i={Scz}EEf-1{!}6T#Y#-Nb)60$Xr;295iO{IR3{mmj&|^2X2MT)v&eiW>dF z%Orn!5`g%o@R?GG4#s2R>nS8#uLCF zs(9y4r%siP3*X;04i8)?z*L?Ws=BDrB7*&l_FEO(R@U;~-5|d@|GV?Z{^pLzkOd09W1e#Sq9*SOZ*wk=uB=H5SV#MtznvfVF@v$(5~@KypU(&$nt zxI?@!>X&iNU|xUIjz5rB2e7K^+QPfd46WGEK^EBg%b_p{E%kgz9IZZFg*x{X#TTB6 z?D}8nQ3BcUZ!p^Ajd<9 z_7hclDaoGw>%Eq>;^O8b>cjI!#11vss+hVTsju@av;K>B~z_u-gl>>2Y zGO(@vE&&4pdE2G>YY$ zo1Q8M5rx@w@de>83Za7^NpY;@)U|R03PUQuhBwH%pH$>IJ{lImV==VmJzmw?u>V`z z0@#iOw%s_3$*Boh2^My^G`ZJaJWZraU06VxbWr%TbN82MZYGhBp)ia>G&X=q-H2V8 zLw~cWB7I8l9-(F<`#$pM@5T%8g($t-Z7Coebq_I}qr0)l38>lBo^808mCb9Elte<; z1KB13c9mS~H7N6+WtorKkzKNo^mJHrfPVWOvE>@xe6|21CJgL zpVh?@6;Jh?%Ydi6X_Y@?rRNWZ@8G4PX#z|ukoyh#CFCPS?GGxro?`6F#(T(4w+Hx* zwd)OXiSImr?{e(Bf~?ZRlbWU3#;y}15!TibYsJ5f_=3gz-mELf86GnEzIdQ+>D$L4 zY$a^D4%I&a_bE4hgvQ1CqKmyWPzx#PbTB1nLB&_CwrTESdwPf3cplK`4+iA}$Y zPTZwHk=UDl)T!C{;TufJKYDR9o`XP!)Sq+*QRNtR22~nRdTid3$LE%`Jx0qzm-#-` zfbD;Dr}|~>RX@J~YHIeyE@wH8+%<+z4e-;1=NG3QWN^H)d~p#B&e*N&+mEMb} z5Z~

uc=UO%z=&d7)8Eidqm4!6Dsp_=hEUcG8=ScR}~kUll3e(X4-tP1VI&$F_l+ zTY7?O>NQhY^Zg@c($_fQ*%!Mz{r)Ck4=Py3W6Cr8&Hq)|Q>eHsJce6}o7&hxMuE^TS#+1620W+OIma_%5><>Ay9TtX_xkH zO?z_~_F3(y@Eq6G=g6o`&N!=EMT(;H@j6rJ4w{`6f|@)$ zft!L#_t z!hw3x+?atHsQ&`tZvuH>@H|aCpoM2Oza%w~y-Qy6_we0(O&=T!B){}s88b}%K?cN! z)19)k^ID4R>bzh4<_eIhR>Cee2sb<<#$!+`2yE=XkIWdes{4&A=3s>dfms7zax}!CQ8iI* zByN_lBG7QK=Jt)Z>)d)0-IfertWylheAoTHct;-R(nuv;EI*@LjU?Dc3J`Z9q8evD=zQKb)OxIuw#dXwHq^F6r z740bXiPY}7xX<^x6nbbpIAdLw)aaQMHRe@|85DbotpP3)$X%#eT}`o_kVPKi$D80& z;@h5WpV9UiP2}mi^%!0=5GuS>sS_yLA)PfklH`r<{MI)4g@vdNQ;9Jaj&rI?ICq>Pz_{hhj{fpXPrQyIqD)4B1N zyC%O5p`rcgC86d$SjO{mApvUsZYCKb#Xh*1;jzc6o^K{;rMUo$3~|=?j4hNASgo@dJ8g=d_7ql)i3&olt za}-Syi|k<%T~c3OanP3_kn;YCPD6Yz2Uy^&KZ-Oo;Q|^)p|&wTcB@r3da~AUIhB5~ zyrRz1-m$22$tnOoMHyghQjjxge1z@;4?{ju_3N2IykAuskEzRxe8%dw5_ zzx^<7^D*+$@6MFVj=BKbG%i-nvD^q9D5^l#AjhK)-o2@2Jf6K0^mWH>Cm;TVX9wDq z*)C$*E!LvGi1SX5{B@Y=^UJ(%4$l;no6?3ksaQb7=Ck!@1=?(~XSP@wN%S->ILx zo;#s!!ZG?eJUQ|+--3tFa_EIHVgcgHAaH;SivB}4fD^}U{zHU0#m)}6dv^`}vUlgL zklDztqGjjpLoSDwRCJo2lIJ{sv8mm5TtDAubXlm=H162(22#v+nL9UolCA{Xh$zG- z$#GF@E8Sbca^#zP14Q)`-&L`$Y@(eo1yt{wI|MSxV%5LIi@pqfz=%2uz z#P>BJpLz{)GEcWF6zTHJ`s_I|9lz}tHmdX)tXu(AL`5<|5b>-ucJ!1G zV3V{qw|N+zxu9`J>#5Ndd_z%>JH~-V_8OvY#qEehdAr2HbRRceq2Oxur$3R5PXYgw zLbssF2atfT*1X~e{xvD@PDjCQh(}06ZOg5#U>L3Uxg2p`OL8J5sg~dpRqx`6y z-NNe9w_dv5sm`TKpI6V>bd%ri(zCqRc8f=8Er*dt zcQcS#Y2-&3)7Q{2;AD~Z;Y=A$I^a&<8@5Vh4Y+P-9*AN>grJO z`{?(5Fq7wrATolMT?nVi0M9P}F2&yf(Z8F8B2V4 zi^Qc}N0?ON9I+mXvgQCun&}L={N$r1bWA;Ka}dyNm)Af?daQv2Of#&iboCu_4UnVT ztg@?DU)-ifzpp6W{C+w+vLV;KLnw^(A=POqsynRdd)+VXZg$l(P$ z)`#r=q3Oo=pL@WS;ti{!zfoUk#@9k*bXXcC*8}?sIha9G<>kCWVy3Wvny!?Wdnge6 z2Z(k9{Jmx-TD@q&=qh;N1(CIfuesYPB#Q=BL6REY^&f+8EJp#1W~Oe9n&$5s$gzSZ zpAIeOM=o+ghM^N~kYLb6$!gzGz zY4uoTv}2HG|3(RgAm{$)@O+H@@iOI2)r-|&7I%yM@DHXvuV&yE_hWu_PBG?b%g)l( zAmz$$wBNknw!-8R&!(E~nie~OK zP%xw^)A5-X1YU(uGDQU|UUr=~oG0sczv%zl>(fMN$DcFk)kb0I83qK^oVX1jbZ-W# z2(N4gHMOtd{*?ofrASQ(;&v6L6wH}@9|ROs(G6}=2(zxlbGm4vqFnsaujuM%xvTXJ z+!j@i+T(S0)jYp99o-bU?{ZJvba7-#{p=z+ec_(N<`Uubi#Z}I11w`k_Ql0h$PrnEa&+-AKv5GSd!BaX>E?prBoxdN_q0ndIx=W%Ygm{)s^mG)faed7H zVh{Ih*KLz!-U>5Y9H|3S{#~izHIxHkFSCt5?u*(1F*}ac&3X_Xx`0T04dn(JG0QP* zQyQs9I5`SQJOLD+)+pNf+$IMva1Me<_viuSA{6zn9)xXm*3pmz$vLvaMEqSg5j8h z_H){darGZj-AupiDq%V#?mOFN5ZEocTX#~or@%L$v1P>Rno&`*0esyP*EW`{WkQh7 z!sFJNB8bXb@$RpG@`RHk0F~0eu_?OGqIkHxLe0pNlNfp&4&|>&HG5I;#Aoqmds+g2 zYq{|$iOjrOqo%}MV$S%$JYnnODO30k8wakF@|XK=Ocy_$Ib1{JbM|k(`Ii;0p#BaT zCEMt7BG4G2h8&zrv7p>Mo?`1LC!sm(zLxy@0E4LTbd9H^Jf`tdyYMF<=G5x;6t>6eI9jXJ#)pwl9n;!KLgXIUvhv) z)*--nl?7;yHH-l|v==KNP5B{c!pV|_Nh2`o3%~nM9ac~1lkwKJeuQQ4Vr(M6L*|+0 z-6lwxS?^}m5|JG@*S+iL)3-8Z(mR;ZUdn4#0p;$}mC-C6#*?3k~%!`iV;O#P*YPB`yHzq_7BBI|0u z3Ke&%I?K)+bFHkpy8(}{L}8rYwV=OhN?mSyR=`6UUNSIP;sK(*`aA#k8yYlLIMfCx zVPqsJN5H`!adEclTw|0{&D*zOusl_sVd%Hnr-28v3{E?29cbh3_*Dc&gzQwv|(P0-nui{vk>f7?B5-{sl4gjn?)1#y8H0woSN4sk?|~l&r-Z=w|EkeJuVd0{XF0ztAk&&`q$m z;%Ie8VL}MT)sH5J>7Ag2v1dy?)Z z+An^$flbrmYr|EMud+?%tgk3CiQwy&yud@_p9@F)W>JZe>1{ZM-ai#7O=!2wXo-r7 zrIoF?>77}b2LZ1<)`D#a6}4Xv3-+^e_~XjQJT)8R)}bWmw$>ZsqSo9?Ws>FRXq_4(d_+sJo?Mw zNcTK>0^?cnQp7KAuI+2stcJ%P|B6zbZoQkTnbi!_#aq*XB_ru2T)DolHCTYj%QQ~` zE0i>6;!eY_f`)zQz%`)!xtY~{f2>A$`COe?&WfOMr*Qk^XN^Q>tn|I#{!F~*mvg*& zXID{?-|>gTBd;{pj*SQQ%3E@6`^s#O6zEB5Ikf#r^j)!&`@ zp@c=*;Iuj?C4!eV8!U>LPe4SoY)k>Ra>{Ur*oV!S~FK{>rk!R!LeIJUJP0P z`sz%Emi4%#r^$#k>j6U{VIwT-RBK$Nm4Yh1FYCG?=8BPB+r8`uJ$GtJ69#O(bcno8 za0VLm#3O(?7XzV~4PI#I_TCQ%O};4&Xt)xw+8Jtcik22mE%FJ%wXgBzobm*>4^MKDcblZzFpC1&2~ zL)^Lc_a;QLJVcoEfQ^e^u z8{vO~>WP_!dL~shT^B!_!F6TdOb@8eD@F-hkb9x)w9BtvMA8k1@Ye)K_)j5|EHWK> zvQMJ~IQdqq8t@12k_fo}I0HGJkL<_iyg^tm?1d@7M2K>bl!mU*8X+Yj=xrDxS@^oj z*hK9}gRbt!2`?OT8!$}Ir%tAQ5GxE0{Hy%u>JZw)q>6({7V3_A#Stma_`aMI9Q#AV zCX%_zi5BhVbrO?lu>d;Q!1sWH%3Wg*(X`4*=DDAxO$y82*Tk?ynGoccg=EbRFsA&`ux3Iy7%`65NM9KD zVIEC^Uz&&=B_J_B@lafp4O_e4*G7DQrdYB&0H5(1d1Z@n+>E>KbmndL=K+qDvS7Vo zl8We}y>VRE1+u}Pl>=Ms8BD??0u%Zqw&4H-eVfyHwqhM(!B7E_-k(^JjmBvYPmKu@$C;8$QWWf+_J!D@4OOh%2bW zATbhcwu1LzM)Bd`ps2AWXWYHnL6>^Ls1CWaDnIY$QD$ zPA&KOot1NY+;zMyOSXU%s9pZk56@hl1!J#pJ=BD>7SIk)aX!WIjy(1^aVho0TY)9i z?;>nQBrSf?YkPn6S4Oo#)?9z(2Sera*Ys9g1#u?)-C5^N3gptnQzx6cKW6Ymb~t1& z%Wd^PnMt0#e^0zm9udQ~Zqaum9jf9@=X;mt-T}FW75}Yk_tC374~IGF=2KZE!M#dY z!pe+&H9aHvw9wv?^MVCx@9`fxCtX68pOvV=7P2RXtjxyDt2S*7iMQ*p9^H}R9H`ap@p zBc}NX!<<6TE;R&BNe1mQsh#6g>&wQ+6#Nw$i45GU8F*@Hsq*am(!i%4%;Mdf+0!-( zMlEY)z8{wje(S->l*jhD*FYwMDg`ODF7%{ayKq=QdPpS>xK*lz4k;A0$5J|Ao)# zi$MaKY=`UP0OL4_#1kBO=Ft`sr$(_phY{eD-j2}}!hQqWvn8U2Kb?j3sLV%#E2{qA zBK?z^?e5ey;n%6_=|jx0oGQP+=I29PrSJkPg+OxLoWCkdvBtUX2gjL*g+$dQEnQ!>RBudHz3z}Gu73Yh?&z7)il>FPi&7ufZmW9Ny80+_KI2u2 z`}>0L?nG8_qx(}_Q?8&TFFP|SB?md8s5&IWG-*x6QZwc#tdFr_GrSb%95x^5t<{i? zqIo-ct%|Olx+wg%T1BtlHgge840)N*((rWPHE5v^kkLmerK@`jR34`9$jzK+0B}iP z35jYnUiDuPGPDu5VC)_S?&pXG&}ul2hZKYV)|B0-0+a=Sd;wHEisp9-GIZc5t?)$H z-nczxcfLE~NPI46*oNdlM&i(E=<=51L0t9SP<1d8=G zT>#6u^J}IAxe{fzQeHC}R2Xj0MS>iZuG5V;J10K6vh>eEvrMwymO`d%#JyX8Xii*r zn=+QDT;}@)PFclc8T323UluWppF|)AR&H5W;tYSat1E}eGww}KwLw>@iAh;V7Ms4ik z{d?#Eynl&wEa~Vmh^*G3^PKUh2@v0}(WEFdq0N}8zT1vJ@1Dl~2ICMlF9rD%ZN`yw zIT>&RbIk)_3b;BO*^6^(P#%mifX9%ab2H7T1A@u(b<23?PP{~(nCsvs((EHN zffE+IVh*D=z<+@TX|Sgsa8%U<4qE&Ko8EEy?qI1pad;-4qcwOMJkp$=+!}#cY8ne zsqW@V=vyJD?>bWADLt|$L|Dnh2f@fs**@-L(v<9tn(mm>ca{^CA$hw9ElxI33YoG{ z0vhR6LCT`iWodXMwaX(6Em#~4H6RC^Lj0L{8LYs=eSiQqe&SlDU(@set_}6wS2sx} z1<6!dNF{g)=p}WT$ib#_mx4`qTFM(X9k|NkIDjEwiJ&EgP8yqC*ME zdJ?rEj0=K{!N=;ZzO$l13LudRMK=zpvB>*SslUh3lyyRE@y(EMGX>UyYHV+r*;Vx%^?nY@T1jd&=*5#D9SqMy+omLq#KU!{OQw52%6(>g z7)muMJlzNWD%was#A1k-aVcxAcvf_4kDs&|m0`2-naXIEQpIyU^kAFhr2Ene5_!w= zATw$?jVoh6FC)llJ8SVVp40+CpuLoi=+EeyMwbdFf&|`oBI8V;op&@?q((b9VA{gnnf-~z1 zxaX5ripDFlm!`{06ps~-r>rG`{hQOW|6L$R*^2S7Lao#y%EbBR(nMugo~|eRer}ox zy%FA#y|cHeU#)KO1E&jWt}4L%zn`k<4#hn)@B7d51n_#O>hC2f&eoQ6Pwzmfxs4I% z^iB*6s^-2amjboBttS^dJMs_DN76c#ON&#*D9fTvIvv`Eea(WdzWmbKF={`wz7x+D zoL)^RS0}y7j6YY=a-#d(7~6vomjwEI=U#-zEPXo1JQeEc8C4A$n)>>qGgu8wHhw;X z82}Oo_5n1P{MdTg)8rg$9ufzx2T))^%;-l5s!)(E;2DF7?ehua1(dvs^p4Z_fVVoR zp1gy{-C*2khy1vEM~I!ml*P*JQ)G4v+4#|ct~1iW-Mad!6xIvaQG2ki?bw0k#JiC;(QmLip?6wHcq$ziSl(KIrr43qcyRI$C46B-^z&s(mn zl&hnoRGF+2uT3?K60vpS^wF$+C{Y3xKg8Sj^=(NLc2pfQ#c4C^XA8rW6qV;^hEHac zn#RutWg)pRUG+(cnU*$=>ouU2$emrkj#^Sq71?Ioc41Zg*h-@|H<6mSPHc){NQ zrP-?S1m$nrJ`0;H40$|=UeFT;E1a#eVBh9KPFy9Dv%vw4A7eB}Q#8>+W+WKmeYb0x56Z{H-88OGc%B`7dnj^nXrU3$y={}KRtg0xCb z1`g78pc4EEH83FA02H{KLas)@QkDyn#Ly;{&5^6%qxqS8{r1~h3;vg{+~&S@OfNFE zQ40;Dq9+ocIICbV3Tas`LorJK2`XK^%i%pO#zcx4=V-nX)RfkZR3%VNa37^70LpD8$v0y{u3$IY`ZuS7@*3jgNnz z%boYvq(PH)yV$4qqme2)LvB#;G#(R;eT+(WO&d0QvZ#JNiHfXFo`7rNDg@$s#$T0N z>%+rEzoe87Eo{AL`#y5=(j~*G3j&nHex-@m2y_?k9%xURQ80Yu-$}+54jB7VOnxW+ zr~r-!XQ+fr0-+fOn`yHhSQYW*3l|Xw7PUY)rx%>9Vruwiu%}0zJJ>R@bpraeTM#k} zh-Hr~kfTe>#+YyF`zCC4#3F`I1zR5+mCs}4$CTP5rP-(4r(68lcFEL^)ywakT_Sl( zU^HD)!CIWoajO3IVZHk7`~enS595J8J%@vde%<_7Ung7w9~Rf2X2twQ8Y+G~lq}BT z>i!|@k~qL7h;MMY?b9{8aV~KCch8K!QGFvWG++>UQQi>tiksUnIq9m{(i4}TIeue- z7K;;ub>wX1-sYzMH^_;79v7*mU^5)&8jp}2aWSOd8Ve3VUO>w-saQCf80@Ou2biA^ z!RYrYjaRw;1)TOVfwr6q7Lp0S0ITvpZsM{DU3+=mvO-VOpt>LDR2;4UvBQ7g<=B-ep*M3tlsF=oK?j)xEhZ2U=$Wj=mXwYm^WrHiL@-}E+;G$bP;>ofSeQa?4Z^douXy&?iuQc5B zwFgJaKXi;tFIL38>o$88?EJAcYea+kq8<$e%iSgt`O>>V)I;+u=Cr(f9jbJ^Pn`S) zhqNXCO3pvZcQc6WUCsTZWd4A? z4FCOnu0z!Qf59~w10WKisf{F6EV)Vu+jS3(SN9(EGi6Uo2$F7o3~++VRgJ~GVX##qni&H?b47$EI)`WH?{p4C8FpI`m`l)^gmD|rVa zvTl&0Q9<9alF%1__7TC_n~h2eVNA#_i?RqO2PdJ*EAf#B0$h(v5g5MZwU%s+6PP^$ z9u{TPTkT#o(d_SWyGuI&Pj*{ja*^p*o}C)E)c!KXOuk^^cC!mKYg<_KXyhomEQOF2 z`uc>B&r8Qy`&t16@Rnrk}CYo>C1grA~R_8mUVwp`x1+9;;tyXG;6z9lJ^LgQ< z!Wiew%=`i`X*By%PRgkK1@4|KmN**!AhqHOUwP!trDG=)x3=_FoV|bGaNt8@qveS* zq>S}w7pY@K(M*hRKwnn3_6_nuUf(5s4GLBt0DPd^H79 zyq`5oQ($%gL}0G~x(Sb|zx!$#<_t08R5mC#I^uHe6+6VfKJ|O6`h!Ra6@UM2>EPwc z8&O~EQ-zUspWO{kO`hP)Lh;BChd)6 zS9koGaO{_N@Al(q_+Wttq-mU(^g^5dovZ6vmnVfYGZ1GVqn={ zmD+cyBIkeeHvlnFJwf)%feoU!k5#$tSanZRf?0@5@=`EqHM2T!E>hG>p)!`8W4_nb zsaJMJyyWmfmcF3Ge4R=#PckIW-oo$}%|Ucbt?w2I3{S~&I&Y9R7Jcrdg=LynS0R3L z5ad(R^Ol?@KhB_^(j3NA_M7f_!7MX&z(5J{KU?X`%4tKR+30|#8#sS+C0<63WdQ|r(?8`sBZ{`Vy_{Sh4!cPp90_##O(01_ z-_b-GHj@grxj|Ny1CECOw^Q(PkrZckLU%00bqs5ccr0xF>$sD&UhjQdZBM5J`nTp& z1~06;r))a%?+Z(uImZV4(`k-w_F9TF7@~qr#FCS+G*ogvl%M6?lO;?YUUr--sGej} z_Gg-QWiXq{bI$HBqz71{N6{;GxBa-EBS?fRhbhnJF!*6M7{3-N#PSe zJDEkj412guA#gSVvaSy#5r{5ov!|mCCguhnb?9{!4r&7Tf_F^i^SfwuXz8!NtUT)M z^P_vA)lgHlJ1@t>NGsXO&{DiJt8zL%5!?H|8%HXC;3=QHg_gDsda(bZ*F2(;jROZ} zuR(G(7Q0&*nU@#276mr3Gn>@Gg^f!OwG(I=(HjJ`nUw#_`9@BH5=#(>=6``z zT{$dcH(B%nB6$&E5hzVvDbp8@u7eS6elXSt=)Bk-693zR)iXk zIS$Hn8GU(DG|+hgviTydYVWo>ww3?(y)=v64W}W3ffh$1GnK_j{Qh0u6B6k2RGGf& z)s~Cv8G3ch?5l~%(Qmf~$p)&!F*}yQt7B!$|07v{dH%>{_%N9jjz_H&|2~fG5m9zM z$TX2`c}9FWnlfiQhrWVGv`YVWTC&@)cx!}Bkplx`i+QNSB)6kS-?cg`Rs?cdO`Rs% zsw#BEPy)Z4@ zKHCErB0Q$pW+vV$@U0S7KwE%;HlZxN7T`sFwr7hc5ClMeN*%4=VtvO=E;bo!l7Y0` z6r7i~sIVawKk!YF55=+VP{`NYzxH8@UD#P&DWFg1O~r0BV_hh zuuEv#N~jGVxoXu-Ez51?213o2eL$Dme409^KWR*2>8|z#rk3mdr6jhb(DmkoEk1r` z@K)T;A9Fd)&nw?=SGkfi+&B3GM;n*0#DpInVtk|()DQF?5f0Zijz|8-TtTzvb`v3aJUo#Av zu|`pLDwMUd(~R=Av>=j{tz>DjuVtA*qOw#JWi3icC}j&n_AOK*>)7{wn_-(Rp>&i8Aoab2{=W!lq3-^4Q;r*w?Wkyo{L79vG4WJEozTv4RF5_G@oEN&mxwZNkDQ~%Ds z0lDV}w9;H=(UW5vL4V)t)%J#gHB8z-_g&5G9@N7C55>Ny78Bd8s&fVd#x2cb>{Q}2iwO#1Yoxbp-CfZgVRwASZ z`^yrfpSuH=iF^uG0|iIZ26=+5R58J7B%PNpHPY{@f0|dgly$xKv?a32)y{g-7`Lr1 z@K1#XHw)^nwb-p>ye+N%8Ur4HfwJH`1(dsV7(8@O_%kR1sn@FLw|pdpjZug`^Hbmy zQ5yr*@pEAM`%*sy@3)$adZ|Q0Jn;0(Xq$ly+^oxJi?FS4_W6J2s%Z0}#tXwf-`@4m zGO`=-HV&a; zq36|~WviS2nI7{=<)wC>}hQB_O{=&KOJ5C1@#& z6=6ufPZCT8QQT%oHS>t8lyDf5N(||7$CmJ~>OHF4fW>yef1| zE7+*!Ac{YKwFT@yBl|yf2G!At8Wei@his>dD2~GE}*04QP`ir(S2~_|qGu1B^x@`6$8r)q4rg!Jus;>%-a}YVmycmP1w7F~3ypM81<< zGn;t77nvl5d-P?Pz-WB+HbTQ=s_91pEe`2&MkE4-X2(3|vjMrD1+t8VO~b6`DSUpV z!q2y>;2mWpxV4`MY*_}^+qM^M8OsUy?J4788TU@bgrz~JUsF&73c-GdhZwK|yRUgO z;VF7))S0hu5O&G1coANO{Ae**j+6OG4p$!cN()ukWY`gHDm!1YQS;AY-NP^k&_90s z5aZ`Qi)Hg=wxfYgK0+3Vl_5n_0X|oTbbt3#2L3IQZH8pakSYO}DKchGk}g2dK-3IT z;JQi!cLh<$XLxx4+4d9n*u7L}ougv6e|-@5VVv%K7o9jT_x8Wtgb9d}(fthLGqxev z(RD?~#*!x02sdg5a%8L5KQJe}sRk3{KCyPy9Q{bC)XyNt`0kXp#QEHx0XwFj0ho?U_ z9@H0=Y_B?(?$@{Q@l^h7G))`NFTGo77utX$mao|)8FD(s-8ZZ5<{s8Kmp6J~cqj=k zL#c+v7yI5S6_io<>wnlEAcLnkq3F2b?S%~G?Kbw)45AT|5jc$P$`)xBww2#&EO~<= zwGI6D5xJ8laV+xEiB#~_qt|I=W6EBu&$zn{* zf2ix&{Gs(JzS%c1{iGofbgiIV~UvGW+KCu z$k+xLks~QcTw)NzGR2$rUU9Rmf+-aS zp1CbRu$m4|5+X_vyjD(KtxndoAS0pqX{Pi2j0_C=fk5JIT^Sy0?-k(XJy3CTWs(y! zZpR?LXuW9Q6!0U2Kj53I?L3QLSBl2p+4#a=(>k*mqrjeG4|V(+X}#khVb>qb_aIaY znAimNrU>6s@~=@$N*f1n_L6~pV*fcI^C2mtfI(S>?%Q67a@17hjB5GQIA?vAu3Hk7 z+Z4w3sLQ^nUS?*v`J}g=h1i)QMCm+WHo!(223}@J0iAdaE9Tczax1F^Ej^1ifl7&% z80l0rB@gXAe%=6ep#U*|aa;-W_?Hx)=+lD+7kt8ZTX|jKes9((ufJ8ANhuU{)f~n8 z$e&yXu5@6uw z@E1FEyKT%j2LCO!X6=)a$f=rpAzTcxdB7?IlX;Qhk3kiX7DHB8{Im)BM!cRJJt#~t z=5}YAKCEt;`s+V#ar|DlM9ujb#sqc z_9;_;J!Ny(E)>+XAqNKq2aDuz-YX&X{i#$3_y+WWZDO<~0$-J--&z*()C}>IA*tag zSxD#u1RjtvzNl-8gv%m+dy(@47tD~9((qeSDtBH;dK)C+`>x9gY7)McE92R8i@6+Q z#v3pK<6uJKs}5sx{H4C(u2 zf{sMxAR9uFXC7U^2sxpiGRSFc6nIJ0LQTEo>9OI$7ifWW)gJm)%ih1;Y&~-y=Va2W zgARKgkC3bM2lz>rPZLoegD1b8&_iK1OU3*>Ssc?JIm#U8k-Ja}eZq9((`m0WNumE` zhL0O}m2SEwI1sHYX77E9>l6DO$D0;=2-7|pHfRx34buG4c#&Mpbp{?5Gs)-%VYA3$ z%PM81&D=W49uE_#;=S!g4o|N-i~Yv{d~b*H1Vriveahc5^^p}NVM~*{6<&q5V7&`h z#Nwokjf{-kv+S()OBZfGGeu%78Tfx9n2Dwg39U~QI?j@G6Dwwen@nKCW4N!Qnqd16 z9pnK#TSWT2m;0a5_%W(I z2Uo0rx54vJHYWxX_;FWFNoECbNla5+eJO$EyDUKqmtKlPj$rZ~prbfqDS0ijBr^Bo zG#tDFA38?D_M)Vu(4%|N+4F44ef<7(S&~Zs_hn!ygWM4Hew_N^hKnF`{CvQ%p1EX3 z&9lFcKU7XL!p9F@0H zAzTh=0fQM}Fd6-B8d#|%Kaw2($ADF8oVc6WJE8fvwXH(`ou|R;?l%Q^xx3u%{+n5z zo*$ZIDryQ5(<4)sEZcpU1PuBn*Dn zUII$jMP`A$bjqW=2Oc`~JL);kF}8=$!@$uHb)o*~SjW%etVEF4l)w0~Wv$d~u@g-- z676z9_feNO*Jdp8Y*JEEU};x;oO_;!(uS$@t{hBQQ(Ox7{Zi~9f!wstMNXUsVBv)v za_J0$`G#yUh{EG3oT8TflG@$XlyKAze2#tX(= z`AED%#GclrW-IxwImER~y7EGt((V;tzHe{E`Nfgb@UGdfq2Q`KTk;7!9)3oul?dU)d5$jOX`hR-$iz9 zPgRa#`p*|1lfL)7DhipsZB68iL)-a?88AZ<#pvj0O&TA?jhFk#X!7OMF;K(<`=)&; ztVftNi+l&KU{0Ezorp^{N`!&yQ z>HU*kyN=g!kPc=9gn~3#j?K91WF9{r_j1LkVL^r>bwKZgK$DzH^&sq0(hDw6dJmXL z49~T9^YxZ=)UZ70|GL%1@yqCu#?{&1U&;4wz%TfC1qgmd#(^jbmYwf6r&ENA0EBYW z+jzvVvxOv$7|BSK=Nc)RlJ|Y-#yM26lJk20jXtOsX5ny&dhy^si_J9V4@@CPhU*@w zHbdvucuYP=N@J6j-6GopP%n+)c4EE;GiRUXNe`@;DozX*`!lDHVodtkpn5p${$dIc z*uWYyyTi+=hm*7K?()VdVF<@rlAbJ@6iM5SmriautOsRCbXsYOrX3!0ul8GRj=htc zuo$qLe5x{H$@5ONsNZZfMlVU+jX{nqQ{S)gA>3$!iv=^hy?(&~6{ao?{Z`6@6ft9;2c-({36ezI7^e_6sR_TIZ9&=!9Cc%C<-KZ7G_r>%*T z;@9--5<%LjI*yHjXQqdVw_stBwpMO#TOPMX0bAl;cT6ku`4EeNdJkRozIS!XJs!M8 z$yTIYOSNB%B;oPQ-)fGl5;aDb&NB(mEi>H@ceASSCuHuz59T~Tklm>6wUDc!C8U*= z6}T&QN*)qku_#<2pLF9fX_&1V6;EXIz3FHbg(IIkEunfrU263VHn z*#82!NoVkj`+mnAYj92wRg?{Ee)33q+ji+fUc#4=b2sG?`XlYm2uL1If~$Z03O1Ed zh5^%5u4!hD4Mz;Z1k`odPDID^7$Km$}hE0Np@UVnyPge%c!a9~*z_%)Z2 zMkmI>nW^Oejs~02@{<2$h26bLGxU_3KBoI76vFuY0K-ds3P8_y0HR!Mba0>Qtcboy z4fUm6fxYfgbK8{2_%?i0VRL4Ev(9f>5q(}v$%X53d$JB0A z^SH7Ee?{Osb^fSblvPNt$tUHWRbHe&9bO3~`|e}sAo|0tWd9{}(Jr>$rCey)CbX+>%yMfc;1=m(2+jBo5ZwmUZKD42HW>XE-)LO`X1=Jm zQs|d!QH4O^pnZ1mlwJSp)<1OG$COx=^37tstSGM3yk)Q%96aB+EIN_Sti^tlWuD)k zVYfBtf6J|IuSV_qAg7gOCp{uXF> z|J&-Z#g*3S*Tqit(P*P98nrXJ%4L~+yv5rkZC-_VC1JvsZF@kxwF_-oL}yg4fiV0m zyM58AgsBCkKJvnwxuaf7p32w9jozq&Gyx}{=V9WbV=vKcHC*TUZs4RwivQ*)rtTpb zyyDCAcwhG{1ZqXfVeeVxdL<0N+rHa{2hcVZfm3e0i<2E0w!yTqi8P{-Ja%;PjbuX) z;q2ZT1!%jr4j-zyB_EZ{dq%>POzp~W^K*aici%k5UT z!5*6PudJ9E?Nex9I;d5x?pOz}4EgMmJin`iwWyV*M_tdh}EpzMCz58f0|l!INOsZZN^ZH`G2) z6+QYNli(Oc_3LkS+k**brpl$DN8V4XM2lCmxez@KmMu6C+d^w z+TnN$E^z$NPZDuC`%1rhi#sikvO@JQTWmg6G^lzej7RhhXX)l3nFwBf-tLEwEMZN+ zLf>NyyR@QGRiLl-IyNM8^q`50N}r=)X31Yc&F}rTWW#S+b8;EFVfN8suPE7BZk4?Nfq|bxpuGfk)gAjVKiZs> zJG-Cjrh|2+TyoTiW5IX-i-<(HX<1#kfc2qmKq0k>C<-GQxv|YrBtMvyU2kjP@qSOY zXe$$qogpR>6Tfd8DU6*^XlA#Bm3YeTnZm!oo-}N%$8+q}=t!`Iu_pnH_Od~{d z*Ie6|RyovY76P%jqmp8iAi<1j94xS{fDjcyEyrTw;F(?r76x$ZhX}FBT{9WPl`bLT zv}-RLzB0gJ5gRA?J+Ria9dC+mEKa+9QD8%F|5vq>?*nyZY(`FUDUO7bnpg>Xl|;e4 zw4L2UkRR3iikA(sSV0i2Uv z;eD40o&O%vR>YYk?o_AQnrQB|SW_&)sxI=w{(Z!Ym!Au4oh;l_yIQnzw8ROUJHPDy zYhb2fk(J}r63r_u3AFvRw-T;>cBG~)fVr)L(s<(N)29i90{Xa$#d}A$+b@dm1X`OS zu9@yXf5@0i1r2pd`uB>qg3*gs6+^JRp@IF0fa}X&ic*iSw#S_-8Z|3@m7h23b<6Dn>KOaxiO;2Dl|5XPGNiP*uTI?_NBxL~O;LIn;3pb3Z$K6tmd% z`yl~ZrBOIU31?!2;wxlQv)q9JzLH=eI}ER4W1O$xBHuaf_r@K;q;D58S)i9vE?ZmI z$W2^&k{wa;O*O`*eEusKJr-)q+!;C}I~3_vewX3b@wU9`?g>L|ZpBCNE4}`|g3T{X z+rUs`h3mae_ge)IKz{VNquVUn`9N-e>6KX)oYZ>qC7+|+Y2LGKCw`QJv@@*Kht{3I z?b3eul7z1^@M6~({B$pw!dYN;(h(l6Df;RYvT25xdN8X7KY2Kk&B`r91qm}Tm0dOs zW+K#0%u^3M48+eLs(E_cGo>|9{N?o3ldu1iUbUM*S+5tprbqeNtI!>daTV|!>8Y*7 zxbiNJHu72tvlZ^DlnV>mH;XfhCos{8e{w>S2XJ44AYb4tidhrQuYz%$V{5t07FjIm zo{SN|^TOtp-*WX)ZcyZA*0wcoT_fUI4gXAJdR+B^*f$Knf&(KfOqaznuciOyL#{{s=5+K!qX@eZ0sx!zkf{gLMly#1#6N zfPNe;Hfd1n(aCgo(J!G|mS9OY*mYne__O6zS$?m~5w=bm)p9V&yk$beJ^D7$sQ0(E*GL%0J5H$d|@3I)@#i3;3_D&I>k-6enK6K2T+3%!i40YOH%--7u* zyN=lF1)gWFfD0}wj$;Mq;-`=4Wuw>+b)`mVtKDZ)wb4^6Cfjmt=y_*E#S!zn1j=Z! zsbsF4T*Esnwlz=-88>>H*o+fmE!p_%c?UJ;e-UoQKZsfaTV_a2WH&4ut4aw1NBV_w zgB~(TcnBz`*ynfO4tv3KmMxtx#bcyu-2d3b;shOxvY_mkYqv1pIwp$XO`x;P$;rvk zZQ6~QNz*JrxNI5jJrL+*GRUBIZj*(6=6^SSk(`@B1)VZT2xz;EmNr0{(1}DWVQrgu z7G))Lv<~R^pIim!+uFxk`GBqY&((&c=!VSJ*FmxU2n{!Wg8FV1QtFp4XQIxCRL60tmL!Sb4wN1x{tUfRMc`DHxkk)$BKu6 zAH>IK>IAkK=vSvr{@*GGMruQ>&u0j9z*I;ty1vcpuJn5iiaUHz9c$$Xtae;S5GFZepnj{b{~l`yKj`EG4%*_QY-x80n8#^ zB%Y6y3b+nIL)7d1=v4)SbYe6gL0!@$I^FBju{v^&A&P@%Dc|6ZZu{gAS!jmoQ|H=C zH1)39MzmVg%O6US@FlhEk}5rqZ7p81bNX%Rv-QCe4uk6*6OjTfe+PvR`>8f!HTUmE2u9?IXgA z;)WZJQ&7XrjrBksD~X zi_A@PnB-)($~{^xGDvCa(l+@1h$EXpw!spN#pVo7aM)ec_Ir9uj^R_qRh)?T9I7mn}Fv2SCIEf5eY6rbq1SQQw5+2&_A7W0(K(+)SHf4lbB&1-$ zwD%h66gYJ)P)ZO#oL5!xV$oKS2Vv0}7Rcro6VPvT6l|HTe9iMFJxY-4VNI&`IG%n- zk-ySbO`-uuAa4X4N|UXj>QiyCnx+3GjBiN#x%CiueodHZMmWlE5obvM%$f*>_f!0oS6In?L+^>xA_W~gfx!4 z>8X~j5#>JhtoQ3o@!B74cXFv{-{Ra->9spl&-Kxu_EyPa zbU>g~d;&cPj`WE6K#Fl3MLgy`xxFJS6AbT!!AT)3X0~0>$y zyp?6XCxhTCWnfP0<)dB~hqGcoX2&-O){?~FBN!FsAHtN4<>8M11ftMf(`k|!Io}Fn ze+SXsixasjoSt53V`csP&6xvarikQC+fqL~a28w+`z%@Fp zqk?Il0v3`EFb^@m!HkXMso|2;?oVUb>dt@u%W_PUd?aRb7RE3CWg9ud{rDzV0XLDrZf=!!UzIc1 zH{+RLS@Kiwqn(C*Y5bIJK;R0S6CFi2$&ie8h;EasJ2@A2OQBm3@3}O)fp$rQImtfx zN_70QVO!TgUAot6ye((W@rGx_a+X`cY>$7d8*XRts|jp`OqJgCri(>i5UPK(fBsp< z_iVT<$xKZpF5cms+=b75Kq_h$LAm|e4z;Zaq|s?Ba4+@hPJE$PLvixj>DcpoDNvu& zi+*($ggp&8IqXf)$=p^hz?wY97`QTX-;=|v;HN-64yC;$-GW|(70d>D@(xJ*hJBp= zLmW=|qagc7A(hSUMK$mFr^c<$whCw3OVr;Xqt346R zbBg6?z8EQN*3`)}SK7R%ld~y9!FejCYlgb-&Ch0SsvXDs={-$4#Vh&F*xzXAPw`Mo z<4Gx#Z4RPG?_k=+NCo;C8mN-$gj*DPNCv5I!rM)B7zYa()p+GlX?9fx;9mi~(54?= zd5B*(x?eXAyv|HUoeFCcPxl%bNhwn2v~EdsRl1vwbDZPtmI)hC&fJfGUfnXqz*m&* zpXFIRaaow{5N328$LKvOTxoI0c>&Tcbs8b%0Gz}^7Dhb;{1R(7z|s_S*RW6CtkE~OZwG)Ms% zL!wU1J&TPnIv*J zcbGZwu2j)3cX1|ZUy~m2b)KL7zy@1GP~sKNcP}w)-u^y&2^7Hk0on5wi8n(^=15*} zdT2>X5Gj$ZtQCitB;TFtPW5rHk#QjH<-R*}jP0hBQ_DGv{Jx>-)zrna+RCY$UT(?R#{puSj+AfE(xFF7eHldgjI} zgG_@h!0qR;6pgII{>6hIqAvtJpHXL6y}#2E%CHg$Wq!603_Y9Sv!AJj80f>Au(7dC zw4$XYq9c!?PxR>$W`WlRW_}fPw5S45tOP(Xtf-at#z>BNI-kAc=SslZT`cb5jxo$x!6B5F{TL%P{zzI z%1ct_0NTCTE|G5$##eg@WK^}`qv)|;s9`>W&dTU+lwpZTCK4(wX~QPny(MeF42S1- z5(H0uirt>{?(V(N8_%f;1qiXxx?7%L&$WsD{rag^wL!Ky2 ziCYLSznU*QVV%15{Qr3~1((~ZCQi57?@i{ed5k(TxKPuVH!tIL>Xq4jXGXQ4CW^AY zMK&@-LAKCcETIOtK?-0HoII)w$-5n4Q<5~{jFu5E5XJCIjHMR)Uo0+#Q%wx1coaid zxOcB)NSXRrL_t8W#k)#NO`cm{)28`+v}p6yac@9Q#=!&B)MMy;KqR~JB0VE?y3j?| z@486Ywb50)!3G)b@sJvAjZh|^k&xsM&=LNn$f{u|d2FjQCtc;zr)5c_x{#{^fy@%& z5R4x)Wm@Q(UV63{a{<$alZe{U|35QHslA!o51bni8 zC;{7tj{R@$t>4*v6ZCNJA_y$bY>S@A8e*HgQlm7;f9*^lX`=#j&zgGY|FuWX#Pq-R z*z~$;;PGvhrD*0pd49-qBt1^v=%B{-RL#ww>k5*o=yyig$Zb%#)9uNAX&&4^4&35O z7-aj0JF`?+nY99abQtbr!z~5OV?LEt|0>06@Z0aJ3OUfTgQNV{x39VdE=7V@w!bH; zwL`;TQHk-NnB%A4{VPMMeZ&tLIRSL)QigtP@b;R80W-b1SBByS1#0%g`2E_P1j&p# zSTS{s*d?|GV^sS#xQ?w%!?^nzKtE9`KqfUJ#&*U#q8L^Ji!A%L$Gnoblj?i+Z6S*Y_D|;yU;}w>Xe`z_?L#D}W4Z98-Fq4M3 z(kh?oEEeR=5Wd!JmL+4Psh|pa*cvf(j>I&9FT!#=D3JQ(ktG`>)DnqTT8$z_0tJUH zD5N~mBr>;LGJ4NyQ9RXnp1Q2QQ5s4LDxX! z-#A-=J^@J^fLF(l5yt>TCrZ!Om1bPa;*Z!Dkol)x*G(BIk%F}@Y=0u*!(#g0GE-gD zFwfc9AL^v>GrIz_56ap}XDOTHkD0?=K3Cd{klk(&s#JmqSpeOyxDvVl&_jL$GWg zOSMZ{iuVa+?;25hG{x4y__KA$EfQ!3MLwhhF+I zO#3*nCv;4KjJTa)5#+;K#ngcXc)CSmSOk!d70$6ssm_CH3yLoCo6GD#^i}p#hTxXu z9zBndit*ylgMgKb_!^|qwI=_;=iFj0InU*Z;^*hjH$#lD)U_^KC_OZeCOpFwf>_#} zC&Xw|q@qWm1vblp-&lIdWWNi>w;Q64t4>B z>!`O>0I5X{K3SrFbSrz|9*|(WZTVfy+l{FynTq_;*Spz-#IRI&YeTI4#wYpjOxHe& zWFd_Cj|%%CIl5#ALxMnTBb17JfV5daxLS=TU6r2%iNX~$!f*WWd(``~N=2Qo@Y?cR zSkVo)+4Xt_+Lk?UEhS|x&n@Lp7v10a&p+D-WUF>xL0Cv)ck3K}j%(Og#+p80I=` z+X|M^_O}<8gkFHyPws0D>eoiEaNJYf{mHXG;NoJUtWG0fJ@N5ayW7!))Yf0<`{He1 z*j{nHcYh$bpPP#g)HRJ_JGlL2K<}aJKr)IhA*V9tNIx+U%``HTJ^i} zSeD?`R$`XHwgc!`;qt!ige6VxyvE5mbPG_OOayp-89-iXOM-0#spT}P{TSLM#AUt# zr9_dZgTAhin`Opmdw?Dl3Y?7KA{V-t!!Y`v`h&hFH=Jq-M&zaeDVNjx6(A+8p( z-?yrC8+cyTmWGFi6ZYLZ#&}+(C`o$CM$oo0LQ#29!*VTf>tc9Mh|ykHeKuVtl<*<9 z{z*^eL;k^?_vX}=6niv=tK+u%N4AabiqcF|=s3zyLqc-0HM%qC%OB&8m6p^G^8QzV zFxMu%BWmn9Uy%C5R42?h{qz-O*R(>%KW8V*ek}?K?}M{czZ5PjNnhRh6Q%lfGSX7Q z9&+afhoEYI)n&rYYzxDE03BLV1Q}UnpQT7VCiv9w{HYUaXo*?S~4G$v6VLqr0^bBXD1xYD3Z_j)oDAKXu+oUQcd*M%aJCC z_di9quLG|d?VEJlr7}b)X&`q6EiW$*og`Ki@7vKD#>yo&PclUD@yvslP$IXRhbl@t z7XlZU?9|2PPZHvVLu4$G_u-^S&v)5gO`r7@b|x$WXzlR{RL3;fT|PiO<6jNW@XgxY zqD=)zI(d7=lBNE4CUMPT?7L3`X3n=9TcK-kv~@f|S*z0NK8DQ2pE^0d8Cj_Zu{ATO zM?ejOOlBApu_VajNkEIiDa1DMA~wAW@Ef8at_b;AY0VxGA8${bglNdH5x}3Y(GuzV z3PcmQ5`0T;v%2$!f4c7>6vf8E_Za)=UneQ?gErt_B=f|upZ%_8i%h}e*Nb;w@F7=N z@B!;BaLWtL9Vr(0)%Niiq_)C1m5=3SF@Zk@W7Es9Ch{0c8(K{hrVo28;Sb1?mUswk z&+U8~56`2r!9T+W*yj!j*ajZkdv2<7XybtyqJGPw!DMh>Uk~?U`^MP<`Duk$Ie8kG zOS3b+(`i?svxonWdx$bHrBbyISf#3yowd}}b#^@zsUTyJ(|>Fwf@ADxpjC$C|3|JF z)Emds0b{2S+xkuiN*98HC0fUL+X|*vCYI4w6KONRk0rmevhbMJf&0-=3wRAVxaeZu zg6@;&U818dA81#M;*bzwGuU&<@sgH#w5S(rd=TSspDt;A3z$|eqL`iwa`ac;_3%JF zK&-@Ik58~#0e5aOzoPoxRZBcX`Yr+yGupxy@Su!9re810Vi3Sx-}qi!y3?NYA5jEL z&W<&%jcr!TJ-?$;R|X}Eu6fq(CLer1=y|evgPzB!I(KgK3`6bcPeC$QluG-zeO^7? z1KF(17q}7+CSS*JbqzjO?!-A~F=fg>zU1~zvMV?KW%+NjKkP+B0T!+1qM4i3xozHf z+dQwyzWkXBFXA2<-F(f$!h~0$#WA}=_Pl=YAh?$V@m6NY1<9!;4(lQ;Xh>p_b`eTX zIiz-XFj&BDD}yVo83!5H;|);7(O(l50}WflKL1)yj()Z-wuJk0$T=FAXH{i{YMhJd zztYZiU)+FreLq|9dvr7Vqoiz)YxA)9kF!f)bR_X04@jyVnMj|qz;0Br9{0Unh8^?9 z%+fHf>JqN*NneIz2^plsoD{jRgPFCKzB2<5aSZ=m38x5|T?$grNKU;MFPmDvnu~QM zwl`z6wYaH=&03cwGrOckeM6ZHHF( zt%$11M!U2--{7Z^p7oCBjj&^c%?pn}XBDGnLY>Q102dS?^!^FD4hs$fFlz9q`n3Ty z3D+~I3)MERqk=VIJAPv)%l!1)ItX*CclXykU8lG@`RFunsBL!T(dyy1H&tQ9nrmy(1`(MKNPY~;biuXh5 zO$MNfKB6!DKs#q5(wh-a(bAbLa8pUQHCU-+kED4;-rGe~lbQr_ZsA-@N<94lRDZo-X`HAo&s~BYYlYWNOzJ~ zP{`vfny-y%Uijp7;&9j7i=V3&a|Z}Q7hPS(B|Oo z_3Um_%>4zJS*`!jN|rv)6@8#BTu+LNv|}*2j1zx=1w#}(0R0RN*LohGLA6U^5`o)s zI|iBUlon%mXxFg*x2n8&U4r^=j$t#`(EvD@nHGIW1((F`n&j5?MaYNxzHDEO^2mwA z{qJXg5F*n1A$qF+b{9QZO$|j~%TN_YoS5l_Ih@|X!NI=1zIX3V=W*Wq#&YW2Q+5Jm zjwn)^Tv!*~#o^+>5wy7G zNyAYH&kEqc>H_=*qaO)^}u3ss93q`d^9^jGkXmg-xvcolPdA<^Og z6BpWmLe|c~)A~XpeU9-YI09VlKZNk2;5ZYu_jVsoX;jvlsYP!l`1lfr_8emOQdD~l ze{)dC6b@zRfLb+09JWsxqfBg~1zw=L9|2Jz?st6trtOAd^9`zjp#uYcr<+CAtX;;4 zudg0)Epua@U)%lR7CEY<<@y;Tk;k`aS9py+hepiUpSe7792N-QDdUIN;)Qv4oMO{( zqd>5@pEQ45;5K!}P3_GnxRr@C#i57!5D!z*0w{U|$1JMSa3maBN*>(bSjbaDW=Q4m zAsrw~zhWG)61yA6N?9GaZHWx}zQGgQ#b=q_B~B{_Dn`kP$y488W#N(5?79afOb}oG zjuM3J?+GFD@gJB`qn3qS4~iGlWRioA7@mKJPeUv~h`vb8(Qp}r>IKU-C`ADA4L%Z? zhfoFb-Xa=KsMQ%!$h+Sr+Q!wyN_n%XM&PJLJk(Bxzn0K#f5L^CU8k!E_{ zJf^Y-ZCAMkhD)WT9-FKijA1;#%lHv!UZriZo8l(UeEaSn9sk)YEZEPe{8&2Y%UIa6 z@h6R|dn&w*=hU5-KDbZ!vWdNi>?bv#YF)nf6MtGO|A6HW8)w(blJ(>O!hNlnBqSi4 z`hAFO<+YigLt7XXZ91<=Q^GCYV!60C-EM(*oA_nJD{bO7nASyL;~lUx>B16UT0sMCCFdN9Axg9*`RWJD>Jn3%ZRlXIVQbLmEfUGV>&3(81fR9lp&Cm1 z`Gwbfm5!2p)y@Ph`NxM9)71xV;1XVrn5+QH3&!jU>&xzByB18P=7i#^ruEYDSaU%^ z0pylfqjii^wRU-;!izE&S17`AaB*)IuC=12U!a{Y0b6BwXzY>awDiA=>zQ<-yj4f| zcP2ri;@HHd`^-Rk7-sI1rly}3{3x2`i!ztr5ei(zpPQ`R=)KNvJ+XR3^tt?Q1v2fs zX7m5Y*qcW~-Nt|8Gs7UVl_L96p@d3yX0%8tq(!ofB$S;*wi&5Vw(QwPrG%o$z6=pb zNV4zyzK(syT)%7F_w!w!^E=N!_kHS=&h@#L_wriaU2Ki)${i_goIfJ!Tr}%bre|`= za)V3{BsnXo9(!8wL%82iZH4-}32awAAjEhU0lhL{1F#wg2i^tJv2EBuJ^{bNi%fvi z5GCxHK8*9*dkJY^c@xuVg-(IcMLI(Bm@b3L&;yTnRHwD4C7(l39oi7hq)?~kFl6W^ zs0thVy2#zRtMy#l__CXTQ|>AmMSW1Yxw+X8CwH6|VaJG;vmAph$z}7(F*5?}lO}i| zZ`?xbT<0B2yYh$rw?_|No#(p_FcD{@j(paj_@4fa(~?UdpYyms5mraVTcq=9USPj_ zul-ofo=i=L#E{oBNq%G1ZRc{wlyIIlD&DV)krUl+L&yWmXY~$uT&{XM{Wl|Ozupg( zQG$0pv(!`C1Igb7fL0neCJS21?qM=VmGazZ~v{OWwA#YY}i)^X@70J6U z$UkHEJ4T{DTgtg6E*hqtRh1D5_1ePQcnlkX@f0`Ih?nfFq=tXDY9cQX(Oi3}PB7^p zgM1q#V=%;vVVH@GLLIiGV+mW-aqt3)pj&l+4Am8H*q!?ONxLFhn&9elpGJzF6X`sE z^Te}7E)q`84hcmEf(LeD3t`+d{g~Duy`F1#>3l+Smur+6cNp`fri$e7S;Ubz&8NDd}cl>{Y1sQwT%Ov%h~#mDTu;%{Gh zcp9_ug(0B)ZW~q5u9UwHR;A)lv}n2OIsyJNI;20@I4e1H%9By%Cgbd__o>acm!qnW}p}nMv@DhGgSQk!uq@p3X~RbC{w3- z`HiaPIlqv@=pr!+9H9)G1I~GfOU&w~QaPy6tDPL8#p1tT1U2pRXEjDVSNQY$oJwf) z3V=fqCLj4{R{VqvEunogNvX!e0ki9;e;=<^tx{5tIy`6g6NH0yc~{tKP7;oHjXb`5 zIz8OF@3hS8>DGdJzLKUXBTa{Coz)qBgUpF31tkmd;+*)6Uzv|J_N`@SFDV}wTzS@N zt;hPt)aSFkTFeT7b`mI^VM!JrBC%A#K5*$tAmHUtPvJMFeAfN9*9Rj&-=9nNC&Q+EdF+!PfH%ENrhE3p&VnWh{2;79z z*y79n+vNz@>?}4#D4|o+Ubv#9r|42*R;Y}}?YkBwcMo-&@ad>x*kjt_{O_yf5+)y) z2KbpEMt;urzrPQyha~ST(cF)9T|i4(Bt0R=ZToxZ zbWkj3Ed&$+N>v<=-5pwb=6r{TlfO~t#ws|-la*rv_%8VGhKk*|X; zVQQ5J(OG+EyG#p{)IB{Kiuv;70q)8FJQb)5l$j!_Hu8f60r{7`yqA5f^Q*ERHg;Y> zr$0@)6|k;7{Qcm_A%YJElD=Twxd;-kB9>5EbR=yT1UBfuleJ@lvP}oh*1!8g7K^4j zZ+0{8-1L*;)hM<*Sxuy9Ks>nyKZFI$vRMYLS*6~W<2wmH{sQw0vE_&g;R*)COEztj zRIbC*d+!7Y;+~jjXZU1%{=mNuOB7e-`c;qxXaAJyGBPSmpiw?O?KuZzCf+K+Va9iq zoCCa;;0(I6uuG0uq>2p(loPh|33{&{WTT#xWR<8nCtkYmC->&6+;w-JY2vas4{hnf zmC+vBZ@(Y7;!(zjc88BHi$^~G6w`6J5T ztevxQdnd7q;5htfJR4J$-gfF(`u@R2yy;rHe^Rpuy^+UkupdZUF<#UKyppKh&>?Bv zPof7Ys&<*V*QDr?zo02FPSjJ=sQa=7Ct<9EL$C)<0o8_xx@iz_Z1Jw~U_kfhPAiZI$mRlYtd85Gr@KdW9oeN37u8Gacq+LmV6nQbtt~VT#quD`v-h?j zbD{%ZcP_5rX2xQm@W9xxmZbteoIQp|UIzn`9~`J>1V8mU@2XbVJ!bKVZw*P|qU z0*^hOSV>(lMN1XJu{yA>dRgR^(F!;k4d0HG@PSm14kSxtrJFO(^5Y1mpIu3{% zh+K}hZ$G)EJ-IAtn%w#e0-7h$2A@8BJ8tqmuQ)fti|KsijH+X-37Be?Ot(q$n{lDV zQ1Lp*=sboRGeWp1`gb1hXC}x_f_)WZI0lvzK1bEcg`0BB2P%CC!@H>D4@cS zwPrUf!d|6fVnIzTIy=(RV!Hc%tuK^=4pPtkx7F&77ImHc28SqYw4W{~H000X&*+<4G@ zx);P9$t->oB<8Srs1TymTlA<{>|<&!@gQ@#-JGIiYhUmb&hYKBeD{rbfA9wT%Fm@# zDA)?30HZ)}r0CO2{&*B^aF{8_W5yau@@8Wk;@66Rmlhj23ONxnSQTz;7h8NJ>I19d z8|?jdT^Dmvt}809cx+QT7wect2E2guTB&qKlktG+sdG_Zpj9pOzY+0`ir){h=x7zRpV_AW{Y5*m zZ^!6jXMa=p6|}HKw7jU?=c@pWlsq6%4rC9zUPclMxWko0?pWqEo-vtg*ne~%?b_Rd z`_V=mOms2;Lrthx7xnQoAo5o`H-*gusas?!2_qNc^G7X%p)y3L9AW|;4Y@|zh~%TF zeeW3{W#l7IrCn1RLAX3R`iy@M7;+vkw-=^qG-`}%DaFj}EAGpP9+I9dd`N8mNLGL! zK7h=t5)xL>^m30PRD|lCgy%~AeSt5DwrINg2!)~`_w~UD^0<+lE4tJaRThUkf z^|F=O_m{zGZ*nAC3h=awKuy-OY&^rzF5>uMxdHnHUa<@?m82(k@FzLo!BGY3A|=*Q z9uS{q0?Y|C*R?p2<}DKB?!pqgAPQ0hL>lz{I8@2j#vCjXVM0@acXkgWGk7j;;o#f7 zn9gr=eI(bFPF=D2(z|es0>)Hd5AcH(3jeauH@rTh6M>moz&*(vD2#7wvqCdj%Jh{_CgE%OB_r`aJa{ z%PpavJ=YLDuKlo!9CzfHHbvV*(a6#}3=`)ioWJDF>CvsXtfDm>9o}lKPt;^Lr-(qX zv99uK*Hy^Bp^gT!x02U18!WV*t77(Qzynfjs_pXLFQZ@taQ4&HrUn2h;rKNoq{ zOiG(=+8^=5W?$9j3qkGkCKEcl67`By(_bBj+n@S^NV?{a+}Zv=J#LG|f62;-GS-gg zNjxsyyJEMeaOCtZASbB5LJg3B#(g@h5TArR&&x>aNq7gefCx#ams1JgaWue^WBPbH z@t#94!cn89j(N3FSu2}fFV*I*@z>2J?9=^tG;$L@e(AK?!H1h&wN5Z zC_Ic3buKlDdI7WUAPFFDiD8!Y|o;FO(P#n zKnCdEdoaZhCRl)hZxbe+&=q0pkw0a;$Bxbzfad}>b|8~C4&aODxgQlKUJMvIfwqWh-5y_TK?Lbs>B3# z@z)m_BmNvq9^<(fd+ujp+!32RE~l$*rGFed^N0)JJLUlBO?{rU=6nR*4@YCEy!0g# zhoP=@0P+>8=|>YP7fTTahf$xYU}`6UgN^iD7~oBZz1XJnrhx}c_jM6pARp?F62Mgh z@VRVyidJsgj&l7e#t~K_8&#CPOXyCNz2f8coz52QC`9nVW51QUi-xJ5=@vz+dYrowavT()V%UQM4y*crnV_U!ST-gH}XY0-hXVB92O85OS)_ZMboaRHBX6ZMk zJ*aWAt&AVnphyFyddd0wkH`ZT4&Pim4+l&*(m8(V2U#A|Gn2(uvBqG!7nBY?n)3iW z+kfmUbk$a9zsRZ1m{a?!HKr^3RPQT(|SMzZML} z9lN2%KMmKpWUV68wgwW}sUvIjlz(F_#>vB(lFcEBE?VMXa=9rw*_eP{Tsn#R0tPTb zqO_97!>nHo$_n&F1)MFu3-DZXZEKgpEI-z$uaWJrNr|!Jv$w{`F0(w8*7!fWXei{Y z*6{KOUUgVW!Qv(xaXVFHuxY}p>u-JV0ZD&ICR_V)r;Pq;K`n|4D?wA*f>)Z51Z&^B zaL7~@G(l8=CIGI(UT=*~`5Hol5(9Ig4^RZzYQqQ(Bh*s|7vlgc#-FFMohR&zw4#8P zU6yZ`W&YdOG|x}>MR{i}wP2$h=k=_NWn>$;vZksWNHW!XH+z@k=);o%`Oz(^HQW2JZ#lfDwMwZH zXGUTh&z0S|_x6*~*_GL0zjM)Ne-h;yGHJ`{XUc>c*Nt*CBr(M8A|RJtn`G%?S$iQP zU7z@70yDFXe=-9rTc(|UKDNgd8y!MebHzf-0~noBA@CtHOv9U~xRignUy4B+r0~F6 z9a8(rAWP3L8~~SOmc>VAY(L~J;d+lw;omKLiyO0UFWr*umkE5w4=Zl`>h4F}?>7KM z$K5_A+wJkh$;O!?c0XP?DZor&Wj=w5NB=l>;Mz?VsrJ)f(zIR=&xB(6Hr|?>Zfcc} zO-uSEN!PKR_|mZY^2DvcA`;5>?`?ez+c> ztj1x4hUaW6NSSjgG#@rj%Bm$zEq{dZ541RgZAM6WlI*7G1OT~66pNpO9+FEE9n!_q zNyU@t7N59SsIw7*)S5G_qEmE>fN@}y>{9}rWstHp+l!FtpDr7A%HV`or#<7RG!XOC ziNh^urOvL{Gfyz~$R=1q()TxU6}Yt{%lB;p^!oaJGa;5bp*Jv~C2#_ubi5V^+PV$O2pSoWnZ4#rpTy6PPGG~JZL#qZvEm9u+H|fEvdQBc!<#_A5(HDOZ9{78=)2|)s@1WF|YU5ZnZ_xV(gg32w#hB z&&RclYksT6;}2ynjxjoy)(OIb%pjM=u);U5H?mw%qh{d_N7tI*+m$k$&5hxwa_&mq z8T{#ZXt%o92psklZ51~sLdML$0l8%jOkL__tE^i_QHJYw^QVx)-`mIqN6!(-bH(_W zqk-mRcsfqec`SaA9M1Biez$>8YxU7PINOGiXD7Q3C%3RGrQ8qeNMJA|17GV+Fu9v6 z6m^k7w}2hP=KQGWuDl+rr@$QVVz-IVhq5M;+%4>rja+2i1Hq2R{7r3!`NEdnDL6+U z+SYgP!6(gb%}*g}_JV5s4vB4=F+X!OJ9@*;Wh5aS*W&~Jh#*y?6)jWy)18xO{z{)Ap?7!*LiBudfEV_gU!DSYW>P4U z^{35YFN)J|pFVOl7TV^4k7n?X#EF}qV$pPo{Bt2}un`2i#C~C`Z5Ww;Wgpcv`Ak6n zRGnQTGGh~HLgYi9Imlcq-=EurErrw6T3s%+%(!wUdT4<(5k69cvEIC%kheuj+rJr{ zlSNfHrB3RlX=RqSPTunU>U&GfyHnX!p<6M__ec!RH@jHqg`oI8G=RDx^CVaXR3kKtmKK(eeCjk;MoKs`@|vrBIWK#e874UhuquN1p1 zeSIE))6944W!2{A>^IOUUK%m{5YguDA9QIQ1gDx*cmNTIKRZLeQs`n;J%>14Yg{X9 z&{pdJPqOM;0Y~*V!JJdfY2$zbyDn_7b!ukdVA1<*g19(VVvMj?C2O9P7WJzKb0sOj zd*Kise+W-H!Wwr#o#iR>!Hp!(FKFv^C9%9xn7u7o|BZ&jDnt11f zU&#>L^C`N!Soc?FI{dO$=odp__D0RkK?7l_VKU#}KM&*k9PEy_Bq%b;mzzn$)2jC_ zWxdH2Rn%t@HtrW=p38Dw<>2+OK|Uq)5+(jjfd++LZ4=f^ej~5o2*d=i&|mIN9&dyb zhD0!abaO!GmZiWxtrg(y%1FC~g}h$}714{@sWvL9FoS2ZrfiG-(hb$OEQP{VbZWkD+xNNpco1=*YjG%Doz{^IXLW91TGa^uz64&nw~I{KTh~d#S^7z zB&XmB!DUm=6gvyUFX}0K+UwJUOwVl^#X*e0A-;3gjZ<&cf4IGR)=$dnVd1CmXYIQA zDpwx9gYT-8#{61%E6I|=XuZ=}qQ%N4vKTp*hk8o(#s;1Y${UllMxxSAL zv@$%P4G#~mtUMq+!^2hS7SW`jACuxY5iooih zhWCLH>CR#Yb*XuPh+g#TvR)$R3K624j)(1W(dfk=?nygYGE{rDap{&D7D7S#^FFTW zpWsvmX~NFRdW$qWXiFcZlsa-g44#hUjUk_z)Vwh7l{n*Xg2~(2y5?7k+W&uBr)RU- z%1sJavFdT_{T$E?ch_m4qEB}E#|0r?FVZ9O_{s`T+*TFz{Cv|?VX+A`)X)?fW=9M} z53+4Ml^BVe@HZ^!{v=oBiFnpFvRDd!{&QvzoOKJa;rpthi*s&7=y1aNlZH{c96@fI z%3y5s(V`mOEo;(W-Z1qS9dhJkU|z3(D1M?k=n5-y%~Xh2rpt{}vPT1*T*@wl=r<_3 z$J`PoTpp~J98e+Z%zvHQDR}u=dYNuGx-QnwmB{|)brMS*{MIn{x{EKvTyA|jjjy#_ zVLh;Wo80bWkwHEVYjEAZy0^F61LU6CJ9YJ5F;b&07gau0Q$H2`+t^QFxb0A@42x}{ z+t%JWj|Yo^&_g449EjklOVe#x5H8U0qi&3jT1A@3)#hC4wg;!h;ml9TZM=9Rjr3y} zqbUF*>3KD|)MQcRcE%_0TYO02T%x)K?wjXnpo3x8j2KS<9D@rOkdA_!Tb!I4DwVLy zD*U@kZ96P)4IgMLwRvWqOnotO(NeH`mI?{a0D466-CZ;q+yb`26F$qCZhE_ay+b_& zAB&7`RYWYiw}ifIVjDg2-uvD>XFkQof%vF>lMhjt@@{^lMSi9#?sP$+{rtPEa$k2- ziz=ZM{)>|`B=)80$b0)4zxJpsxrBMEY}6#b-iv|PwDczQerLz0A6P#<`xh)g5lI{( zauq1;7-1yR`Iyt2T+JAdo14~~G?F1}s=AtJ!4_Kvk__jPQ-Da>9AbXCm3S=dq~g

xh$O@^I@xjC#7m1LQC)skF5=yU4nn6U$G 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�X6!sB5Txc9+wd%hF!Y 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

%w0*B{*Mi>(Z_)U2LK*vwXZ~)m$HaAW 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,*g|kIB_4_KG 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 z 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�_;Vj2W1eDi6xnPe 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 ZpzUlaAG~ZCjn3+3&mdTI<_5Xa6~4 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 z

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 NuoW6XK{Y+~{{rUp!#Dr{ literal 0 HcmV?d00001 diff --git a/docs/app-modules/interchain-accounts/auth-modules.md b/docs/apps/interchain-accounts/auth-modules.md similarity index 100% rename from docs/app-modules/interchain-accounts/auth-modules.md rename to docs/apps/interchain-accounts/auth-modules.md diff --git a/docs/app-modules/interchain-accounts/integration.md b/docs/apps/interchain-accounts/integration.md similarity index 100% rename from docs/app-modules/interchain-accounts/integration.md rename to docs/apps/interchain-accounts/integration.md diff --git a/docs/app-modules/interchain-accounts/overview.md b/docs/apps/interchain-accounts/overview.md similarity index 100% rename from docs/app-modules/interchain-accounts/overview.md rename to docs/apps/interchain-accounts/overview.md diff --git a/docs/app-modules/interchain-accounts/parameters.md b/docs/apps/interchain-accounts/parameters.md similarity index 100% rename from docs/app-modules/interchain-accounts/parameters.md rename to docs/apps/interchain-accounts/parameters.md diff --git a/docs/app-modules/interchain-accounts/transactions.md b/docs/apps/interchain-accounts/transactions.md similarity index 100% rename from docs/app-modules/interchain-accounts/transactions.md rename to docs/apps/interchain-accounts/transactions.md diff --git a/docs/ibc/integration.md b/docs/ibc/integration.md index f823ffe07e4..09c1d2d2de9 100644 --- a/docs/ibc/integration.md +++ b/docs/ibc/integration.md @@ -92,13 +92,13 @@ func NewApp(...args) *App { // Create IBC Keeper app.IBCKeeper = ibckeeper.NewKeeper( - appCodec, keys[ibchost.StoreKey], app.StakingKeeper, scopedIBCKeeper, + appCodec, keys[ibchost.StoreKey], app.GetSubspace(ibchost.ModuleName), app.StakingKeeper, app.UpgradeKeeper, scopedIBCKeeper, ) // Create Transfer Keepers app.TransferKeeper = ibctransferkeeper.NewKeeper( - appCodec, keys[ibctransfertypes.StoreKey], - app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper, + appCodec, keys[ibctransfertypes.StoreKey], app.GetSubspace(ibctransfertypes.ModuleName), + app.IBCKeeper.ChannelKeeper, app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper, app.AccountKeeper, app.BankKeeper, scopedTransferKeeper, ) transferModule := transfer.NewAppModule(app.TransferKeeper) diff --git a/docs/ibc/proto-docs.md b/docs/ibc/proto-docs.md index cedc9fac02e..569289dc01f 100644 --- a/docs/ibc/proto-docs.md +++ b/docs/ibc/proto-docs.md @@ -127,6 +127,8 @@ - [MsgTimeoutOnCloseResponse](#ibc.core.channel.v1.MsgTimeoutOnCloseResponse) - [MsgTimeoutResponse](#ibc.core.channel.v1.MsgTimeoutResponse) + - [ResponseResultType](#ibc.core.channel.v1.ResponseResultType) + - [Msg](#ibc.core.channel.v1.Msg) - [ibc/core/client/v1/genesis.proto](#ibc/core/client/v1/genesis.proto) @@ -1738,6 +1740,11 @@ MsgAcknowledgement receives incoming IBC acknowledgement MsgAcknowledgementResponse defines the Msg/Acknowledgement response type. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `result` | [ResponseResultType](#ibc.core.channel.v1.ResponseResultType) | | | + + @@ -1954,6 +1961,11 @@ MsgRecvPacket receives incoming IBC packet MsgRecvPacketResponse defines the Msg/RecvPacket response type. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `result` | [ResponseResultType](#ibc.core.channel.v1.ResponseResultType) | | | + + @@ -2003,6 +2015,11 @@ MsgTimeoutOnClose timed-out packet upon counterparty channel closure. MsgTimeoutOnCloseResponse defines the Msg/TimeoutOnClose response type. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `result` | [ResponseResultType](#ibc.core.channel.v1.ResponseResultType) | | | + + @@ -2013,11 +2030,29 @@ MsgTimeoutOnCloseResponse defines the Msg/TimeoutOnClose response type. MsgTimeoutResponse defines the Msg/Timeout response type. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `result` | [ResponseResultType](#ibc.core.channel.v1.ResponseResultType) | | | + + + + + +### ResponseResultType +ResponseResultType defines the possible outcomes of the execution of a message + +| Name | Number | Description | +| ---- | ------ | ----------- | +| RESPONSE_RESULT_UNSPECIFIED | 0 | Default zero value enumeration | +| RESPONSE_RESULT_NOOP | 1 | The message did not call the IBC application callbacks (because, for example, the packet had already been relayed) | +| RESPONSE_RESULT_SUCCESS | 2 | The message was executed successfully | + + diff --git a/docs/ibc/relayer.md b/docs/ibc/relayer.md index c846f2c7320..ce3fabe252d 100644 --- a/docs/ibc/relayer.md +++ b/docs/ibc/relayer.md @@ -27,7 +27,7 @@ a module event emission with the attribute value `ibc_` (02-clien ### Subscribing with Tendermint -Calling the Tendermint RPC method `Subscribe` via [Tendermint's Websocket](https://docs.tendermint.com/master/rpc/) will return events using +Calling the Tendermint RPC method `Subscribe` via [Tendermint's Websocket](https://docs.tendermint.com/v0.35/rpc/) will return events using Tendermint's internal representation of them. Instead of receiving back a list of events as they were emitted, Tendermint will return the type `map[string][]string` which maps a string in the form `.` to `attribute_value`. This causes extraction of the event diff --git a/docs/migrations/v2-to-v3.md b/docs/migrations/v2-to-v3.md index 710e273a954..6b1c61735fc 100644 --- a/docs/migrations/v2-to-v3.md +++ b/docs/migrations/v2-to-v3.md @@ -26,7 +26,98 @@ The ICS4Wrapper should be the IBC Channel Keeper unless ICS 20 is being connecte ### ICS27 ICS27 Interchain Accounts has been added as a supported IBC application of ibc-go. -Please see the [ICS27 documentation](../app-modules/interchain-accounts/overview.md) for more information. +Please see the [ICS27 documentation](../apps/interchain-accounts/overview.md) for more information. + +### Upgrade Proposal + +If the chain will adopt ICS27, it must set the appropriate params during the execution of the upgrade handler in `app.go`: +```go +app.UpgradeKeeper.SetUpgradeHandler("v3", + func(ctx sdk.Context, _ upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { + // set the ICS27 consensus version so InitGenesis is not run + fromVM[icatypes.ModuleName] = icamodule.ConsensusVersion() + + // create ICS27 Controller submodule params + controllerParams := icacontrollertypes.Params{ + ControllerEnabled: true, + } + + // create ICS27 Host submodule params + hostParams := icahosttypes.Params{ + HostEnabled: true, + AllowMessages: []string{"/cosmos.bank.v1beta1.MsgSend", ...}, + } + + // initialize ICS27 module + icamodule.InitModule(ctx, controllerParams, hostParams) + + ... + + return app.mm.RunMigrations(ctx, app.configurator, fromVM) + }) + +``` + +The host and controller submodule params only need to be set if you integrate those submodules. +For example, if a chain chooses not to integrate a controller submodule, it does not need to set the controller params. + +#### Add `StoreUpgrades` for ICS27 module + +For ICS27 it is also necessary to [manually add store upgrades](https://docs.cosmos.network/v0.44/core/upgrade.html#add-storeupgrades-for-new-modules) for the new ICS27 module and then configure the store loader to apply those upgrades in `app.go`: + +```go +if upgradeInfo.Name == "v3" && !app.UpgradeKeeper.IsSkipHeight(upgradeInfo.Height) { + storeUpgrades := store.StoreUpgrades{ + Added: []string{icacontrollertypes.StoreKey, icahosttypes.StoreKey}, + } + + app.SetStoreLoader(upgradetypes.UpgradeStoreLoader(upgradeInfo.Height, &storeUpgrades)) +} +``` + +This ensures that the new module's stores are added to the multistore before the migrations begin. + +### Genesis migrations + +If the chain will adopt ICS27 and chooses to upgrade via a genesis export, then the ICS27 parameters must be set during genesis migration. + +The migration code required may look like: + +```go + controllerGenesisState := icatypes.DefaultControllerGenesis() + // overwrite parameters as desired + controllerGenesisState.Params = icacontrollertypes.Params{ + ControllerEnabled: true, + } + + hostGenesisState := icatypes.DefaultHostGenesis() + // overwrite parameters as desired + hostGenesisState.Params = icahosttypes.Params{ + HostEnabled: true, + AllowMessages: []string{"/cosmos.bank.v1beta1.MsgSend", ...}, + } + + icaGenesisState := icatypes.NewGenesisState(controllerGenesisState, hostGenesisState) + + // set new ics27 genesis state + appState[icatypes.ModuleName] = clientCtx.JSONCodec.MustMarshalJSON(icaGenesisState) +``` + +### Ante decorator + +The field of type `channelkeeper.Keeper` in the `AnteDecorator` structure has been replaced with a field of type `*keeper.Keeper`: + +```diff +type AnteDecorator struct { +- k channelkeeper.Keeper ++ k *keeper.Keeper +} + +- func NewAnteDecorator(k channelkeeper.Keeper) AnteDecorator { ++ func NewAnteDecorator(k *keeper.Keeper) AnteDecorator { + return AnteDecorator{k: k} +} +``` ## IBC Apps @@ -39,6 +130,10 @@ IBC applications must perform application version negoitation in `OnChanOpenTry` The negotiated application version then must be returned in `OnChanOpenTry` to core IBC. Core IBC will set this version in the TRYOPEN channel. +### `OnChanOpenAck` will take additional `counterpartyChannelID` argument +The `OnChanOpenAck` application callback has been modified. +The arguments now include the counterparty channel id. + ### `NegotiateAppVersion` removed from `IBCModule` interface Previously this logic was handled by the `NegotiateAppVersion` function. @@ -64,6 +159,10 @@ As apart of this release, the mock module now supports middleware testing. Pleas Please review the [mock](../../testing/mock/ibc_module.go) and [transfer](../../modules/apps/transfer/ibc_module.go) modules as examples. Additionally, [simapp](../../testing/simapp/app.go) provides an example of how `IBCModule` types should now be added to the IBC router in favour of `AppModule`. +### IBC testing package + +`TestChain`s are now created with chainID's beginning from an index of 1. Any calls to `GetChainID(0)` will now fail. Please increment all calls to `GetChainID` by 1. + ## Relayers `AppVersion` gRPC has been removed. diff --git a/go.mod b/go.mod index 639c5607ced..eb9dc1a9548 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alp require ( github.com/armon/go-metrics v0.3.10 - github.com/confio/ics23/go v0.6.6 + github.com/confio/ics23/go v0.7.0 github.com/cosmos/cosmos-sdk v0.45.1 github.com/gogo/protobuf v1.3.3 github.com/golang/protobuf v1.5.2 diff --git a/go.sum b/go.sum index 25a2e5dd4c4..5544d71eb5f 100644 --- a/go.sum +++ b/go.sum @@ -184,8 +184,9 @@ github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:z github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coinbase/rosetta-sdk-go v0.7.0 h1:lmTO/JEpCvZgpbkOITL95rA80CPKb5CtMzLaqF2mCNg= github.com/coinbase/rosetta-sdk-go v0.7.0/go.mod h1:7nD3oBPIiHqhRprqvMgPoGxe/nyq3yftRmpsy29coWE= -github.com/confio/ics23/go v0.6.6 h1:pkOy18YxxJ/r0XFDCnrl4Bjv6h4LkBSpLS6F38mrKL8= github.com/confio/ics23/go v0.6.6/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg= +github.com/confio/ics23/go v0.7.0 h1:00d2kukk7sPoHWL4zZBZwzxnpA2pec1NPdwbSokJ5w8= +github.com/confio/ics23/go v0.7.0/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg= github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6 h1:NmTXa/uVnDyp0TY5MKi197+3HWcnYWfnHGyaFthlnGw= github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= @@ -465,7 +466,6 @@ github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIv github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= -github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= @@ -800,7 +800,6 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= -github.com/sagikazarmark/crypt v0.4.0/go.mod h1:ALv2SRj7GxYV4HO9elxH9nS6M9gW+xDNxqmyJ6RfDFM= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa h1:0U2s5loxrTy6/VgfVoLuVLFJcURKLH49ie0zSch7gh4= github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= @@ -1312,7 +1311,6 @@ google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdr google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU= google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw= -google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1425,7 +1423,6 @@ google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.44.0 h1:weqSxi/TMs1SqFRMHCtBgXRs8k3X39QIDEZ0pRcttUg= google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= diff --git a/modules/apps/27-interchain-accounts/controller/ibc_module.go b/modules/apps/27-interchain-accounts/controller/ibc_module.go index 1aa362a4247..c00c9f5d1c2 100644 --- a/modules/apps/27-interchain-accounts/controller/ibc_module.go +++ b/modules/apps/27-interchain-accounts/controller/ibc_module.go @@ -80,6 +80,7 @@ func (im IBCModule) OnChanOpenAck( ctx sdk.Context, portID, channelID string, + counterpartyChannelID string, counterpartyVersion string, ) error { if !im.keeper.IsControllerEnabled(ctx) { @@ -91,7 +92,7 @@ func (im IBCModule) OnChanOpenAck( } // call underlying app's OnChanOpenAck callback with the counterparty app version. - return im.app.OnChanOpenAck(ctx, portID, channelID, counterpartyVersion) + return im.app.OnChanOpenAck(ctx, portID, channelID, counterpartyChannelID, counterpartyVersion) } // OnChanOpenAck implements the IBCModule interface diff --git a/modules/apps/27-interchain-accounts/controller/ibc_module_test.go b/modules/apps/27-interchain-accounts/controller/ibc_module_test.go index f2b5d36ac63..db4412d144e 100644 --- a/modules/apps/27-interchain-accounts/controller/ibc_module_test.go +++ b/modules/apps/27-interchain-accounts/controller/ibc_module_test.go @@ -285,7 +285,7 @@ func (suite *InterchainAccountsTestSuite) TestOnChanOpenAck() { { "ICA auth module callback fails", func() { suite.chainA.GetSimApp().ICAAuthModule.IBCApp.OnChanOpenAck = func( - ctx sdk.Context, portID, channelID string, counterpartyVersion string, + ctx sdk.Context, portID, channelID string, counterpartyChannelID string, counterpartyVersion string, ) error { return fmt.Errorf("mock ica auth fails") } @@ -316,7 +316,7 @@ func (suite *InterchainAccountsTestSuite) TestOnChanOpenAck() { cbs, ok := suite.chainA.App.GetIBCKeeper().Router.GetRoute(module) suite.Require().True(ok) - err = cbs.OnChanOpenAck(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.Version) + err = cbs.OnChanOpenAck(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelID, path.EndpointB.ChannelConfig.Version) if tc.expPass { suite.Require().NoError(err) diff --git a/modules/apps/27-interchain-accounts/host/ibc_module.go b/modules/apps/27-interchain-accounts/host/ibc_module.go index 630c4d44fc1..fb403c71937 100644 --- a/modules/apps/27-interchain-accounts/host/ibc_module.go +++ b/modules/apps/27-interchain-accounts/host/ibc_module.go @@ -61,6 +61,7 @@ func (im IBCModule) OnChanOpenAck( ctx sdk.Context, portID, channelID string, + counterpartyChannelID string, counterpartyVersion string, ) error { return sdkerrors.Wrap(icatypes.ErrInvalidChannelFlow, "channel handshake must be initiated by controller chain") diff --git a/modules/apps/27-interchain-accounts/module.go b/modules/apps/27-interchain-accounts/module.go index b11c611e0c9..969c07caf9d 100644 --- a/modules/apps/27-interchain-accounts/module.go +++ b/modules/apps/27-interchain-accounts/module.go @@ -24,6 +24,7 @@ import ( hosttypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/types" "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" porttypes "github.com/cosmos/ibc-go/v3/modules/core/05-port/types" + ibchost "github.com/cosmos/ibc-go/v3/modules/core/24-host" ) var ( @@ -101,6 +102,18 @@ func NewAppModule(controllerKeeper *controllerkeeper.Keeper, hostKeeper *hostkee } } +// InitModule will initialize the interchain accounts moudule. It should only be +// called once and as an alternative to InitGenesis. +func (am AppModule) InitModule(ctx sdk.Context, controllerParams controllertypes.Params, hostParams hosttypes.Params) { + am.controllerKeeper.SetParams(ctx, controllerParams) + am.hostKeeper.SetParams(ctx, hostParams) + + cap := am.hostKeeper.BindPort(ctx, types.PortID) + if err := am.hostKeeper.ClaimCapability(ctx, cap, ibchost.PortPath(types.PortID)); err != nil { + panic(fmt.Sprintf("could not claim port capability: %v", err)) + } +} + // RegisterInvariants implements the AppModule interface func (AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { } diff --git a/modules/apps/27-interchain-accounts/module_test.go b/modules/apps/27-interchain-accounts/module_test.go new file mode 100644 index 00000000000..de5f51ae921 --- /dev/null +++ b/modules/apps/27-interchain-accounts/module_test.go @@ -0,0 +1,74 @@ +package ica_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + "github.com/tendermint/tendermint/libs/log" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + dbm "github.com/tendermint/tm-db" + + ica "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts" + controllertypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller/types" + hosttypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/types" + "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" + ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v3/testing/simapp" +) + +type InterchainAccountsTestSuite struct { + suite.Suite + + coordinator *ibctesting.Coordinator +} + +func TestICATestSuite(t *testing.T) { + suite.Run(t, new(InterchainAccountsTestSuite)) +} + +func (suite *InterchainAccountsTestSuite) SetupTest() { + suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) +} + +func (suite *InterchainAccountsTestSuite) TestInitModule() { + app := simapp.NewSimApp(log.NewNopLogger(), dbm.NewMemDB(), nil, true, map[int64]bool{}, simapp.DefaultNodeHome, 5, simapp.MakeTestEncodingConfig(), simapp.EmptyAppOptions{}) + icamodule, ok := app.GetModuleManager().Modules[types.ModuleName].(ica.AppModule) + suite.Require().True(ok) + + header := tmproto.Header{ + ChainID: "testchain", + Height: 1, + Time: suite.coordinator.CurrentTime.UTC(), + } + + ctx := app.GetBaseApp().NewContext(true, header) + + // ensure params are not set + suite.Require().Panics(func() { + app.ICAControllerKeeper.GetParams(ctx) + }) + suite.Require().Panics(func() { + app.ICAHostKeeper.GetParams(ctx) + }) + + controllerParams := controllertypes.DefaultParams() + controllerParams.ControllerEnabled = true + + hostParams := hosttypes.DefaultParams() + expAllowMessages := []string{"sdk.Msg"} + hostParams.HostEnabled = true + hostParams.AllowMessages = expAllowMessages + + suite.Require().False(app.IBCKeeper.PortKeeper.IsBound(ctx, types.PortID)) + + icamodule.InitModule(ctx, controllerParams, hostParams) + + controllerParams = app.ICAControllerKeeper.GetParams(ctx) + suite.Require().True(controllerParams.ControllerEnabled) + + hostParams = app.ICAHostKeeper.GetParams(ctx) + suite.Require().True(hostParams.HostEnabled) + suite.Require().Equal(expAllowMessages, hostParams.AllowMessages) + + suite.Require().True(app.IBCKeeper.PortKeeper.IsBound(ctx, types.PortID)) +} diff --git a/modules/apps/transfer/ibc_module.go b/modules/apps/transfer/ibc_module.go index 26f1c533434..f5ed807d8b2 100644 --- a/modules/apps/transfer/ibc_module.go +++ b/modules/apps/transfer/ibc_module.go @@ -125,6 +125,7 @@ func (im IBCModule) OnChanOpenAck( ctx sdk.Context, portID, channelID string, + _ string, counterpartyVersion string, ) error { if counterpartyVersion != types.Version { @@ -162,7 +163,7 @@ func (im IBCModule) OnChanCloseConfirm( } // OnRecvPacket implements the IBCModule interface. A successful acknowledgement -// is returned if the packet data is succesfully decoded and the receive application +// is returned if the packet data is successfully decoded and the receive application // logic returns without error. func (im IBCModule) OnRecvPacket( ctx sdk.Context, diff --git a/modules/apps/transfer/ibc_module_test.go b/modules/apps/transfer/ibc_module_test.go index b5f834a3a8e..92d0f30d4c7 100644 --- a/modules/apps/transfer/ibc_module_test.go +++ b/modules/apps/transfer/ibc_module_test.go @@ -228,7 +228,7 @@ func (suite *TransferTestSuite) TestOnChanOpenAck() { tc.malleate() // explicitly change fields in channel and testChannel - err = cbs.OnChanOpenAck(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, counterpartyVersion) + err = cbs.OnChanOpenAck(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointA.Counterparty.ChannelID, counterpartyVersion) if tc.expPass { suite.Require().NoError(err) diff --git a/modules/apps/transfer/keeper/relay_test.go b/modules/apps/transfer/keeper/relay_test.go index 8e77c73db9e..9d03bbde962 100644 --- a/modules/apps/transfer/keeper/relay_test.go +++ b/modules/apps/transfer/keeper/relay_test.go @@ -186,12 +186,12 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { // send coin from chainB to chainA, receive them, acknowledge them, and send back to chainB coinFromBToA := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) transferMsg := types.NewMsgTransfer(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, coinFromBToA, suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), clienttypes.NewHeight(0, 110), 0) - _, err := suite.chainB.SendMsgs(transferMsg) + res, err := suite.chainB.SendMsgs(transferMsg) suite.Require().NoError(err) // message committed - // relay send packet - fungibleTokenPacket := types.NewFungibleTokenPacketData(coinFromBToA.Denom, coinFromBToA.Amount.String(), suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String()) - packet := channeltypes.NewPacket(fungibleTokenPacket.GetBytes(), 1, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, clienttypes.NewHeight(0, 110), 0) + packet, err := ibctesting.ParsePacketFromEvents(res.GetEvents()) + suite.Require().NoError(err) + err = path.RelayPacket(packet) suite.Require().NoError(err) // relay committed diff --git a/modules/apps/transfer/transfer_test.go b/modules/apps/transfer/transfer_test.go index ec36af78ceb..5190cdc8d29 100644 --- a/modules/apps/transfer/transfer_test.go +++ b/modules/apps/transfer/transfer_test.go @@ -8,7 +8,6 @@ import ( "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" ibctesting "github.com/cosmos/ibc-go/v3/testing" ) @@ -56,13 +55,13 @@ func (suite *TransferTestSuite) TestHandleMsgTransfer() { // send from chainA to chainB msg := types.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, coinToSendToB, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), timeoutHeight, 0) - - _, err := suite.chainA.SendMsgs(msg) + res, err := suite.chainA.SendMsgs(msg) suite.Require().NoError(err) // message committed + packet, err := ibctesting.ParsePacketFromEvents(res.GetEvents()) + suite.Require().NoError(err) + // relay send - fungibleTokenPacket := types.NewFungibleTokenPacketData(coinToSendToB.Denom, coinToSendToB.Amount.String(), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String()) - packet := channeltypes.NewPacket(fungibleTokenPacket.GetBytes(), 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) err = path.RelayPacket(packet) suite.Require().NoError(err) // relay committed @@ -82,18 +81,18 @@ func (suite *TransferTestSuite) TestHandleMsgTransfer() { // send from chainB to chainC msg = types.NewMsgTransfer(pathBtoC.EndpointA.ChannelConfig.PortID, pathBtoC.EndpointA.ChannelID, coinSentFromAToB, suite.chainB.SenderAccount.GetAddress().String(), suite.chainC.SenderAccount.GetAddress().String(), timeoutHeight, 0) - - _, err = suite.chainB.SendMsgs(msg) + res, err = suite.chainB.SendMsgs(msg) suite.Require().NoError(err) // message committed - // relay send - // NOTE: fungible token is prefixed with the full trace in order to verify the packet commitment - fullDenomPath := types.GetPrefixedDenom(pathBtoC.EndpointB.ChannelConfig.PortID, pathBtoC.EndpointB.ChannelID, voucherDenomTrace.GetFullDenomPath()) - fungibleTokenPacket = types.NewFungibleTokenPacketData(voucherDenomTrace.GetFullDenomPath(), coinSentFromAToB.Amount.String(), suite.chainB.SenderAccount.GetAddress().String(), suite.chainC.SenderAccount.GetAddress().String()) - packet = channeltypes.NewPacket(fungibleTokenPacket.GetBytes(), 1, pathBtoC.EndpointA.ChannelConfig.PortID, pathBtoC.EndpointA.ChannelID, pathBtoC.EndpointB.ChannelConfig.PortID, pathBtoC.EndpointB.ChannelID, timeoutHeight, 0) + packet, err = ibctesting.ParsePacketFromEvents(res.GetEvents()) + suite.Require().NoError(err) + err = pathBtoC.RelayPacket(packet) suite.Require().NoError(err) // relay committed + // NOTE: fungible token is prefixed with the full trace in order to verify the packet commitment + fullDenomPath := types.GetPrefixedDenom(pathBtoC.EndpointB.ChannelConfig.PortID, pathBtoC.EndpointB.ChannelID, voucherDenomTrace.GetFullDenomPath()) + coinSentFromBToC := sdk.NewCoin(types.ParseDenomTrace(fullDenomPath).IBCDenom(), amount) balance = suite.chainC.GetSimApp().BankKeeper.GetBalance(suite.chainC.GetContext(), suite.chainC.SenderAccount.GetAddress(), coinSentFromBToC.Denom) @@ -106,14 +105,12 @@ func (suite *TransferTestSuite) TestHandleMsgTransfer() { // send from chainC back to chainB msg = types.NewMsgTransfer(pathBtoC.EndpointB.ChannelConfig.PortID, pathBtoC.EndpointB.ChannelID, coinSentFromBToC, suite.chainC.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), timeoutHeight, 0) - - _, err = suite.chainC.SendMsgs(msg) + res, err = suite.chainC.SendMsgs(msg) suite.Require().NoError(err) // message committed - // relay send - // NOTE: fungible token is prefixed with the full trace in order to verify the packet commitment - fungibleTokenPacket = types.NewFungibleTokenPacketData(fullDenomPath, coinSentFromBToC.Amount.String(), suite.chainC.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String()) - packet = channeltypes.NewPacket(fungibleTokenPacket.GetBytes(), 1, pathBtoC.EndpointB.ChannelConfig.PortID, pathBtoC.EndpointB.ChannelID, pathBtoC.EndpointA.ChannelConfig.PortID, pathBtoC.EndpointA.ChannelID, timeoutHeight, 0) + packet, err = ibctesting.ParsePacketFromEvents(res.GetEvents()) + suite.Require().NoError(err) + err = pathBtoC.RelayPacket(packet) suite.Require().NoError(err) // relay committed diff --git a/modules/apps/transfer/types/ack_test.go b/modules/apps/transfer/types/ack_test.go index bc4e2d07afc..4f4c3a874d7 100644 --- a/modules/apps/transfer/types/ack_test.go +++ b/modules/apps/transfer/types/ack_test.go @@ -9,7 +9,7 @@ import ( tmprotostate "github.com/tendermint/tendermint/proto/tendermint/state" tmstate "github.com/tendermint/tendermint/state" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/types" + "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" ibctesting "github.com/cosmos/ibc-go/v3/testing" ) diff --git a/modules/apps/transfer/types/msgs_test.go b/modules/apps/transfer/types/msgs_test.go index 5cf074a6d8d..00570ac15ed 100644 --- a/modules/apps/transfer/types/msgs_test.go +++ b/modules/apps/transfer/types/msgs_test.go @@ -32,7 +32,7 @@ var ( coin = sdk.NewCoin("atom", sdk.NewInt(100)) ibcCoin = sdk.NewCoin("ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", sdk.NewInt(100)) - invalidIBCCoin = sdk.NewCoin("notibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", sdk.NewInt(100)) + invalidIBCCoin = sdk.NewCoin("ibc/7F1D3FCF4AE79E1554", sdk.NewInt(100)) invalidDenomCoin = sdk.Coin{Denom: "0atom", Amount: sdk.NewInt(100)} zeroCoin = sdk.Coin{Denom: "atoms", Amount: sdk.NewInt(0)} diff --git a/modules/apps/transfer/types/trace.go b/modules/apps/transfer/types/trace.go index 6bbbbadfc23..303ddd3769e 100644 --- a/modules/apps/transfer/types/trace.go +++ b/modules/apps/transfer/types/trace.go @@ -162,7 +162,7 @@ func ValidatePrefixedDenom(denom string) error { // ValidateIBCDenom validates that the given denomination is either: // -// - A valid base denomination (eg: 'uatom') +// - A valid base denomination (eg: 'uatom' or 'gamm/pool/1' as in https://github.com/cosmos/ibc-go/issues/894) // - A valid fungible token representation (i.e 'ibc/{hash}') per ADR 001 https://github.com/cosmos/ibc-go/blob/main/docs/architecture/adr-001-coin-source-tracing.md func ValidateIBCDenom(denom string) error { if err := sdk.ValidateDenom(denom); err != nil { @@ -172,17 +172,17 @@ func ValidateIBCDenom(denom string) error { denomSplit := strings.SplitN(denom, "/", 2) switch { - case strings.TrimSpace(denom) == "", - len(denomSplit) == 1 && denomSplit[0] == DenomPrefix, - len(denomSplit) == 2 && (denomSplit[0] != DenomPrefix || strings.TrimSpace(denomSplit[1]) == ""): + case denom == DenomPrefix: return sdkerrors.Wrapf(ErrInvalidDenomForTransfer, "denomination should be prefixed with the format 'ibc/{hash(trace + \"/\" + %s)}'", denom) - case denomSplit[0] == denom && strings.TrimSpace(denom) != "": - return nil - } + case len(denomSplit) == 2 && denomSplit[0] == DenomPrefix: + if strings.TrimSpace(denomSplit[1]) == "" { + return sdkerrors.Wrapf(ErrInvalidDenomForTransfer, "denomination should be prefixed with the format 'ibc/{hash(trace + \"/\" + %s)}'", denom) + } - if _, err := ParseHexHash(denomSplit[1]); err != nil { - return sdkerrors.Wrapf(err, "invalid denom trace hash %s", denomSplit[1]) + if _, err := ParseHexHash(denomSplit[1]); err != nil { + return sdkerrors.Wrapf(err, "invalid denom trace hash %s", denomSplit[1]) + } } return nil diff --git a/modules/apps/transfer/types/trace_test.go b/modules/apps/transfer/types/trace_test.go index f0868d5680e..e35fd33317b 100644 --- a/modules/apps/transfer/types/trace_test.go +++ b/modules/apps/transfer/types/trace_test.go @@ -131,11 +131,12 @@ func TestValidateIBCDenom(t *testing.T) { }{ {"denom with trace hash", "ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", false}, {"base denom", "uatom", false}, + {"base denom with single '/'s", "gamm/pool/1", false}, + {"base denom with double '/'s", "gamm//pool//1", false}, + {"non-ibc prefix with hash", "notibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", false}, {"empty denom", "", true}, - {"invalid prefixed denom", "transfer/channelToA/uatom", true}, {"denom 'ibc'", "ibc", true}, {"denom 'ibc/'", "ibc/", true}, - {"invald prefix", "notibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", true}, {"invald hash", "ibc/!@#$!@#", true}, } diff --git a/modules/core/02-client/keeper/grpc_query_test.go b/modules/core/02-client/keeper/grpc_query_test.go index f4fa3c59322..5e393c33a97 100644 --- a/modules/core/02-client/keeper/grpc_query_test.go +++ b/modules/core/02-client/keeper/grpc_query_test.go @@ -452,7 +452,7 @@ func (suite *KeeperTestSuite) TestQueryClientStatus() { ClientId: path.EndpointA.ClientID, } }, - true, exported.Unknown.String(), + true, exported.Expired.String(), }, { "Frozen client status", diff --git a/modules/core/04-channel/keeper/keeper.go b/modules/core/04-channel/keeper/keeper.go index d1ea53ac180..65378039ad9 100644 --- a/modules/core/04-channel/keeper/keeper.go +++ b/modules/core/04-channel/keeper/keeper.go @@ -403,7 +403,7 @@ func (k Keeper) GetChannelClientState(ctx sdk.Context, portID, channelID string) return connection.ClientId, clientState, nil } -// GetConnection wraps the conenction keeper's GetConnection function. +// GetConnection wraps the connection keeper's GetConnection function. func (k Keeper) GetConnection(ctx sdk.Context, connectionID string) (exported.ConnectionI, error) { connection, found := k.connectionKeeper.GetConnection(ctx, connectionID) if !found { diff --git a/modules/core/04-channel/keeper/packet.go b/modules/core/04-channel/keeper/packet.go index 5879c9ecb08..b54926b85e9 100644 --- a/modules/core/04-channel/keeper/packet.go +++ b/modules/core/04-channel/keeper/packet.go @@ -494,7 +494,7 @@ func (k Keeper) AcknowledgePacket( // log that a packet has been acknowledged k.Logger(ctx).Info( "packet acknowledged", - "sequence", packet.GetSequence, + "sequence", packet.GetSequence(), "src_port", packet.GetSourcePort(), "src_channel", packet.GetSourceChannel(), "dst_port", packet.GetDestPort(), diff --git a/modules/core/04-channel/types/tx.pb.go b/modules/core/04-channel/types/tx.pb.go index 6fcc1a44276..e497cf802b1 100644 --- a/modules/core/04-channel/types/tx.pb.go +++ b/modules/core/04-channel/types/tx.pb.go @@ -29,6 +29,38 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package +// ResponseResultType defines the possible outcomes of the execution of a message +type ResponseResultType int32 + +const ( + // Default zero value enumeration + UNSPECIFIED ResponseResultType = 0 + // The message did not call the IBC application callbacks (because, for example, the packet had already been relayed) + NOOP ResponseResultType = 1 + // The message was executed successfully + SUCCESS ResponseResultType = 2 +) + +var ResponseResultType_name = map[int32]string{ + 0: "RESPONSE_RESULT_UNSPECIFIED", + 1: "RESPONSE_RESULT_NOOP", + 2: "RESPONSE_RESULT_SUCCESS", +} + +var ResponseResultType_value = map[string]int32{ + "RESPONSE_RESULT_UNSPECIFIED": 0, + "RESPONSE_RESULT_NOOP": 1, + "RESPONSE_RESULT_SUCCESS": 2, +} + +func (x ResponseResultType) String() string { + return proto.EnumName(ResponseResultType_name, int32(x)) +} + +func (ResponseResultType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_bc4637e0ac3fc7b7, []int{0} +} + // MsgChannelOpenInit defines an sdk.Msg to initialize a channel handshake. It // is called by a relayer on Chain A. type MsgChannelOpenInit struct { @@ -566,6 +598,7 @@ var xxx_messageInfo_MsgRecvPacket proto.InternalMessageInfo // MsgRecvPacketResponse defines the Msg/RecvPacket response type. type MsgRecvPacketResponse struct { + Result ResponseResultType `protobuf:"varint,1,opt,name=result,proto3,enum=ibc.core.channel.v1.ResponseResultType" json:"result,omitempty"` } func (m *MsgRecvPacketResponse) Reset() { *m = MsgRecvPacketResponse{} } @@ -645,6 +678,7 @@ var xxx_messageInfo_MsgTimeout proto.InternalMessageInfo // MsgTimeoutResponse defines the Msg/Timeout response type. type MsgTimeoutResponse struct { + Result ResponseResultType `protobuf:"varint,1,opt,name=result,proto3,enum=ibc.core.channel.v1.ResponseResultType" json:"result,omitempty"` } func (m *MsgTimeoutResponse) Reset() { *m = MsgTimeoutResponse{} } @@ -725,6 +759,7 @@ var xxx_messageInfo_MsgTimeoutOnClose proto.InternalMessageInfo // MsgTimeoutOnCloseResponse defines the Msg/TimeoutOnClose response type. type MsgTimeoutOnCloseResponse struct { + Result ResponseResultType `protobuf:"varint,1,opt,name=result,proto3,enum=ibc.core.channel.v1.ResponseResultType" json:"result,omitempty"` } func (m *MsgTimeoutOnCloseResponse) Reset() { *m = MsgTimeoutOnCloseResponse{} } @@ -804,6 +839,7 @@ var xxx_messageInfo_MsgAcknowledgement proto.InternalMessageInfo // MsgAcknowledgementResponse defines the Msg/Acknowledgement response type. type MsgAcknowledgementResponse struct { + Result ResponseResultType `protobuf:"varint,1,opt,name=result,proto3,enum=ibc.core.channel.v1.ResponseResultType" json:"result,omitempty"` } func (m *MsgAcknowledgementResponse) Reset() { *m = MsgAcknowledgementResponse{} } @@ -840,6 +876,7 @@ func (m *MsgAcknowledgementResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgAcknowledgementResponse proto.InternalMessageInfo func init() { + proto.RegisterEnum("ibc.core.channel.v1.ResponseResultType", ResponseResultType_name, ResponseResultType_value) proto.RegisterType((*MsgChannelOpenInit)(nil), "ibc.core.channel.v1.MsgChannelOpenInit") proto.RegisterType((*MsgChannelOpenInitResponse)(nil), "ibc.core.channel.v1.MsgChannelOpenInitResponse") proto.RegisterType((*MsgChannelOpenTry)(nil), "ibc.core.channel.v1.MsgChannelOpenTry") @@ -865,79 +902,87 @@ func init() { func init() { proto.RegisterFile("ibc/core/channel/v1/tx.proto", fileDescriptor_bc4637e0ac3fc7b7) } var fileDescriptor_bc4637e0ac3fc7b7 = []byte{ - // 1138 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0x4d, 0x6f, 0xdb, 0x46, - 0x13, 0xd6, 0x87, 0x2d, 0xdb, 0x63, 0xbf, 0xb1, 0x4d, 0xf9, 0x43, 0xa1, 0x6c, 0xd1, 0x2f, 0x0f, - 0x89, 0x91, 0x22, 0x62, 0x6c, 0x07, 0x28, 0x12, 0xf4, 0x62, 0x09, 0x28, 0x6a, 0x14, 0x6e, 0x0a, - 0xda, 0xed, 0xc1, 0x28, 0x20, 0x48, 0xab, 0x0d, 0x45, 0x48, 0xe2, 0xaa, 0x24, 0xa5, 0x44, 0xff, - 0xa0, 0xc7, 0x9c, 0x7b, 0x4a, 0xcf, 0x3d, 0xa4, 0x3f, 0x23, 0xc7, 0x9c, 0xda, 0xa2, 0x07, 0xa2, - 0xb0, 0x2f, 0x3d, 0xf3, 0x17, 0x14, 0x5c, 0x2e, 0x3f, 0x24, 0x91, 0x15, 0x95, 0x54, 0x6e, 0x6e, - 0xe4, 0xcc, 0xb3, 0xb3, 0xb3, 0xcf, 0x33, 0x9c, 0xdd, 0x25, 0xec, 0xa9, 0x0d, 0x24, 0x21, 0xa2, - 0x63, 0x09, 0xb5, 0xea, 0x9a, 0x86, 0x3b, 0xd2, 0xe0, 0x48, 0x32, 0x5f, 0x96, 0x7b, 0x3a, 0x31, - 0x09, 0x97, 0x57, 0x1b, 0xa8, 0xec, 0x78, 0xcb, 0xcc, 0x5b, 0x1e, 0x1c, 0xf1, 0x5b, 0x0a, 0x51, - 0x08, 0xf5, 0x4b, 0xce, 0x93, 0x0b, 0xe5, 0x85, 0x20, 0x50, 0x47, 0xc5, 0x9a, 0xe9, 0xc4, 0x71, - 0x9f, 0x18, 0xe0, 0xff, 0x51, 0x33, 0x79, 0x61, 0x29, 0x44, 0xfc, 0x29, 0x0d, 0xdc, 0xb9, 0xa1, - 0x54, 0x5d, 0xe3, 0xb3, 0x1e, 0xd6, 0xce, 0x34, 0xd5, 0xe4, 0x3e, 0x81, 0xa5, 0x1e, 0xd1, 0xcd, - 0x9a, 0xda, 0x2c, 0xa4, 0x0f, 0xd2, 0x87, 0x2b, 0x15, 0xce, 0xb6, 0x84, 0x3b, 0xc3, 0x7a, 0xb7, - 0xf3, 0x54, 0x64, 0x0e, 0x51, 0xce, 0x39, 0x4f, 0x67, 0x4d, 0xee, 0x33, 0x58, 0x62, 0x41, 0x0b, - 0x99, 0x83, 0xf4, 0xe1, 0xea, 0xf1, 0x5e, 0x39, 0x62, 0x11, 0x65, 0x36, 0x47, 0x65, 0xe1, 0xad, - 0x25, 0xa4, 0x64, 0x6f, 0x08, 0xb7, 0x03, 0x39, 0x43, 0x55, 0x34, 0xac, 0x17, 0xb2, 0xce, 0x4c, - 0x32, 0x7b, 0x7b, 0xba, 0xfc, 0xc3, 0x6b, 0x21, 0xf5, 0xd7, 0x6b, 0x21, 0x25, 0xca, 0xc0, 0x4f, - 0xa6, 0x28, 0x63, 0xa3, 0x47, 0x34, 0x03, 0x73, 0x8f, 0x01, 0x58, 0xa8, 0x20, 0xdb, 0x6d, 0xdb, - 0x12, 0x36, 0xdd, 0x6c, 0x03, 0x9f, 0x28, 0xaf, 0xb0, 0x97, 0xb3, 0xa6, 0xf8, 0x6b, 0x16, 0x36, - 0x47, 0x83, 0x5e, 0xea, 0xc3, 0xd9, 0x96, 0xfd, 0x15, 0xe4, 0x7b, 0x3a, 0x1e, 0xa8, 0xa4, 0x6f, - 0xd4, 0x42, 0x19, 0x64, 0xe8, 0xc0, 0x92, 0x6d, 0x09, 0x3c, 0x1b, 0x38, 0x09, 0x12, 0xe5, 0x4d, - 0xcf, 0x5a, 0xf5, 0x52, 0x0a, 0xd3, 0x98, 0x9d, 0x9d, 0x46, 0x19, 0xb6, 0x10, 0xe9, 0x6b, 0x26, - 0xd6, 0x7b, 0x75, 0xdd, 0x1c, 0xd6, 0x06, 0x58, 0x37, 0x54, 0xa2, 0x15, 0x16, 0x68, 0x3a, 0x82, - 0x6d, 0x09, 0x45, 0x46, 0x48, 0x04, 0x4a, 0x94, 0xf3, 0x61, 0xf3, 0xb7, 0xae, 0xd5, 0xa1, 0xb6, - 0xa7, 0x13, 0xf2, 0xbc, 0xa6, 0x6a, 0xaa, 0x59, 0x58, 0x3c, 0x48, 0x1f, 0xae, 0x85, 0xa9, 0x0d, - 0x7c, 0xa2, 0xbc, 0x42, 0x5f, 0x68, 0xed, 0x5c, 0xc1, 0x9a, 0xeb, 0x69, 0x61, 0x55, 0x69, 0x99, - 0x85, 0x1c, 0x5d, 0x0c, 0x1f, 0x5a, 0x8c, 0x5b, 0xa3, 0x83, 0xa3, 0xf2, 0x17, 0x14, 0x51, 0x29, - 0x3a, 0x4b, 0xb1, 0x2d, 0x21, 0x1f, 0x8e, 0xeb, 0x8e, 0x16, 0xe5, 0x55, 0xfa, 0xea, 0x22, 0x43, - 0xc5, 0xb2, 0x14, 0x53, 0x2c, 0x45, 0xb8, 0x3b, 0xa1, 0xab, 0x57, 0x2b, 0xe2, 0x6f, 0x13, 0xaa, - 0x9f, 0xa2, 0xf6, 0x6c, 0xaa, 0x8f, 0x96, 0x5b, 0x26, 0x59, 0xb9, 0x71, 0x57, 0xb0, 0x3b, 0xc2, - 0x7b, 0x28, 0x04, 0xad, 0xfa, 0x8a, 0x68, 0x5b, 0x42, 0x29, 0x42, 0xa0, 0x70, 0xbc, 0xed, 0xb0, - 0x27, 0xa8, 0x9b, 0x79, 0x28, 0x7f, 0x04, 0xae, 0xa0, 0x35, 0x53, 0x1f, 0x32, 0xe1, 0xb7, 0x6c, - 0x4b, 0xd8, 0x08, 0x0b, 0x64, 0xea, 0x43, 0x51, 0x5e, 0xa6, 0xcf, 0xce, 0xb7, 0xf3, 0x91, 0xc9, - 0x7e, 0x8a, 0xda, 0xbe, 0xec, 0x3f, 0x67, 0x60, 0x7b, 0xd4, 0x5b, 0x25, 0xda, 0x73, 0x55, 0xef, - 0xde, 0x86, 0xf4, 0x3e, 0x95, 0x75, 0xd4, 0xa6, 0x62, 0x47, 0x50, 0x59, 0x47, 0x6d, 0x8f, 0x4a, - 0xa7, 0x20, 0xc7, 0xa9, 0x5c, 0x98, 0x0b, 0x95, 0x8b, 0x31, 0x54, 0x0a, 0xb0, 0x1f, 0x49, 0x96, - 0x4f, 0xe7, 0x8f, 0x69, 0xc8, 0x07, 0x88, 0x6a, 0x87, 0x18, 0x78, 0xf6, 0x4d, 0xe3, 0xfd, 0xc8, - 0x9c, 0xbe, 0x59, 0xec, 0x43, 0x31, 0x22, 0x37, 0x3f, 0xf7, 0x37, 0x19, 0xd8, 0x19, 0xf3, 0xdf, - 0x62, 0x2d, 0x8c, 0x36, 0xd4, 0xec, 0x7b, 0x36, 0xd4, 0xdb, 0x2d, 0x87, 0x03, 0x28, 0x45, 0x13, - 0xe6, 0x73, 0xfa, 0x2a, 0x03, 0xff, 0x3b, 0x37, 0x14, 0x19, 0xa3, 0xc1, 0xd7, 0x75, 0xd4, 0xc6, - 0x26, 0xf7, 0x04, 0x72, 0x3d, 0xfa, 0x44, 0x99, 0x5c, 0x3d, 0x2e, 0x46, 0xee, 0x64, 0x2e, 0x98, - 0x6d, 0x64, 0x6c, 0x00, 0xf7, 0x39, 0x6c, 0xb8, 0xe9, 0x22, 0xd2, 0xed, 0xaa, 0x66, 0x17, 0x6b, - 0x26, 0xa5, 0x77, 0xad, 0x52, 0xb4, 0x2d, 0x61, 0x37, 0xbc, 0xa0, 0x00, 0x21, 0xca, 0xeb, 0xd4, - 0x54, 0xf5, 0x2d, 0x13, 0xa4, 0x65, 0xe7, 0x42, 0xda, 0x42, 0x0c, 0x69, 0xbb, 0xb4, 0xe1, 0x04, - 0x8c, 0xf8, 0x5c, 0xfd, 0x91, 0x01, 0x38, 0x37, 0x94, 0x4b, 0xb5, 0x8b, 0x49, 0xff, 0xdf, 0x21, - 0xaa, 0xaf, 0xe9, 0x18, 0x61, 0x75, 0x80, 0x9b, 0x71, 0x44, 0x05, 0x08, 0x8f, 0xa8, 0x6f, 0x7c, - 0xcb, 0x5c, 0x89, 0xfa, 0x12, 0x38, 0x0d, 0xbf, 0x34, 0x6b, 0x06, 0xfe, 0xbe, 0x8f, 0x35, 0x84, - 0x6b, 0x3a, 0x46, 0x03, 0x4a, 0xda, 0x42, 0x65, 0xdf, 0xb6, 0x84, 0xbb, 0x6e, 0x84, 0x49, 0x8c, - 0x28, 0x6f, 0x38, 0xc6, 0x0b, 0x66, 0x73, 0x88, 0x4c, 0x50, 0xaa, 0x5b, 0xf4, 0x2c, 0xcb, 0xb8, - 0x0d, 0xda, 0x95, 0xbb, 0xe9, 0x33, 0xf3, 0x33, 0x8d, 0xd6, 0xf0, 0xc7, 0xc0, 0xfc, 0xa7, 0xb0, - 0xca, 0x0a, 0xd9, 0xc9, 0x88, 0xb5, 0x83, 0x1d, 0xdb, 0x12, 0xb8, 0x91, 0x2a, 0x77, 0x9c, 0xa2, - 0xec, 0x36, 0x0e, 0x37, 0xf7, 0x79, 0x36, 0x84, 0x68, 0xc9, 0x16, 0x3f, 0x54, 0xb2, 0xdc, 0x3f, - 0xee, 0xdb, 0xa3, 0xda, 0xf8, 0xca, 0xfd, 0x92, 0xa1, 0x82, 0x9e, 0xa2, 0xb6, 0x46, 0x5e, 0x74, - 0x70, 0x53, 0xc1, 0xf4, 0xd3, 0xfe, 0x00, 0xe9, 0x0e, 0x61, 0xbd, 0x3e, 0x1a, 0xcd, 0x55, 0x4e, - 0x1e, 0x37, 0x07, 0xe2, 0x38, 0x03, 0x9b, 0x71, 0xe2, 0x50, 0xa7, 0x27, 0xce, 0xa9, 0xf3, 0xf2, - 0x1f, 0x77, 0xeb, 0x3d, 0x7a, 0x57, 0x1a, 0x63, 0xcc, 0x23, 0xf4, 0xf8, 0xcd, 0x32, 0x64, 0xcf, - 0x0d, 0x85, 0x6b, 0xc3, 0xfa, 0xf8, 0x8d, 0xef, 0x7e, 0x24, 0x89, 0x93, 0xf7, 0x2e, 0x5e, 0x4a, - 0x08, 0xf4, 0x2f, 0x68, 0x2d, 0xb8, 0x33, 0x76, 0xcd, 0xba, 0x97, 0x20, 0xc4, 0xa5, 0x3e, 0xe4, - 0xcb, 0xc9, 0x70, 0x31, 0x33, 0x39, 0x27, 0xa9, 0x24, 0x33, 0x9d, 0xa2, 0x76, 0xa2, 0x99, 0x42, - 0x27, 0x4a, 0xce, 0x04, 0x2e, 0xe2, 0x34, 0xf9, 0x20, 0x41, 0x14, 0x86, 0xe5, 0x8f, 0x93, 0x63, - 0xfd, 0x59, 0x35, 0xd8, 0x98, 0x38, 0x74, 0x1d, 0x4e, 0x89, 0xe3, 0x23, 0xf9, 0x47, 0x49, 0x91, - 0xfe, 0x7c, 0x2f, 0x20, 0x1f, 0x79, 0x50, 0x4a, 0x12, 0xc8, 0x5b, 0xe7, 0xc9, 0x0c, 0x60, 0x7f, - 0xe2, 0xef, 0x00, 0x42, 0xa7, 0x09, 0x31, 0x2e, 0x44, 0x80, 0xe1, 0x1f, 0x4c, 0xc7, 0xf8, 0xd1, - 0x2f, 0x60, 0xc9, 0xdb, 0x7f, 0x85, 0xb8, 0x61, 0x0c, 0xc0, 0xdf, 0x9f, 0x02, 0x08, 0xd7, 0xde, - 0xd8, 0x0e, 0x73, 0x6f, 0xca, 0x50, 0x86, 0x8b, 0xaf, 0xbd, 0xe8, 0xae, 0xe8, 0x7c, 0xbc, 0xe3, - 0x1d, 0x31, 0x36, 0xcb, 0x31, 0x60, 0xfc, 0xc7, 0x1b, 0xd3, 0x31, 0x2a, 0x17, 0x6f, 0xaf, 0x4b, - 0xe9, 0x77, 0xd7, 0xa5, 0xf4, 0x9f, 0xd7, 0xa5, 0xf4, 0xab, 0x9b, 0x52, 0xea, 0xdd, 0x4d, 0x29, - 0xf5, 0xfb, 0x4d, 0x29, 0x75, 0xf5, 0x44, 0x51, 0xcd, 0x56, 0xbf, 0x51, 0x46, 0xa4, 0x2b, 0x21, - 0x62, 0x74, 0x89, 0x21, 0xa9, 0x0d, 0xf4, 0x50, 0x21, 0xd2, 0xe0, 0x44, 0xea, 0x92, 0x66, 0xbf, - 0x83, 0x0d, 0xf7, 0xe7, 0xd3, 0xa3, 0xc7, 0x0f, 0xbd, 0xff, 0x4f, 0xe6, 0xb0, 0x87, 0x8d, 0x46, - 0x8e, 0xfe, 0x7b, 0x3a, 0xf9, 0x3b, 0x00, 0x00, 0xff, 0xff, 0xc5, 0x64, 0xa7, 0xfd, 0x0a, 0x13, - 0x00, 0x00, + // 1267 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0x4b, 0x6f, 0xdb, 0x46, + 0x10, 0xd6, 0xcb, 0xb2, 0x3d, 0x72, 0x6d, 0x99, 0xf2, 0x43, 0xa1, 0x62, 0x51, 0xe5, 0x21, 0x11, + 0x5c, 0x44, 0xf2, 0x23, 0x40, 0x11, 0xa3, 0x40, 0x61, 0xa9, 0x0a, 0x6a, 0xb4, 0x7e, 0x80, 0xb2, + 0x7b, 0x70, 0x8b, 0x0a, 0x12, 0xb5, 0x91, 0x09, 0x49, 0x5c, 0x95, 0xa4, 0x94, 0xe8, 0x1f, 0x04, + 0x3e, 0xe5, 0x6c, 0xc0, 0x40, 0x8a, 0x1e, 0x7b, 0x48, 0x7f, 0x46, 0x8e, 0x39, 0xb5, 0x45, 0x0f, + 0x42, 0x61, 0x5f, 0x7a, 0xd6, 0x2f, 0x28, 0xb8, 0x5c, 0x52, 0x94, 0x44, 0xc2, 0x74, 0x62, 0x3b, + 0xb9, 0xed, 0xce, 0x7c, 0x3b, 0x3b, 0xfb, 0x7d, 0xc3, 0x7d, 0x10, 0xee, 0x4b, 0x15, 0x31, 0x2b, + 0x62, 0x05, 0x65, 0xc5, 0x93, 0xb2, 0x2c, 0xa3, 0x46, 0xb6, 0xb3, 0x9e, 0xd5, 0x5e, 0x64, 0x5a, + 0x0a, 0xd6, 0x30, 0x13, 0x93, 0x2a, 0x62, 0x46, 0xf7, 0x66, 0xa8, 0x37, 0xd3, 0x59, 0x67, 0x17, + 0x6a, 0xb8, 0x86, 0x89, 0x3f, 0xab, 0xb7, 0x0c, 0x28, 0xcb, 0x0d, 0x02, 0x35, 0x24, 0x24, 0x6b, + 0x7a, 0x1c, 0xa3, 0x45, 0x01, 0x9f, 0x3b, 0xcd, 0x64, 0x86, 0x25, 0x10, 0xfe, 0x57, 0x3f, 0x30, + 0xbb, 0x6a, 0x2d, 0x6f, 0x18, 0xf7, 0x5b, 0x48, 0xde, 0x91, 0x25, 0x8d, 0xf9, 0x02, 0x26, 0x5b, + 0x58, 0xd1, 0x4a, 0x52, 0x35, 0xee, 0x4f, 0xf9, 0xd3, 0xd3, 0x39, 0xa6, 0xdf, 0xe3, 0x66, 0xbb, + 0xe5, 0x66, 0x63, 0x8b, 0xa7, 0x0e, 0x5e, 0x08, 0xeb, 0xad, 0x9d, 0x2a, 0xf3, 0x15, 0x4c, 0xd2, + 0xa0, 0xf1, 0x40, 0xca, 0x9f, 0x8e, 0x6c, 0xdc, 0xcf, 0x38, 0x2c, 0x22, 0x43, 0xe7, 0xc8, 0x85, + 0xde, 0xf6, 0x38, 0x9f, 0x60, 0x0e, 0x61, 0x96, 0x20, 0xac, 0x4a, 0x35, 0x19, 0x29, 0xf1, 0xa0, + 0x3e, 0x93, 0x40, 0x7b, 0x5b, 0x53, 0x2f, 0x5f, 0x73, 0xbe, 0xff, 0x5e, 0x73, 0x3e, 0x5e, 0x00, + 0x76, 0x3c, 0x45, 0x01, 0xa9, 0x2d, 0x2c, 0xab, 0x88, 0x79, 0x0c, 0x40, 0x43, 0x0d, 0xb2, 0x5d, + 0xec, 0xf7, 0xb8, 0x79, 0x23, 0xdb, 0x81, 0x8f, 0x17, 0xa6, 0x69, 0x67, 0xa7, 0xca, 0xff, 0x19, + 0x84, 0xf9, 0xe1, 0xa0, 0x87, 0x4a, 0xf7, 0x7a, 0xcb, 0xde, 0x83, 0x58, 0x4b, 0x41, 0x1d, 0x09, + 0xb7, 0xd5, 0x92, 0x2d, 0x83, 0x00, 0x19, 0x98, 0xec, 0xf7, 0x38, 0x96, 0x0e, 0x1c, 0x07, 0xf1, + 0xc2, 0xbc, 0x69, 0xcd, 0x9b, 0x29, 0xd9, 0x69, 0x0c, 0x5e, 0x9f, 0x46, 0x01, 0x16, 0x44, 0xdc, + 0x96, 0x35, 0xa4, 0xb4, 0xca, 0x8a, 0xd6, 0x2d, 0x75, 0x90, 0xa2, 0x4a, 0x58, 0x8e, 0x87, 0x48, + 0x3a, 0x5c, 0xbf, 0xc7, 0x25, 0x28, 0x21, 0x0e, 0x28, 0x5e, 0x88, 0xd9, 0xcd, 0x3f, 0x18, 0x56, + 0x9d, 0xda, 0x96, 0x82, 0xf1, 0xb3, 0x92, 0x24, 0x4b, 0x5a, 0x7c, 0x22, 0xe5, 0x4f, 0xcf, 0xd8, + 0xa9, 0x1d, 0xf8, 0x78, 0x61, 0x9a, 0x74, 0x48, 0xed, 0x1c, 0xc3, 0x8c, 0xe1, 0x39, 0x41, 0x52, + 0xed, 0x44, 0x8b, 0x87, 0xc9, 0x62, 0x58, 0xdb, 0x62, 0x8c, 0x1a, 0xed, 0xac, 0x67, 0xbe, 0x25, + 0x88, 0x5c, 0x42, 0x5f, 0x4a, 0xbf, 0xc7, 0xc5, 0xec, 0x71, 0x8d, 0xd1, 0xbc, 0x10, 0x21, 0x5d, + 0x03, 0x69, 0x2b, 0x96, 0x49, 0x97, 0x62, 0x49, 0xc0, 0xbd, 0x31, 0x5d, 0xcd, 0x5a, 0xe1, 0xff, + 0x1a, 0x53, 0x7d, 0x5b, 0xac, 0x5f, 0x4f, 0xf5, 0xe1, 0x72, 0x0b, 0x78, 0x2b, 0x37, 0xe6, 0x18, + 0x96, 0x87, 0x78, 0xb7, 0x85, 0x20, 0x55, 0x9f, 0xe3, 0xfb, 0x3d, 0x2e, 0xe9, 0x20, 0x90, 0x3d, + 0xde, 0xa2, 0xdd, 0x33, 0xa8, 0x9b, 0xdb, 0x50, 0x7e, 0x1d, 0x0c, 0x41, 0x4b, 0x9a, 0xd2, 0xa5, + 0xc2, 0x2f, 0xf4, 0x7b, 0x5c, 0xd4, 0x2e, 0x90, 0xa6, 0x74, 0x79, 0x61, 0x8a, 0xb4, 0xf5, 0x6f, + 0xe7, 0x13, 0x93, 0x7d, 0x5b, 0xac, 0x5b, 0xb2, 0xff, 0x1e, 0x80, 0xc5, 0x61, 0x6f, 0x1e, 0xcb, + 0xcf, 0x24, 0xa5, 0x79, 0x17, 0xd2, 0x5b, 0x54, 0x96, 0xc5, 0x3a, 0x11, 0xdb, 0x81, 0xca, 0xb2, + 0x58, 0x37, 0xa9, 0xd4, 0x0b, 0x72, 0x94, 0xca, 0xd0, 0xad, 0x50, 0x39, 0xe1, 0x42, 0x25, 0x07, + 0x2b, 0x8e, 0x64, 0x59, 0x74, 0x9e, 0xf9, 0x21, 0x36, 0x40, 0xe4, 0x1b, 0x58, 0x45, 0xd7, 0x3f, + 0x34, 0xde, 0x8f, 0xcc, 0xab, 0x0f, 0x8b, 0x15, 0x48, 0x38, 0xe4, 0x66, 0xe5, 0xfe, 0x26, 0x00, + 0x4b, 0x23, 0xfe, 0x3b, 0xac, 0x85, 0xe1, 0x0d, 0x35, 0xf8, 0x9e, 0x1b, 0xea, 0xdd, 0x96, 0x43, + 0x0a, 0x92, 0xce, 0x84, 0x59, 0x9c, 0xbe, 0x0a, 0xc0, 0x67, 0xbb, 0x6a, 0x4d, 0x40, 0x62, 0xe7, + 0xa0, 0x2c, 0xd6, 0x91, 0xc6, 0x3c, 0x81, 0x70, 0x8b, 0xb4, 0x08, 0x93, 0x91, 0x8d, 0x84, 0xe3, + 0x49, 0x66, 0x80, 0xe9, 0x41, 0x46, 0x07, 0x30, 0x4f, 0x21, 0x6a, 0xa4, 0x2b, 0xe2, 0x66, 0x53, + 0xd2, 0x9a, 0x48, 0xd6, 0x08, 0xbd, 0x33, 0xb9, 0x44, 0xbf, 0xc7, 0x2d, 0xdb, 0x17, 0x34, 0x40, + 0xf0, 0xc2, 0x1c, 0x31, 0xe5, 0x2d, 0xcb, 0x18, 0x69, 0xc1, 0x5b, 0x21, 0x2d, 0xe4, 0x42, 0xda, + 0xcf, 0x64, 0xc3, 0x19, 0x30, 0x62, 0xdd, 0x56, 0xbe, 0x86, 0xb0, 0x82, 0xd4, 0x76, 0xc3, 0x60, + 0x66, 0x76, 0xe3, 0xa1, 0x23, 0x33, 0x26, 0x5c, 0x20, 0xd0, 0xc3, 0x6e, 0x0b, 0x09, 0x74, 0xd8, + 0x56, 0x48, 0x9f, 0x83, 0xff, 0x27, 0x00, 0xb0, 0xab, 0xd6, 0x0e, 0xa5, 0x26, 0xc2, 0xed, 0x9b, + 0xe1, 0xbb, 0x2d, 0x2b, 0x48, 0x44, 0x52, 0x07, 0x55, 0xdd, 0xf8, 0x1e, 0x20, 0x4c, 0xbe, 0x8f, + 0x2c, 0xcb, 0xad, 0xf2, 0xfd, 0x1d, 0x30, 0x32, 0x7a, 0xa1, 0x95, 0x54, 0xf4, 0x4b, 0x1b, 0xc9, + 0x22, 0x2a, 0x29, 0x48, 0xec, 0x10, 0xee, 0x43, 0xb9, 0x95, 0x7e, 0x8f, 0xbb, 0x67, 0x44, 0x18, + 0xc7, 0xf0, 0x42, 0x54, 0x37, 0x16, 0xa9, 0x4d, 0xd7, 0xc3, 0x43, 0xc5, 0xff, 0x48, 0xae, 0xc4, + 0x94, 0xdb, 0x9b, 0x56, 0xee, 0xcc, 0xb8, 0x82, 0xd0, 0xe8, 0xfb, 0x32, 0xf9, 0xa2, 0x3e, 0x05, + 0x01, 0xbf, 0x84, 0x08, 0xfd, 0xac, 0xf4, 0x8c, 0xe8, 0xe6, 0xb4, 0xd4, 0xef, 0x71, 0xcc, 0xd0, + 0x37, 0xa7, 0x3b, 0x79, 0xc1, 0xd8, 0xc6, 0x8c, 0xdc, 0x6f, 0x73, 0x7b, 0x72, 0x56, 0x7e, 0xe2, + 0x43, 0x95, 0x0f, 0xbb, 0x28, 0x5f, 0x21, 0xb7, 0x88, 0x61, 0x6d, 0x6e, 0xba, 0x00, 0xfe, 0x08, + 0x90, 0xf2, 0xda, 0x16, 0xeb, 0x32, 0x7e, 0xde, 0x40, 0xd5, 0x1a, 0x22, 0xfb, 0xd5, 0x07, 0x54, + 0x40, 0x1a, 0xe6, 0xca, 0xc3, 0xd1, 0x8c, 0x02, 0x10, 0x46, 0xcd, 0x03, 0x8d, 0xf5, 0x81, 0x55, + 0x37, 0x8d, 0x89, 0xd3, 0xd4, 0x78, 0x5b, 0xef, 0x7c, 0xe4, 0x23, 0x48, 0x24, 0x0f, 0xc0, 0x11, + 0xc6, 0x6e, 0x58, 0x97, 0xd5, 0x33, 0x3f, 0x30, 0xe3, 0x20, 0x66, 0x0d, 0x12, 0x42, 0xa1, 0x78, + 0xb0, 0xbf, 0x57, 0x2c, 0x94, 0x84, 0x42, 0xf1, 0xe8, 0xfb, 0xc3, 0xd2, 0xd1, 0x5e, 0xf1, 0xa0, + 0x90, 0xdf, 0x79, 0xba, 0x53, 0xf8, 0x26, 0xea, 0x63, 0xe7, 0x4e, 0xcf, 0x53, 0x11, 0x9b, 0x89, + 0xe1, 0x61, 0x61, 0x74, 0xc4, 0xde, 0xfe, 0xfe, 0x41, 0xd4, 0xcf, 0x4e, 0x9d, 0x9e, 0xa7, 0x42, + 0x7a, 0x9b, 0x49, 0xc3, 0xf2, 0x28, 0xa6, 0x78, 0x94, 0xcf, 0x17, 0x8a, 0xc5, 0x68, 0x80, 0x8d, + 0x9c, 0x9e, 0xa7, 0x26, 0x69, 0x97, 0x0d, 0xbd, 0xfc, 0x2d, 0xe9, 0xdb, 0x78, 0x33, 0x05, 0xc1, + 0x5d, 0xb5, 0xc6, 0xd4, 0x61, 0x6e, 0xf4, 0xa9, 0xee, 0xbc, 0xdc, 0xf1, 0x07, 0x33, 0x9b, 0xf5, + 0x08, 0xb4, 0x88, 0x3d, 0x81, 0xd9, 0x91, 0xf7, 0xf1, 0x03, 0x0f, 0x21, 0x0e, 0x95, 0x2e, 0x9b, + 0xf1, 0x86, 0x73, 0x99, 0x49, 0xbf, 0x02, 0x7b, 0x99, 0x69, 0x5b, 0xac, 0x7b, 0x9a, 0xc9, 0xf6, + 0x14, 0x60, 0x34, 0x60, 0x1c, 0x9e, 0x01, 0xab, 0x1e, 0xa2, 0x50, 0x2c, 0xbb, 0xe1, 0x1d, 0x6b, + 0xcd, 0x2a, 0x43, 0x74, 0xec, 0xb6, 0x9c, 0xbe, 0x22, 0x8e, 0x85, 0x64, 0xd7, 0xbc, 0x22, 0xad, + 0xf9, 0x9e, 0x43, 0xcc, 0xf1, 0x86, 0xeb, 0x25, 0x90, 0xb9, 0xce, 0xcd, 0x6b, 0x80, 0xad, 0x89, + 0x7f, 0x02, 0xb0, 0x5d, 0x03, 0x79, 0xb7, 0x10, 0x03, 0x0c, 0xbb, 0x7a, 0x35, 0xc6, 0x8a, 0x5e, + 0x84, 0x49, 0xf3, 0xc6, 0xc3, 0xb9, 0x0d, 0xa3, 0x00, 0xf6, 0xe1, 0x15, 0x00, 0x7b, 0xed, 0x8d, + 0x1c, 0xc6, 0x0f, 0xae, 0x18, 0x4a, 0x71, 0xee, 0xb5, 0xe7, 0x72, 0x80, 0xd4, 0x61, 0x6e, 0x74, + 0xd7, 0x77, 0xcd, 0x72, 0x04, 0xe8, 0xfe, 0xf1, 0xba, 0xec, 0x8a, 0xb9, 0xe2, 0xdb, 0x8b, 0xa4, + 0xff, 0xdd, 0x45, 0xd2, 0xff, 0xef, 0x45, 0xd2, 0xff, 0xea, 0x32, 0xe9, 0x7b, 0x77, 0x99, 0xf4, + 0xfd, 0x7d, 0x99, 0xf4, 0x1d, 0x3f, 0xa9, 0x49, 0xda, 0x49, 0xbb, 0x92, 0x11, 0x71, 0x33, 0x2b, + 0x62, 0xb5, 0x89, 0xd5, 0xac, 0x54, 0x11, 0x1f, 0xd5, 0x70, 0xb6, 0xb3, 0x99, 0x6d, 0xe2, 0x6a, + 0xbb, 0x81, 0x54, 0xe3, 0xaf, 0xe1, 0xda, 0xe3, 0x47, 0xe6, 0x8f, 0x43, 0xad, 0xdb, 0x42, 0x6a, + 0x25, 0x4c, 0x7e, 0x1a, 0x6e, 0xfe, 0x1f, 0x00, 0x00, 0xff, 0xff, 0xd5, 0x99, 0xe1, 0x0c, 0xc3, + 0x14, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1954,6 +1999,11 @@ func (m *MsgRecvPacketResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.Result != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.Result)) + i-- + dAtA[i] = 0x8 + } return len(dAtA) - i, nil } @@ -2039,6 +2089,11 @@ func (m *MsgTimeoutResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.Result != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.Result)) + i-- + dAtA[i] = 0x8 + } return len(dAtA) - i, nil } @@ -2131,6 +2186,11 @@ func (m *MsgTimeoutOnCloseResponse) MarshalToSizedBuffer(dAtA []byte) (int, erro _ = i var l int _ = l + if m.Result != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.Result)) + i-- + dAtA[i] = 0x8 + } return len(dAtA) - i, nil } @@ -2218,6 +2278,11 @@ func (m *MsgAcknowledgementResponse) MarshalToSizedBuffer(dAtA []byte) (int, err _ = i var l int _ = l + if m.Result != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.Result)) + i-- + dAtA[i] = 0x8 + } return len(dAtA) - i, nil } @@ -2479,6 +2544,9 @@ func (m *MsgRecvPacketResponse) Size() (n int) { } var l int _ = l + if m.Result != 0 { + n += 1 + sovTx(uint64(m.Result)) + } return n } @@ -2512,6 +2580,9 @@ func (m *MsgTimeoutResponse) Size() (n int) { } var l int _ = l + if m.Result != 0 { + n += 1 + sovTx(uint64(m.Result)) + } return n } @@ -2549,6 +2620,9 @@ func (m *MsgTimeoutOnCloseResponse) Size() (n int) { } var l int _ = l + if m.Result != 0 { + n += 1 + sovTx(uint64(m.Result)) + } return n } @@ -2583,6 +2657,9 @@ func (m *MsgAcknowledgementResponse) Size() (n int) { } var l int _ = l + if m.Result != 0 { + n += 1 + sovTx(uint64(m.Result)) + } return n } @@ -4409,6 +4486,25 @@ func (m *MsgRecvPacketResponse) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: MsgRecvPacketResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Result", wireType) + } + m.Result = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Result |= ResponseResultType(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) @@ -4660,6 +4756,25 @@ func (m *MsgTimeoutResponse) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: MsgTimeoutResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Result", wireType) + } + m.Result = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Result |= ResponseResultType(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) @@ -4945,6 +5060,25 @@ func (m *MsgTimeoutOnCloseResponse) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: MsgTimeoutOnCloseResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Result", wireType) + } + m.Result = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Result |= ResponseResultType(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) @@ -5211,6 +5345,25 @@ func (m *MsgAcknowledgementResponse) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: MsgAcknowledgementResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Result", wireType) + } + m.Result = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Result |= ResponseResultType(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) diff --git a/modules/core/05-port/types/module.go b/modules/core/05-port/types/module.go index dea418b7250..9f754fe0a3e 100644 --- a/modules/core/05-port/types/module.go +++ b/modules/core/05-port/types/module.go @@ -51,6 +51,7 @@ type IBCModule interface { ctx sdk.Context, portID, channelID string, + counterpartyChannelID string, counterpartyVersion string, ) error diff --git a/modules/core/ante/ante.go b/modules/core/ante/ante.go index 42e392905ec..e9218ea4b94 100644 --- a/modules/core/ante/ante.go +++ b/modules/core/ante/ante.go @@ -4,22 +4,23 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - channelkeeper "github.com/cosmos/ibc-go/v3/modules/core/04-channel/keeper" channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v3/modules/core/keeper" ) type AnteDecorator struct { - k channelkeeper.Keeper + k *keeper.Keeper } -func NewAnteDecorator(k channelkeeper.Keeper) AnteDecorator { +func NewAnteDecorator(k *keeper.Keeper) AnteDecorator { return AnteDecorator{k: k} } -// AnteDecorator returns an error if a multiMsg tx only contains packet messages (Recv, Ack, Timeout) and additional update messages and all packet messages -// are redundant. If the transaction is just a single UpdateClient message, or the multimsg transaction contains some other message type, then the antedecorator returns no error -// and continues processing to ensure these transactions are included. -// This will ensure that relayers do not waste fees on multiMsg transactions when another relayer has already submitted all packets, by rejecting the tx at the mempool layer. +// AnteDecorator returns an error if a multiMsg tx only contains packet messages (Recv, Ack, Timeout) and additional update messages +// and all packet messages are redundant. If the transaction is just a single UpdateClient message, or the multimsg transaction +// contains some other message type, then the antedecorator returns no error and continues processing to ensure these transactions +// are included. This will ensure that relayers do not waste fees on multiMsg transactions when another relayer has already submitted +// all packets, by rejecting the tx at the mempool layer. func (ad AnteDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { // do not run redundancy check on DeliverTx or simulate if (ctx.IsCheckTx() || ctx.IsReCheckTx()) && !simulate { @@ -29,31 +30,50 @@ func (ad AnteDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, ne for _, m := range tx.GetMsgs() { switch msg := m.(type) { case *channeltypes.MsgRecvPacket: - if _, found := ad.k.GetPacketReceipt(ctx, msg.Packet.GetDestPort(), msg.Packet.GetDestChannel(), msg.Packet.GetSequence()); found { + response, err := ad.k.RecvPacket(sdk.WrapSDKContext(ctx), msg) + if err != nil { + return ctx, err + } + if response.Result == channeltypes.NOOP { redundancies += 1 } packetMsgs += 1 case *channeltypes.MsgAcknowledgement: - if commitment := ad.k.GetPacketCommitment(ctx, msg.Packet.GetSourcePort(), msg.Packet.GetSourceChannel(), msg.Packet.GetSequence()); len(commitment) == 0 { + response, err := ad.k.Acknowledgement(sdk.WrapSDKContext(ctx), msg) + if err != nil { + return ctx, err + } + if response.Result == channeltypes.NOOP { redundancies += 1 } packetMsgs += 1 case *channeltypes.MsgTimeout: - if commitment := ad.k.GetPacketCommitment(ctx, msg.Packet.GetSourcePort(), msg.Packet.GetSourceChannel(), msg.Packet.GetSequence()); len(commitment) == 0 { + response, err := ad.k.Timeout(sdk.WrapSDKContext(ctx), msg) + if err != nil { + return ctx, err + } + if response.Result == channeltypes.NOOP { redundancies += 1 } packetMsgs += 1 case *channeltypes.MsgTimeoutOnClose: - if commitment := ad.k.GetPacketCommitment(ctx, msg.Packet.GetSourcePort(), msg.Packet.GetSourceChannel(), msg.Packet.GetSequence()); len(commitment) == 0 { + response, err := ad.k.TimeoutOnClose(sdk.WrapSDKContext(ctx), msg) + if err != nil { + return ctx, err + } + if response.Result == channeltypes.NOOP { redundancies += 1 } packetMsgs += 1 case *clienttypes.MsgUpdateClient: - // do nothing here, as we want to avoid updating clients if it is batched with only redundant messages + _, err := ad.k.UpdateClient(sdk.WrapSDKContext(ctx), msg) + if err != nil { + return ctx, err + } default: // if the multiMsg tx has a msg that is not a packet msg or update msg, then we will not return error @@ -61,7 +81,6 @@ func (ad AnteDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, ne // even if they get batched with redundant packet messages. return next(ctx, tx, simulate) } - } // only return error if all packet messages are redundant diff --git a/modules/core/ante/ante_test.go b/modules/core/ante/ante_test.go index 0336e07a3ae..c04f6483f74 100644 --- a/modules/core/ante/ante_test.go +++ b/modules/core/ante/ante_test.go @@ -4,13 +4,15 @@ import ( "testing" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/ante" + "github.com/cosmos/ibc-go/v3/modules/core/exported" ibctesting "github.com/cosmos/ibc-go/v3/testing" - "github.com/cosmos/ibc-go/v3/testing/mock" ) type AnteTestSuite struct { @@ -42,6 +44,133 @@ func TestAnteTestSuite(t *testing.T) { suite.Run(t, new(AnteTestSuite)) } +// createRecvPacketMessage creates a RecvPacket message for a packet sent from chain A to chain B. +func (suite *AnteTestSuite) createRecvPacketMessage(sequenceNumber uint64, isRedundant bool) sdk.Msg { + packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequenceNumber, + suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, + suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, + clienttypes.NewHeight(1, 0), 0) + + err := suite.path.EndpointA.SendPacket(packet) + suite.Require().NoError(err) + + if isRedundant { + err = suite.path.EndpointB.RecvPacket(packet) + suite.Require().NoError(err) + } + + err = suite.path.EndpointB.UpdateClient() + suite.Require().NoError(err) + + packetKey := host.PacketCommitmentKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + proof, proofHeight := suite.chainA.QueryProof(packetKey) + + return channeltypes.NewMsgRecvPacket(packet, proof, proofHeight, suite.path.EndpointA.Chain.SenderAccount.GetAddress().String()) +} + +// createAcknowledgementMessage creates an Acknowledgement message for a packet sent from chain B to chain A. +func (suite *AnteTestSuite) createAcknowledgementMessage(sequenceNumber uint64, isRedundant bool) sdk.Msg { + packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequenceNumber, + suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, + suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, + clienttypes.NewHeight(1, 0), 0) + + err := suite.path.EndpointB.SendPacket(packet) + suite.Require().NoError(err) + err = suite.path.EndpointA.RecvPacket(packet) + suite.Require().NoError(err) + + if isRedundant { + err = suite.path.EndpointB.AcknowledgePacket(packet, ibctesting.MockAcknowledgement) + suite.Require().NoError(err) + } + + packetKey := host.PacketAcknowledgementKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + proof, proofHeight := suite.chainA.QueryProof(packetKey) + + return channeltypes.NewMsgAcknowledgement(packet, ibctesting.MockAcknowledgement, proof, proofHeight, suite.path.EndpointA.Chain.SenderAccount.GetAddress().String()) +} + +// createTimeoutMessage creates an Timeout message for a packet sent from chain B to chain A. +func (suite *AnteTestSuite) createTimeoutMessage(sequenceNumber uint64, isRedundant bool) sdk.Msg { + height := suite.chainA.LastHeader.GetHeight() + timeoutHeight := clienttypes.NewHeight(height.GetRevisionNumber(), height.GetRevisionHeight()+1) + packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequenceNumber, + suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, + suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, + timeoutHeight, 0) + + err := suite.path.EndpointB.SendPacket(packet) + suite.Require().NoError(err) + + suite.coordinator.CommitNBlocks(suite.chainA, 3) + + err = suite.path.EndpointB.UpdateClient() + suite.Require().NoError(err) + + if isRedundant { + err = suite.path.EndpointB.TimeoutPacket(packet) + suite.Require().NoError(err) + } + + packetKey := host.PacketReceiptKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + proof, proofHeight := suite.chainA.QueryProof(packetKey) + + return channeltypes.NewMsgTimeout(packet, sequenceNumber, proof, proofHeight, suite.path.EndpointA.Chain.SenderAccount.GetAddress().String()) +} + +// createTimeoutOnCloseMessage creates an TimeoutOnClose message for a packet sent from chain B to chain A. +func (suite *AnteTestSuite) createTimeoutOnCloseMessage(sequenceNumber uint64, isRedundant bool) sdk.Msg { + height := suite.chainA.LastHeader.GetHeight() + timeoutHeight := clienttypes.NewHeight(height.GetRevisionNumber(), height.GetRevisionHeight()+1) + packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequenceNumber, + suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, + suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, + timeoutHeight, 0) + + err := suite.path.EndpointB.SendPacket(packet) + suite.Require().NoError(err) + err = suite.path.EndpointA.SetChannelClosed() + suite.Require().NoError(err) + + if isRedundant { + err = suite.path.EndpointB.TimeoutOnClose(packet) + suite.Require().NoError(err) + } + + packetKey := host.PacketReceiptKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + proof, proofHeight := suite.chainA.QueryProof(packetKey) + + channelKey := host.ChannelKey(packet.GetDestPort(), packet.GetDestChannel()) + proofClosed, _ := suite.chainA.QueryProof(channelKey) + + return channeltypes.NewMsgTimeoutOnClose(packet, 1, proof, proofClosed, proofHeight, suite.path.EndpointA.Chain.SenderAccount.GetAddress().String()) +} + +func (suite *AnteTestSuite) createUpdateClientMessage() sdk.Msg { + endpoint := suite.path.EndpointB + + // ensure counterparty has committed state + endpoint.Chain.Coordinator.CommitBlock(endpoint.Counterparty.Chain) + + var header exported.Header + + switch endpoint.ClientConfig.GetClientType() { + case exported.Tendermint: + header, _ = endpoint.Chain.ConstructUpdateTMClientHeader(endpoint.Counterparty.Chain, endpoint.ClientID) + + default: + } + + msg, err := clienttypes.NewMsgUpdateClient( + endpoint.ClientID, header, + endpoint.Chain.SenderAccount.GetAddress().String(), + ) + require.NoError(endpoint.Chain.T, err) + + return msg +} + func (suite *AnteTestSuite) TestAnteDecorator() { testCases := []struct { name string @@ -49,404 +178,276 @@ func (suite *AnteTestSuite) TestAnteDecorator() { expPass bool }{ { - "success on single msg", + "success on one new RecvPacket message", func(suite *AnteTestSuite) []sdk.Msg { - packet := channeltypes.NewPacket([]byte(mock.MockPacketData), 1, - suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, - suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, - clienttypes.NewHeight(1, 0), 0) - - return []sdk.Msg{channeltypes.NewMsgRecvPacket(packet, []byte("proof"), clienttypes.NewHeight(0, 1), "signer")} + // the RecvPacket message has not been submitted to the chain yet, so it will succeed + return []sdk.Msg{suite.createRecvPacketMessage(1, false)} }, true, }, { - "success on multiple msgs", + "success on one new Acknowledgement message", func(suite *AnteTestSuite) []sdk.Msg { - var msgs []sdk.Msg - - for i := 1; i <= 5; i++ { - packet := channeltypes.NewPacket([]byte(mock.MockPacketData), uint64(i), - suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, - suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, - clienttypes.NewHeight(1, 0), 0) - - msgs = append(msgs, channeltypes.NewMsgRecvPacket(packet, []byte("proof"), clienttypes.NewHeight(0, 1), "signer")) - } - return msgs + // the Acknowledgement message has not been submitted to the chain yet, so it will succeed + return []sdk.Msg{suite.createAcknowledgementMessage(1, false)} }, true, }, { - "success on multiple msgs: 1 fresh recv packet", + "success on one new Timeout message", func(suite *AnteTestSuite) []sdk.Msg { - var msgs []sdk.Msg - - for i := 1; i <= 5; i++ { - packet := channeltypes.NewPacket([]byte(mock.MockPacketData), uint64(i), - suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, - suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, - clienttypes.NewHeight(1, 0), 0) - - err := suite.path.EndpointA.SendPacket(packet) - suite.Require().NoError(err) - - // receive all sequences except packet 3 - if i != 3 { - err = suite.path.EndpointB.RecvPacket(packet) - suite.Require().NoError(err) - } - - msgs = append(msgs, channeltypes.NewMsgRecvPacket(packet, []byte("proof"), clienttypes.NewHeight(0, 1), "signer")) - } - - return msgs + // the Timeout message has not been submitted to the chain yet, so it will succeed + return []sdk.Msg{suite.createTimeoutMessage(1, false)} }, true, }, { - "success on multiple mixed msgs", + "success on one new TimeoutOnClose message", + func(suite *AnteTestSuite) []sdk.Msg { + // the TimeoutOnClose message has not been submitted to the chain yet, so it will succeed + return []sdk.Msg{suite.createTimeoutOnCloseMessage(uint64(1), false)} + }, + true, + }, + { + "success on three new messages of each type", func(suite *AnteTestSuite) []sdk.Msg { var msgs []sdk.Msg + // none of the messages of each type has been submitted to the chain yet, + // the first message is succeed and the next two of each type will be rejected + // because they are redundant. + + // from A to B for i := 1; i <= 3; i++ { - packet := channeltypes.NewPacket([]byte(mock.MockPacketData), uint64(i), - suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, - suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, - clienttypes.NewHeight(1, 0), 0) - err := suite.path.EndpointA.SendPacket(packet) - suite.Require().NoError(err) - - msgs = append(msgs, channeltypes.NewMsgRecvPacket(packet, []byte("proof"), clienttypes.NewHeight(0, 1), "signer")) - } - for i := 1; i <= 3; i++ { - packet := channeltypes.NewPacket([]byte(mock.MockPacketData), uint64(i), - suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, - suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, - clienttypes.NewHeight(1, 0), 0) - err := suite.path.EndpointB.SendPacket(packet) - suite.Require().NoError(err) - - msgs = append(msgs, channeltypes.NewMsgAcknowledgement(packet, []byte("ack"), []byte("proof"), clienttypes.NewHeight(0, 1), "signer")) + msgs = append(msgs, suite.createRecvPacketMessage(uint64(i), false)) } - for i := 4; i <= 6; i++ { - packet := channeltypes.NewPacket([]byte(mock.MockPacketData), uint64(i), - suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, - suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, - clienttypes.NewHeight(1, 0), 0) - err := suite.path.EndpointB.SendPacket(packet) - suite.Require().NoError(err) - - msgs = append(msgs, channeltypes.NewMsgTimeout(packet, uint64(i), []byte("proof"), clienttypes.NewHeight(0, 1), "signer")) + + // from B to A + for i := 1; i <= 9; i++ { + switch { + case i >= 1 && i <= 3: + msgs = append(msgs, suite.createAcknowledgementMessage(uint64(i), false)) + case i >= 4 && i <= 6: + msgs = append(msgs, suite.createTimeoutMessage(uint64(i), false)) + case i >= 7 && i <= 9: + msgs = append(msgs, suite.createTimeoutOnCloseMessage(uint64(i), false)) + } } return msgs }, true, }, { - "success on multiple mixed msgs: 1 fresh packet of each type", + "success on three redundant messages of RecvPacket, Acknowledgement and TimeoutOnClose, and one new Timeout message", func(suite *AnteTestSuite) []sdk.Msg { var msgs []sdk.Msg - for i := 1; i <= 3; i++ { - packet := channeltypes.NewPacket([]byte(mock.MockPacketData), uint64(i), - suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, - suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, - clienttypes.NewHeight(1, 0), 0) - err := suite.path.EndpointA.SendPacket(packet) - suite.Require().NoError(err) - - // receive all sequences except packet 3 - if i != 3 { - - err := suite.path.EndpointB.RecvPacket(packet) - suite.Require().NoError(err) - } + // we pass three messages of RecvPacket, Acknowledgement and TimeoutOnClose that + // are all redundant (i.e. those messages have already been submitted and + // processed by the chain). But these messages will not be rejected because the + // Timeout message is new. - msgs = append(msgs, channeltypes.NewMsgRecvPacket(packet, []byte("proof"), clienttypes.NewHeight(0, 1), "signer")) - } + // from A to B for i := 1; i <= 3; i++ { - packet := channeltypes.NewPacket([]byte(mock.MockPacketData), uint64(i), - suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, - suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, - clienttypes.NewHeight(1, 0), 0) - err := suite.path.EndpointB.SendPacket(packet) - suite.Require().NoError(err) - - // receive all acks except ack 2 - if i != 2 { - err = suite.path.EndpointA.RecvPacket(packet) - suite.Require().NoError(err) - err = suite.path.EndpointB.AcknowledgePacket(packet, mock.MockAcknowledgement.Acknowledgement()) - suite.Require().NoError(err) - } - - msgs = append(msgs, channeltypes.NewMsgAcknowledgement(packet, []byte("ack"), []byte("proof"), clienttypes.NewHeight(0, 1), "signer")) + msgs = append(msgs, suite.createRecvPacketMessage(uint64(i), true)) } - for i := 4; i <= 6; i++ { - height := suite.chainA.LastHeader.GetHeight() - timeoutHeight := clienttypes.NewHeight(height.GetRevisionNumber(), height.GetRevisionHeight()+1) - packet := channeltypes.NewPacket([]byte(mock.MockPacketData), uint64(i), - suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, - suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, - timeoutHeight, 0) - err := suite.path.EndpointB.SendPacket(packet) - suite.Require().NoError(err) - - // timeout packet - suite.coordinator.CommitNBlocks(suite.chainA, 3) - - // timeout packets except sequence 5 - if i != 5 { - suite.path.EndpointB.UpdateClient() - err = suite.path.EndpointB.TimeoutPacket(packet) - suite.Require().NoError(err) - } - msgs = append(msgs, channeltypes.NewMsgTimeout(packet, uint64(i), []byte("proof"), clienttypes.NewHeight(0, 1), "signer")) + // from B to A + for i := 1; i <= 7; i++ { + switch { + case i >= 1 && i <= 3: + msgs = append(msgs, suite.createAcknowledgementMessage(uint64(i), true)) + case i == 4: + msgs = append(msgs, suite.createTimeoutMessage(uint64(i), false)) + case i >= 5 && i <= 7: + msgs = append(msgs, suite.createTimeoutOnCloseMessage(uint64(i), true)) + } } return msgs }, true, }, { - "success on multiple mixed msgs: only 1 fresh msg in total", + "success on one new message and two redundant messages of each type", func(suite *AnteTestSuite) []sdk.Msg { var msgs []sdk.Msg - for i := 1; i <= 3; i++ { - packet := channeltypes.NewPacket([]byte(mock.MockPacketData), uint64(i), - suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, - suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, - clienttypes.NewHeight(1, 0), 0) - - // receive all packets - suite.path.EndpointA.SendPacket(packet) - suite.path.EndpointB.RecvPacket(packet) + // For each type there is a new message and two messages that are redundant + // (i.e. they have been already submitted and processed by the chain). But all + // the redundant messages will not be rejected because there is a new message + // of each type. - msgs = append(msgs, channeltypes.NewMsgRecvPacket(packet, []byte("proof"), clienttypes.NewHeight(0, 1), "signer")) - } + // from A to B for i := 1; i <= 3; i++ { - packet := channeltypes.NewPacket([]byte(mock.MockPacketData), uint64(i), - suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, - suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, - clienttypes.NewHeight(1, 0), 0) - - // receive all acks - suite.path.EndpointB.SendPacket(packet) - suite.path.EndpointA.RecvPacket(packet) - suite.path.EndpointB.AcknowledgePacket(packet, mock.MockAcknowledgement.Acknowledgement()) - - msgs = append(msgs, channeltypes.NewMsgAcknowledgement(packet, []byte("ack"), []byte("proof"), clienttypes.NewHeight(0, 1), "signer")) + msgs = append(msgs, suite.createRecvPacketMessage(uint64(i), i != 2)) } - for i := 4; i < 5; i++ { - height := suite.chainA.LastHeader.GetHeight() - timeoutHeight := clienttypes.NewHeight(height.GetRevisionNumber(), height.GetRevisionHeight()+1) - packet := channeltypes.NewPacket([]byte(mock.MockPacketData), uint64(i), - suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, - suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, - timeoutHeight, 0) - - // do not timeout packet, timeout msg is fresh - suite.path.EndpointB.SendPacket(packet) - - msgs = append(msgs, channeltypes.NewMsgTimeout(packet, uint64(i), []byte("proof"), clienttypes.NewHeight(0, 1), "signer")) + + // from B to A + for i := 1; i <= 9; i++ { + switch { + case i >= 1 && i <= 3: + msgs = append(msgs, suite.createAcknowledgementMessage(uint64(i), i != 2)) + case i >= 4 && i <= 6: + msgs = append(msgs, suite.createTimeoutMessage(uint64(i), i != 5)) + case i >= 7 && i <= 9: + msgs = append(msgs, suite.createTimeoutOnCloseMessage(uint64(i), i != 8)) + } } return msgs }, true, }, { - "success on single update client msg", + "success on one new UpdateClient message", func(suite *AnteTestSuite) []sdk.Msg { - return []sdk.Msg{&clienttypes.MsgUpdateClient{}} + return []sdk.Msg{suite.createUpdateClientMessage()} }, true, }, { - "success on multiple update clients", + "success on three new UpdateClient messages", func(suite *AnteTestSuite) []sdk.Msg { - return []sdk.Msg{&clienttypes.MsgUpdateClient{}, &clienttypes.MsgUpdateClient{}, &clienttypes.MsgUpdateClient{}} + return []sdk.Msg{suite.createUpdateClientMessage(), suite.createUpdateClientMessage(), suite.createUpdateClientMessage()} }, true, }, { - "success on multiple update clients and fresh packet message", + "success on three new Updateclient messages and one new RecvPacket message", func(suite *AnteTestSuite) []sdk.Msg { - msgs := []sdk.Msg{&clienttypes.MsgUpdateClient{}, &clienttypes.MsgUpdateClient{}, &clienttypes.MsgUpdateClient{}} - - packet := channeltypes.NewPacket([]byte(mock.MockPacketData), 1, - suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, - suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, - clienttypes.NewHeight(1, 0), 0) - - return append(msgs, channeltypes.NewMsgRecvPacket(packet, []byte("proof"), clienttypes.NewHeight(0, 1), "signer")) + return []sdk.Msg{ + suite.createUpdateClientMessage(), + suite.createUpdateClientMessage(), + suite.createUpdateClientMessage(), + suite.createRecvPacketMessage(uint64(1), false), + } }, true, }, { - "success of tx with different msg type even if all packet messages are redundant", + "success on three redundant RecvPacket messages and one SubmitMisbehaviour message", func(suite *AnteTestSuite) []sdk.Msg { - msgs := []sdk.Msg{&clienttypes.MsgUpdateClient{}} + msgs := []sdk.Msg{suite.createUpdateClientMessage()} for i := 1; i <= 3; i++ { - packet := channeltypes.NewPacket([]byte(mock.MockPacketData), uint64(i), - suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, - suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, - clienttypes.NewHeight(1, 0), 0) - - // receive all packets - suite.path.EndpointA.SendPacket(packet) - suite.path.EndpointB.RecvPacket(packet) - - msgs = append(msgs, channeltypes.NewMsgRecvPacket(packet, []byte("proof"), clienttypes.NewHeight(0, 1), "signer")) - } - for i := 1; i <= 3; i++ { - packet := channeltypes.NewPacket([]byte(mock.MockPacketData), uint64(i), - suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, - suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, - clienttypes.NewHeight(1, 0), 0) - - // receive all acks - suite.path.EndpointB.SendPacket(packet) - suite.path.EndpointA.RecvPacket(packet) - suite.path.EndpointB.AcknowledgePacket(packet, mock.MockAcknowledgement.Acknowledgement()) - - msgs = append(msgs, channeltypes.NewMsgAcknowledgement(packet, []byte("ack"), []byte("proof"), clienttypes.NewHeight(0, 1), "signer")) - } - for i := 4; i < 6; i++ { - height := suite.chainA.LastHeader.GetHeight() - timeoutHeight := clienttypes.NewHeight(height.GetRevisionNumber(), height.GetRevisionHeight()+1) - packet := channeltypes.NewPacket([]byte(mock.MockPacketData), uint64(i), - suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, - suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, - timeoutHeight, 0) - - err := suite.path.EndpointB.SendPacket(packet) - suite.Require().NoError(err) - - // timeout packet - suite.coordinator.CommitNBlocks(suite.chainA, 3) - - suite.path.EndpointB.UpdateClient() - suite.path.EndpointB.TimeoutPacket(packet) - - msgs = append(msgs, channeltypes.NewMsgTimeoutOnClose(packet, uint64(i), []byte("proof"), []byte("channelProof"), clienttypes.NewHeight(0, 1), "signer")) + msgs = append(msgs, suite.createRecvPacketMessage(uint64(i), true)) } // append non packet and update message to msgs to ensure multimsg tx should pass msgs = append(msgs, &clienttypes.MsgSubmitMisbehaviour{}) - return msgs }, true, }, { - "no success on multiple mixed message: all are redundant", + "no success on one redundant RecvPacket message", + func(suite *AnteTestSuite) []sdk.Msg { + return []sdk.Msg{suite.createRecvPacketMessage(uint64(1), true)} + }, + false, + }, + { + "no success on three redundant messages of each type", func(suite *AnteTestSuite) []sdk.Msg { var msgs []sdk.Msg + // from A to B for i := 1; i <= 3; i++ { - packet := channeltypes.NewPacket([]byte(mock.MockPacketData), uint64(i), - suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, - suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, - clienttypes.NewHeight(1, 0), 0) - - // receive all packets - suite.path.EndpointA.SendPacket(packet) - suite.path.EndpointB.RecvPacket(packet) - - msgs = append(msgs, channeltypes.NewMsgRecvPacket(packet, []byte("proof"), clienttypes.NewHeight(0, 1), "signer")) + msgs = append(msgs, suite.createRecvPacketMessage(uint64(i), true)) } - for i := 1; i <= 3; i++ { - packet := channeltypes.NewPacket([]byte(mock.MockPacketData), uint64(i), - suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, - suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, - clienttypes.NewHeight(1, 0), 0) - // receive all acks - suite.path.EndpointB.SendPacket(packet) - suite.path.EndpointA.RecvPacket(packet) - suite.path.EndpointB.AcknowledgePacket(packet, mock.MockAcknowledgement.Acknowledgement()) - - msgs = append(msgs, channeltypes.NewMsgAcknowledgement(packet, []byte("ack"), []byte("proof"), clienttypes.NewHeight(0, 1), "signer")) - } - for i := 4; i < 6; i++ { - height := suite.chainA.LastHeader.GetHeight() - timeoutHeight := clienttypes.NewHeight(height.GetRevisionNumber(), height.GetRevisionHeight()+1) - packet := channeltypes.NewPacket([]byte(mock.MockPacketData), uint64(i), - suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, - suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, - timeoutHeight, 0) - - err := suite.path.EndpointB.SendPacket(packet) - suite.Require().NoError(err) - - // timeout packet - suite.coordinator.CommitNBlocks(suite.chainA, 3) - - suite.path.EndpointB.UpdateClient() - suite.path.EndpointB.TimeoutPacket(packet) - - msgs = append(msgs, channeltypes.NewMsgTimeoutOnClose(packet, uint64(i), []byte("proof"), []byte("channelProof"), clienttypes.NewHeight(0, 1), "signer")) + // from B to A + for i := 1; i <= 9; i++ { + switch { + case i >= 1 && i <= 3: + msgs = append(msgs, suite.createAcknowledgementMessage(uint64(i), true)) + case i >= 4 && i <= 6: + msgs = append(msgs, suite.createTimeoutMessage(uint64(i), true)) + case i >= 7 && i <= 9: + msgs = append(msgs, suite.createTimeoutOnCloseMessage(uint64(i), true)) + } } return msgs }, false, }, { - "no success if msgs contain update clients and redundant packet messages", + "no success on one new UpdateClient message and three redundant RecvPacket messages", func(suite *AnteTestSuite) []sdk.Msg { - msgs := []sdk.Msg{&clienttypes.MsgUpdateClient{}, &clienttypes.MsgUpdateClient{}, &clienttypes.MsgUpdateClient{}} + msgs := []sdk.Msg{&clienttypes.MsgUpdateClient{}} for i := 1; i <= 3; i++ { - packet := channeltypes.NewPacket([]byte(mock.MockPacketData), uint64(i), - suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, - suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, - clienttypes.NewHeight(1, 0), 0) - - // receive all packets - suite.path.EndpointA.SendPacket(packet) - suite.path.EndpointB.RecvPacket(packet) - - msgs = append(msgs, channeltypes.NewMsgRecvPacket(packet, []byte("proof"), clienttypes.NewHeight(0, 1), "signer")) + msgs = append(msgs, suite.createRecvPacketMessage(uint64(i), true)) } - for i := 1; i <= 3; i++ { - packet := channeltypes.NewPacket([]byte(mock.MockPacketData), uint64(i), - suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, - suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, - clienttypes.NewHeight(1, 0), 0) - // receive all acks - suite.path.EndpointB.SendPacket(packet) - suite.path.EndpointA.RecvPacket(packet) - suite.path.EndpointB.AcknowledgePacket(packet, mock.MockAcknowledgement.Acknowledgement()) + return msgs + }, + false, + }, + { + "no success on three new UpdateClient messages and three redundant messages of each type", + func(suite *AnteTestSuite) []sdk.Msg { + msgs := []sdk.Msg{suite.createUpdateClientMessage(), suite.createUpdateClientMessage(), suite.createUpdateClientMessage()} - msgs = append(msgs, channeltypes.NewMsgAcknowledgement(packet, []byte("ack"), []byte("proof"), clienttypes.NewHeight(0, 1), "signer")) + // from A to B + for i := 1; i <= 3; i++ { + msgs = append(msgs, suite.createRecvPacketMessage(uint64(i), true)) } - for i := 4; i < 6; i++ { - height := suite.chainA.LastHeader.GetHeight() - timeoutHeight := clienttypes.NewHeight(height.GetRevisionNumber(), height.GetRevisionHeight()+1) - packet := channeltypes.NewPacket([]byte(mock.MockPacketData), uint64(i), - suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, - suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, - timeoutHeight, 0) - - err := suite.path.EndpointB.SendPacket(packet) - suite.Require().NoError(err) - - // timeout packet - suite.coordinator.CommitNBlocks(suite.chainA, 3) - suite.path.EndpointB.UpdateClient() - suite.path.EndpointB.TimeoutPacket(packet) - - msgs = append(msgs, channeltypes.NewMsgTimeoutOnClose(packet, uint64(i), []byte("proof"), []byte("channelProof"), clienttypes.NewHeight(0, 1), "signer")) + // from B to A + for i := 1; i <= 9; i++ { + switch { + case i >= 1 && i <= 3: + msgs = append(msgs, suite.createAcknowledgementMessage(uint64(i), true)) + case i >= 4 && i <= 6: + msgs = append(msgs, suite.createTimeoutMessage(uint64(i), true)) + case i >= 7 && i <= 9: + msgs = append(msgs, suite.createTimeoutOnCloseMessage(uint64(i), true)) + } } return msgs }, false, }, + { + "no success on one new message and one invalid message", + func(suite *AnteTestSuite) []sdk.Msg { + packet := channeltypes.NewPacket(ibctesting.MockPacketData, 2, + suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, + suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, + clienttypes.NewHeight(1, 0), 0) + + return []sdk.Msg{ + suite.createRecvPacketMessage(uint64(1), false), + channeltypes.NewMsgRecvPacket(packet, []byte("proof"), clienttypes.NewHeight(0, 1), "signer"), + } + }, + false, + }, + { + "no success on one new message and one redundant message in the same block", + func(suite *AnteTestSuite) []sdk.Msg { + msg := suite.createRecvPacketMessage(uint64(1), false) + + // We want to be able to run check tx with the non-redundant message without + // commiting it to a block, so that the when check tx runs with the redundant + // message they are both in the same block + k := suite.chainB.App.GetIBCKeeper() + decorator := ante.NewAnteDecorator(k) + checkCtx := suite.chainB.GetContext().WithIsCheckTx(true) + next := func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) { return ctx, nil } + txBuilder := suite.chainB.TxConfig.NewTxBuilder() + err := txBuilder.SetMsgs([]sdk.Msg{msg}...) + suite.Require().NoError(err) + tx := txBuilder.GetTx() + + _, err = decorator.AnteHandle(checkCtx, tx, false, next) + suite.Require().NoError(err) + + return []sdk.Msg{msg} + }, + false, + }, } for _, tc := range testCases { @@ -456,7 +457,7 @@ func (suite *AnteTestSuite) TestAnteDecorator() { // reset suite suite.SetupTest() - k := suite.chainB.App.GetIBCKeeper().ChannelKeeper + k := suite.chainB.App.GetIBCKeeper() decorator := ante.NewAnteDecorator(k) msgs := tc.malleate(suite) diff --git a/modules/core/keeper/msg_server.go b/modules/core/keeper/msg_server.go index a36f064e8ae..74e7cc19ae4 100644 --- a/modules/core/keeper/msg_server.go +++ b/modules/core/keeper/msg_server.go @@ -261,7 +261,7 @@ func (k Keeper) ChannelOpenAck(goCtx context.Context, msg *channeltypes.MsgChann } // Perform application logic callback - if err = cbs.OnChanOpenAck(ctx, msg.PortId, msg.ChannelId, msg.CounterpartyVersion); err != nil { + if err = cbs.OnChanOpenAck(ctx, msg.PortId, msg.ChannelId, msg.CounterpartyChannelId, msg.CounterpartyVersion); err != nil { return nil, sdkerrors.Wrap(err, "channel open ack callback failed") } @@ -395,7 +395,7 @@ func (k Keeper) RecvPacket(goCtx context.Context, msg *channeltypes.MsgRecvPacke case nil: writeFn() case channeltypes.ErrNoOpMsg: - return &channeltypes.MsgRecvPacketResponse{}, nil // no-op + return &channeltypes.MsgRecvPacketResponse{Result: channeltypes.NOOP}, nil default: return nil, sdkerrors.Wrap(err, "receive packet verification failed") } @@ -435,7 +435,7 @@ func (k Keeper) RecvPacket(goCtx context.Context, msg *channeltypes.MsgRecvPacke ) }() - return &channeltypes.MsgRecvPacketResponse{}, nil + return &channeltypes.MsgRecvPacketResponse{Result: channeltypes.SUCCESS}, nil } // Timeout defines a rpc handler method for MsgTimeout. @@ -473,7 +473,7 @@ func (k Keeper) Timeout(goCtx context.Context, msg *channeltypes.MsgTimeout) (*c case nil: writeFn() case channeltypes.ErrNoOpMsg: - return &channeltypes.MsgTimeoutResponse{}, nil // no-op + return &channeltypes.MsgTimeoutResponse{Result: channeltypes.NOOP}, nil default: return nil, sdkerrors.Wrap(err, "timeout packet verification failed") } @@ -503,7 +503,7 @@ func (k Keeper) Timeout(goCtx context.Context, msg *channeltypes.MsgTimeout) (*c ) }() - return &channeltypes.MsgTimeoutResponse{}, nil + return &channeltypes.MsgTimeoutResponse{Result: channeltypes.SUCCESS}, nil } // TimeoutOnClose defines a rpc handler method for MsgTimeoutOnClose. @@ -541,7 +541,7 @@ func (k Keeper) TimeoutOnClose(goCtx context.Context, msg *channeltypes.MsgTimeo case nil: writeFn() case channeltypes.ErrNoOpMsg: - return &channeltypes.MsgTimeoutOnCloseResponse{}, nil // no-op + return &channeltypes.MsgTimeoutOnCloseResponse{Result: channeltypes.NOOP}, nil default: return nil, sdkerrors.Wrap(err, "timeout on close packet verification failed") } @@ -574,7 +574,7 @@ func (k Keeper) TimeoutOnClose(goCtx context.Context, msg *channeltypes.MsgTimeo ) }() - return &channeltypes.MsgTimeoutOnCloseResponse{}, nil + return &channeltypes.MsgTimeoutOnCloseResponse{Result: channeltypes.SUCCESS}, nil } // Acknowledgement defines a rpc handler method for MsgAcknowledgement. @@ -612,7 +612,7 @@ func (k Keeper) Acknowledgement(goCtx context.Context, msg *channeltypes.MsgAckn case nil: writeFn() case channeltypes.ErrNoOpMsg: - return &channeltypes.MsgAcknowledgementResponse{}, nil // no-op + return &channeltypes.MsgAcknowledgementResponse{Result: channeltypes.NOOP}, nil default: return nil, sdkerrors.Wrap(err, "acknowledge packet verification failed") } @@ -636,5 +636,5 @@ func (k Keeper) Acknowledgement(goCtx context.Context, msg *channeltypes.MsgAckn ) }() - return &channeltypes.MsgAcknowledgementResponse{}, nil + return &channeltypes.MsgAcknowledgementResponse{Result: channeltypes.SUCCESS}, nil } diff --git a/modules/light-clients/07-tendermint/types/client_state.go b/modules/light-clients/07-tendermint/types/client_state.go index a0430337d0b..51f826979fd 100644 --- a/modules/light-clients/07-tendermint/types/client_state.go +++ b/modules/light-clients/07-tendermint/types/client_state.go @@ -78,7 +78,9 @@ func (cs ClientState) Status( // get latest consensus state from clientStore to check for expiry consState, err := GetConsensusState(clientStore, cdc, cs.GetLatestHeight()) if err != nil { - return exported.Unknown + // if the client state does not have an associated consensus state for its latest height + // then it must be expired + return exported.Expired } if cs.IsExpired(consState.Timestamp, ctx.BlockTime()) { diff --git a/modules/light-clients/07-tendermint/types/client_state_test.go b/modules/light-clients/07-tendermint/types/client_state_test.go index b0434579b76..cf52d2996b5 100644 --- a/modules/light-clients/07-tendermint/types/client_state_test.go +++ b/modules/light-clients/07-tendermint/types/client_state_test.go @@ -47,10 +47,10 @@ func (suite *TendermintTestSuite) TestStatus() { clientState.FrozenHeight = clienttypes.NewHeight(0, 1) path.EndpointA.SetClientState(clientState) }, exported.Frozen}, - {"client status is unknown", func() { + {"client status without consensus state", func() { clientState.LatestHeight = clientState.LatestHeight.Increment().(clienttypes.Height) path.EndpointA.SetClientState(clientState) - }, exported.Unknown}, + }, exported.Expired}, {"client status is expired", func() { suite.coordinator.IncrementTimeBy(clientState.TrustingPeriod) }, exported.Expired}, diff --git a/proto/ibc/core/channel/v1/tx.proto b/proto/ibc/core/channel/v1/tx.proto index 7fc8f730e33..d4d6df1d5e9 100644 --- a/proto/ibc/core/channel/v1/tx.proto +++ b/proto/ibc/core/channel/v1/tx.proto @@ -42,6 +42,18 @@ service Msg { rpc Acknowledgement(MsgAcknowledgement) returns (MsgAcknowledgementResponse); } +// ResponseResultType defines the possible outcomes of the execution of a message +enum ResponseResultType { + option (gogoproto.goproto_enum_prefix) = false; + + // Default zero value enumeration + RESPONSE_RESULT_UNSPECIFIED = 0 [(gogoproto.enumvalue_customname) = "UNSPECIFIED"]; + // The message did not call the IBC application callbacks (because, for example, the packet had already been relayed) + RESPONSE_RESULT_NOOP = 1 [(gogoproto.enumvalue_customname) = "NOOP"]; + // The message was executed successfully + RESPONSE_RESULT_SUCCESS = 2 [(gogoproto.enumvalue_customname) = "SUCCESS"]; +} + // MsgChannelOpenInit defines an sdk.Msg to initialize a channel handshake. It // is called by a relayer on Chain A. message MsgChannelOpenInit { @@ -163,7 +175,11 @@ message MsgRecvPacket { } // MsgRecvPacketResponse defines the Msg/RecvPacket response type. -message MsgRecvPacketResponse {} +message MsgRecvPacketResponse { + option (gogoproto.goproto_getters) = false; + + ResponseResultType result = 1; +} // MsgTimeout receives timed-out packet message MsgTimeout { @@ -179,7 +195,11 @@ message MsgTimeout { } // MsgTimeoutResponse defines the Msg/Timeout response type. -message MsgTimeoutResponse {} +message MsgTimeoutResponse { + option (gogoproto.goproto_getters) = false; + + ResponseResultType result = 1; +} // MsgTimeoutOnClose timed-out packet upon counterparty channel closure. message MsgTimeoutOnClose { @@ -196,7 +216,11 @@ message MsgTimeoutOnClose { } // MsgTimeoutOnCloseResponse defines the Msg/TimeoutOnClose response type. -message MsgTimeoutOnCloseResponse {} +message MsgTimeoutOnCloseResponse { + option (gogoproto.goproto_getters) = false; + + ResponseResultType result = 1; +} // MsgAcknowledgement receives incoming IBC acknowledgement message MsgAcknowledgement { @@ -212,4 +236,8 @@ message MsgAcknowledgement { } // MsgAcknowledgementResponse defines the Msg/Acknowledgement response type. -message MsgAcknowledgementResponse {} +message MsgAcknowledgementResponse { + option (gogoproto.goproto_getters) = false; + + ResponseResultType result = 1; +} diff --git a/scripts/protocgen.sh b/scripts/protocgen.sh index 1015709e861..efcfb36b30a 100755 --- a/scripts/protocgen.sh +++ b/scripts/protocgen.sh @@ -32,8 +32,6 @@ buf protoc \ --doc_out=./docs/ibc \ --doc_opt=./docs/protodoc-markdown.tmpl,proto-docs.md \ $(find "$(pwd)/proto" -maxdepth 5 -name '*.proto') -go mod tidy - # move proto files to the right places cp -r github.com/cosmos/ibc-go/v*/modules/* modules/ diff --git a/testing/README.md b/testing/README.md index 97f540d5c4c..ad02a80e640 100644 --- a/testing/README.md +++ b/testing/README.md @@ -296,6 +296,7 @@ The mock module may also be leveraged to act as a base application in the instan The mock IBC module contains a `MockIBCApp`. This struct contains a function field for every IBC App Module callback. Each of these functions can be individually set to mock expected behaviour of a base application. +The portID and scoped keeper for the `MockIBCApp` should be set within `MockIBCApp` before calling `NewIBCModule`. For example, if one wanted to test that the base application cannot affect the outcome of the `OnChanOpenTry` callback, the mock module base application callback could be updated as such: ```go diff --git a/testing/app.go b/testing/app.go index 1c5bbe7c88a..3c6a14ed453 100644 --- a/testing/app.go +++ b/testing/app.go @@ -58,8 +58,9 @@ func SetupTestingApp() (TestingApp, map[string]json.RawMessage) { // that also act as delegators. For simplicity, each validator is bonded with a delegation // of one consensus engine unit (10^6) in the default token of the simapp from first genesis // account. A Nop logger is set in SimApp. -func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs []authtypes.GenesisAccount, chainID string, balances ...banktypes.Balance) TestingApp { +func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs []authtypes.GenesisAccount, chainID string, powerReduction sdk.Int, balances ...banktypes.Balance) TestingApp { app, genesisState := DefaultTestingAppInit() + // set genesis accounts authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs) genesisState[authtypes.ModuleName] = app.AppCodec().MustMarshalJSON(authGenesis) @@ -67,7 +68,7 @@ func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs validators := make([]stakingtypes.Validator, 0, len(valSet.Validators)) delegations := make([]stakingtypes.Delegation, 0, len(valSet.Validators)) - bondAmt := sdk.NewInt(1000000) + bondAmt := sdk.TokensFromConsensusPower(1, powerReduction) for _, val := range valSet.Validators { pk, err := cryptocodec.FromTmPubKeyInterface(val.PubKey) @@ -87,26 +88,29 @@ func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs Commission: stakingtypes.NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), MinSelfDelegation: sdk.ZeroInt(), } + validators = append(validators, validator) delegations = append(delegations, stakingtypes.NewDelegation(genAccs[0].GetAddress(), val.Address.Bytes(), sdk.OneDec())) } // set validators and delegations - stakingGenesis := stakingtypes.NewGenesisState(stakingtypes.DefaultParams(), validators, delegations) - genesisState[stakingtypes.ModuleName] = app.AppCodec().MustMarshalJSON(stakingGenesis) + var stakingGenesis stakingtypes.GenesisState + app.AppCodec().MustUnmarshalJSON(genesisState[stakingtypes.ModuleName], &stakingGenesis) + + bondDenom := stakingGenesis.Params.BondDenom totalSupply := sdk.NewCoins() - for _, b := range balances { - // add genesis acc tokens and delegated tokens to total supply - totalSupply = totalSupply.Add(b.Coins.Add(sdk.NewCoin(sdk.DefaultBondDenom, bondAmt))...) - } // add bonded amount to bonded pool module account balances = append(balances, banktypes.Balance{ Address: authtypes.NewModuleAddress(stakingtypes.BondedPoolName).String(), - Coins: sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, bondAmt)}, + Coins: sdk.Coins{sdk.NewCoin(bondDenom, bondAmt.Mul(sdk.NewInt(int64(len(valSet.Validators)))))}, }) + // set validators and delegations + stakingGenesis = *stakingtypes.NewGenesisState(stakingGenesis.Params, validators, delegations) + genesisState[stakingtypes.ModuleName] = app.AppCodec().MustMarshalJSON(&stakingGenesis) + // update total supply bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, totalSupply, []banktypes.Metadata{}) genesisState[banktypes.ModuleName] = app.AppCodec().MustMarshalJSON(bankGenesis) @@ -126,13 +130,17 @@ func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs // commit genesis changes app.Commit() - app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{ - ChainID: chainID, - Height: app.LastBlockHeight() + 1, - AppHash: app.LastCommitID().Hash, - ValidatorsHash: valSet.Hash(), - NextValidatorsHash: valSet.Hash(), - }}) + app.BeginBlock( + abci.RequestBeginBlock{ + Header: tmproto.Header{ + ChainID: chainID, + Height: app.LastBlockHeight() + 1, + AppHash: app.LastCommitID().Hash, + ValidatorsHash: valSet.Hash(), + NextValidatorsHash: valSet.Hash(), + }, + }, + ) return app } diff --git a/testing/chain.go b/testing/chain.go index 281782f7837..28382a6c463 100644 --- a/testing/chain.go +++ b/testing/chain.go @@ -36,13 +36,20 @@ import ( "github.com/cosmos/ibc-go/v3/testing/simapp" ) +var MaxAccounts = 10 + +type SenderAccount struct { + SenderPrivKey cryptotypes.PrivKey + SenderAccount authtypes.AccountI +} + // TestChain is a testing struct that wraps a simapp with the last TM Header, the current ABCI // header and the validators of the TestChain. It also contains a field called ChainID. This // is the clientID that *other* chains use to refer to this TestChain. The SenderAccount // is used for delivering transactions through the application state. // NOTE: the actual application uses an empty chain-id for ease of testing. type TestChain struct { - t *testing.T + *testing.T Coordinator *Coordinator App TestingApp @@ -56,41 +63,57 @@ type TestChain struct { Vals *tmtypes.ValidatorSet Signers []tmtypes.PrivValidator - senderPrivKey cryptotypes.PrivKey + // autogenerated sender private key + SenderPrivKey cryptotypes.PrivKey SenderAccount authtypes.AccountI + + SenderAccounts []SenderAccount } -// NewTestChain initializes a new TestChain instance with a single validator set using a -// generated private key. It also creates a sender account to be used for delivering transactions. +// NewTestChainWithValSet initializes a new TestChain instance with the given validator set +// and signer array. It also initializes 10 Sender accounts with a balance of 10000000000000000000 coins of +// bond denom to use for tests. // // The first block height is committed to state in order to allow for client creations on // counterparty chains. The TestChain will return with a block height starting at 2. // // Time management is handled by the Coordinator in order to ensure synchrony between chains. // Each update of any chain increments the block header time for all chains by 5 seconds. -func NewTestChain(t *testing.T, coord *Coordinator, chainID string) *TestChain { - // generate validator private/public key - privVal := mock.NewPV() - pubKey, err := privVal.GetPubKey() - require.NoError(t, err) - - // create validator set with single validator - validator := tmtypes.NewValidator(pubKey, 1) - valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) - signers := []tmtypes.PrivValidator{privVal} - - // generate genesis account - senderPrivKey := secp256k1.GenPrivKey() - acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0) - amount, ok := sdk.NewIntFromString("10000000000000000000") - require.True(t, ok) - - balance := banktypes.Balance{ - Address: acc.GetAddress().String(), - Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, amount)), +// +// NOTE: to use a custom sender privkey and account for testing purposes, replace and modify this +// constructor function. +// +// CONTRACT: Validator and signer array must be provided in the order expected by Tendermint. +// i.e. sorted first by power and then lexicographically by address. +func NewTestChainWithValSet(t *testing.T, coord *Coordinator, chainID string, valSet *tmtypes.ValidatorSet, signers []tmtypes.PrivValidator) *TestChain { + genAccs := []authtypes.GenesisAccount{} + genBals := []banktypes.Balance{} + senderAccs := []SenderAccount{} + + // generate genesis accounts + for i := 0; i < MaxAccounts; i++ { + senderPrivKey := secp256k1.GenPrivKey() + acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), uint64(i), 0) + amount, ok := sdk.NewIntFromString("10000000000000000000") + require.True(t, ok) + + balance := banktypes.Balance{ + Address: acc.GetAddress().String(), + Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, amount)), + } + + genAccs = append(genAccs, acc) + genBals = append(genBals, balance) + + senderAcc := SenderAccount{ + SenderAccount: acc, + SenderPrivKey: senderPrivKey, + } + + senderAccs = append(senderAccs, senderAcc) } - app := SetupWithGenesisValSet(t, valSet, []authtypes.GenesisAccount{acc}, chainID, balance) + app := SetupWithGenesisValSet(t, valSet, genAccs, chainID, sdk.DefaultPowerReduction, genBals...) // create current header and call begin block header := tmproto.Header{ @@ -103,18 +126,19 @@ func NewTestChain(t *testing.T, coord *Coordinator, chainID string) *TestChain { // create an account to send transactions from chain := &TestChain{ - t: t, - Coordinator: coord, - ChainID: chainID, - App: app, - CurrentHeader: header, - QueryServer: app.GetIBCKeeper(), - TxConfig: txConfig, - Codec: app.AppCodec(), - Vals: valSet, - Signers: signers, - senderPrivKey: senderPrivKey, - SenderAccount: acc, + T: t, + Coordinator: coord, + ChainID: chainID, + App: app, + CurrentHeader: header, + QueryServer: app.GetIBCKeeper(), + TxConfig: txConfig, + Codec: app.AppCodec(), + Vals: valSet, + Signers: signers, + SenderPrivKey: senderAccs[0].SenderPrivKey, + SenderAccount: senderAccs[0].SenderAccount, + SenderAccounts: senderAccs, } coord.CommitBlock(chain) @@ -122,6 +146,38 @@ func NewTestChain(t *testing.T, coord *Coordinator, chainID string) *TestChain { return chain } +// NewTestChain initializes a new test chain with a default of 4 validators +// Use this function if the tests do not need custom control over the validator set +func NewTestChain(t *testing.T, coord *Coordinator, chainID string) *TestChain { + // generate validators private/public key + var ( + validatorsPerChain = 4 + validators []*tmtypes.Validator + signersByAddress = make(map[string]tmtypes.PrivValidator, validatorsPerChain) + ) + + for i := 0; i < validatorsPerChain; i++ { + privVal := mock.NewPV() + pubKey, err := privVal.GetPubKey() + require.NoError(t, err) + validators = append(validators, tmtypes.NewValidator(pubKey, 1)) + signersByAddress[pubKey.Address().String()] = privVal + } + + // construct validator set; + // Note that the validators are sorted by voting power + // or, if equal, by address lexical order + valSet := tmtypes.NewValidatorSet(validators) + + // create signers indexed by the valSet validators's order + signers := []tmtypes.PrivValidator{} + for _, val := range valSet.Validators { + signers = append(signers, signersByAddress[val.PubKey.Address().String()]) + } + + return NewTestChainWithValSet(t, coord, chainID, valSet, signers) +} + // GetContext returns the current context for the application. func (chain *TestChain) GetContext() sdk.Context { return chain.App.GetBaseApp().NewContext(false, chain.CurrentHeader) @@ -132,7 +188,7 @@ func (chain *TestChain) GetContext() sdk.Context { // their own SimApp. func (chain *TestChain) GetSimApp() *simapp.SimApp { app, ok := chain.App.(*simapp.SimApp) - require.True(chain.t, ok) + require.True(chain.T, ok) return app } @@ -154,10 +210,10 @@ func (chain *TestChain) QueryProofAtHeight(key []byte, height int64) ([]byte, cl }) merkleProof, err := commitmenttypes.ConvertProofs(res.ProofOps) - require.NoError(chain.t, err) + require.NoError(chain.T, err) proof, err := chain.App.AppCodec().Marshal(&merkleProof) - require.NoError(chain.t, err) + require.NoError(chain.T, err) revision := clienttypes.ParseChainID(chain.ChainID) @@ -178,10 +234,10 @@ func (chain *TestChain) QueryUpgradeProof(key []byte, height uint64) ([]byte, cl }) merkleProof, err := commitmenttypes.ConvertProofs(res.ProofOps) - require.NoError(chain.t, err) + require.NoError(chain.T, err) proof, err := chain.App.AppCodec().Marshal(&merkleProof) - require.NoError(chain.t, err) + require.NoError(chain.T, err) revision := clienttypes.ParseChainID(chain.ChainID) @@ -237,12 +293,11 @@ func (chain *TestChain) sendMsgs(msgs ...sdk.Msg) error { // number and updates the TestChain's headers. It returns the result and error if one // occurred. func (chain *TestChain) SendMsgs(msgs ...sdk.Msg) (*sdk.Result, error) { - // ensure the chain has the latest time chain.Coordinator.UpdateTimeForChain(chain) _, r, err := simapp.SignAndDeliver( - chain.t, + chain.T, chain.TxConfig, chain.App.GetBaseApp(), chain.GetContext().BlockHeader(), @@ -250,7 +305,7 @@ func (chain *TestChain) SendMsgs(msgs ...sdk.Msg) (*sdk.Result, error) { chain.ChainID, []uint64{chain.SenderAccount.GetAccountNumber()}, []uint64{chain.SenderAccount.GetSequence()}, - true, true, chain.senderPrivKey, + true, true, chain.SenderPrivKey, ) if err != nil { return nil, err @@ -271,7 +326,7 @@ func (chain *TestChain) SendMsgs(msgs ...sdk.Msg) (*sdk.Result, error) { // expected to exist otherwise testing will fail. func (chain *TestChain) GetClientState(clientID string) exported.ClientState { clientState, found := chain.App.GetIBCKeeper().ClientKeeper.GetClientState(chain.GetContext(), clientID) - require.True(chain.t, found) + require.True(chain.T, found) return clientState } @@ -303,7 +358,7 @@ func (chain *TestChain) GetValsAtHeight(height int64) (*tmtypes.ValidatorSet, bo // acknowledgement does not exist then testing will fail. func (chain *TestChain) GetAcknowledgement(packet exported.PacketI) []byte { ack, found := chain.App.GetIBCKeeper().ChannelKeeper.GetPacketAcknowledgement(chain.GetContext(), packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) - require.True(chain.t, found) + require.True(chain.T, found) return ack } @@ -357,7 +412,6 @@ func (chain *TestChain) ConstructUpdateTMClientHeaderWithTrustedHeight(counterpa header.TrustedValidators = trustedVals return header, nil - } // ExpireClient fast forwards the chain's block time by the provided amount of time which will @@ -379,7 +433,7 @@ func (chain *TestChain) CreateTMClientHeader(chainID string, blockHeight int64, valSet *tmproto.ValidatorSet trustedVals *tmproto.ValidatorSet ) - require.NotNil(chain.t, tmValSet) + require.NotNil(chain.T, tmValSet) vsetHash := tmValSet.Hash() @@ -399,12 +453,13 @@ func (chain *TestChain) CreateTMClientHeader(chainID string, blockHeight int64, EvidenceHash: tmhash.Sum([]byte("evidence_hash")), ProposerAddress: tmValSet.Proposer.Address, //nolint:staticcheck } + hhash := tmHeader.Hash() blockID := MakeBlockID(hhash, 3, tmhash.Sum([]byte("part_set"))) voteSet := tmtypes.NewVoteSet(chainID, blockHeight, 1, tmproto.PrecommitType, tmValSet) commit, err := tmtypes.MakeCommit(blockID, blockHeight, 1, voteSet, signers, timestamp) - require.NoError(chain.t, err) + require.NoError(chain.T, err) signedHeader := &tmproto.SignedHeader{ Header: tmHeader.ToProto(), @@ -413,16 +468,12 @@ func (chain *TestChain) CreateTMClientHeader(chainID string, blockHeight int64, if tmValSet != nil { valSet, err = tmValSet.ToProto() - if err != nil { - panic(err) - } + require.NoError(chain.T, err) } if tmTrustedVals != nil { trustedVals, err = tmTrustedVals.ToProto() - if err != nil { - panic(err) - } + require.NoError(chain.T, err) } // The trusted fields may be nil. They may be filled before relaying messages to a client. @@ -451,8 +502,8 @@ func MakeBlockID(hash []byte, partSetSize uint32, partSetHash []byte) tmtypes.Bl // sorting of ValidatorSet. // The sorting is first by .VotingPower (descending), with secondary index of .Address (ascending). func CreateSortedSignerArray(altPrivVal, suitePrivVal tmtypes.PrivValidator, - altVal, suiteVal *tmtypes.Validator) []tmtypes.PrivValidator { - + altVal, suiteVal *tmtypes.Validator, +) []tmtypes.PrivValidator { switch { case altVal.VotingPower > suiteVal.VotingPower: return []tmtypes.PrivValidator{altPrivVal, suitePrivVal} @@ -468,7 +519,7 @@ func CreateSortedSignerArray(altPrivVal, suitePrivVal tmtypes.PrivValidator, // CreatePortCapability binds and claims a capability for the given portID if it does not // already exist. This function will fail testing on any resulting error. -// NOTE: only creation of a capbility for a transfer or mock port is supported +// NOTE: only creation of a capability for a transfer or mock port is supported // Other applications must bind to the port in InitGenesis or modify this code. func (chain *TestChain) CreatePortCapability(scopedKeeper capabilitykeeper.ScopedKeeper, portID string) { // check if the portId is already binded, if not bind it @@ -476,11 +527,11 @@ func (chain *TestChain) CreatePortCapability(scopedKeeper capabilitykeeper.Scope if !ok { // create capability using the IBC capability keeper cap, err := chain.App.GetScopedIBCKeeper().NewCapability(chain.GetContext(), host.PortPath(portID)) - require.NoError(chain.t, err) + require.NoError(chain.T, err) // claim capability using the scopedKeeper err = scopedKeeper.ClaimCapability(chain.GetContext(), cap, host.PortPath(portID)) - require.NoError(chain.t, err) + require.NoError(chain.T, err) } chain.App.Commit() @@ -492,7 +543,7 @@ func (chain *TestChain) CreatePortCapability(scopedKeeper capabilitykeeper.Scope // exist, otherwise testing will fail. func (chain *TestChain) GetPortCapability(portID string) *capabilitytypes.Capability { cap, ok := chain.App.GetScopedIBCKeeper().GetCapability(chain.GetContext(), host.PortPath(portID)) - require.True(chain.t, ok) + require.True(chain.T, ok) return cap } @@ -506,9 +557,9 @@ func (chain *TestChain) CreateChannelCapability(scopedKeeper capabilitykeeper.Sc _, ok := chain.App.GetScopedIBCKeeper().GetCapability(chain.GetContext(), capName) if !ok { cap, err := chain.App.GetScopedIBCKeeper().NewCapability(chain.GetContext(), capName) - require.NoError(chain.t, err) + require.NoError(chain.T, err) err = scopedKeeper.ClaimCapability(chain.GetContext(), cap, capName) - require.NoError(chain.t, err) + require.NoError(chain.T, err) } chain.App.Commit() @@ -520,7 +571,7 @@ func (chain *TestChain) CreateChannelCapability(scopedKeeper capabilitykeeper.Sc // The capability must exist, otherwise testing will fail. func (chain *TestChain) GetChannelCapability(portID, channelID string) *capabilitytypes.Capability { cap, ok := chain.App.GetScopedIBCKeeper().GetCapability(chain.GetContext(), host.ChannelCapabilityPath(portID, channelID)) - require.True(chain.t, ok) + require.True(chain.T, ok) return cap } diff --git a/testing/coordinator.go b/testing/coordinator.go index 615bd830ced..217c257473a 100644 --- a/testing/coordinator.go +++ b/testing/coordinator.go @@ -19,7 +19,7 @@ var ( // Coordinator is a testing struct which contains N TestChain's. It handles keeping all chains // in sync with regards to time. type Coordinator struct { - t *testing.T + *testing.T CurrentTime time.Time Chains map[string]*TestChain @@ -29,7 +29,7 @@ type Coordinator struct { func NewCoordinator(t *testing.T, n int) *Coordinator { chains := make(map[string]*TestChain) coord := &Coordinator{ - t: t, + T: t, CurrentTime: globalStartTime, } @@ -55,7 +55,6 @@ func (coord *Coordinator) IncrementTime() { func (coord *Coordinator) IncrementTimeBy(increment time.Duration) { coord.CurrentTime = coord.CurrentTime.Add(increment).UTC() coord.UpdateTime() - } // UpdateTime updates all clocks for the TestChains to the current global time. @@ -85,10 +84,10 @@ func (coord *Coordinator) Setup(path *Path) { // caller does not anticipate any errors. func (coord *Coordinator) SetupClients(path *Path) { err := path.EndpointA.CreateClient() - require.NoError(coord.t, err) + require.NoError(coord.T, err) err = path.EndpointB.CreateClient() - require.NoError(coord.t, err) + require.NoError(coord.T, err) } // SetupClientConnections is a helper function to create clients and the appropriate @@ -105,21 +104,21 @@ func (coord *Coordinator) SetupConnections(path *Path) { // are returned within a TestConnection struct. The function expects the connections to be // successfully opened otherwise testing will fail. func (coord *Coordinator) CreateConnections(path *Path) { - err := path.EndpointA.ConnOpenInit() - require.NoError(coord.t, err) + require.NoError(coord.T, err) err = path.EndpointB.ConnOpenTry() - require.NoError(coord.t, err) + require.NoError(coord.T, err) err = path.EndpointA.ConnOpenAck() - require.NoError(coord.t, err) + require.NoError(coord.T, err) err = path.EndpointB.ConnOpenConfirm() - require.NoError(coord.t, err) + require.NoError(coord.T, err) // ensure counterparty is up to date - path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() + require.NoError(coord.T, err) } // CreateMockChannels constructs and executes channel handshake messages to create OPEN @@ -148,26 +147,27 @@ func (coord *Coordinator) CreateTransferChannels(path *Path) { // opened otherwise testing will fail. func (coord *Coordinator) CreateChannels(path *Path) { err := path.EndpointA.ChanOpenInit() - require.NoError(coord.t, err) + require.NoError(coord.T, err) err = path.EndpointB.ChanOpenTry() - require.NoError(coord.t, err) + require.NoError(coord.T, err) err = path.EndpointA.ChanOpenAck() - require.NoError(coord.t, err) + require.NoError(coord.T, err) err = path.EndpointB.ChanOpenConfirm() - require.NoError(coord.t, err) + require.NoError(coord.T, err) // ensure counterparty is up to date - path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() + require.NoError(coord.T, err) } // GetChain returns the TestChain using the given chainID and returns an error if it does // not exist. func (coord *Coordinator) GetChain(chainID string) *TestChain { chain, found := coord.Chains[chainID] - require.True(coord.t, found, fmt.Sprintf("%s chain does not exist", chainID)) + require.True(coord.T, found, fmt.Sprintf("%s chain does not exist", chainID)) return chain } @@ -212,11 +212,7 @@ func (coord *Coordinator) ConnOpenInitOnBothChains(path *Path) error { return err } - if err := path.EndpointB.UpdateClient(); err != nil { - return err - } - - return nil + return path.EndpointB.UpdateClient() } // ChanOpenInitOnBothChains initializes a channel on the source chain and counterparty chain @@ -237,9 +233,5 @@ func (coord *Coordinator) ChanOpenInitOnBothChains(path *Path) error { return err } - if err := path.EndpointB.UpdateClient(); err != nil { - return err - } - - return nil + return path.EndpointB.UpdateClient() } diff --git a/testing/endpoint.go b/testing/endpoint.go index 962ecf5ef35..607f7a16843 100644 --- a/testing/endpoint.go +++ b/testing/endpoint.go @@ -67,7 +67,7 @@ func (endpoint *Endpoint) QueryProof(key []byte) ([]byte, clienttypes.Height) { } // QueryProofAtHeight queries proof associated with this endpoint using the proof height -// providied +// provided func (endpoint *Endpoint) QueryProofAtHeight(key []byte, height uint64) ([]byte, clienttypes.Height) { // query proof on the counterparty using the latest height of the IBC client return endpoint.Chain.QueryProofAtHeight(key, int64(height)) @@ -88,7 +88,7 @@ func (endpoint *Endpoint) CreateClient() (err error) { switch endpoint.ClientConfig.GetClientType() { case exported.Tendermint: tmConfig, ok := endpoint.ClientConfig.(*TendermintConfig) - require.True(endpoint.Chain.t, ok) + require.True(endpoint.Chain.T, ok) height := endpoint.Counterparty.Chain.LastHeader.GetHeight().(clienttypes.Height) clientState = ibctmtypes.NewClientState( @@ -98,7 +98,7 @@ func (endpoint *Endpoint) CreateClient() (err error) { consensusState = endpoint.Counterparty.Chain.LastHeader.ConsensusState() case exported.Solomachine: // TODO - // solo := NewSolomachine(chain.t, endpoint.Chain.Codec, clientID, "", 1) + // solo := NewSolomachine(endpoint.Chain.T, endpoint.Chain.Codec, clientID, "", 1) // clientState = solo.ClientState() // consensusState = solo.ConsensusState() @@ -113,7 +113,7 @@ func (endpoint *Endpoint) CreateClient() (err error) { msg, err := clienttypes.NewMsgCreateClient( clientState, consensusState, endpoint.Chain.SenderAccount.GetAddress().String(), ) - require.NoError(endpoint.Chain.t, err) + require.NoError(endpoint.Chain.T, err) res, err := endpoint.Chain.SendMsgs(msg) if err != nil { @@ -121,7 +121,7 @@ func (endpoint *Endpoint) CreateClient() (err error) { } endpoint.ClientID, err = ParseClientIDFromEvents(res.GetEvents()) - require.NoError(endpoint.Chain.t, err) + require.NoError(endpoint.Chain.T, err) return nil } @@ -131,9 +131,7 @@ func (endpoint *Endpoint) UpdateClient() (err error) { // ensure counterparty has committed state endpoint.Chain.Coordinator.CommitBlock(endpoint.Counterparty.Chain) - var ( - header exported.Header - ) + var header exported.Header switch endpoint.ClientConfig.GetClientType() { case exported.Tendermint: @@ -151,10 +149,9 @@ func (endpoint *Endpoint) UpdateClient() (err error) { endpoint.ClientID, header, endpoint.Chain.SenderAccount.GetAddress().String(), ) - require.NoError(endpoint.Chain.t, err) + require.NoError(endpoint.Chain.T, err) return endpoint.Chain.sendMsgs(msg) - } // ConnOpenInit will construct and execute a MsgConnectionOpenInit on the associated endpoint. @@ -171,14 +168,15 @@ func (endpoint *Endpoint) ConnOpenInit() error { } endpoint.ConnectionID, err = ParseConnectionIDFromEvents(res.GetEvents()) - require.NoError(endpoint.Chain.t, err) + require.NoError(endpoint.Chain.T, err) return nil } // ConnOpenTry will construct and execute a MsgConnectionOpenTry on the associated endpoint. func (endpoint *Endpoint) ConnOpenTry() error { - endpoint.UpdateClient() + err := endpoint.UpdateClient() + require.NoError(endpoint.Chain.T, err) counterpartyClient, proofClient, proofConsensus, consensusHeight, proofInit, proofHeight := endpoint.QueryConnectionHandshakeProof() @@ -197,7 +195,7 @@ func (endpoint *Endpoint) ConnOpenTry() error { if endpoint.ConnectionID == "" { endpoint.ConnectionID, err = ParseConnectionIDFromEvents(res.GetEvents()) - require.NoError(endpoint.Chain.t, err) + require.NoError(endpoint.Chain.T, err) } return nil @@ -205,7 +203,8 @@ func (endpoint *Endpoint) ConnOpenTry() error { // ConnOpenAck will construct and execute a MsgConnectionOpenAck on the associated endpoint. func (endpoint *Endpoint) ConnOpenAck() error { - endpoint.UpdateClient() + err := endpoint.UpdateClient() + require.NoError(endpoint.Chain.T, err) counterpartyClient, proofClient, proofConsensus, consensusHeight, proofTry, proofHeight := endpoint.QueryConnectionHandshakeProof() @@ -221,7 +220,8 @@ func (endpoint *Endpoint) ConnOpenAck() error { // ConnOpenConfirm will construct and execute a MsgConnectionOpenConfirm on the associated endpoint. func (endpoint *Endpoint) ConnOpenConfirm() error { - endpoint.UpdateClient() + err := endpoint.UpdateClient() + require.NoError(endpoint.Chain.T, err) connectionKey := host.ConnectionKey(endpoint.Counterparty.ConnectionID) proof, height := endpoint.Counterparty.Chain.QueryProof(connectionKey) @@ -277,14 +277,15 @@ func (endpoint *Endpoint) ChanOpenInit() error { } endpoint.ChannelID, err = ParseChannelIDFromEvents(res.GetEvents()) - require.NoError(endpoint.Chain.t, err) + require.NoError(endpoint.Chain.T, err) return nil } // ChanOpenTry will construct and execute a MsgChannelOpenTry on the associated endpoint. func (endpoint *Endpoint) ChanOpenTry() error { - endpoint.UpdateClient() + err := endpoint.UpdateClient() + require.NoError(endpoint.Chain.T, err) channelKey := host.ChannelKey(endpoint.Counterparty.ChannelConfig.PortID, endpoint.Counterparty.ChannelID) proof, height := endpoint.Counterparty.Chain.QueryProof(channelKey) @@ -303,7 +304,7 @@ func (endpoint *Endpoint) ChanOpenTry() error { if endpoint.ChannelID == "" { endpoint.ChannelID, err = ParseChannelIDFromEvents(res.GetEvents()) - require.NoError(endpoint.Chain.t, err) + require.NoError(endpoint.Chain.T, err) } // update version to selected app version @@ -315,7 +316,8 @@ func (endpoint *Endpoint) ChanOpenTry() error { // ChanOpenAck will construct and execute a MsgChannelOpenAck on the associated endpoint. func (endpoint *Endpoint) ChanOpenAck() error { - endpoint.UpdateClient() + err := endpoint.UpdateClient() + require.NoError(endpoint.Chain.T, err) channelKey := host.ChannelKey(endpoint.Counterparty.ChannelConfig.PortID, endpoint.Counterparty.ChannelID) proof, height := endpoint.Counterparty.Chain.QueryProof(channelKey) @@ -331,7 +333,8 @@ func (endpoint *Endpoint) ChanOpenAck() error { // ChanOpenConfirm will construct and execute a MsgChannelOpenConfirm on the associated endpoint. func (endpoint *Endpoint) ChanOpenConfirm() error { - endpoint.UpdateClient() + err := endpoint.UpdateClient() + require.NoError(endpoint.Chain.T, err) channelKey := host.ChannelKey(endpoint.Counterparty.ChannelConfig.PortID, endpoint.Counterparty.ChannelID) proof, height := endpoint.Counterparty.Chain.QueryProof(channelKey) @@ -449,7 +452,7 @@ func (endpoint *Endpoint) TimeoutPacket(packet channeltypes.Packet) error { proof, proofHeight := endpoint.Counterparty.QueryProof(packetKey) nextSeqRecv, found := endpoint.Counterparty.Chain.App.GetIBCKeeper().ChannelKeeper.GetNextSequenceRecv(endpoint.Counterparty.Chain.GetContext(), endpoint.ChannelConfig.PortID, endpoint.ChannelID) - require.True(endpoint.Chain.t, found) + require.True(endpoint.Chain.T, found) timeoutMsg := channeltypes.NewMsgTimeout( packet, nextSeqRecv, @@ -459,6 +462,36 @@ func (endpoint *Endpoint) TimeoutPacket(packet channeltypes.Packet) error { return endpoint.Chain.sendMsgs(timeoutMsg) } +// TimeoutOnClose sends a MsgTimeoutOnClose to the channel associated with the endpoint. +func (endpoint *Endpoint) TimeoutOnClose(packet channeltypes.Packet) error { + // get proof for timeout based on channel order + var packetKey []byte + + switch endpoint.ChannelConfig.Order { + case channeltypes.ORDERED: + packetKey = host.NextSequenceRecvKey(packet.GetDestPort(), packet.GetDestChannel()) + case channeltypes.UNORDERED: + packetKey = host.PacketReceiptKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + default: + return fmt.Errorf("unsupported order type %s", endpoint.ChannelConfig.Order) + } + + proof, proofHeight := endpoint.Counterparty.QueryProof(packetKey) + + channelKey := host.ChannelKey(packet.GetDestPort(), packet.GetDestChannel()) + proofClosed, _ := endpoint.Counterparty.QueryProof(channelKey) + + nextSeqRecv, found := endpoint.Counterparty.Chain.App.GetIBCKeeper().ChannelKeeper.GetNextSequenceRecv(endpoint.Counterparty.Chain.GetContext(), endpoint.ChannelConfig.PortID, endpoint.ChannelID) + require.True(endpoint.Chain.T, found) + + timeoutOnCloseMsg := channeltypes.NewMsgTimeoutOnClose( + packet, nextSeqRecv, + proof, proofClosed, proofHeight, endpoint.Chain.SenderAccount.GetAddress().String(), + ) + + return endpoint.Chain.sendMsgs(timeoutOnCloseMsg) +} + // SetChannelClosed sets a channel state to CLOSED. func (endpoint *Endpoint) SetChannelClosed() error { channel := endpoint.GetChannel() @@ -486,7 +519,7 @@ func (endpoint *Endpoint) SetClientState(clientState exported.ClientState) { // The consensus state is expected to exist otherwise testing will fail. func (endpoint *Endpoint) GetConsensusState(height exported.Height) exported.ConsensusState { consensusState, found := endpoint.Chain.GetConsensusState(endpoint.ClientID, height) - require.True(endpoint.Chain.t, found) + require.True(endpoint.Chain.T, found) return consensusState } @@ -500,7 +533,7 @@ func (endpoint *Endpoint) SetConsensusState(consensusState exported.ConsensusSta // connection is expected to exist otherwise testing will fail. func (endpoint *Endpoint) GetConnection() connectiontypes.ConnectionEnd { connection, found := endpoint.Chain.App.GetIBCKeeper().ConnectionKeeper.GetConnection(endpoint.Chain.GetContext(), endpoint.ConnectionID) - require.True(endpoint.Chain.t, found) + require.True(endpoint.Chain.T, found) return connection } @@ -514,7 +547,7 @@ func (endpoint *Endpoint) SetConnection(connection connectiontypes.ConnectionEnd // is expected to exist otherwise testing will fail. func (endpoint *Endpoint) GetChannel() channeltypes.Channel { channel, found := endpoint.Chain.App.GetIBCKeeper().ChannelKeeper.GetChannel(endpoint.Chain.GetContext(), endpoint.ChannelConfig.PortID, endpoint.ChannelID) - require.True(endpoint.Chain.t, found) + require.True(endpoint.Chain.T, found) return channel } diff --git a/testing/events.go b/testing/events.go index 037a4c342e3..7828b42619f 100644 --- a/testing/events.go +++ b/testing/events.go @@ -2,6 +2,7 @@ package ibctesting import ( "fmt" + "strconv" sdk "github.com/cosmos/cosmos-sdk/types" @@ -56,6 +57,65 @@ func ParseChannelIDFromEvents(events sdk.Events) (string, error) { return "", fmt.Errorf("channel identifier event attribute not found") } +// ParsePacketFromEvents parses events emitted from a MsgRecvPacket and returns the +// acknowledgement. +func ParsePacketFromEvents(events sdk.Events) (channeltypes.Packet, error) { + for _, ev := range events { + if ev.Type == channeltypes.EventTypeSendPacket { + packet := channeltypes.Packet{} + for _, attr := range ev.Attributes { + + switch string(attr.Key) { + case channeltypes.AttributeKeyData: + packet.Data = attr.Value + + case channeltypes.AttributeKeySequence: + seq, err := strconv.ParseUint(string(attr.Value), 10, 64) + if err != nil { + return channeltypes.Packet{}, err + } + + packet.Sequence = seq + + case channeltypes.AttributeKeySrcPort: + packet.SourcePort = string(attr.Value) + + case channeltypes.AttributeKeySrcChannel: + packet.SourceChannel = string(attr.Value) + + case channeltypes.AttributeKeyDstPort: + packet.DestinationPort = string(attr.Value) + + case channeltypes.AttributeKeyDstChannel: + packet.DestinationChannel = string(attr.Value) + + case channeltypes.AttributeKeyTimeoutHeight: + height, err := clienttypes.ParseHeight(string(attr.Value)) + if err != nil { + return channeltypes.Packet{}, err + } + + packet.TimeoutHeight = height + + case channeltypes.AttributeKeyTimeoutTimestamp: + timestamp, err := strconv.ParseUint(string(attr.Value), 10, 64) + if err != nil { + return channeltypes.Packet{}, err + } + + packet.TimeoutTimestamp = timestamp + + default: + continue + } + } + + return packet, nil + } + } + return channeltypes.Packet{}, fmt.Errorf("acknowledgement event attribute not found") +} + // ParseAckFromEvents parses events emitted from a MsgRecvPacket and returns the // acknowledgement. func ParseAckFromEvents(events sdk.Events) ([]byte, error) { diff --git a/testing/mock/ibc_app.go b/testing/mock/ibc_app.go index a3f2db3bc6d..77eb17b8c6f 100644 --- a/testing/mock/ibc_app.go +++ b/testing/mock/ibc_app.go @@ -2,6 +2,7 @@ package mock import ( sdk "github.com/cosmos/cosmos-sdk/types" + capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" @@ -10,6 +11,9 @@ import ( // MockIBCApp contains IBC application module callbacks as defined in 05-port. type MockIBCApp struct { + PortID string + ScopedKeeper capabilitykeeper.ScopedKeeper + OnChanOpenInit func( ctx sdk.Context, order channeltypes.Order, @@ -36,6 +40,7 @@ type MockIBCApp struct { ctx sdk.Context, portID, channelID string, + counterpartyChannelID string, counterpartyVersion string, ) error @@ -81,3 +86,11 @@ type MockIBCApp struct { relayer sdk.AccAddress, ) error } + +// NewMockIBCApp returns a MockIBCApp. An empty PortID indicates the mock app doesn't bind/claim ports. +func NewMockIBCApp(portID string, scopedKeeper capabilitykeeper.ScopedKeeper) *MockIBCApp { + return &MockIBCApp{ + PortID: portID, + ScopedKeeper: scopedKeeper, + } +} diff --git a/testing/mock/ibc_module.go b/testing/mock/ibc_module.go index fb46864709b..e58f6ae7156 100644 --- a/testing/mock/ibc_module.go +++ b/testing/mock/ibc_module.go @@ -6,7 +6,6 @@ import ( "strconv" sdk "github.com/cosmos/cosmos-sdk/types" - capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" @@ -16,15 +15,16 @@ import ( // IBCModule implements the ICS26 callbacks for testing/mock. type IBCModule struct { - IBCApp *MockIBCApp // base application of an IBC middleware stack - scopedKeeper capabilitykeeper.ScopedKeeper + appModule *AppModule + IBCApp *MockIBCApp // base application of an IBC middleware stack } // NewIBCModule creates a new IBCModule given the underlying mock IBC application and scopedKeeper. -func NewIBCModule(app *MockIBCApp, scopedKeeper capabilitykeeper.ScopedKeeper) IBCModule { +func NewIBCModule(appModule *AppModule, app *MockIBCApp) IBCModule { + appModule.ibcApps = append(appModule.ibcApps, app) return IBCModule{ - IBCApp: app, - scopedKeeper: scopedKeeper, + appModule: appModule, + IBCApp: app, } } @@ -39,7 +39,7 @@ func (im IBCModule) OnChanOpenInit( } // Claim channel capability passed back by IBC module - if err := im.scopedKeeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { + if err := im.IBCApp.ScopedKeeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { return err } @@ -56,7 +56,7 @@ func (im IBCModule) OnChanOpenTry( } // Claim channel capability passed back by IBC module - if err := im.scopedKeeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { + if err := im.IBCApp.ScopedKeeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { return "", err } @@ -64,9 +64,9 @@ func (im IBCModule) OnChanOpenTry( } // OnChanOpenAck implements the IBCModule interface. -func (im IBCModule) OnChanOpenAck(ctx sdk.Context, portID string, channelID string, counterpartyVersion string) error { +func (im IBCModule) OnChanOpenAck(ctx sdk.Context, portID string, channelID string, counterpartyChannelID string, counterpartyVersion string) error { if im.IBCApp.OnChanOpenAck != nil { - return im.IBCApp.OnChanOpenAck(ctx, portID, channelID, counterpartyVersion) + return im.IBCApp.OnChanOpenAck(ctx, portID, channelID, counterpartyChannelID, counterpartyVersion) } return nil @@ -107,7 +107,7 @@ func (im IBCModule) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet, re // set state by claiming capability to check if revert happens return capName := GetMockRecvCanaryCapabilityName(packet) - if _, err := im.scopedKeeper.NewCapability(ctx, capName); err != nil { + if _, err := im.IBCApp.ScopedKeeper.NewCapability(ctx, capName); err != nil { // application callback called twice on same packet sequence // must never occur panic(err) @@ -129,7 +129,7 @@ func (im IBCModule) OnAcknowledgementPacket(ctx sdk.Context, packet channeltypes } capName := GetMockAckCanaryCapabilityName(packet) - if _, err := im.scopedKeeper.NewCapability(ctx, capName); err != nil { + if _, err := im.IBCApp.ScopedKeeper.NewCapability(ctx, capName); err != nil { // application callback called twice on same packet sequence // must never occur panic(err) @@ -145,7 +145,7 @@ func (im IBCModule) OnTimeoutPacket(ctx sdk.Context, packet channeltypes.Packet, } capName := GetMockTimeoutCanaryCapabilityName(packet) - if _, err := im.scopedKeeper.NewCapability(ctx, capName); err != nil { + if _, err := im.IBCApp.ScopedKeeper.NewCapability(ctx, capName); err != nil { // application callback called twice on same packet sequence // must never occur panic(err) diff --git a/testing/mock/mock.go b/testing/mock/mock.go index fd454aa80d9..b621a05e9f7 100644 --- a/testing/mock/mock.go +++ b/testing/mock/mock.go @@ -8,7 +8,6 @@ import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" - capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" "github.com/gorilla/mux" "github.com/grpc-ecosystem/grpc-gateway/runtime" @@ -44,6 +43,7 @@ var _ porttypes.IBCModule = IBCModule{} // PortKeeper defines the expected IBC port keeper type PortKeeper interface { BindPort(ctx sdk.Context, portID string) *capabilitytypes.Capability + IsBound(ctx sdk.Context, portID string) bool } // AppModuleBasic is the mock AppModuleBasic. @@ -89,15 +89,14 @@ func (AppModuleBasic) GetQueryCmd() *cobra.Command { // AppModule represents the AppModule for the mock module. type AppModule struct { AppModuleBasic - scopedKeeper capabilitykeeper.ScopedKeeper - portKeeper PortKeeper + ibcApps []*MockIBCApp + portKeeper PortKeeper } // NewAppModule returns a mock AppModule instance. -func NewAppModule(sk capabilitykeeper.ScopedKeeper, pk PortKeeper) AppModule { +func NewAppModule(pk PortKeeper) AppModule { return AppModule{ - scopedKeeper: sk, - portKeeper: pk, + portKeeper: pk, } } @@ -124,9 +123,13 @@ func (am AppModule) RegisterServices(module.Configurator) {} // InitGenesis implements the AppModule interface. func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate { - // bind mock port ID - cap := am.portKeeper.BindPort(ctx, ModuleName) - am.scopedKeeper.ClaimCapability(ctx, cap, host.PortPath(ModuleName)) + for _, ibcApp := range am.ibcApps { + if ibcApp.PortID != "" && !am.portKeeper.IsBound(ctx, ibcApp.PortID) { + // bind mock portID + cap := am.portKeeper.BindPort(ctx, ibcApp.PortID) + ibcApp.ScopedKeeper.ClaimCapability(ctx, cap, host.PortPath(ibcApp.PortID)) + } + } return []abci.ValidatorUpdate{} } diff --git a/testing/path.go b/testing/path.go index d447102c7b9..731d3cd5e1b 100644 --- a/testing/path.go +++ b/testing/path.go @@ -43,7 +43,9 @@ func (path *Path) RelayPacket(packet channeltypes.Packet) error { if bytes.Equal(pc, channeltypes.CommitPacket(path.EndpointA.Chain.App.AppCodec(), packet)) { // packet found, relay from A to B - path.EndpointB.UpdateClient() + if err := path.EndpointB.UpdateClient(); err != nil { + return err + } res, err := path.EndpointB.RecvPacketWithResult(packet) if err != nil { @@ -58,15 +60,17 @@ func (path *Path) RelayPacket(packet channeltypes.Packet) error { if err := path.EndpointA.AcknowledgePacket(packet, ack); err != nil { return err } - return nil + return nil } pc = path.EndpointB.Chain.App.GetIBCKeeper().ChannelKeeper.GetPacketCommitment(path.EndpointB.Chain.GetContext(), packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) if bytes.Equal(pc, channeltypes.CommitPacket(path.EndpointB.Chain.App.AppCodec(), packet)) { // packet found, relay B to A - path.EndpointA.UpdateClient() + if err := path.EndpointA.UpdateClient(); err != nil { + return err + } res, err := path.EndpointA.RecvPacketWithResult(packet) if err != nil { diff --git a/testing/simapp/ante_handler.go b/testing/simapp/ante_handler.go index 8e3e1f069ec..04ffad13e2a 100644 --- a/testing/simapp/ante_handler.go +++ b/testing/simapp/ante_handler.go @@ -5,16 +5,15 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/auth/ante" - channelkeeper "github.com/cosmos/ibc-go/v3/modules/core/04-channel/keeper" ibcante "github.com/cosmos/ibc-go/v3/modules/core/ante" + "github.com/cosmos/ibc-go/v3/modules/core/keeper" ) -// HandlerOptions extend the SDK's AnteHandler options by requiring the IBC -// channel keeper. +// HandlerOptions extend the SDK's AnteHandler options by requiring the IBC keeper. type HandlerOptions struct { ante.HandlerOptions - IBCChannelkeeper channelkeeper.Keeper + IBCKeeper *keeper.Keeper } // NewAnteHandler creates a new ante handler @@ -49,7 +48,7 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) { ante.NewSigGasConsumeDecorator(options.AccountKeeper, sigGasConsumer), ante.NewSigVerificationDecorator(options.AccountKeeper, options.SignModeHandler), ante.NewIncrementSequenceDecorator(options.AccountKeeper), - ibcante.NewAnteDecorator(options.IBCChannelkeeper), + ibcante.NewAnteDecorator(options.IBCKeeper), } return sdk.ChainAnteDecorators(anteDecorators...), nil diff --git a/testing/simapp/app.go b/testing/simapp/app.go index 890d7c4661d..ee148806852 100644 --- a/testing/simapp/app.go +++ b/testing/simapp/app.go @@ -350,8 +350,8 @@ func NewSimApp( // NOTE: the IBC mock keeper and application module is used only for testing core IBC. Do // not replicate if you do not need to test core IBC or light clients. - mockModule := ibcmock.NewAppModule(scopedIBCMockKeeper, &app.IBCKeeper.PortKeeper) - mockIBCModule := ibcmock.NewIBCModule(&ibcmock.MockIBCApp{}, scopedIBCMockKeeper) + mockModule := ibcmock.NewAppModule(&app.IBCKeeper.PortKeeper) + mockIBCModule := ibcmock.NewIBCModule(&mockModule, ibcmock.NewMockIBCApp(ibcmock.ModuleName, scopedIBCMockKeeper)) app.ICAControllerKeeper = icacontrollerkeeper.NewKeeper( appCodec, keys[icacontrollertypes.StoreKey], app.GetSubspace(icacontrollertypes.SubModuleName), @@ -369,7 +369,7 @@ func NewSimApp( icaModule := ica.NewAppModule(&app.ICAControllerKeeper, &app.ICAHostKeeper) // initialize ICA module with mock module as the authentication module on the controller side - icaAuthModule := ibcmock.NewIBCModule(&ibcmock.MockIBCApp{}, scopedICAMockKeeper) + icaAuthModule := ibcmock.NewIBCModule(&mockModule, ibcmock.NewMockIBCApp("", scopedICAMockKeeper)) app.ICAAuthModule = icaAuthModule icaControllerIBCModule := icacontroller.NewIBCModule(app.ICAControllerKeeper, icaAuthModule) @@ -503,7 +503,7 @@ func NewSimApp( FeegrantKeeper: app.FeeGrantKeeper, SigGasConsumer: ante.DefaultSigVerificationGasConsumer, }, - IBCChannelkeeper: app.IBCKeeper.ChannelKeeper, + IBCKeeper: app.IBCKeeper, }, ) if err != nil { @@ -571,6 +571,12 @@ func (app *SimApp) ModuleAccountAddrs() map[string]bool { return modAccAddrs } +// GetModuleManager returns the app module manager +// NOTE: used for testing purposes +func (app *SimApp) GetModuleManager() *module.Manager { + return app.mm +} + // LegacyAmino returns SimApp's amino codec. // // NOTE: This is solely to be used for testing purposes as it may be desirable From ebf40bbc00b66eb435a76804192a72c660439a04 Mon Sep 17 00:00:00 2001 From: Sean King Date: Tue, 15 Mar 2022 11:37:53 +0100 Subject: [PATCH 08/71] 02-client: merge misbehavior & header interfaces (#1107) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: allow the mock module to be used multiple times as base ibc application in middleware stack (#892) ## Description Currently the `AppModule` assumes a single scoped keeper. This doesn't allow the mock module to be used as a base application for different middleware stack (ica stack, fee stack, etc) I broke the API because I think it is cleaner. If we want this to be non API breaking, I can try to readjust ref: #891 --- 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. - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * feat: adding Pack/Unpack acknowledgement helper fns (#895) * feat: adding Pack/Unpack acknowledgement helper fns * chore: changelog * fix: docs * Update modules/core/04-channel/types/codec.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * imp: support custom keys for testing (#893) * chore: add ParsePacketFromEvents testing helper function (#904) ## Description ref: #891 --- 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. - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * fix: correctly claim capability for mock module, handle genesis exports (#921) ## Description This contains two fixes: - the capability being claimed by the scoped keeper was incorrect (mock.ModuleName -> port ID) - the mock module wasn't accounting for non empty genesis state in capabilities (after genesis export, capability will create the bound ports so rebinding doesn't need to happen) closes: #XXXX --- 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. - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * docs: update migration docs for upgrade proposal in relation to ICS27 (#920) ## Description closes: #XXXX --- 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. - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * chore(ica): add trail of bits audit report (#903) * chore(ica): add trail of bits audit report * relocate the audit report for ICA Co-authored-by: Carlos Rodriguez * testing: adding multiple sender accounts for testing purposes (#935) * testing: adding multiple sender accounts for testing puproses * fix genesis setup (#936) * Update testing/chain.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * refactor: code hygiene * Update testing/chain.go Co-authored-by: Aditya * fix: setting totalySupply to empty * nit: CamelCase not UPPERCASE Co-authored-by: Aditya Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * Create test chain with multiple validators (#942) * testing: adding multiple sender accounts for testing puproses * fix genesis setup (#936) * Update testing/chain.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * refactor: code hygiene * Update testing/chain.go Co-authored-by: Aditya * multi validator commit taken from @saione * add function to pass custom valset * add changelog Co-authored-by: Sean King Co-authored-by: Sean King Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * add changelog entry for SDK bump * fix: classify client states without consensus states as expired (#941) ## Description closes: #850 --- 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. - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * chore: fix broken link (#972) * add backport actions for v1.3.x and v2.1.x (#958) * Revert "feat: adding Pack/Unpack acknowledgement helper fns (#895)" (#973) This reverts commit 843b459635da8cedd92945141c4efe3a762f305d. Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> * chore: update migration docs (#985) * chore: update migration docs * Update docs/migrations/v2-to-v3.md Co-authored-by: Damian Nolan Co-authored-by: Damian Nolan * chore: fix mispelled words (#991) * fix: remove go mod tidy from proto-gen script (#989) * bug: support base denoms with slashes (#978) * bug: support base denoms with slashes * add changelog entry Co-authored-by: Carlos Rodriguez * upgrade ics23 to v0.7 (#948) * upgrade ics23 to v0.7-rc * add changelog entry * update ics23 to final 0.7 Co-authored-by: Carlos Rodriguez * ibctesting: make `testing.T` public (#1020) * add changelog entry for #941 * fix package import (#1007) * feat: Add a function to initialize the ICS27 module via an upgrade proposal (#1037) ## Description closes: #1034 --- 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. - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * docs: add missing args to NewKeeper in integration docs (#1038) ## Description This add some missing arguments to `ibckeeper.NewKeeper` and `ibctransferkeeper.NewKeeper` in integration docs --- 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)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [x] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * small fixes for v2 to v3 migration (#1016) * small fixes for v2 to v3 migration * review comment * Update v2-to-v3.md * add store upgrade documentation Co-authored-by: Carlos Rodriguez * add missing slash * build(deps): bump actions/checkout from 2.4.0 to 3 (#1045) Bumps [actions/checkout](https://github.com/actions/checkout) from 2.4.0 to 3.

Release notes

Sourced from actions/checkout's releases.

v3.0.0

  • Update default runtime to node16
Changelog

Sourced from actions/checkout's changelog.

Changelog

v2.3.1

v2.3.0

v2.2.0

v2.1.1

  • Changes to support GHES (here and here)

v2.1.0

v2.0.0

v2 (beta)

  • Improved fetch performance
    • The default behavior now fetches only the SHA being checked-out
  • Script authenticated git commands
    • Persists with.token in the local git config
    • Enables your scripts to run authenticated git commands
    • Post-job cleanup removes the token
    • Coming soon: Opt out by setting with.persist-credentials to false
  • Creates a local branch
    • No longer detached HEAD when checking out a branch
    • A local branch is created with the corresponding upstream branch set
  • Improved layout

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=2.4.0&new-version=3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
* call packet.GetSequence() rather than passing the func as argument (#995) * Add counterpartyChannelID param to IBCModule.OnChanOpenAck (#1086) * add counterpartyChannelID param to IBCModule OnChanOpenAck() * change testing mock * change ica IBCModules ChannelOpenAck * change transfer IBCModules ChannelOpenAck * change core keeper ChannelOpenAck() * CHANGELOG.md * update v2-to-v3 migration doc * Update docs/migrations/v2-to-v3.md Co-authored-by: Carlos Rodriguez Co-authored-by: Carlos Rodriguez * fix mirgation docs (#1091) * fix: handle testing update client errors (#1094) * replace channel keeper with IBC keeper in AnteDecorator (#950) * replace channel keeper with IBC keeper in AnteDecorator and pass message to rpc handler * fix error checking condition * fix for proper way of getting go context * refactor tests for ante handler * review comment * review comments and some fixes * review comments * execute message for update client as well * add migration Co-authored-by: Carlos Rodriguez * add backport rules for v1.4.x and v2.2.x (#1085) * ibctesting: custom voting power reduction for testing (#939) * ibctesting: custom voting power reduction for testing * changelog * fix * make T public * fix * revert changes * fix test * merging Header & Misbehavior interfaces into ClientMessage & fixing associated 02-client helpers * fix: update related functions * chore: comments * Update modules/core/02-client/types/encoding.go Co-authored-by: Damian Nolan * chore: changelog * chore: comment Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> Co-authored-by: Carlos Rodriguez Co-authored-by: Carlos Rodriguez Co-authored-by: Aditya Co-authored-by: Tim Lind Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Damian Nolan Co-authored-by: daeMOn Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Joe Bowman Co-authored-by: khanh <50263489+catShaark@users.noreply.github.com> --- CHANGELOG.md | 2 + docs/architecture/adr-027-ibc-wasm.md | 2 +- modules/core/02-client/client/cli/tx.go | 4 +- modules/core/02-client/keeper/client.go | 6 +- modules/core/02-client/keeper/client_test.go | 2 +- .../core/02-client/legacy/v100/solomachine.go | 4 +- modules/core/02-client/types/codec.go | 60 +++++------------- modules/core/02-client/types/codec_test.go | 61 +++---------------- modules/core/02-client/types/encoding.go | 24 ++++---- modules/core/02-client/types/encoding_test.go | 7 +-- modules/core/02-client/types/msgs.go | 16 ++--- modules/core/ante/ante_test.go | 2 +- modules/core/exported/client.go | 20 +++--- modules/core/keeper/msg_server.go | 4 +- .../06-solomachine/types/codec.go | 4 +- .../06-solomachine/types/header.go | 2 +- .../06-solomachine/types/misbehaviour.go | 8 ++- .../types/misbehaviour_handle.go | 2 +- .../types/misbehaviour_handle_test.go | 2 +- .../06-solomachine/types/update.go | 2 +- .../06-solomachine/types/update_test.go | 2 +- .../07-tendermint/types/codec.go | 4 +- .../07-tendermint/types/header.go | 2 +- .../07-tendermint/types/misbehaviour.go | 8 ++- .../types/misbehaviour_handle.go | 2 +- .../types/misbehaviour_handle_test.go | 2 +- .../07-tendermint/types/update.go | 2 +- .../09-localhost/types/client_state.go | 12 ++-- testing/endpoint.go | 2 +- 29 files changed, 99 insertions(+), 171 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66296a985a8..929183fb2fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,6 +63,8 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (testing) [\#892](https://github.com/cosmos/ibc-go/pull/892) IBC Mock modules store the scoped keeper and portID within the IBCMockApp. They also maintain reference to the AppModule to update the AppModule's list of IBC applications it references. Allows for the mock module to be reused as a base application in middleware stacks. * (channel) [\#882](https://github.com/cosmos/ibc-go/pull/882) The `WriteAcknowledgement` API now takes `exported.Acknowledgement` instead of a byte array * (modules/core/ante) [\#950](https://github.com/cosmos/ibc-go/pull/950) Replaces the channel keeper with the IBC keeper in the IBC `AnteDecorator` in order to execute the entire message and be able to reject redundant messages that are in the same block as the non-redundant messages. +* (modules/core/exported) [\#1107](https://github.com/cosmos/ibc-go/pull/1107) Merging the `Header` and `Misbehaviour` interfaces into a single `ClientMessage` type + ### State Machine Breaking diff --git a/docs/architecture/adr-027-ibc-wasm.md b/docs/architecture/adr-027-ibc-wasm.md index d105c9854b4..4197735e3a7 100644 --- a/docs/architecture/adr-027-ibc-wasm.md +++ b/docs/architecture/adr-027-ibc-wasm.md @@ -100,7 +100,7 @@ packaged inside a payload which is json serialized and passed to `callContract` array of bytes returned by the smart contract. This data is deserialized and passed as return argument. ```go -func (c *ClientState) CheckProposedHeaderAndUpdateState(context sdk.Context, marshaler codec.BinaryMarshaler, store sdk.KVStore, header exported.Header) (exported.ClientState, exported.ConsensusState, error) { +func (c *ClientState) CheckProposedHeaderAndUpdateState(context sdk.Context, marshaler codec.BinaryMarshaler, store sdk.KVStore, header exported.ClientMessage) (exported.ClientState, exported.ConsensusState, error) { // get consensus state corresponding to client state to check if the client is expired consensusState, err := GetConsensusState(store, marshaler, c.LatestHeight) if err != nil { diff --git a/modules/core/02-client/client/cli/tx.go b/modules/core/02-client/client/cli/tx.go index 65703fb1f4c..097baaa5f82 100644 --- a/modules/core/02-client/client/cli/tx.go +++ b/modules/core/02-client/client/cli/tx.go @@ -100,7 +100,7 @@ func NewUpdateClientCmd() *cobra.Command { cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) - var header exported.Header + var header exported.ClientMessage headerContentOrFileName := args[1] if err := cdc.UnmarshalInterfaceJSON([]byte(headerContentOrFileName), &header); err != nil { @@ -141,7 +141,7 @@ func NewSubmitMisbehaviourCmd() *cobra.Command { } cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) - var misbehaviour exported.Misbehaviour + var misbehaviour exported.ClientMessage clientID := args[0] misbehaviourContentOrFileName := args[1] if err := cdc.UnmarshalInterfaceJSON([]byte(misbehaviourContentOrFileName), &misbehaviour); err != nil { diff --git a/modules/core/02-client/keeper/client.go b/modules/core/02-client/keeper/client.go index 600519bf5f4..78086ddb4d6 100644 --- a/modules/core/02-client/keeper/client.go +++ b/modules/core/02-client/keeper/client.go @@ -57,7 +57,7 @@ func (k Keeper) CreateClient( } // UpdateClient updates the consensus state and the state root from a provided header. -func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.Header) error { +func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.ClientMessage) error { clientState, found := k.GetClientState(ctx, clientID) if !found { return sdkerrors.Wrapf(types.ErrClientNotFound, "cannot update client with ID %s", clientID) @@ -85,7 +85,7 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.H // Marshal the Header as an Any and encode the resulting bytes to hex. // This prevents the event value from containing invalid UTF-8 characters // which may cause data to be lost when JSON encoding/decoding. - headerStr = hex.EncodeToString(types.MustMarshalHeader(k.cdc, header)) + headerStr = hex.EncodeToString(types.MustMarshalClientMessage(k.cdc, header)) // set default consensus height with header height consensusHeight = header.GetHeight() @@ -188,7 +188,7 @@ func (k Keeper) UpgradeClient(ctx sdk.Context, clientID string, upgradedClient e // CheckMisbehaviourAndUpdateState checks for client misbehaviour and freezes the // client if so. -func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, clientID string, misbehaviour exported.Misbehaviour) error { +func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, clientID string, misbehaviour exported.ClientMessage) error { clientState, found := k.GetClientState(ctx, clientID) if !found { return sdkerrors.Wrapf(types.ErrClientNotFound, "cannot check misbehaviour for client with ID %s", clientID) diff --git a/modules/core/02-client/keeper/client_test.go b/modules/core/02-client/keeper/client_test.go index dad38787c47..8d70a1816e0 100644 --- a/modules/core/02-client/keeper/client_test.go +++ b/modules/core/02-client/keeper/client_test.go @@ -691,7 +691,7 @@ func (suite *KeeperTestSuite) TestUpdateClientEventEmission() { bz, err := hex.DecodeString(string(attr.Value)) suite.Require().NoError(err) - emittedHeader, err := types.UnmarshalHeader(suite.chainA.App.AppCodec(), bz) + emittedHeader, err := types.UnmarshalClientMessage(suite.chainA.App.AppCodec(), bz) suite.Require().NoError(err) suite.Require().Equal(header, emittedHeader) } diff --git a/modules/core/02-client/legacy/v100/solomachine.go b/modules/core/02-client/legacy/v100/solomachine.go index b9ae2b1005e..e16ba5b60ac 100644 --- a/modules/core/02-client/legacy/v100/solomachine.go +++ b/modules/core/02-client/legacy/v100/solomachine.go @@ -90,14 +90,14 @@ func (cs ClientState) ExportMetadata(_ sdk.KVStore) []exported.GenesisMetadata { // CheckHeaderAndUpdateState panics! func (cs *ClientState) CheckHeaderAndUpdateState( - _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.Header, + _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientMessage, ) (exported.ClientState, exported.ConsensusState, error) { panic("legacy solo machine is deprecated!") } // CheckMisbehaviourAndUpdateState panics! func (cs ClientState) CheckMisbehaviourAndUpdateState( - _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.Misbehaviour, + _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientMessage, ) (exported.ClientState, error) { panic("legacy solo machine is deprecated!") } diff --git a/modules/core/02-client/types/codec.go b/modules/core/02-client/types/codec.go index 0497fa15f37..defdf4c3480 100644 --- a/modules/core/02-client/types/codec.go +++ b/modules/core/02-client/types/codec.go @@ -23,7 +23,7 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) { ) registry.RegisterInterface( "ibc.core.client.v1.Header", - (*exported.Header)(nil), + (*exported.ClientMessage)(nil), ) registry.RegisterInterface( "ibc.core.client.v1.Height", @@ -32,7 +32,7 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) { ) registry.RegisterInterface( "ibc.core.client.v1.Misbehaviour", - (*exported.Misbehaviour)(nil), + (*exported.ClientMessage)(nil), ) registry.RegisterImplementations( (*govtypes.Content)(nil), @@ -124,66 +124,34 @@ func UnpackConsensusState(any *codectypes.Any) (exported.ConsensusState, error) return consensusState, nil } -// PackHeader constructs a new Any packed with the given header value. It returns -// an error if the header can't be casted to a protobuf message or if the concrete +// PackClientMessage constructs a new Any packed with the given value. It returns +// an error if the value can't be casted to a protobuf message or if the concrete // implemention is not registered to the protobuf codec. -func PackHeader(header exported.Header) (*codectypes.Any, error) { - msg, ok := header.(proto.Message) +func PackClientMessage(clientMessage exported.ClientMessage) (*codectypes.Any, error) { + msg, ok := clientMessage.(proto.Message) if !ok { - return nil, sdkerrors.Wrapf(sdkerrors.ErrPackAny, "cannot proto marshal %T", header) + return nil, sdkerrors.Wrapf(sdkerrors.ErrPackAny, "cannot proto marshal %T", clientMessage) } - anyHeader, err := codectypes.NewAnyWithValue(msg) + any, err := codectypes.NewAnyWithValue(msg) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrPackAny, err.Error()) } - return anyHeader, nil + return any, nil } -// UnpackHeader unpacks an Any into a Header. It returns an error if the -// consensus state can't be unpacked into a Header. -func UnpackHeader(any *codectypes.Any) (exported.Header, error) { +// UnpackClientMessage unpacks an Any into a ClientMessage. It returns an error if the +// consensus state can't be unpacked into a ClientMessage. +func UnpackClientMessage(any *codectypes.Any) (exported.ClientMessage, error) { if any == nil { return nil, sdkerrors.Wrap(sdkerrors.ErrUnpackAny, "protobuf Any message cannot be nil") } - header, ok := any.GetCachedValue().(exported.Header) + clientMessage, ok := any.GetCachedValue().(exported.ClientMessage) if !ok { return nil, sdkerrors.Wrapf(sdkerrors.ErrUnpackAny, "cannot unpack Any into Header %T", any) } - return header, nil -} - -// PackMisbehaviour constructs a new Any packed with the given misbehaviour value. It returns -// an error if the misbehaviour can't be casted to a protobuf message or if the concrete -// implemention is not registered to the protobuf codec. -func PackMisbehaviour(misbehaviour exported.Misbehaviour) (*codectypes.Any, error) { - msg, ok := misbehaviour.(proto.Message) - if !ok { - return nil, sdkerrors.Wrapf(sdkerrors.ErrPackAny, "cannot proto marshal %T", misbehaviour) - } - - anyMisbhaviour, err := codectypes.NewAnyWithValue(msg) - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrPackAny, err.Error()) - } - - return anyMisbhaviour, nil -} - -// UnpackMisbehaviour unpacks an Any into a Misbehaviour. It returns an error if the -// Any can't be unpacked into a Misbehaviour. -func UnpackMisbehaviour(any *codectypes.Any) (exported.Misbehaviour, error) { - if any == nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrUnpackAny, "protobuf Any message cannot be nil") - } - - misbehaviour, ok := any.GetCachedValue().(exported.Misbehaviour) - if !ok { - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnpackAny, "cannot unpack Any into Misbehaviour %T", any) - } - - return misbehaviour, nil + return clientMessage, nil } diff --git a/modules/core/02-client/types/codec_test.go b/modules/core/02-client/types/codec_test.go index 85b53e5ad84..b93773bebe5 100644 --- a/modules/core/02-client/types/codec_test.go +++ b/modules/core/02-client/types/codec_test.go @@ -116,11 +116,11 @@ func (suite *TypesTestSuite) TestPackConsensusState() { } } -func (suite *TypesTestSuite) TestPackHeader() { +func (suite *TypesTestSuite) TestPackClientMessage() { testCases := []struct { - name string - header exported.Header - expPass bool + name string + clientMessage exported.ClientMessage + expPass bool }{ { "solo machine header", @@ -142,7 +142,7 @@ func (suite *TypesTestSuite) TestPackHeader() { testCasesAny := []caseAny{} for _, tc := range testCases { - clientAny, err := types.PackHeader(tc.header) + clientAny, err := types.PackClientMessage(tc.clientMessage) if tc.expPass { suite.Require().NoError(err, tc.name) } else { @@ -153,57 +153,10 @@ func (suite *TypesTestSuite) TestPackHeader() { } for i, tc := range testCasesAny { - cs, err := types.UnpackHeader(tc.any) + cs, err := types.UnpackClientMessage(tc.any) if tc.expPass { suite.Require().NoError(err, tc.name) - suite.Require().Equal(testCases[i].header, cs, tc.name) - } else { - suite.Require().Error(err, tc.name) - } - } -} - -func (suite *TypesTestSuite) TestPackMisbehaviour() { - testCases := []struct { - name string - misbehaviour exported.Misbehaviour - expPass bool - }{ - { - "solo machine misbehaviour", - ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2).CreateMisbehaviour(), - true, - }, - { - "tendermint misbehaviour", - ibctmtypes.NewMisbehaviour("tendermint", suite.chainA.LastHeader, suite.chainA.LastHeader), - true, - }, - { - "nil", - nil, - false, - }, - } - - testCasesAny := []caseAny{} - - for _, tc := range testCases { - clientAny, err := types.PackMisbehaviour(tc.misbehaviour) - if tc.expPass { - suite.Require().NoError(err, tc.name) - } else { - suite.Require().Error(err, tc.name) - } - - testCasesAny = append(testCasesAny, caseAny{tc.name, clientAny, tc.expPass}) - } - - for i, tc := range testCasesAny { - cs, err := types.UnpackMisbehaviour(tc.any) - if tc.expPass { - suite.Require().NoError(err, tc.name) - suite.Require().Equal(testCases[i].misbehaviour, cs, tc.name) + suite.Require().Equal(testCases[i].clientMessage, cs, tc.name) } else { suite.Require().Error(err, tc.name) } diff --git a/modules/core/02-client/types/encoding.go b/modules/core/02-client/types/encoding.go index 5693ba41e2a..0ffff6971c5 100644 --- a/modules/core/02-client/types/encoding.go +++ b/modules/core/02-client/types/encoding.go @@ -86,29 +86,29 @@ func UnmarshalConsensusState(cdc codec.BinaryCodec, bz []byte) (exported.Consens return consensusState, nil } -// MarshalHeader protobuf serializes a Header interface -func MarshalHeader(cdc codec.BinaryCodec, h exported.Header) ([]byte, error) { - return cdc.MarshalInterface(h) +// MarshalClientMessage protobuf serializes a ClientMessage interface +func MarshalClientMessage(cdc codec.BinaryCodec, clientMessage exported.ClientMessage) ([]byte, error) { + return cdc.MarshalInterface(clientMessage) } -// MustMarshalHeader attempts to encode a Header object and returns the +// MustMarshalClientMessage attempts to encode a ClientMessage object and returns the // raw encoded bytes. It panics on error. -func MustMarshalHeader(cdc codec.BinaryCodec, header exported.Header) []byte { - bz, err := MarshalHeader(cdc, header) +func MustMarshalClientMessage(cdc codec.BinaryCodec, clientMessage exported.ClientMessage) []byte { + bz, err := MarshalClientMessage(cdc, clientMessage) if err != nil { - panic(fmt.Errorf("failed to encode header: %w", err)) + panic(fmt.Errorf("failed to encode ClientMessage: %w", err)) } return bz } -// UnmarshalHeader returns a Header interface from raw proto encoded header bytes. +// UnmarshalClientMessage returns a ClientMessage interface from raw proto encoded header bytes. // An error is returned upon decoding failure. -func UnmarshalHeader(cdc codec.BinaryCodec, bz []byte) (exported.Header, error) { - var header exported.Header - if err := cdc.UnmarshalInterface(bz, &header); err != nil { +func UnmarshalClientMessage(cdc codec.BinaryCodec, bz []byte) (exported.ClientMessage, error) { + var clientMessage exported.ClientMessage + if err := cdc.UnmarshalInterface(bz, &clientMessage); err != nil { return nil, err } - return header, nil + return clientMessage, nil } diff --git a/modules/core/02-client/types/encoding_test.go b/modules/core/02-client/types/encoding_test.go index 9bd619260ea..efc4f85aad3 100644 --- a/modules/core/02-client/types/encoding_test.go +++ b/modules/core/02-client/types/encoding_test.go @@ -13,18 +13,17 @@ func (suite *TypesTestSuite) TestMarshalHeader() { } // marshal header - bz, err := types.MarshalHeader(cdc, h) + bz, err := types.MarshalClientMessage(cdc, h) suite.Require().NoError(err) // unmarshal header - newHeader, err := types.UnmarshalHeader(cdc, bz) + newHeader, err := types.UnmarshalClientMessage(cdc, bz) suite.Require().NoError(err) suite.Require().Equal(h, newHeader) // use invalid bytes - invalidHeader, err := types.UnmarshalHeader(cdc, []byte("invalid bytes")) + invalidHeader, err := types.UnmarshalClientMessage(cdc, []byte("invalid bytes")) suite.Require().Error(err) suite.Require().Nil(invalidHeader) - } diff --git a/modules/core/02-client/types/msgs.go b/modules/core/02-client/types/msgs.go index 843070b289f..f1b8076c5b9 100644 --- a/modules/core/02-client/types/msgs.go +++ b/modules/core/02-client/types/msgs.go @@ -104,8 +104,8 @@ func (msg MsgCreateClient) UnpackInterfaces(unpacker codectypes.AnyUnpacker) err // NewMsgUpdateClient creates a new MsgUpdateClient instance //nolint:interfacer -func NewMsgUpdateClient(id string, header exported.Header, signer string) (*MsgUpdateClient, error) { - anyHeader, err := PackHeader(header) +func NewMsgUpdateClient(id string, header exported.ClientMessage, signer string) (*MsgUpdateClient, error) { + anyHeader, err := PackClientMessage(header) if err != nil { return nil, err } @@ -123,7 +123,7 @@ func (msg MsgUpdateClient) ValidateBasic() error { if err != nil { return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) } - header, err := UnpackHeader(msg.Header) + header, err := UnpackClientMessage(msg.Header) if err != nil { return err } @@ -147,7 +147,7 @@ func (msg MsgUpdateClient) GetSigners() []sdk.AccAddress { // UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces func (msg MsgUpdateClient) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { - var header exported.Header + var header exported.ClientMessage return unpacker.UnpackAny(msg.Header, &header) } @@ -229,8 +229,8 @@ func (msg MsgUpgradeClient) UnpackInterfaces(unpacker codectypes.AnyUnpacker) er // NewMsgSubmitMisbehaviour creates a new MsgSubmitMisbehaviour instance. //nolint:interfacer -func NewMsgSubmitMisbehaviour(clientID string, misbehaviour exported.Misbehaviour, signer string) (*MsgSubmitMisbehaviour, error) { - anyMisbehaviour, err := PackMisbehaviour(misbehaviour) +func NewMsgSubmitMisbehaviour(clientID string, misbehaviour exported.ClientMessage, signer string) (*MsgSubmitMisbehaviour, error) { + anyMisbehaviour, err := PackClientMessage(misbehaviour) if err != nil { return nil, err } @@ -248,7 +248,7 @@ func (msg MsgSubmitMisbehaviour) ValidateBasic() error { if err != nil { return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) } - misbehaviour, err := UnpackMisbehaviour(msg.Misbehaviour) + misbehaviour, err := UnpackClientMessage(msg.Misbehaviour) if err != nil { return err } @@ -270,6 +270,6 @@ func (msg MsgSubmitMisbehaviour) GetSigners() []sdk.AccAddress { // UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces func (msg MsgSubmitMisbehaviour) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { - var misbehaviour exported.Misbehaviour + var misbehaviour exported.ClientMessage return unpacker.UnpackAny(msg.Misbehaviour, &misbehaviour) } diff --git a/modules/core/ante/ante_test.go b/modules/core/ante/ante_test.go index c04f6483f74..737b5f97b7b 100644 --- a/modules/core/ante/ante_test.go +++ b/modules/core/ante/ante_test.go @@ -153,7 +153,7 @@ func (suite *AnteTestSuite) createUpdateClientMessage() sdk.Msg { // ensure counterparty has committed state endpoint.Chain.Coordinator.CommitBlock(endpoint.Counterparty.Chain) - var header exported.Header + var header exported.ClientMessage switch endpoint.ClientConfig.GetClientType() { case exported.Tendermint: diff --git a/modules/core/exported/client.go b/modules/core/exported/client.go index a4839bdbf97..bb473468041 100644 --- a/modules/core/exported/client.go +++ b/modules/core/exported/client.go @@ -58,8 +58,8 @@ type ClientState interface { // Update and Misbehaviour functions - CheckHeaderAndUpdateState(sdk.Context, codec.BinaryCodec, sdk.KVStore, Header) (ClientState, ConsensusState, error) - CheckMisbehaviourAndUpdateState(sdk.Context, codec.BinaryCodec, sdk.KVStore, Misbehaviour) (ClientState, error) + CheckHeaderAndUpdateState(sdk.Context, codec.BinaryCodec, sdk.KVStore, ClientMessage) (ClientState, ConsensusState, error) + CheckMisbehaviourAndUpdateState(sdk.Context, codec.BinaryCodec, sdk.KVStore, ClientMessage) (ClientState, error) CheckSubstituteAndUpdateState(ctx sdk.Context, cdc codec.BinaryCodec, subjectClientStore, substituteClientStore sdk.KVStore, substituteClient ClientState) (ClientState, error) // Upgrade functions @@ -194,20 +194,14 @@ type ConsensusState interface { ValidateBasic() error } -// Misbehaviour defines counterparty misbehaviour for a specific consensus type -type Misbehaviour interface { +// ClientMessage is an interface used to update an IBC client. +// The update may be done by a single header, a batch of headers, misbehaviour, or any type which when verified produces +// a change to state of the IBC client +type ClientMessage interface { proto.Message - ClientType() string - ValidateBasic() error -} - -// Header is the consensus state update information -type Header interface { - proto.Message - - ClientType() string GetHeight() Height + ClientType() string ValidateBasic() error } diff --git a/modules/core/keeper/msg_server.go b/modules/core/keeper/msg_server.go index 74e7cc19ae4..673978e053b 100644 --- a/modules/core/keeper/msg_server.go +++ b/modules/core/keeper/msg_server.go @@ -45,7 +45,7 @@ func (k Keeper) CreateClient(goCtx context.Context, msg *clienttypes.MsgCreateCl func (k Keeper) UpdateClient(goCtx context.Context, msg *clienttypes.MsgUpdateClient) (*clienttypes.MsgUpdateClientResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - header, err := clienttypes.UnpackHeader(msg.Header) + header, err := clienttypes.UnpackClientMessage(msg.Header) if err != nil { return nil, err } @@ -82,7 +82,7 @@ func (k Keeper) UpgradeClient(goCtx context.Context, msg *clienttypes.MsgUpgrade func (k Keeper) SubmitMisbehaviour(goCtx context.Context, msg *clienttypes.MsgSubmitMisbehaviour) (*clienttypes.MsgSubmitMisbehaviourResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - misbehaviour, err := clienttypes.UnpackMisbehaviour(msg.Misbehaviour) + misbehaviour, err := clienttypes.UnpackClientMessage(msg.Misbehaviour) if err != nil { return nil, err } diff --git a/modules/light-clients/06-solomachine/types/codec.go b/modules/light-clients/06-solomachine/types/codec.go index 1db36165157..9ceccaef3cb 100644 --- a/modules/light-clients/06-solomachine/types/codec.go +++ b/modules/light-clients/06-solomachine/types/codec.go @@ -22,11 +22,11 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) { &ConsensusState{}, ) registry.RegisterImplementations( - (*exported.Header)(nil), + (*exported.ClientMessage)(nil), &Header{}, ) registry.RegisterImplementations( - (*exported.Misbehaviour)(nil), + (*exported.ClientMessage)(nil), &Misbehaviour{}, ) } diff --git a/modules/light-clients/06-solomachine/types/header.go b/modules/light-clients/06-solomachine/types/header.go index 7bcfb9937c5..eb2cbe5f8b3 100644 --- a/modules/light-clients/06-solomachine/types/header.go +++ b/modules/light-clients/06-solomachine/types/header.go @@ -10,7 +10,7 @@ import ( "github.com/cosmos/ibc-go/v3/modules/core/exported" ) -var _ exported.Header = &Header{} +var _ exported.ClientMessage = &Header{} // ClientType defines that the Header is a Solo Machine. func (Header) ClientType() string { diff --git a/modules/light-clients/06-solomachine/types/misbehaviour.go b/modules/light-clients/06-solomachine/types/misbehaviour.go index 31b9b1dc97c..0b4c2f3bcd0 100644 --- a/modules/light-clients/06-solomachine/types/misbehaviour.go +++ b/modules/light-clients/06-solomachine/types/misbehaviour.go @@ -10,7 +10,7 @@ import ( "github.com/cosmos/ibc-go/v3/modules/core/exported" ) -var _ exported.Misbehaviour = &Misbehaviour{} +var _ exported.ClientMessage = &Misbehaviour{} // ClientType is a Solo Machine light client. func (misbehaviour Misbehaviour) ClientType() string { @@ -70,3 +70,9 @@ func (sd SignatureAndData) ValidateBasic() error { return nil } + +// TODO: Remove GetHeight() +// GetHeight implements the curret exported.Header interface, to be updated +func (misbehaviour Misbehaviour) GetHeight() exported.Height { + return nil +} diff --git a/modules/light-clients/06-solomachine/types/misbehaviour_handle.go b/modules/light-clients/06-solomachine/types/misbehaviour_handle.go index d5a1d57cb57..171ad08e5f0 100644 --- a/modules/light-clients/06-solomachine/types/misbehaviour_handle.go +++ b/modules/light-clients/06-solomachine/types/misbehaviour_handle.go @@ -19,7 +19,7 @@ func (cs ClientState) CheckMisbehaviourAndUpdateState( ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, - misbehaviour exported.Misbehaviour, + misbehaviour exported.ClientMessage, ) (exported.ClientState, error) { soloMisbehaviour, ok := misbehaviour.(*Misbehaviour) diff --git a/modules/light-clients/06-solomachine/types/misbehaviour_handle_test.go b/modules/light-clients/06-solomachine/types/misbehaviour_handle_test.go index db58b710772..0fd4aa0edd9 100644 --- a/modules/light-clients/06-solomachine/types/misbehaviour_handle_test.go +++ b/modules/light-clients/06-solomachine/types/misbehaviour_handle_test.go @@ -10,7 +10,7 @@ import ( func (suite *SoloMachineTestSuite) TestCheckMisbehaviourAndUpdateState() { var ( clientState exported.ClientState - misbehaviour exported.Misbehaviour + misbehaviour exported.ClientMessage ) // test singlesig and multisig public keys diff --git a/modules/light-clients/06-solomachine/types/update.go b/modules/light-clients/06-solomachine/types/update.go index 3896d2dddec..881a8d3a5d5 100644 --- a/modules/light-clients/06-solomachine/types/update.go +++ b/modules/light-clients/06-solomachine/types/update.go @@ -17,7 +17,7 @@ import ( // - the currently registered public key did not provide the update signature func (cs ClientState) CheckHeaderAndUpdateState( ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, - header exported.Header, + header exported.ClientMessage, ) (exported.ClientState, exported.ConsensusState, error) { smHeader, ok := header.(*Header) if !ok { diff --git a/modules/light-clients/06-solomachine/types/update_test.go b/modules/light-clients/06-solomachine/types/update_test.go index f13d5f198d1..6c8d34486e6 100644 --- a/modules/light-clients/06-solomachine/types/update_test.go +++ b/modules/light-clients/06-solomachine/types/update_test.go @@ -13,7 +13,7 @@ import ( func (suite *SoloMachineTestSuite) TestCheckHeaderAndUpdateState() { var ( clientState exported.ClientState - header exported.Header + header exported.ClientMessage ) // test singlesig and multisig public keys diff --git a/modules/light-clients/07-tendermint/types/codec.go b/modules/light-clients/07-tendermint/types/codec.go index c363a0cbe65..0652321ba16 100644 --- a/modules/light-clients/07-tendermint/types/codec.go +++ b/modules/light-clients/07-tendermint/types/codec.go @@ -18,11 +18,11 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) { &ConsensusState{}, ) registry.RegisterImplementations( - (*exported.Header)(nil), + (*exported.ClientMessage)(nil), &Header{}, ) registry.RegisterImplementations( - (*exported.Misbehaviour)(nil), + (*exported.ClientMessage)(nil), &Misbehaviour{}, ) } diff --git a/modules/light-clients/07-tendermint/types/header.go b/modules/light-clients/07-tendermint/types/header.go index 58e7d671e4a..b5c51ec2cbf 100644 --- a/modules/light-clients/07-tendermint/types/header.go +++ b/modules/light-clients/07-tendermint/types/header.go @@ -12,7 +12,7 @@ import ( "github.com/cosmos/ibc-go/v3/modules/core/exported" ) -var _ exported.Header = &Header{} +var _ exported.ClientMessage = &Header{} // ConsensusState returns the updated consensus state associated with the header func (h Header) ConsensusState() *ConsensusState { diff --git a/modules/light-clients/07-tendermint/types/misbehaviour.go b/modules/light-clients/07-tendermint/types/misbehaviour.go index f03ab08f7a0..e2407e6830d 100644 --- a/modules/light-clients/07-tendermint/types/misbehaviour.go +++ b/modules/light-clients/07-tendermint/types/misbehaviour.go @@ -12,7 +12,7 @@ import ( "github.com/cosmos/ibc-go/v3/modules/core/exported" ) -var _ exported.Misbehaviour = &Misbehaviour{} +var _ exported.ClientMessage = &Misbehaviour{} // Use the same FrozenHeight for all misbehaviour var FrozenHeight = clienttypes.NewHeight(0, 1) @@ -125,3 +125,9 @@ func validCommit(chainID string, blockID tmtypes.BlockID, commit *tmproto.Commit return nil } + +// TODO: Remove GetHeight() +// GetHeight implements the curret exported.Header interface, to be updated +func (misbehaviour Misbehaviour) GetHeight() exported.Height { + return nil +} diff --git a/modules/light-clients/07-tendermint/types/misbehaviour_handle.go b/modules/light-clients/07-tendermint/types/misbehaviour_handle.go index 4c8224bde09..98878fbbbe8 100644 --- a/modules/light-clients/07-tendermint/types/misbehaviour_handle.go +++ b/modules/light-clients/07-tendermint/types/misbehaviour_handle.go @@ -25,7 +25,7 @@ func (cs ClientState) CheckMisbehaviourAndUpdateState( ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, - misbehaviour exported.Misbehaviour, + misbehaviour exported.ClientMessage, ) (exported.ClientState, error) { tmMisbehaviour, ok := misbehaviour.(*Misbehaviour) if !ok { diff --git a/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go b/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go index da1efc665da..8efe54c7fba 100644 --- a/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go +++ b/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go @@ -45,7 +45,7 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { height1 clienttypes.Height consensusState2 exported.ConsensusState height2 clienttypes.Height - misbehaviour exported.Misbehaviour + misbehaviour exported.ClientMessage timestamp time.Time expPass bool }{ diff --git a/modules/light-clients/07-tendermint/types/update.go b/modules/light-clients/07-tendermint/types/update.go index c4d422ccb73..2699ebc2cc9 100644 --- a/modules/light-clients/07-tendermint/types/update.go +++ b/modules/light-clients/07-tendermint/types/update.go @@ -51,7 +51,7 @@ import ( // becoming bloated with expired consensus states that can no longer be used for updates and packet verification. func (cs ClientState) CheckHeaderAndUpdateState( ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, - header exported.Header, + header exported.ClientMessage, ) (exported.ClientState, exported.ConsensusState, error) { tmHeader, ok := header.(*Header) if !ok { diff --git a/modules/light-clients/09-localhost/types/client_state.go b/modules/light-clients/09-localhost/types/client_state.go index d8f0af38f00..e818f9c8d7e 100644 --- a/modules/light-clients/09-localhost/types/client_state.go +++ b/modules/light-clients/09-localhost/types/client_state.go @@ -79,28 +79,28 @@ func (cs ClientState) ExportMetadata(_ sdk.KVStore) []exported.GenesisMetadata { // CheckHeaderAndUpdateState updates the localhost client. It only needs access to the context func (cs *ClientState) CheckHeaderAndUpdateState( - ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, header exported.Header, + ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, header exported.ClientMessage, ) (exported.ClientState, exported.ConsensusState, error) { return cs.UpdateState(ctx, cdc, clientStore, header) } // VerifyHeader is a no-op. func (cs *ClientState) VerifyHeader( - _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.Header, + _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientMessage, ) (exported.ClientState, exported.ConsensusState, error) { return cs, nil, nil } // CheckForMisbehaviour returns false. func (cs *ClientState) CheckForMisbehaviour( - _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.Header, + _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientMessage, ) (bool, error) { return false, nil } // UpdateState updates the localhost client. It only needs access to the context func (cs *ClientState) UpdateState( - ctx sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.Header, + ctx sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientMessage, ) (exported.ClientState, exported.ConsensusState, error) { // use the chain ID from context since the localhost client is from the running chain (i.e self). cs.ChainId = ctx.ChainID() @@ -111,7 +111,7 @@ func (cs *ClientState) UpdateState( // UpdateStateOnMisbehaviour returns an error (no misbehaviour case). func (cs *ClientState) UpdateStateOnMisbehaviour( - _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.Header, + _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientMessage, ) (*ClientState, error) { return nil, sdkerrors.Wrapf(clienttypes.ErrUpdateClientFailed, "cannot update localhost client on misbehaviour") } @@ -120,7 +120,7 @@ func (cs *ClientState) UpdateStateOnMisbehaviour( // Since localhost is the client of the running chain, misbehaviour cannot be submitted to it // Thus, CheckMisbehaviourAndUpdateState returns an error for localhost func (cs ClientState) CheckMisbehaviourAndUpdateState( - _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.Misbehaviour, + _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientMessage, ) (exported.ClientState, error) { return nil, sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "cannot submit misbehaviour to localhost client") } diff --git a/testing/endpoint.go b/testing/endpoint.go index 607f7a16843..e466bd781e9 100644 --- a/testing/endpoint.go +++ b/testing/endpoint.go @@ -131,7 +131,7 @@ func (endpoint *Endpoint) UpdateClient() (err error) { // ensure counterparty has committed state endpoint.Chain.Coordinator.CommitBlock(endpoint.Counterparty.Chain) - var header exported.Header + var header exported.ClientMessage switch endpoint.ClientConfig.GetClientType() { case exported.Tendermint: From b0fa2401bf533589a5b8b9ed7506b4c0efc9c942 Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Wed, 16 Mar 2022 15:53:11 +0100 Subject: [PATCH 09/71] chore: 06-solomachine rename checkHeader to VerifyClientMessage (#1109) * adding VerifyClientMessage and tests * splitting tests for verify header and misbehaviour * cleaning up error handling * adding additional test for old misbehaviour is sucessful * split type switch logic into verifyHeader and verifyMisbehaviour priv funcs * updating godoc for VerifyClientMessage --- .../06-solomachine/types/update.go | 58 ++- .../06-solomachine/types/update_test.go | 393 +++++++++++++++++- 2 files changed, 436 insertions(+), 15 deletions(-) diff --git a/modules/light-clients/06-solomachine/types/update.go b/modules/light-clients/06-solomachine/types/update.go index 881a8d3a5d5..fd775544b61 100644 --- a/modules/light-clients/06-solomachine/types/update.go +++ b/modules/light-clients/06-solomachine/types/update.go @@ -17,38 +17,52 @@ import ( // - the currently registered public key did not provide the update signature func (cs ClientState) CheckHeaderAndUpdateState( ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, - header exported.ClientMessage, + msg exported.ClientMessage, ) (exported.ClientState, exported.ConsensusState, error) { - smHeader, ok := header.(*Header) + if err := cs.VerifyClientMessage(ctx, cdc, clientStore, msg); err != nil { + return nil, nil, err + } + + // TODO: Remove this type assertion, replace with misbehaviour checking and update state + smHeader, ok := msg.(*Header) if !ok { return nil, nil, sdkerrors.Wrapf( - clienttypes.ErrInvalidHeader, "header type %T, expected %T", header, &Header{}, + clienttypes.ErrInvalidHeader, "expected %T, got %T", &Header{}, msg, ) } - if err := checkHeader(cdc, &cs, smHeader); err != nil { - return nil, nil, err - } - clientState, consensusState := update(&cs, smHeader) return clientState, consensusState, nil } -// checkHeader checks if the Solo Machine update signature is valid. -func checkHeader(cdc codec.BinaryCodec, clientState *ClientState, header *Header) error { +// VerifyClientMessage introspects the provided ClientMessage and checks its validity +// A Solomachine Header is considered valid if the currently registered public key has signed over the new public key with the correct sequence +// A Solomachine Misbehaviour is considered valid if duplicate signatures of the current public key are found on two different messages at a given sequence +func (cs ClientState) VerifyClientMessage(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg exported.ClientMessage) error { + switch msg := clientMsg.(type) { + case *Header: + return cs.verifyHeader(ctx, cdc, clientStore, msg) + case *Misbehaviour: + return cs.verifyMisbehaviour(ctx, cdc, clientStore, msg) + default: + return sdkerrors.Wrapf(clienttypes.ErrInvalidClientType, "expected type of %T or %T, got type %T", Header{}, Misbehaviour{}, msg) + } +} + +func (cs ClientState) verifyHeader(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, header *Header) error { // assert update sequence is current sequence - if header.Sequence != clientState.Sequence { + if header.Sequence != cs.Sequence { return sdkerrors.Wrapf( clienttypes.ErrInvalidHeader, - "header sequence does not match the client state sequence (%d != %d)", header.Sequence, clientState.Sequence, + "header sequence does not match the client state sequence (%d != %d)", header.Sequence, cs.Sequence, ) } // assert update timestamp is not less than current consensus state timestamp - if header.Timestamp < clientState.ConsensusState.Timestamp { + if header.Timestamp < cs.ConsensusState.Timestamp { return sdkerrors.Wrapf( clienttypes.ErrInvalidHeader, - "header timestamp is less than to the consensus state timestamp (%d < %d)", header.Timestamp, clientState.ConsensusState.Timestamp, + "header timestamp is less than to the consensus state timestamp (%d < %d)", header.Timestamp, cs.ConsensusState.Timestamp, ) } @@ -63,7 +77,7 @@ func checkHeader(cdc codec.BinaryCodec, clientState *ClientState, header *Header return err } - publicKey, err := clientState.ConsensusState.GetPubKey() + publicKey, err := cs.ConsensusState.GetPubKey() if err != nil { return err } @@ -75,6 +89,22 @@ func checkHeader(cdc codec.BinaryCodec, clientState *ClientState, header *Header return nil } +func (cs ClientState) verifyMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, misbehaviour *Misbehaviour) error { + // NOTE: a check that the misbehaviour message data are not equal is done by + // misbehaviour.ValidateBasic which is called by the 02-client keeper. + // verify first signature + if err := verifySignatureAndData(cdc, cs, misbehaviour, misbehaviour.SignatureOne); err != nil { + return sdkerrors.Wrap(err, "failed to verify signature one") + } + + // verify second signature + if err := verifySignatureAndData(cdc, cs, misbehaviour, misbehaviour.SignatureTwo); err != nil { + return sdkerrors.Wrap(err, "failed to verify signature two") + } + + return nil +} + // update the consensus state to the new public key and an incremented sequence func update(clientState *ClientState, header *Header) (*ClientState, *ConsensusState) { consensusState := &ConsensusState{ diff --git a/modules/light-clients/06-solomachine/types/update_test.go b/modules/light-clients/06-solomachine/types/update_test.go index 6c8d34486e6..336b9926be0 100644 --- a/modules/light-clients/06-solomachine/types/update_test.go +++ b/modules/light-clients/06-solomachine/types/update_test.go @@ -13,7 +13,7 @@ import ( func (suite *SoloMachineTestSuite) TestCheckHeaderAndUpdateState() { var ( clientState exported.ClientState - header exported.ClientMessage + header exported.ClientMessage ) // test singlesig and multisig public keys @@ -180,3 +180,394 @@ func (suite *SoloMachineTestSuite) TestCheckHeaderAndUpdateState() { } } } + +func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { + var ( + clientMsg exported.ClientMessage + clientState *types.ClientState + ) + + // test singlesig and multisig public keys + for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + testCases := []struct { + name string + setup func() + expPass bool + }{ + { + "successful header", + func() { + clientMsg = solomachine.CreateHeader() + }, + true, + }, + { + "successful misbehaviour", + func() { + clientMsg = solomachine.CreateMisbehaviour() + }, + true, + }, + { + "invalid client message type", + func() { + clientMsg = &ibctmtypes.Header{} + }, + false, + }, + { + "wrong sequence in header", + func() { + // store in temp before assigning to interface type + h := solomachine.CreateHeader() + h.Sequence++ + clientMsg = h + }, + false, + }, + { + "invalid header Signature", + func() { + h := solomachine.CreateHeader() + h.Signature = suite.GetInvalidProof() + clientMsg = h + }, false, + }, + { + "invalid timestamp in header", + func() { + h := solomachine.CreateHeader() + h.Timestamp-- + clientMsg = h + }, false, + }, + { + "signature uses wrong sequence", + func() { + + solomachine.Sequence++ + clientMsg = solomachine.CreateHeader() + }, + false, + }, + { + "signature uses new pubkey to sign", + func() { + // store in temp before assinging to interface type + cs := solomachine.ClientState() + h := solomachine.CreateHeader() + + publicKey, err := codectypes.NewAnyWithValue(solomachine.PublicKey) + suite.NoError(err) + + data := &types.HeaderData{ + NewPubKey: publicKey, + NewDiversifier: h.NewDiversifier, + } + + dataBz, err := suite.chainA.Codec.Marshal(data) + suite.Require().NoError(err) + + // generate invalid signature + signBytes := &types.SignBytes{ + Sequence: cs.Sequence, + Timestamp: solomachine.Time, + Diversifier: solomachine.Diversifier, + DataType: types.CLIENT, + Data: dataBz, + } + + signBz, err := suite.chainA.Codec.Marshal(signBytes) + suite.Require().NoError(err) + + sig := solomachine.GenerateSignature(signBz) + suite.Require().NoError(err) + h.Signature = sig + + clientState = cs + clientMsg = h + + }, + false, + }, + { + "signature signs over old pubkey", + func() { + // store in temp before assinging to interface type + cs := solomachine.ClientState() + oldPubKey := solomachine.PublicKey + h := solomachine.CreateHeader() + + // generate invalid signature + data := append(sdk.Uint64ToBigEndian(cs.Sequence), oldPubKey.Bytes()...) + sig := solomachine.GenerateSignature(data) + h.Signature = sig + + clientState = cs + clientMsg = h + }, + false, + }, + { + "consensus state public key is nil - header", + func() { + clientState.ConsensusState.PublicKey = nil + clientMsg = solomachine.CreateHeader() + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + clientState = solomachine.ClientState() + + // setup test + tc.setup() + + err := clientState.VerifyClientMessage(suite.chainA.GetContext(), suite.chainA.Codec, nil, clientMsg) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } + } +} + +func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { + var ( + clientMsg exported.ClientMessage + clientState *types.ClientState + ) + + // test singlesig and multisig public keys + for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + testCases := []struct { + name string + setup func() + expPass bool + }{ + { + "successful misbehaviour", + func() { + clientMsg = solomachine.CreateMisbehaviour() + }, + true, + }, + { + "old misbehaviour is successful (timestamp is less than current consensus state)", + func() { + clientState = solomachine.ClientState() + solomachine.Time = solomachine.Time - 5 + clientMsg = solomachine.CreateMisbehaviour() + }, true, + }, + { + "invalid client message type", + func() { + clientMsg = &ibctmtypes.Header{} + }, + false, + }, + { + "consensus state pubkey is nil", + func() { + clientState.ConsensusState.PublicKey = nil + clientMsg = solomachine.CreateMisbehaviour() + }, + false, + }, + { + "invalid SignatureOne SignatureData", + func() { + m := solomachine.CreateMisbehaviour() + + m.SignatureOne.Signature = suite.GetInvalidProof() + clientMsg = m + }, false, + }, + { + "invalid SignatureTwo SignatureData", + func() { + m := solomachine.CreateMisbehaviour() + + m.SignatureTwo.Signature = suite.GetInvalidProof() + clientMsg = m + }, false, + }, + { + "invalid SignatureOne timestamp", + func() { + m := solomachine.CreateMisbehaviour() + + m.SignatureOne.Timestamp = 1000000000000 + clientMsg = m + }, false, + }, + { + "invalid SignatureTwo timestamp", + func() { + m := solomachine.CreateMisbehaviour() + + m.SignatureTwo.Timestamp = 1000000000000 + clientMsg = m + }, false, + }, + { + "invalid first signature data", + func() { + // store in temp before assigning to interface type + m := solomachine.CreateMisbehaviour() + + msg := []byte("DATA ONE") + signBytes := &types.SignBytes{ + Sequence: solomachine.Sequence + 1, + Timestamp: solomachine.Time, + Diversifier: solomachine.Diversifier, + DataType: types.CLIENT, + Data: msg, + } + + data, err := suite.chainA.Codec.Marshal(signBytes) + suite.Require().NoError(err) + + sig := solomachine.GenerateSignature(data) + + m.SignatureOne.Signature = sig + m.SignatureOne.Data = msg + clientMsg = m + }, + false, + }, + { + "invalid second signature data", + func() { + // store in temp before assigning to interface type + m := solomachine.CreateMisbehaviour() + + msg := []byte("DATA TWO") + signBytes := &types.SignBytes{ + Sequence: solomachine.Sequence + 1, + Timestamp: solomachine.Time, + Diversifier: solomachine.Diversifier, + DataType: types.CLIENT, + Data: msg, + } + + data, err := suite.chainA.Codec.Marshal(signBytes) + suite.Require().NoError(err) + + sig := solomachine.GenerateSignature(data) + + m.SignatureTwo.Signature = sig + m.SignatureTwo.Data = msg + clientMsg = m + }, + false, + }, + { + "wrong pubkey generates first signature", + func() { + badMisbehaviour := solomachine.CreateMisbehaviour() + + // update public key to a new one + solomachine.CreateHeader() + m := solomachine.CreateMisbehaviour() + + // set SignatureOne to use the wrong signature + m.SignatureOne = badMisbehaviour.SignatureOne + clientMsg = m + }, false, + }, + { + "wrong pubkey generates second signature", + func() { + badMisbehaviour := solomachine.CreateMisbehaviour() + + // update public key to a new one + solomachine.CreateHeader() + m := solomachine.CreateMisbehaviour() + + // set SignatureTwo to use the wrong signature + m.SignatureTwo = badMisbehaviour.SignatureTwo + clientMsg = m + }, false, + }, + { + "signatures sign over different sequence", + func() { + + // store in temp before assigning to interface type + m := solomachine.CreateMisbehaviour() + + // Signature One + msg := []byte("DATA ONE") + // sequence used is plus 1 + signBytes := &types.SignBytes{ + Sequence: solomachine.Sequence + 1, + Timestamp: solomachine.Time, + Diversifier: solomachine.Diversifier, + DataType: types.CLIENT, + Data: msg, + } + + data, err := suite.chainA.Codec.Marshal(signBytes) + suite.Require().NoError(err) + + sig := solomachine.GenerateSignature(data) + + m.SignatureOne.Signature = sig + m.SignatureOne.Data = msg + + // Signature Two + msg = []byte("DATA TWO") + // sequence used is minus 1 + + signBytes = &types.SignBytes{ + Sequence: solomachine.Sequence - 1, + Timestamp: solomachine.Time, + Diversifier: solomachine.Diversifier, + DataType: types.CLIENT, + Data: msg, + } + data, err = suite.chainA.Codec.Marshal(signBytes) + suite.Require().NoError(err) + + sig = solomachine.GenerateSignature(data) + + m.SignatureTwo.Signature = sig + m.SignatureTwo.Data = msg + + clientMsg = m + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + clientState = solomachine.ClientState() + + // setup test + tc.setup() + + err := clientState.VerifyClientMessage(suite.chainA.GetContext(), suite.chainA.Codec, nil, clientMsg) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } + } +} From 5e9785eb32cca36a7112a25deb683a3a44c67249 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?colin=20axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Wed, 16 Mar 2022 16:42:13 +0100 Subject: [PATCH 10/71] refactor: modify VerifyUpgradeAndUpdateState to set upgraded client and consensus state (#598) * modify VerifyUpgradeAndUpdateState interface function to remove returned client and consensus state removes the client and consensus state from the return values in VerifyUpgradeAndUpdateState client state interface function Updates light client implementations to set client and consensus state in client store Fixes and updates tests * add changelog entry * add migration docs * use upgraded client to emit height in events --- CHANGELOG.md | 2 ++ docs/migrations/v3-to-v4.md | 4 +++ modules/core/02-client/keeper/client.go | 16 ++++------ .../core/02-client/legacy/v100/solomachine.go | 2 +- modules/core/exported/client.go | 3 +- .../06-solomachine/types/client_state.go | 4 +-- .../07-tendermint/types/store.go | 7 +++++ .../07-tendermint/types/upgrade.go | 31 ++++++++++--------- .../07-tendermint/types/upgrade_test.go | 11 ++++--- .../09-localhost/types/client_state.go | 4 +-- 10 files changed, 48 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 929183fb2fe..02777a689bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,8 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (core) [\#709](https://github.com/cosmos/ibc-go/pull/709) Replace github.com/pkg/errors with stdlib errors ### API Breaking + +* (02-client) [\#598](https://github.com/cosmos/ibc-go/pull/598) The client state and consensus state return value has been removed from `VerifyUpgradeAndUpdateState`. Light client implementations must update the client state and consensus state after verifying a valid client upgrade. * (testing) [\#939](https://github.com/cosmos/ibc-go/pull/939) Support custom power reduction for testing. * (modules/core/05-port) [\#1086](https://github.com/cosmos/ibc-go/pull/1086) Added `counterpartyChannelID` argument to IBCModule.OnChanOpenAck * (06-solomachine) [\#1100](https://github.com/cosmos/ibc-go/pull/1100) Remove `GetClientID` function from 06-solomachine `Misbehaviour` type. diff --git a/docs/migrations/v3-to-v4.md b/docs/migrations/v3-to-v4.md index 90e9af256d2..f933a7380f7 100644 --- a/docs/migrations/v3-to-v4.md +++ b/docs/migrations/v3-to-v4.md @@ -23,4 +23,8 @@ No genesis or in-place migrations required when upgrading from v1 or v2 of ibc-g The `WriteAcknowledgement` API now takes the `exported.Acknowledgement` type instead of passing in the acknowledgement byte array directly. This is an API breaking change and as such IBC application developers will have to update any calls to `WriteAcknowledgement`. +## IBC Light Clients +The `VerifyUpgradeAndUpdateState` function has been modified. The client state and consensus state return value has been removed. + +Light clients **must** set the updated client state and consensus state in the client store after verifying a valid client upgrade. diff --git a/modules/core/02-client/keeper/client.go b/modules/core/02-client/keeper/client.go index 78086ddb4d6..bfadcda026e 100644 --- a/modules/core/02-client/keeper/client.go +++ b/modules/core/02-client/keeper/client.go @@ -158,30 +158,26 @@ func (k Keeper) UpgradeClient(ctx sdk.Context, clientID string, upgradedClient e return sdkerrors.Wrapf(types.ErrClientNotActive, "cannot upgrade client (%s) with status %s", clientID, status) } - updatedClientState, updatedConsState, err := clientState.VerifyUpgradeAndUpdateState(ctx, k.cdc, clientStore, - upgradedClient, upgradedConsState, proofUpgradeClient, proofUpgradeConsState) - if err != nil { + if err := clientState.VerifyUpgradeAndUpdateState(ctx, k.cdc, clientStore, + upgradedClient, upgradedConsState, proofUpgradeClient, proofUpgradeConsState, + ); err != nil { return sdkerrors.Wrapf(err, "cannot upgrade client with ID %s", clientID) } - k.SetClientState(ctx, clientID, updatedClientState) - k.SetClientConsensusState(ctx, clientID, updatedClientState.GetLatestHeight(), updatedConsState) - - k.Logger(ctx).Info("client state upgraded", "client-id", clientID, "height", updatedClientState.GetLatestHeight().String()) + k.Logger(ctx).Info("client state upgraded", "client-id", clientID, "height", upgradedClient.GetLatestHeight().String()) defer func() { telemetry.IncrCounterWithLabels( []string{"ibc", "client", "upgrade"}, 1, []metrics.Label{ - telemetry.NewLabel(types.LabelClientType, updatedClientState.ClientType()), + telemetry.NewLabel(types.LabelClientType, upgradedClient.ClientType()), telemetry.NewLabel(types.LabelClientID, clientID), }, ) }() - // emitting events in the keeper emits for client upgrades - EmitUpgradeClientEvent(ctx, clientID, updatedClientState) + EmitUpgradeClientEvent(ctx, clientID, upgradedClient) return nil } diff --git a/modules/core/02-client/legacy/v100/solomachine.go b/modules/core/02-client/legacy/v100/solomachine.go index e16ba5b60ac..c9814439902 100644 --- a/modules/core/02-client/legacy/v100/solomachine.go +++ b/modules/core/02-client/legacy/v100/solomachine.go @@ -114,7 +114,7 @@ func (cs ClientState) CheckSubstituteAndUpdateState( func (cs ClientState) VerifyUpgradeAndUpdateState( _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientState, _ exported.ConsensusState, _, _ []byte, -) (exported.ClientState, exported.ConsensusState, error) { +) error { panic("legacy solo machine is deprecated!") } diff --git a/modules/core/exported/client.go b/modules/core/exported/client.go index bb473468041..39095aff28f 100644 --- a/modules/core/exported/client.go +++ b/modules/core/exported/client.go @@ -68,6 +68,7 @@ type ClientState interface { // height of the current revision is somehow encoded in the proof verification process. // This is to ensure that no premature upgrades occur, since upgrade plans committed to by the counterparty // may be cancelled or modified before the last planned height. + // If the upgrade is verified, the upgraded client and consensus states must be set in the client store. VerifyUpgradeAndUpdateState( ctx sdk.Context, cdc codec.BinaryCodec, @@ -76,7 +77,7 @@ type ClientState interface { newConsState ConsensusState, proofUpgradeClient, proofUpgradeConsState []byte, - ) (ClientState, ConsensusState, error) + ) error // Utility function that zeroes out any client customizable fields in client state // Ledger enforced fields are maintained while all custom fields are zero values // Used to verify upgrades diff --git a/modules/light-clients/06-solomachine/types/client_state.go b/modules/light-clients/06-solomachine/types/client_state.go index d92f69b98e4..ef3088b314f 100644 --- a/modules/light-clients/06-solomachine/types/client_state.go +++ b/modules/light-clients/06-solomachine/types/client_state.go @@ -88,8 +88,8 @@ func (cs ClientState) ExportMetadata(_ sdk.KVStore) []exported.GenesisMetadata { func (cs ClientState) VerifyUpgradeAndUpdateState( _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientState, _ exported.ConsensusState, _, _ []byte, -) (exported.ClientState, exported.ConsensusState, error) { - return nil, nil, sdkerrors.Wrap(clienttypes.ErrInvalidUpgradeClient, "cannot upgrade solomachine client") +) error { + return sdkerrors.Wrap(clienttypes.ErrInvalidUpgradeClient, "cannot upgrade solomachine client") } // VerifyClientState verifies a proof of the client state of the running chain diff --git a/modules/light-clients/07-tendermint/types/store.go b/modules/light-clients/07-tendermint/types/store.go index 785ed77ba97..33c2386bb78 100644 --- a/modules/light-clients/07-tendermint/types/store.go +++ b/modules/light-clients/07-tendermint/types/store.go @@ -43,6 +43,13 @@ var ( KeyIteration = []byte("/iterationKey") ) +// setClientState stores the client state +func setClientState(clientStore sdk.KVStore, cdc codec.BinaryCodec, clientState *ClientState) { + key := host.ClientStateKey() + val := clienttypes.MustMarshalClientState(cdc, clientState) + clientStore.Set(key, val) +} + // SetConsensusState stores the consensus state at the given height. func SetConsensusState(clientStore sdk.KVStore, cdc codec.BinaryCodec, consensusState *ConsensusState, height exported.Height) { key := host.ConsensusStateKey(height) diff --git a/modules/light-clients/07-tendermint/types/upgrade.go b/modules/light-clients/07-tendermint/types/upgrade.go index 5e23c8d9036..471769f0610 100644 --- a/modules/light-clients/07-tendermint/types/upgrade.go +++ b/modules/light-clients/07-tendermint/types/upgrade.go @@ -28,16 +28,16 @@ func (cs ClientState) VerifyUpgradeAndUpdateState( ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, upgradedClient exported.ClientState, upgradedConsState exported.ConsensusState, proofUpgradeClient, proofUpgradeConsState []byte, -) (exported.ClientState, exported.ConsensusState, error) { +) error { if len(cs.UpgradePath) == 0 { - return nil, nil, sdkerrors.Wrap(clienttypes.ErrInvalidUpgradeClient, "cannot upgrade client, no upgrade path set") + return sdkerrors.Wrap(clienttypes.ErrInvalidUpgradeClient, "cannot upgrade client, no upgrade path set") } // last height of current counterparty chain must be client's latest height lastHeight := cs.GetLatestHeight() if !upgradedClient.GetLatestHeight().GT(lastHeight) { - return nil, nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidHeight, "upgraded client height %s must be at greater than current client height %s", + return sdkerrors.Wrapf(sdkerrors.ErrInvalidHeight, "upgraded client height %s must be at greater than current client height %s", upgradedClient.GetLatestHeight(), lastHeight) } @@ -46,22 +46,22 @@ func (cs ClientState) VerifyUpgradeAndUpdateState( // counterparty must also commit to the upgraded consensus state at a sub-path under the upgrade path specified tmUpgradeClient, ok := upgradedClient.(*ClientState) if !ok { - return nil, nil, sdkerrors.Wrapf(clienttypes.ErrInvalidClientType, "upgraded client must be Tendermint client. expected: %T got: %T", + return sdkerrors.Wrapf(clienttypes.ErrInvalidClientType, "upgraded client must be Tendermint client. expected: %T got: %T", &ClientState{}, upgradedClient) } tmUpgradeConsState, ok := upgradedConsState.(*ConsensusState) if !ok { - return nil, nil, sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "upgraded consensus state must be Tendermint consensus state. expected %T, got: %T", + return sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "upgraded consensus state must be Tendermint consensus state. expected %T, got: %T", &ConsensusState{}, upgradedConsState) } // unmarshal proofs var merkleProofClient, merkleProofConsState commitmenttypes.MerkleProof if err := cdc.Unmarshal(proofUpgradeClient, &merkleProofClient); err != nil { - return nil, nil, sdkerrors.Wrapf(commitmenttypes.ErrInvalidProof, "could not unmarshal client merkle proof: %v", err) + return sdkerrors.Wrapf(commitmenttypes.ErrInvalidProof, "could not unmarshal client merkle proof: %v", err) } if err := cdc.Unmarshal(proofUpgradeConsState, &merkleProofConsState); err != nil { - return nil, nil, sdkerrors.Wrapf(commitmenttypes.ErrInvalidProof, "could not unmarshal consensus state merkle proof: %v", err) + return sdkerrors.Wrapf(commitmenttypes.ErrInvalidProof, "could not unmarshal consensus state merkle proof: %v", err) } // Must prove against latest consensus state to ensure we are verifying against latest upgrade plan @@ -69,29 +69,29 @@ func (cs ClientState) VerifyUpgradeAndUpdateState( // at this consensus state consState, err := GetConsensusState(clientStore, cdc, lastHeight) if err != nil { - return nil, nil, sdkerrors.Wrap(err, "could not retrieve consensus state for lastHeight") + return sdkerrors.Wrap(err, "could not retrieve consensus state for lastHeight") } // Verify client proof bz, err := cdc.MarshalInterface(upgradedClient) if err != nil { - return nil, nil, sdkerrors.Wrapf(clienttypes.ErrInvalidClient, "could not marshal client state: %v", err) + return sdkerrors.Wrapf(clienttypes.ErrInvalidClient, "could not marshal client state: %v", err) } // construct clientState Merkle path upgradeClientPath := constructUpgradeClientMerklePath(cs.UpgradePath, lastHeight) if err := merkleProofClient.VerifyMembership(cs.ProofSpecs, consState.GetRoot(), upgradeClientPath, bz); err != nil { - return nil, nil, sdkerrors.Wrapf(err, "client state proof failed. Path: %s", upgradeClientPath.Pretty()) + return sdkerrors.Wrapf(err, "client state proof failed. Path: %s", upgradeClientPath.Pretty()) } // Verify consensus state proof bz, err = cdc.MarshalInterface(upgradedConsState) if err != nil { - return nil, nil, sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "could not marshal consensus state: %v", err) + return sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "could not marshal consensus state: %v", err) } // construct consensus state Merkle path upgradeConsStatePath := constructUpgradeConsStateMerklePath(cs.UpgradePath, lastHeight) if err := merkleProofConsState.VerifyMembership(cs.ProofSpecs, consState.GetRoot(), upgradeConsStatePath, bz); err != nil { - return nil, nil, sdkerrors.Wrapf(err, "consensus state proof failed. Path: %s", upgradeConsStatePath.Pretty()) + return sdkerrors.Wrapf(err, "consensus state proof failed. Path: %s", upgradeConsStatePath.Pretty()) } // Construct new client state and consensus state @@ -105,7 +105,7 @@ func (cs ClientState) VerifyUpgradeAndUpdateState( ) if err := newClientState.Validate(); err != nil { - return nil, nil, sdkerrors.Wrap(err, "updated client state failed basic validation") + return sdkerrors.Wrap(err, "updated client state failed basic validation") } // The new consensus state is merely used as a trusted kernel against which headers on the new @@ -119,10 +119,11 @@ func (cs ClientState) VerifyUpgradeAndUpdateState( tmUpgradeConsState.Timestamp, commitmenttypes.NewMerkleRoot([]byte(SentinelRoot)), tmUpgradeConsState.NextValidatorsHash, ) - // set metadata for this consensus state + setClientState(clientStore, cdc, newClientState) + SetConsensusState(clientStore, cdc, newConsState, newClientState.LatestHeight) setConsensusMetadata(ctx, clientStore, tmUpgradeClient.LatestHeight) - return newClientState, newConsState, nil + return nil } // construct MerklePath for the committed client from upgradePath diff --git a/modules/light-clients/07-tendermint/types/upgrade_test.go b/modules/light-clients/07-tendermint/types/upgrade_test.go index 175ce7fc358..112d3366cda 100644 --- a/modules/light-clients/07-tendermint/types/upgrade_test.go +++ b/modules/light-clients/07-tendermint/types/upgrade_test.go @@ -457,7 +457,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { // Call ZeroCustomFields on upgraded clients to clear any client-chosen parameters in test-case upgradedClient upgradedClient = upgradedClient.ZeroCustomFields() - clientState, consensusState, err := cs.VerifyUpgradeAndUpdateState( + err := cs.VerifyUpgradeAndUpdateState( suite.chainA.GetContext(), suite.cdc, clientStore, @@ -469,14 +469,15 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { if tc.expPass { suite.Require().NoError(err, "verify upgrade failed on valid case: %s", tc.name) + + clientState := suite.chainA.GetClientState(path.EndpointA.ClientID) suite.Require().NotNil(clientState, "verify upgrade failed on valid case: %s", tc.name) + + consensusState, found := suite.chainA.GetConsensusState(path.EndpointA.ClientID, clientState.GetLatestHeight()) suite.Require().NotNil(consensusState, "verify upgrade failed on valid case: %s", tc.name) + suite.Require().True(found) } else { suite.Require().Error(err, "verify upgrade passed on invalid case: %s", tc.name) - suite.Require().Nil(clientState, "verify upgrade passed on invalid case: %s", tc.name) - - suite.Require().Nil(consensusState, "verify upgrade passed on invalid case: %s", tc.name) - } } } diff --git a/modules/light-clients/09-localhost/types/client_state.go b/modules/light-clients/09-localhost/types/client_state.go index e818f9c8d7e..728e4ec5f12 100644 --- a/modules/light-clients/09-localhost/types/client_state.go +++ b/modules/light-clients/09-localhost/types/client_state.go @@ -138,8 +138,8 @@ func (cs ClientState) CheckSubstituteAndUpdateState( func (cs ClientState) VerifyUpgradeAndUpdateState( _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientState, _ exported.ConsensusState, _, _ []byte, -) (exported.ClientState, exported.ConsensusState, error) { - return nil, nil, sdkerrors.Wrap(clienttypes.ErrInvalidUpgradeClient, "cannot upgrade localhost client") +) error { + return sdkerrors.Wrap(clienttypes.ErrInvalidUpgradeClient, "cannot upgrade localhost client") } // VerifyClientState verifies that the localhost client state is stored locally From 4c8b7c50e3a6af4aa76a3b1319b3351a1e9c7b2f Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Wed, 16 Mar 2022 17:34:46 +0100 Subject: [PATCH 11/71] chore: 06-solomachine rename update to UpdateState (#1110) * adding VerifyClientMessage and tests * splitting tests for verify header and misbehaviour * rename update to UpdateState * cleaning up error handling * adding additional test for old misbehaviour is sucessful * split type switch logic into verifyHeader and verifyMisbehaviour priv funcs * adding in place test function for solomachine UpdateState * rename update to UpdateState * adding in place test function for solomachine UpdateState --- .../06-solomachine/types/update.go | 29 ++++------ .../06-solomachine/types/update_test.go | 57 ++++++++++++++++++- 2 files changed, 65 insertions(+), 21 deletions(-) diff --git a/modules/light-clients/06-solomachine/types/update.go b/modules/light-clients/06-solomachine/types/update.go index fd775544b61..eb3941b89d2 100644 --- a/modules/light-clients/06-solomachine/types/update.go +++ b/modules/light-clients/06-solomachine/types/update.go @@ -23,16 +23,7 @@ func (cs ClientState) CheckHeaderAndUpdateState( return nil, nil, err } - // TODO: Remove this type assertion, replace with misbehaviour checking and update state - smHeader, ok := msg.(*Header) - if !ok { - return nil, nil, sdkerrors.Wrapf( - clienttypes.ErrInvalidHeader, "expected %T, got %T", &Header{}, msg, - ) - } - - clientState, consensusState := update(&cs, smHeader) - return clientState, consensusState, nil + return cs.UpdateState(ctx, cdc, clientStore, msg) } // VerifyClientMessage introspects the provided ClientMessage and checks its validity @@ -105,16 +96,16 @@ func (cs ClientState) verifyMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, return nil } -// update the consensus state to the new public key and an incremented sequence -func update(clientState *ClientState, header *Header) (*ClientState, *ConsensusState) { +// UpdateState updates the consensus state to the new public key and an incremented sequence. +func (cs ClientState) UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg exported.ClientMessage) (exported.ClientState, exported.ConsensusState, error) { + smHeader := clientMsg.(*Header) consensusState := &ConsensusState{ - PublicKey: header.NewPublicKey, - Diversifier: header.NewDiversifier, - Timestamp: header.Timestamp, + PublicKey: smHeader.NewPublicKey, + Diversifier: smHeader.NewDiversifier, + Timestamp: smHeader.Timestamp, } - // increment sequence number - clientState.Sequence++ - clientState.ConsensusState = consensusState - return clientState, consensusState + cs.Sequence++ + cs.ConsensusState = consensusState + return &cs, consensusState, nil } diff --git a/modules/light-clients/06-solomachine/types/update_test.go b/modules/light-clients/06-solomachine/types/update_test.go index 336b9926be0..ddd239ab9a4 100644 --- a/modules/light-clients/06-solomachine/types/update_test.go +++ b/modules/light-clients/06-solomachine/types/update_test.go @@ -328,7 +328,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { // setup test tc.setup() - err := clientState.VerifyClientMessage(suite.chainA.GetContext(), suite.chainA.Codec, nil, clientMsg) + err := clientState.VerifyClientMessage(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, clientMsg) if tc.expPass { suite.Require().NoError(err) @@ -560,7 +560,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { // setup test tc.setup() - err := clientState.VerifyClientMessage(suite.chainA.GetContext(), suite.chainA.Codec, nil, clientMsg) + err := clientState.VerifyClientMessage(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, clientMsg) if tc.expPass { suite.Require().NoError(err) @@ -571,3 +571,56 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { } } } + +func (suite *SoloMachineTestSuite) TestUpdateState() { + var ( + clientState exported.ClientState + clientMsg exported.ClientMessage + ) + + // test singlesig and multisig public keys + for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + testCases := []struct { + name string + setup func() + expPass bool + }{ + { + "successful update", + func() { + clientState = solomachine.ClientState() + clientMsg = solomachine.CreateHeader() + }, + true, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + // setup test + tc.setup() + + clientState, ok := clientState.(*types.ClientState) + if ok { + cs, consensusState, err := clientState.UpdateState(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, clientMsg) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(clientMsg.(*types.Header).NewPublicKey, cs.(*types.ClientState).ConsensusState.PublicKey) + suite.Require().Equal(false, cs.(*types.ClientState).IsFrozen) + suite.Require().Equal(clientMsg.(*types.Header).Sequence+1, cs.(*types.ClientState).Sequence) + suite.Require().Equal(consensusState, cs.(*types.ClientState).ConsensusState) + } else { + suite.Require().Error(err) + suite.Require().Nil(clientState) + suite.Require().Nil(consensusState) + } + } + + }) + } + } +} From bdbaa91838bbc20161b068cd43bf9e76f0127dd2 Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Wed, 16 Mar 2022 18:39:49 +0100 Subject: [PATCH 12/71] chore: 06-solomachine adding CheckForMisbehaviour and UpdateStateOnMisbehaviour (#1111) * adding VerifyClientMessage and tests * splitting tests for verify header and misbehaviour * rename update to UpdateState * adding CheckForMisbehaviour and UpdateStateOnMisbehaviour * adding misbehaviour checks * updating CheckForMisbehaviour codestyle and adding basic test cases --- .../06-solomachine/types/update.go | 23 ++++++ .../06-solomachine/types/update_test.go | 82 +++++++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/modules/light-clients/06-solomachine/types/update.go b/modules/light-clients/06-solomachine/types/update.go index eb3941b89d2..d04d93c36af 100644 --- a/modules/light-clients/06-solomachine/types/update.go +++ b/modules/light-clients/06-solomachine/types/update.go @@ -23,6 +23,11 @@ func (cs ClientState) CheckHeaderAndUpdateState( return nil, nil, err } + foundMisbehaviour := cs.CheckForMisbehaviour(ctx, cdc, clientStore, msg) + if foundMisbehaviour { + return cs.UpdateStateOnMisbehaviour(ctx, cdc, clientStore) + } + return cs.UpdateState(ctx, cdc, clientStore, msg) } @@ -109,3 +114,21 @@ func (cs ClientState) UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, client cs.ConsensusState = consensusState return &cs, consensusState, nil } + +// CheckForMisbehaviour returns true for type Misbehaviour (passed VerifyClientMessage check), otherwise returns false +func (cs ClientState) CheckForMisbehaviour(_ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, clientMsg exported.ClientMessage) bool { + if _, ok := clientMsg.(*Misbehaviour); ok { + return true + } + + return false +} + +// UpdateStateOnMisbehaviour updates state upon misbehaviour. This method should only be called on misbehaviour +// as it does not perform any misbehaviour checks. +func (cs ClientState) UpdateStateOnMisbehaviour( + _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, // prematurely include args for self storage of consensus state +) (*ClientState, exported.ConsensusState, error) { + cs.IsFrozen = true + return &cs, cs.ConsensusState, nil +} diff --git a/modules/light-clients/06-solomachine/types/update_test.go b/modules/light-clients/06-solomachine/types/update_test.go index ddd239ab9a4..56f620b15aa 100644 --- a/modules/light-clients/06-solomachine/types/update_test.go +++ b/modules/light-clients/06-solomachine/types/update_test.go @@ -624,3 +624,85 @@ func (suite *SoloMachineTestSuite) TestUpdateState() { } } } + +func (suite *SoloMachineTestSuite) TestCheckForMisbehaviour() { + var ( + clientMsg exported.ClientMessage + ) + + // test singlesig and multisig public keys + for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() { + clientMsg = solomachine.CreateMisbehaviour() + }, + true, + }, + { + "normal header returns false", + func() { + clientMsg = solomachine.CreateHeader() + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + clientState := solomachine.ClientState() + + tc.malleate() + + foundMisbehaviour := clientState.CheckForMisbehaviour(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, clientMsg) + + if tc.expPass { + suite.Require().True(foundMisbehaviour) + } else { + suite.Require().False(foundMisbehaviour) + } + + }) + } + } +} + +func (suite *SoloMachineTestSuite) TestUpdateStateOnMisbehaviour() { + // test singlesig and multisig public keys + for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() {}, + true, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + clientState := solomachine.ClientState() + + tc.malleate() + + cs, _, _ := clientState.UpdateStateOnMisbehaviour(suite.chainA.GetContext(), suite.chainA.Codec, suite.store) + + if tc.expPass { + suite.Require().True(cs.IsFrozen) + } + }) + } + } +} From f4480fb6a33322face4732362125e245ffa45f7b Mon Sep 17 00:00:00 2001 From: Sean King Date: Mon, 21 Mar 2022 14:30:50 +0100 Subject: [PATCH 13/71] chore: 02-client-refactor: merge main into feature branch (#1149) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: allow the mock module to be used multiple times as base ibc application in middleware stack (#892) ## Description Currently the `AppModule` assumes a single scoped keeper. This doesn't allow the mock module to be used as a base application for different middleware stack (ica stack, fee stack, etc) I broke the API because I think it is cleaner. If we want this to be non API breaking, I can try to readjust ref: #891 --- 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. - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * feat: adding Pack/Unpack acknowledgement helper fns (#895) * feat: adding Pack/Unpack acknowledgement helper fns * chore: changelog * fix: docs * Update modules/core/04-channel/types/codec.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * imp: support custom keys for testing (#893) * chore: add ParsePacketFromEvents testing helper function (#904) ## Description ref: #891 --- 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. - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * fix: correctly claim capability for mock module, handle genesis exports (#921) ## Description This contains two fixes: - the capability being claimed by the scoped keeper was incorrect (mock.ModuleName -> port ID) - the mock module wasn't accounting for non empty genesis state in capabilities (after genesis export, capability will create the bound ports so rebinding doesn't need to happen) closes: #XXXX --- 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. - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * docs: update migration docs for upgrade proposal in relation to ICS27 (#920) ## Description closes: #XXXX --- 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. - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * chore(ica): add trail of bits audit report (#903) * chore(ica): add trail of bits audit report * relocate the audit report for ICA Co-authored-by: Carlos Rodriguez * testing: adding multiple sender accounts for testing purposes (#935) * testing: adding multiple sender accounts for testing puproses * fix genesis setup (#936) * Update testing/chain.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * refactor: code hygiene * Update testing/chain.go Co-authored-by: Aditya * fix: setting totalySupply to empty * nit: CamelCase not UPPERCASE Co-authored-by: Aditya Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * Create test chain with multiple validators (#942) * testing: adding multiple sender accounts for testing puproses * fix genesis setup (#936) * Update testing/chain.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * refactor: code hygiene * Update testing/chain.go Co-authored-by: Aditya * multi validator commit taken from @saione * add function to pass custom valset * add changelog Co-authored-by: Sean King Co-authored-by: Sean King Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * add changelog entry for SDK bump * fix: classify client states without consensus states as expired (#941) ## Description closes: #850 --- 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. - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * chore: fix broken link (#972) * add backport actions for v1.3.x and v2.1.x (#958) * Revert "feat: adding Pack/Unpack acknowledgement helper fns (#895)" (#973) This reverts commit 843b459635da8cedd92945141c4efe3a762f305d. Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> * chore: update migration docs (#985) * chore: update migration docs * Update docs/migrations/v2-to-v3.md Co-authored-by: Damian Nolan Co-authored-by: Damian Nolan * chore: fix mispelled words (#991) * fix: remove go mod tidy from proto-gen script (#989) * bug: support base denoms with slashes (#978) * bug: support base denoms with slashes * add changelog entry Co-authored-by: Carlos Rodriguez * upgrade ics23 to v0.7 (#948) * upgrade ics23 to v0.7-rc * add changelog entry * update ics23 to final 0.7 Co-authored-by: Carlos Rodriguez * ibctesting: make `testing.T` public (#1020) * add changelog entry for #941 * fix package import (#1007) * feat: Add a function to initialize the ICS27 module via an upgrade proposal (#1037) ## Description closes: #1034 --- 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. - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * docs: add missing args to NewKeeper in integration docs (#1038) ## Description This add some missing arguments to `ibckeeper.NewKeeper` and `ibctransferkeeper.NewKeeper` in integration docs --- 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)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [x] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * small fixes for v2 to v3 migration (#1016) * small fixes for v2 to v3 migration * review comment * Update v2-to-v3.md * add store upgrade documentation Co-authored-by: Carlos Rodriguez * add missing slash * build(deps): bump actions/checkout from 2.4.0 to 3 (#1045) Bumps [actions/checkout](https://github.com/actions/checkout) from 2.4.0 to 3.
Release notes

Sourced from actions/checkout's releases.

v3.0.0

  • Update default runtime to node16
Changelog

Sourced from actions/checkout's changelog.

Changelog

v2.3.1

v2.3.0

v2.2.0

v2.1.1

  • Changes to support GHES (here and here)

v2.1.0

v2.0.0

v2 (beta)

  • Improved fetch performance
    • The default behavior now fetches only the SHA being checked-out
  • Script authenticated git commands
    • Persists with.token in the local git config
    • Enables your scripts to run authenticated git commands
    • Post-job cleanup removes the token
    • Coming soon: Opt out by setting with.persist-credentials to false
  • Creates a local branch
    • No longer detached HEAD when checking out a branch
    • A local branch is created with the corresponding upstream branch set
  • Improved layout

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=2.4.0&new-version=3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
* call packet.GetSequence() rather than passing the func as argument (#995) * Add counterpartyChannelID param to IBCModule.OnChanOpenAck (#1086) * add counterpartyChannelID param to IBCModule OnChanOpenAck() * change testing mock * change ica IBCModules ChannelOpenAck * change transfer IBCModules ChannelOpenAck * change core keeper ChannelOpenAck() * CHANGELOG.md * update v2-to-v3 migration doc * Update docs/migrations/v2-to-v3.md Co-authored-by: Carlos Rodriguez Co-authored-by: Carlos Rodriguez * fix mirgation docs (#1091) * fix: handle testing update client errors (#1094) * replace channel keeper with IBC keeper in AnteDecorator (#950) * replace channel keeper with IBC keeper in AnteDecorator and pass message to rpc handler * fix error checking condition * fix for proper way of getting go context * refactor tests for ante handler * review comment * review comments and some fixes * review comments * execute message for update client as well * add migration Co-authored-by: Carlos Rodriguez * add backport rules for v1.4.x and v2.2.x (#1085) * ibctesting: custom voting power reduction for testing (#939) * ibctesting: custom voting power reduction for testing * changelog * fix * make T public * fix * revert changes * fix test * build(deps): bump github.com/spf13/cobra from 1.3.0 to 1.4.0 (#1105) * build(deps): bump google.golang.org/grpc from 1.44.0 to 1.45.0 (#1098) Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.44.0 to 1.45.0. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.44.0...v1.45.0) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix: adjust InitModule to account for empty controller and host keepers (#1120) ## Description closes: #XXXX --- 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//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` - [x] Re-reviewed `Files changed` in the Github PR explorer - [x] Review `Codecov Report` in the comment section below once CI passes * Merge pull request from GHSA-j658-c98j-fww4 Co-authored-by: Carlos Rodriguez * fixes for the documentation about handling ack for SDK <= 0.45 (#1122) * fixes for documentation * review comment Co-authored-by: Carlos Rodriguez * Allow testing to update ValidatorSet (#1003) * testing: adding multiple sender accounts for testing puproses * fix genesis setup (#936) * Update testing/chain.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * refactor: code hygiene * Update testing/chain.go Co-authored-by: Aditya * multi validator commit taken from @saione * add function to pass custom valset * create simplest failing test * progress * fix changevalset test * fix errors in tendermint package * fix client types test * fix client keeper * fix cap functions * fix genesis core tests * fix ica tests * fix doc * CHANGELOG * replace signer array with signer map * add documentation * fix merge * documentation * ordered signer array doc * add new delegation and comment to change valset test Co-authored-by: Sean King Co-authored-by: Sean King Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> Co-authored-by: Carlos Rodriguez * imp: create codeql-analysis action (#1128) ## Description Noticed that [CodeQL](https://codeql.github.com/) wasn't enabled on the IBC-go repo --- 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. - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * update changelog (#1131) * update changelog * fix typo * build(deps): bump github.com/stretchr/testify from 1.7.0 to 1.7.1 (#1134) Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.7.0 to 1.7.1. - [Release notes](https://github.com/stretchr/testify/releases) - [Commits](https://github.com/stretchr/testify/compare/v1.7.0...v1.7.1) --- updated-dependencies: - dependency-name: github.com/stretchr/testify dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Damian Nolan * call packet.GetSequence() rather than passing the func as argument (#1130) * call packet.GetSequence() rather than passing the func as argument * add changelog entry Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> Co-authored-by: Carlos Rodriguez Co-authored-by: Carlos Rodriguez Co-authored-by: Aditya Co-authored-by: Tim Lind Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Damian Nolan Co-authored-by: daeMOn Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Joe Bowman Co-authored-by: khanh <50263489+catShaark@users.noreply.github.com> Co-authored-by: Carlos Rodriguez --- .github/workflows/codeql-analysis.yml | 79 ++++++++++++++ CHANGELOG.md | 97 ++++++++++++++++- docs/apps/interchain-accounts/auth-modules.md | 21 ++-- docs/migrations/v2-to-v3.md | 7 +- go.mod | 6 +- go.sum | 26 ++--- .../controller/ibc_module_test.go | 2 - .../controller/keeper/keeper_test.go | 1 - .../host/ibc_module_test.go | 2 - .../host/keeper/keeper_test.go | 1 - modules/apps/27-interchain-accounts/module.go | 15 ++- .../27-interchain-accounts/module_test.go | 75 +++++++++++-- modules/apps/transfer/keeper/relay.go | 4 + modules/apps/transfer/keeper/relay_test.go | 10 ++ .../apps/transfer/types/expected_keepers.go | 1 + modules/core/02-client/keeper/client_test.go | 45 ++++---- modules/core/02-client/keeper/keeper_test.go | 10 +- modules/core/02-client/types/genesis_test.go | 5 +- modules/core/02-client/types/msgs_test.go | 8 +- modules/core/04-channel/keeper/packet.go | 2 +- modules/core/genesis_test.go | 4 +- .../types/misbehaviour_handle_test.go | 101 +++++++++--------- .../07-tendermint/types/misbehaviour_test.go | 42 ++++---- .../07-tendermint/types/tendermint_test.go | 16 ++- .../07-tendermint/types/update_test.go | 54 +++++----- testing/app.go | 4 +- testing/chain.go | 85 +++++++-------- testing/chain_test.go | 51 ++++----- testing/coordinator.go | 3 +- testing/simapp/test_helpers.go | 8 +- testing/utils.go | 23 ++++ 31 files changed, 531 insertions(+), 277 deletions(-) create mode 100644 .github/workflows/codeql-analysis.yml create mode 100644 testing/utils.go diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 00000000000..11d7c0e90bb --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,79 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ main ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ main ] + schedule: + - cron: '37 21 * * 4' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'go' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] + # Learn more: + # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed + + steps: + - name: Checkout repository + - uses: actions/checkout@v3 + - uses: technote-space/get-diff-action@v6.0.1 + with: + PATTERNS: | + **/**.go + go.mod + go.sum + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + if: env.GIT_DIFF + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 + if: env.GIT_DIFF diff --git a/CHANGELOG.md b/CHANGELOG.md index 02777a689bf..c302b4c759e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,22 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Dependencies +### API Breaking + +### State Machine Breaking + +### Improvements + +### Features + +### Bug Fixes + +* (modules/core/04-channel) [\#1130](https://github.com/cosmos/ibc-go/pull/1130) Call `packet.GetSequence()` rather than passing func in `WriteAcknowledgement` log output + +## [v3.0.0](https://github.com/cosmos/ibc-go/releases/tag/v3.0.0) - 2022-03-15 + +### Dependencies + * [\#404](https://github.com/cosmos/ibc-go/pull/404) Bump Go version to 1.17 * [\#851](https://github.com/cosmos/ibc-go/pull/851) Bump SDK version to v0.45.1 * [\#948](https://github.com/cosmos/ibc-go/pull/948) Bump ics23/go to v0.7 @@ -54,6 +70,9 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (07-tendermint) [\#1097](https://github.com/cosmos/ibc-go/pull/1097) Deprecate `ClientId` field in 07-tendermint `Misbehaviour` type. * (channel( [\#848](https://github.com/cosmos/ibc-go/pull/848) Added `ChannelId` to MsgChannelOpenInitResponse * (testing( [\#813](https://github.com/cosmos/ibc-go/pull/813) The `ack` argument to the testing function `RelayPacket` has been removed as it is no longer needed. +* (testing) [\#1003](https://github.com/cosmos/ibc-go/pull/1003) `CreateTMClientHeader` takes an additional `nextVals *tmtypes.ValidatorSet` as an argument +* (testing) [\#939](https://github.com/cosmos/ibc-go/pull/939) Support custom power reduction for testing. +* (modules/core/05-port) [\#1086](https://github.com/cosmos/ibc-go/pull/1086) Added `counterpartyChannelID` argument to IBCModule.OnChanOpenAck * (testing) [\#774](https://github.com/cosmos/ibc-go/pull/774) Added `ChainID` arg to `SetupWithGenesisValSet` on the testing app. `Coordinator` generated ChainIDs now starts at index 1 * (transfer) [\#675](https://github.com/cosmos/ibc-go/pull/675) Transfer `NewKeeper` now takes in an ICS4Wrapper. The ICS4Wrapper may be the IBC Channel Keeper when ICS20 is not used in a middleware stack. The ICS4Wrapper is required for applications wishing to connect middleware to ICS20. * (core) [\#650](https://github.com/cosmos/ibc-go/pull/650) Modify `OnChanOpenTry` IBC application module callback to return the negotiated app version. The version passed into the `MsgChanOpenTry` has been deprecated and will be ignored by core IBC. @@ -67,7 +86,6 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (modules/core/ante) [\#950](https://github.com/cosmos/ibc-go/pull/950) Replaces the channel keeper with the IBC keeper in the IBC `AnteDecorator` in order to execute the entire message and be able to reject redundant messages that are in the same block as the non-redundant messages. * (modules/core/exported) [\#1107](https://github.com/cosmos/ibc-go/pull/1107) Merging the `Header` and `Misbehaviour` interfaces into a single `ClientMessage` type - ### State Machine Breaking * (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. @@ -75,6 +93,9 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Improvements * (interchain-accounts) [\#1037](https://github.com/cosmos/ibc-go/pull/1037) Add a function `InitModule` to the interchain accounts `AppModule`. This function should be called within the upgrade handler when adding the interchain accounts module to a chain. It should be called in place of InitGenesis (set the consensus version in the version map). +* (testing) [\#1003](https://github.com/cosmos/ibc-go/pull/1003) Testing chain's `Signer` fields has changed from `[]tmtypes.PrivValidator` to `map[string]tmtypes.PrivValidator` to accomodate valset updates changing the order of the ValidatorSet. +* (testing) [\#1003](https://github.com/cosmos/ibc-go/pull/1003) `SignAndDeliver` will now just deliver the transaction without creating and committing a block. Thus, it requires that `BeginBlock` MUST be called before `SignAndDeliver` +* (testing) [\#1003](https://github.com/cosmos/ibc-go/pull/1003) `NextBlock` will now call `EndBlock` and `Commit` internally and apply validator updates to the `NextVals` of `TestChain` and the `NextValsHash` of the current header. Test writers can now make changes to validator set and have them reflected in the `TestChain` and handled appropriately in `UpdateClient` * (testing) [\#942](https://github.com/cosmos/ibc-go/pull/942) `NewTestChain` will create 4 validators in validator set by default. A new constructor function `NewTestChainWithValSet` is provided for test writers who want custom control over the validator set of test chains. * (testing) [\#904](https://github.com/cosmos/ibc-go/pull/904) Add `ParsePacketFromEvents` function to the testing package. Useful when sending/relaying packets via the testing package. * (testing) [\#893](https://github.com/cosmos/ibc-go/pull/893) Support custom private keys for testing. @@ -98,7 +119,40 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (testing) [\#884](https://github.com/cosmos/ibc-go/pull/884) Add and use in simapp a custom ante handler that rejects redundant transactions * (transfer) [\#978](https://github.com/cosmos/ibc-go/pull/978) Support base denoms with slashes in denom validation * (client) [\#941](https://github.com/cosmos/ibc-go/pull/941) Classify client states without consensus states as expired -* (modules/core/04-channel) [\#994](https://github.com/cosmos/ibc-go/pull/944) Call `packet.GetSequence()` rather than passing func in `AcknowledgePacket` log output +* (channel) [\#995](https://github.com/cosmos/ibc-go/pull/995) Call `packet.GetSequence()` rather than passing func in `AcknowledgePacket` log output + +## [v2.2.0](https://github.com/cosmos/ibc-go/releases/tag/v2.2.0) - 2022-03-15 + +### Dependencies + +* [\#851](https://github.com/cosmos/ibc-go/pull/851) Bump SDK version to v0.45.1 + +## [v2.1.0](https://github.com/cosmos/ibc-go/releases/tag/v2.1.0) - 2022-03-15 + +### Dependencies + +* [\#1084](https://github.com/cosmos/ibc-go/pull/1084) Bump SDK version to v0.44.6 +* [\#948](https://github.com/cosmos/ibc-go/pull/948) Bump ics23/go to v0.7 + +### State Machine Breaking + +* (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. + +### Features + +* [\#679](https://github.com/cosmos/ibc-go/pull/679) New CLI command `query ibc-transfer denom-hash ` to get the denom hash for a denom trace; this might be useful for debug + +### Bug Fixes + +* (client) [\#941](https://github.com/cosmos/ibc-go/pull/941) Classify client states without consensus states as expired +* (transfer) [\#978](https://github.com/cosmos/ibc-go/pull/978) Support base denoms with slashes in denom validation +* (channel) [\#995](https://github.com/cosmos/ibc-go/pull/995) Call `packet.GetSequence()` rather than passing func in `AcknowledgePacket` log output + +## [v2.0.3](https://github.com/cosmos/ibc-go/releases/tag/v2.0.2) - 2022-02-03 + +### Improvements + +* (channel) [\#692](https://github.com/cosmos/ibc-go/pull/692) Minimize channel logging by only emitting the packet sequence, source port/channel, destination port/channel upon packet receives, acknowledgements and timeouts. ## [v2.0.2](https://github.com/cosmos/ibc-go/releases/tag/v2.0.2) - 2021-12-15 @@ -141,6 +195,39 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [\#384](https://github.com/cosmos/ibc-go/pull/384) Added `NegotiateAppVersion` method to `IBCModule` interface supported by a gRPC query service in `05-port`. This provides routing of requests to the desired application module callback, which in turn performs application version negotiation. +## [v1.4.0](https://github.com/cosmos/ibc-go/releases/tag/v1.4.0) - 2022-03-15 + +### Dependencies + +* [\#851](https://github.com/cosmos/ibc-go/pull/851) Bump SDK version to v0.45.1 + +## [v1.3.0](https://github.com/cosmos/ibc-go/releases/tag/v1.3.0) - 2022-03-15 + +### Dependencies + +* [\#1073](https://github.com/cosmos/ibc-go/pull/1073) Bump SDK version to v0.44.6 +* [\#948](https://github.com/cosmos/ibc-go/pull/948) Bump ics23/go to v0.7 + +### State Machine Breaking + +* (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. + +### Features + +* [\#679](https://github.com/cosmos/ibc-go/pull/679) New CLI command `query ibc-transfer denom-hash ` to get the denom hash for a denom trace; this might be useful for debug + +### Bug Fixes + +* (client) [\#941](https://github.com/cosmos/ibc-go/pull/941) Classify client states without consensus states as expired +* (transfer) [\#978](https://github.com/cosmos/ibc-go/pull/978) Support base denoms with slashes in denom validation +* (channel) [\#995](https://github.com/cosmos/ibc-go/pull/995) Call `packet.GetSequence()` rather than passing func in `AcknowledgePacket` log output + +## [v1.2.6](https://github.com/cosmos/ibc-go/releases/tag/v1.2.6) - 2022-02-03 + +### Improvements + +* (channel) [\#692](https://github.com/cosmos/ibc-go/pull/692) Minimize channel logging by only emitting the packet sequence, source port/channel, destination port/channel upon packet receives, acknowledgements and timeouts. + ## [v1.2.5](https://github.com/cosmos/ibc-go/releases/tag/v1.2.5) - 2021-12-15 ### Dependencies @@ -198,6 +285,12 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [\#386](https://github.com/cosmos/ibc-go/pull/386) Bump [tendermint](https://github.com/tendermint/tendermint) from v0.34.12 to v0.34.13. +## [v1.1.6](https://github.com/cosmos/ibc-go/releases/tag/v1.1.6) - 2022-01-25 + +### Improvements + +* (channel) [\#692](https://github.com/cosmos/ibc-go/pull/692) Minimize channel logging by only emitting the packet sequence, source port/channel, destination port/channel upon packet receives, acknowledgements and timeouts. + ## [v1.1.5](https://github.com/cosmos/ibc-go/releases/tag/v1.1.5) - 2021-12-15 ### Dependencies diff --git a/docs/apps/interchain-accounts/auth-modules.md b/docs/apps/interchain-accounts/auth-modules.md index b87265d4e28..0e8738f50c0 100644 --- a/docs/apps/interchain-accounts/auth-modules.md +++ b/docs/apps/interchain-accounts/auth-modules.md @@ -220,8 +220,13 @@ If the controller chain is connected to a host chain using the host module on ib Begin by unmarshaling the acknowledgement into sdk.TxMsgData: ```go +var ack channeltypes.Acknowledgement +if err := channeltypes.SubModuleCdc.UnmarshalJSON(acknowledgement, &ack); err != nil { + return err +} + txMsgData := &sdk.TxMsgData{} -if err := proto.Unmarshal(ack.Acknowledgement(), txMsgData); err != nil { +if err := proto.Unmarshal(ack.GetResult(), txMsgData); err != nil { return err } ``` @@ -232,6 +237,8 @@ The auth module should interpret the txMsgData.Data as follows: ```go switch len(txMsgData.Data) { case 0: + // see documentation below for SDK 0.46.x or greater +default: for _, msgData := range txMsgData.Data { if err := handler(msgData); err != nil { return err @@ -246,8 +253,8 @@ A router could be used, or more simply a switch statement. ```go func handler(msgData sdk.MsgData) error { -switch msgData.TypeURL { -case banktypes.MsgSend: +switch msgData.MsgType { +case sdk.MsgTypeURL(&banktypes.MsgSend{}): msgResponse := &banktypes.MsgSendResponse{} if err := proto.Unmarshal(msgData.Data, msgResponse}; err != nil { return err @@ -255,7 +262,7 @@ case banktypes.MsgSend: handleBankSendMsg(msgResponse) -case stakingtypes.MsgDelegate: +case sdk.MsgTypeURL(&stakingtypes.MsgDelegate{}): msgResponse := &stakingtypes.MsgDelegateResponse{} if err := proto.Unmarshal(msgData.Data, msgResponse}; err != nil { return err @@ -263,7 +270,7 @@ case stakingtypes.MsgDelegate: handleStakingDelegateMsg(msgResponse) -case transfertypes.MsgTransfer: +case sdk.MsgTypeURL(&transfertypes.MsgTransfer{}): msgResponse := &transfertypes.MsgTransferResponse{} if err := proto.Unmarshal(msgData.Data, msgResponse}; err != nil { return err @@ -281,8 +288,8 @@ The auth module should interpret the txMsgData.Responses as follows: ```go ... -// switch statement from above continued -default: +// switch statement from above +case 0: for _, any := range txMsgData.MsgResponses { if err := handleAny(any); err != nil { return err diff --git a/docs/migrations/v2-to-v3.md b/docs/migrations/v2-to-v3.md index 6b1c61735fc..a37f74cf420 100644 --- a/docs/migrations/v2-to-v3.md +++ b/docs/migrations/v2-to-v3.md @@ -58,8 +58,8 @@ app.UpgradeKeeper.SetUpgradeHandler("v3", ``` -The host and controller submodule params only need to be set if you integrate those submodules. -For example, if a chain chooses not to integrate a controller submodule, it does not need to set the controller params. +The host and controller submodule params only need to be set if the chain integrates those submodules. +For example, if a chain chooses not to integrate a controller submodule, it may pass empty params into `InitModule`. #### Add `StoreUpgrades` for ICS27 module @@ -76,6 +76,9 @@ if upgradeInfo.Name == "v3" && !app.UpgradeKeeper.IsSkipHeight(upgradeInfo.Heigh ``` This ensures that the new module's stores are added to the multistore before the migrations begin. +The host and controller submodule keys only need to be added if the chain integrates those submodules. +For example, if a chain chooses not to integrate a controller submodule, it does not need to add the controller key to the `Added` field. + ### Genesis migrations diff --git a/go.mod b/go.mod index eb9dc1a9548..2bc717ea5f3 100644 --- a/go.mod +++ b/go.mod @@ -15,13 +15,13 @@ require ( github.com/rakyll/statik v0.1.7 github.com/regen-network/cosmos-proto v0.3.1 github.com/spf13/cast v1.4.1 - github.com/spf13/cobra v1.3.0 + github.com/spf13/cobra v1.4.0 github.com/spf13/viper v1.10.1 - github.com/stretchr/testify v1.7.0 + github.com/stretchr/testify v1.7.1 github.com/tendermint/tendermint v0.34.14 github.com/tendermint/tm-db v0.6.4 google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa - google.golang.org/grpc v1.44.0 + google.golang.org/grpc v1.45.0 google.golang.org/protobuf v1.27.1 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 5544d71eb5f..cb1b3b22474 100644 --- a/go.sum +++ b/go.sum @@ -25,7 +25,6 @@ cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aD cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM= cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= @@ -465,7 +464,7 @@ github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uM github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= -github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= +github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= @@ -499,13 +498,10 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= github.com/hdevalence/ed25519consensus v0.0.0-20210204194344-59a8610d2b87 h1:uUjLpLt6bVvZ72SQc/B4dXcPBw4Vgd7soowdRl52qEM= github.com/hdevalence/ed25519consensus v0.0.0-20210204194344-59a8610d2b87/go.mod h1:XGsKKeXxeRr95aEOgipvluMPlgjr7dGlk9ZTWOjcUcg= @@ -799,7 +795,7 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= +github.com/sagikazarmark/crypt v0.4.0/go.mod h1:ALv2SRj7GxYV4HO9elxH9nS6M9gW+xDNxqmyJ6RfDFM= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa h1:0U2s5loxrTy6/VgfVoLuVLFJcURKLH49ie0zSch7gh4= github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= @@ -835,8 +831,8 @@ github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3 github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= -github.com/spf13/cobra v1.3.0 h1:R7cSvGu+Vv+qX0gW5R/85dx2kmmJT5z5NM8ifdYjdn0= -github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4= +github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= +github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= @@ -848,7 +844,6 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= -github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM= github.com/spf13/viper v1.10.1 h1:nuJZuYpG7gTj/XqiUwg8bA0cp1+M2mC3J4g5luUYBKk= github.com/spf13/viper v1.10.1/go.mod h1:IGlFPqhNAPKRxohIzWpI5QEy4kuI7tcl5WvR+8qy1rU= github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= @@ -866,8 +861,9 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= @@ -1188,7 +1184,6 @@ golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211210111614-af8b64212486 h1:5hpz5aRr+W1erYCL5JRhSUBJRph7l9XkNveoExlrKYk= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -1310,7 +1305,7 @@ google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqiv google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU= google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw= +google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1384,8 +1379,6 @@ google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ6 google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa h1:I0YcKz0I7OAhddo7ya8kMnvprhcWM045PmkBdMO9zN0= google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= @@ -1423,8 +1416,9 @@ google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.44.0 h1:weqSxi/TMs1SqFRMHCtBgXRs8k3X39QIDEZ0pRcttUg= -google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= diff --git a/modules/apps/27-interchain-accounts/controller/ibc_module_test.go b/modules/apps/27-interchain-accounts/controller/ibc_module_test.go index db4412d144e..c0163e954e1 100644 --- a/modules/apps/27-interchain-accounts/controller/ibc_module_test.go +++ b/modules/apps/27-interchain-accounts/controller/ibc_module_test.go @@ -87,7 +87,6 @@ func RegisterInterchainAccount(endpoint *ibctesting.Endpoint, owner string) erro } // commit state changes for proof verification - endpoint.Chain.App.Commit() endpoint.Chain.NextBlock() // update port/channel ids @@ -350,7 +349,6 @@ func (suite *InterchainAccountsTestSuite) TestChanOpenConfirm() { suite.chainB.GetSimApp().GetIBCKeeper().ChannelKeeper.SetChannel(suite.chainB.GetContext(), path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, channel) // commit state changes so proof can be created - suite.chainB.App.Commit() suite.chainB.NextBlock() path.EndpointA.UpdateClient() diff --git a/modules/apps/27-interchain-accounts/controller/keeper/keeper_test.go b/modules/apps/27-interchain-accounts/controller/keeper/keeper_test.go index c3e1a48ee0c..a0772f1e648 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/keeper_test.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/keeper_test.go @@ -100,7 +100,6 @@ func RegisterInterchainAccount(endpoint *ibctesting.Endpoint, owner string) erro } // commit state changes for proof verification - endpoint.Chain.App.Commit() endpoint.Chain.NextBlock() // update port/channel ids diff --git a/modules/apps/27-interchain-accounts/host/ibc_module_test.go b/modules/apps/27-interchain-accounts/host/ibc_module_test.go index 56baa70e847..3fa45384104 100644 --- a/modules/apps/27-interchain-accounts/host/ibc_module_test.go +++ b/modules/apps/27-interchain-accounts/host/ibc_module_test.go @@ -91,7 +91,6 @@ func RegisterInterchainAccount(endpoint *ibctesting.Endpoint, owner string) erro } // commit state changes for proof verification - endpoint.Chain.App.Commit() endpoint.Chain.NextBlock() // update port/channel ids @@ -247,7 +246,6 @@ func (suite *InterchainAccountsTestSuite) TestChanOpenAck() { suite.chainA.GetSimApp().GetIBCKeeper().ChannelKeeper.SetChannel(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, channel) // commit state changes so proof can be created - suite.chainA.App.Commit() suite.chainA.NextBlock() path.EndpointB.UpdateClient() diff --git a/modules/apps/27-interchain-accounts/host/keeper/keeper_test.go b/modules/apps/27-interchain-accounts/host/keeper/keeper_test.go index 96c80c45f99..609fb06884d 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/keeper_test.go +++ b/modules/apps/27-interchain-accounts/host/keeper/keeper_test.go @@ -100,7 +100,6 @@ func RegisterInterchainAccount(endpoint *ibctesting.Endpoint, owner string) erro } // commit state changes for proof verification - endpoint.Chain.App.Commit() endpoint.Chain.NextBlock() // update port/channel ids diff --git a/modules/apps/27-interchain-accounts/module.go b/modules/apps/27-interchain-accounts/module.go index 969c07caf9d..1bb870fca5d 100644 --- a/modules/apps/27-interchain-accounts/module.go +++ b/modules/apps/27-interchain-accounts/module.go @@ -105,12 +105,17 @@ func NewAppModule(controllerKeeper *controllerkeeper.Keeper, hostKeeper *hostkee // InitModule will initialize the interchain accounts moudule. It should only be // called once and as an alternative to InitGenesis. func (am AppModule) InitModule(ctx sdk.Context, controllerParams controllertypes.Params, hostParams hosttypes.Params) { - am.controllerKeeper.SetParams(ctx, controllerParams) - am.hostKeeper.SetParams(ctx, hostParams) + if am.controllerKeeper != nil { + am.controllerKeeper.SetParams(ctx, controllerParams) + } + + if am.hostKeeper != nil { + am.hostKeeper.SetParams(ctx, hostParams) - cap := am.hostKeeper.BindPort(ctx, types.PortID) - if err := am.hostKeeper.ClaimCapability(ctx, cap, ibchost.PortPath(types.PortID)); err != nil { - panic(fmt.Sprintf("could not claim port capability: %v", err)) + cap := am.hostKeeper.BindPort(ctx, types.PortID) + if err := am.hostKeeper.ClaimCapability(ctx, cap, ibchost.PortPath(types.PortID)); err != nil { + panic(fmt.Sprintf("could not claim port capability: %v", err)) + } } } diff --git a/modules/apps/27-interchain-accounts/module_test.go b/modules/apps/27-interchain-accounts/module_test.go index de5f51ae921..f030eac9075 100644 --- a/modules/apps/27-interchain-accounts/module_test.go +++ b/modules/apps/27-interchain-accounts/module_test.go @@ -31,8 +31,9 @@ func (suite *InterchainAccountsTestSuite) SetupTest() { } func (suite *InterchainAccountsTestSuite) TestInitModule() { + // setup and basic testing app := simapp.NewSimApp(log.NewNopLogger(), dbm.NewMemDB(), nil, true, map[int64]bool{}, simapp.DefaultNodeHome, 5, simapp.MakeTestEncodingConfig(), simapp.EmptyAppOptions{}) - icamodule, ok := app.GetModuleManager().Modules[types.ModuleName].(ica.AppModule) + appModule, ok := app.GetModuleManager().Modules[types.ModuleName].(ica.AppModule) suite.Require().True(ok) header := tmproto.Header{ @@ -58,17 +59,73 @@ func (suite *InterchainAccountsTestSuite) TestInitModule() { expAllowMessages := []string{"sdk.Msg"} hostParams.HostEnabled = true hostParams.AllowMessages = expAllowMessages - suite.Require().False(app.IBCKeeper.PortKeeper.IsBound(ctx, types.PortID)) - icamodule.InitModule(ctx, controllerParams, hostParams) + testCases := []struct { + name string + malleate func() + expControllerPass bool + expHostPass bool + }{ + { + "both controller and host set", func() { + var ok bool + appModule, ok = app.GetModuleManager().Modules[types.ModuleName].(ica.AppModule) + suite.Require().True(ok) + }, true, true, + }, + { + "neither controller or host is set", func() { + appModule = ica.NewAppModule(nil, nil) + }, false, false, + }, + { + "only controller is set", func() { + appModule = ica.NewAppModule(&app.ICAControllerKeeper, nil) + }, true, false, + }, + { + "only host is set", func() { + appModule = ica.NewAppModule(nil, &app.ICAHostKeeper) + }, false, true, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() // reset + + // reset app state + app = simapp.NewSimApp(log.NewNopLogger(), dbm.NewMemDB(), nil, true, map[int64]bool{}, simapp.DefaultNodeHome, 5, simapp.MakeTestEncodingConfig(), simapp.EmptyAppOptions{}) + header := tmproto.Header{ + ChainID: "testchain", + Height: 1, + Time: suite.coordinator.CurrentTime.UTC(), + } - controllerParams = app.ICAControllerKeeper.GetParams(ctx) - suite.Require().True(controllerParams.ControllerEnabled) + ctx := app.GetBaseApp().NewContext(true, header) - hostParams = app.ICAHostKeeper.GetParams(ctx) - suite.Require().True(hostParams.HostEnabled) - suite.Require().Equal(expAllowMessages, hostParams.AllowMessages) + tc.malleate() - suite.Require().True(app.IBCKeeper.PortKeeper.IsBound(ctx, types.PortID)) + suite.Require().NotPanics(func() { + appModule.InitModule(ctx, controllerParams, hostParams) + }) + + if tc.expControllerPass { + controllerParams = app.ICAControllerKeeper.GetParams(ctx) + suite.Require().True(controllerParams.ControllerEnabled) + } + + if tc.expHostPass { + hostParams = app.ICAHostKeeper.GetParams(ctx) + suite.Require().True(hostParams.HostEnabled) + suite.Require().Equal(expAllowMessages, hostParams.AllowMessages) + + suite.Require().True(app.IBCKeeper.PortKeeper.IsBound(ctx, types.PortID)) + } + + }) + } } diff --git a/modules/apps/transfer/keeper/relay.go b/modules/apps/transfer/keeper/relay.go index ab7f3751588..3c3a5aa6690 100644 --- a/modules/apps/transfer/keeper/relay.go +++ b/modules/apps/transfer/keeper/relay.go @@ -239,6 +239,10 @@ func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet, data t } token := sdk.NewCoin(denom, transferAmount) + if k.bankKeeper.BlockedAddr(receiver) { + return sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive funds", receiver) + } + // unescrow tokens escrowAddress := types.GetEscrowAddress(packet.GetDestPort(), packet.GetDestChannel()) if err := k.bankKeeper.SendCoins(ctx, escrowAddress, receiver, sdk.NewCoins(token)); err != nil { diff --git a/modules/apps/transfer/keeper/relay_test.go b/modules/apps/transfer/keeper/relay_test.go index 9d03bbde962..ce34f316669 100644 --- a/modules/apps/transfer/keeper/relay_test.go +++ b/modules/apps/transfer/keeper/relay_test.go @@ -167,6 +167,16 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { {"tries to unescrow more tokens than allowed", func() { amount = sdk.NewInt(1000000) }, true, false}, + + // - coin being sent to module address on chainA + {"failure: receive on module account", func() { + receiver = suite.chainA.GetSimApp().AccountKeeper.GetModuleAddress(types.ModuleName).String() + }, false, false}, + + // - coin being sent back to original chain (chainB) to module address + {"failure: receive on module account on source chain", func() { + receiver = suite.chainB.GetSimApp().AccountKeeper.GetModuleAddress(types.ModuleName).String() + }, true, false}, } for _, tc := range testCases { diff --git a/modules/apps/transfer/types/expected_keepers.go b/modules/apps/transfer/types/expected_keepers.go index 8ae670d27b2..22ad54b9e62 100644 --- a/modules/apps/transfer/types/expected_keepers.go +++ b/modules/apps/transfer/types/expected_keepers.go @@ -23,6 +23,7 @@ type BankKeeper interface { BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error + BlockedAddr(addr sdk.AccAddress) bool } // ICS4Wrapper defines the expected ICS4Wrapper for middleware diff --git a/modules/core/02-client/keeper/client_test.go b/modules/core/02-client/keeper/client_test.go index 8d70a1816e0..424382a4d7f 100644 --- a/modules/core/02-client/keeper/client_test.go +++ b/modules/core/02-client/keeper/client_test.go @@ -58,7 +58,7 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { suite.Require().True(found) return suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(fillHeight.RevisionHeight), trustedHeight, consState.(*ibctmtypes.ConsensusState).Timestamp.Add(time.Second*5), - suite.chainB.Vals, suite.chainB.Vals, suite.chainB.Signers) + suite.chainB.Vals, suite.chainB.Vals, suite.chainB.Vals, suite.chainB.Signers) } cases := []struct { @@ -444,9 +444,12 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() { // Create signer array and ensure it is in same order as bothValSet _, suiteVal := suite.valSet.GetByIndex(0) - bothSigners := ibctesting.CreateSortedSignerArray(altPrivVal, suite.privVal, altVal, suiteVal) + bothSigners := make(map[string]tmtypes.PrivValidator, 2) + bothSigners[suiteVal.Address.String()] = suite.privVal + bothSigners[altVal.Address.String()] = altPrivVal - altSigners := []tmtypes.PrivValidator{altPrivVal} + altSigners := make(map[string]tmtypes.PrivValidator, 1) + altSigners[altVal.Address.String()] = altPrivVal // Create valid Misbehaviour by making a duplicate header that signs over different block time altTime := suite.ctx.BlockTime().Add(time.Minute) @@ -463,8 +466,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() { { "trusting period misbehavior should pass", &ibctmtypes.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, altTime, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, altTime, bothValSet, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothValSet, bothSigners), ClientId: clientID, }, func() error { @@ -479,8 +482,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() { { "time misbehavior should pass", &ibctmtypes.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+5), testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, altTime, bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+5), testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, altTime, bothValSet, bothValSet, bothValSet, bothSigners), ClientId: clientID, }, func() error { @@ -495,8 +498,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() { { "misbehavior at later height should pass", &ibctmtypes.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), testClientHeight, altTime, bothValSet, valSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), testClientHeight, suite.ctx.BlockTime(), bothValSet, valSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), testClientHeight, altTime, bothValSet, bothValSet, valSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, valSet, bothSigners), ClientId: clientID, }, func() error { @@ -521,8 +524,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() { { "misbehavior at later height with different trusted heights should pass", &ibctmtypes.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), testClientHeight, altTime, bothValSet, valSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), heightPlus3, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), testClientHeight, altTime, bothValSet, bothValSet, valSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), heightPlus3, suite.ctx.BlockTime(), bothValSet, bothValSet, bothValSet, bothSigners), ClientId: clientID, }, func() error { @@ -547,8 +550,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() { { "misbehavior ValidateBasic fails: misbehaviour height is at same height as trusted height", &ibctmtypes.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight), testClientHeight, altTime, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight), testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight), testClientHeight, altTime, bothValSet, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight), testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothValSet, bothSigners), ClientId: clientID, }, func() error { @@ -563,8 +566,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() { { "trusted ConsensusState1 not found", &ibctmtypes.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), heightPlus3, altTime, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), testClientHeight, suite.ctx.BlockTime(), bothValSet, valSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), heightPlus3, altTime, bothValSet, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, valSet, bothSigners), ClientId: clientID, }, func() error { @@ -579,8 +582,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() { { "trusted ConsensusState2 not found", &ibctmtypes.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), testClientHeight, altTime, bothValSet, valSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), heightPlus3, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), testClientHeight, altTime, bothValSet, bothValSet, valSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), heightPlus3, suite.ctx.BlockTime(), bothValSet, bothValSet, bothValSet, bothSigners), ClientId: clientID, }, func() error { @@ -601,8 +604,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() { { "client already is not active - client is frozen", &ibctmtypes.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, altTime, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, altTime, bothValSet, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothValSet, bothSigners), ClientId: clientID, }, func() error { @@ -620,8 +623,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() { { "misbehaviour check failed", &ibctmtypes.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, altTime, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, suite.ctx.BlockTime(), altValSet, bothValSet, altSigners), + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, altTime, bothValSet, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, suite.ctx.BlockTime(), altValSet, altValSet, bothValSet, altSigners), ClientId: clientID, }, func() error { diff --git a/modules/core/02-client/keeper/keeper_test.go b/modules/core/02-client/keeper/keeper_test.go index e3dfb4d2e3b..bbcc6dafde9 100644 --- a/modules/core/02-client/keeper/keeper_test.go +++ b/modules/core/02-client/keeper/keeper_test.go @@ -66,6 +66,8 @@ type KeeperTestSuite struct { now time.Time past time.Time + signers map[string]tmtypes.PrivValidator + // TODO: deprecate queryClient types.QueryClient } @@ -95,7 +97,11 @@ func (suite *KeeperTestSuite) SetupTest() { validator := tmtypes.NewValidator(pubKey, 1) suite.valSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) suite.valSetHash = suite.valSet.Hash() - suite.header = suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight), testClientHeightMinus1, now2, suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) + + suite.signers = make(map[string]tmtypes.PrivValidator, 1) + suite.signers[validator.Address.String()] = suite.privVal + + suite.header = suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight), testClientHeightMinus1, now2, suite.valSet, suite.valSet, suite.valSet, suite.signers) suite.consensusState = ibctmtypes.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot([]byte("hash")), suite.valSetHash) var validators stakingtypes.Validators @@ -329,7 +335,7 @@ func (suite KeeperTestSuite) TestConsensusStateHelpers() { testClientHeightPlus5 := types.NewHeight(0, height+5) header := suite.chainA.CreateTMClientHeader(testClientID, int64(testClientHeightPlus5.RevisionHeight), testClientHeight, suite.header.Header.Time.Add(time.Minute), - suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) + suite.valSet, suite.valSet, suite.valSet, suite.signers) // mock update functionality clientState.LatestHeight = header.GetHeight().(types.Height) diff --git a/modules/core/02-client/types/genesis_test.go b/modules/core/02-client/types/genesis_test.go index 6972a8d5983..6fc37070b31 100644 --- a/modules/core/02-client/types/genesis_test.go +++ b/modules/core/02-client/types/genesis_test.go @@ -55,8 +55,11 @@ func (suite *TypesTestSuite) TestValidateGenesis() { val := tmtypes.NewValidator(pubKey, 10) valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{val}) + signers := make(map[string]tmtypes.PrivValidator) + signers[val.Address.String()] = privVal + heightMinus1 := types.NewHeight(0, height-1) - header := suite.chainA.CreateTMClientHeader(chainID, int64(clientHeight.RevisionHeight), heightMinus1, now, valSet, valSet, []tmtypes.PrivValidator{privVal}) + header := suite.chainA.CreateTMClientHeader(chainID, int64(clientHeight.RevisionHeight), heightMinus1, now, valSet, valSet, valSet, signers) testCases := []struct { name string diff --git a/modules/core/02-client/types/msgs_test.go b/modules/core/02-client/types/msgs_test.go index 35dd08aedba..ce5ecd159cb 100644 --- a/modules/core/02-client/types/msgs_test.go +++ b/modules/core/02-client/types/msgs_test.go @@ -490,8 +490,8 @@ func (suite *TypesTestSuite) TestMarshalMsgSubmitMisbehaviour() { "tendermint client", func() { height := types.NewHeight(0, uint64(suite.chainA.CurrentHeader.Height)) heightMinus1 := types.NewHeight(0, uint64(suite.chainA.CurrentHeader.Height)-1) - header1 := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, int64(height.RevisionHeight), heightMinus1, suite.chainA.CurrentHeader.Time, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) - header2 := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, int64(height.RevisionHeight), heightMinus1, suite.chainA.CurrentHeader.Time.Add(time.Minute), suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) + header1 := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, int64(height.RevisionHeight), heightMinus1, suite.chainA.CurrentHeader.Time, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) + header2 := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, int64(height.RevisionHeight), heightMinus1, suite.chainA.CurrentHeader.Time.Add(time.Minute), suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) misbehaviour := ibctmtypes.NewMisbehaviour("tendermint", header1, header2) msg, err = types.NewMsgSubmitMisbehaviour("tendermint", misbehaviour, suite.chainA.SenderAccount.GetAddress().String()) @@ -548,8 +548,8 @@ func (suite *TypesTestSuite) TestMsgSubmitMisbehaviour_ValidateBasic() { func() { height := types.NewHeight(0, uint64(suite.chainA.CurrentHeader.Height)) heightMinus1 := types.NewHeight(0, uint64(suite.chainA.CurrentHeader.Height)-1) - header1 := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, int64(height.RevisionHeight), heightMinus1, suite.chainA.CurrentHeader.Time, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) - header2 := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, int64(height.RevisionHeight), heightMinus1, suite.chainA.CurrentHeader.Time.Add(time.Minute), suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) + header1 := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, int64(height.RevisionHeight), heightMinus1, suite.chainA.CurrentHeader.Time, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) + header2 := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, int64(height.RevisionHeight), heightMinus1, suite.chainA.CurrentHeader.Time.Add(time.Minute), suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) misbehaviour := ibctmtypes.NewMisbehaviour("tendermint", header1, header2) msg, err = types.NewMsgSubmitMisbehaviour("tendermint", misbehaviour, suite.chainA.SenderAccount.GetAddress().String()) diff --git a/modules/core/04-channel/keeper/packet.go b/modules/core/04-channel/keeper/packet.go index b54926b85e9..51b02f9bece 100644 --- a/modules/core/04-channel/keeper/packet.go +++ b/modules/core/04-channel/keeper/packet.go @@ -360,7 +360,7 @@ func (k Keeper) WriteAcknowledgement( // log that a packet acknowledgement has been written k.Logger(ctx).Info( "acknowledgement written", - "sequence", packet.GetSequence, + "sequence", packet.GetSequence(), "src_port", packet.GetSourcePort(), "src_channel", packet.GetSourceChannel(), "dst_port", packet.GetDestPort(), diff --git a/modules/core/genesis_test.go b/modules/core/genesis_test.go index 00b9a0c3b84..aa921f7c19e 100644 --- a/modules/core/genesis_test.go +++ b/modules/core/genesis_test.go @@ -59,7 +59,7 @@ func TestIBCTestSuite(t *testing.T) { } func (suite *IBCTestSuite) TestValidateGenesis() { - header := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, suite.chainA.CurrentHeader.Height, clienttypes.NewHeight(0, uint64(suite.chainA.CurrentHeader.Height-1)), suite.chainA.CurrentHeader.Time, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) + header := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, suite.chainA.CurrentHeader.Height, clienttypes.NewHeight(0, uint64(suite.chainA.CurrentHeader.Height-1)), suite.chainA.CurrentHeader.Time, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) testCases := []struct { name string @@ -225,7 +225,7 @@ func (suite *IBCTestSuite) TestValidateGenesis() { } func (suite *IBCTestSuite) TestInitGenesis() { - header := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, suite.chainA.CurrentHeader.Height, clienttypes.NewHeight(0, uint64(suite.chainA.CurrentHeader.Height-1)), suite.chainA.CurrentHeader.Time, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) + header := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, suite.chainA.CurrentHeader.Height, clienttypes.NewHeight(0, uint64(suite.chainA.CurrentHeader.Height-1)), suite.chainA.CurrentHeader.Time, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) testCases := []struct { name string diff --git a/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go b/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go index 8efe54c7fba..93178f57816 100644 --- a/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go +++ b/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go @@ -11,7 +11,6 @@ import ( commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v3/modules/core/exported" "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" ibctestingmock "github.com/cosmos/ibc-go/v3/testing/mock" ) @@ -22,18 +21,14 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { altVal := tmtypes.NewValidator(altPubKey, 4) - // Create bothValSet with both suite validator and altVal - bothValSet := tmtypes.NewValidatorSet(append(suite.valSet.Validators, altVal)) - bothValsHash := bothValSet.Hash() // Create alternative validator set with only altVal altValSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{altVal}) - _, suiteVal := suite.valSet.GetByIndex(0) - - // Create signer array and ensure it is in same order as bothValSet - bothSigners := ibctesting.CreateSortedSignerArray(altPrivVal, suite.privVal, altVal, suiteVal) + // Create bothValSet with both suite validator and altVal + bothValSet, bothSigners := getBothSigners(suite, altVal, altPrivVal) + bothValsHash := bothValSet.Hash() - altSigners := []tmtypes.PrivValidator{altPrivVal} + altSigners := getAltSigners(altVal, altPrivVal) heightMinus1 := clienttypes.NewHeight(height.RevisionNumber, height.RevisionHeight-1) heightMinus3 := clienttypes.NewHeight(height.RevisionNumber, height.RevisionHeight-3) @@ -57,8 +52,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), height, &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothValSet, bothSigners), ClientId: chainID, }, suite.now, @@ -72,8 +67,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), height, &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+3), height, suite.now, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+3), height, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), ClientId: chainID, }, suite.now, @@ -87,8 +82,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), height, &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+3), height, suite.now, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Hour), bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+3), height, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Hour), bothValSet, bothValSet, bothValSet, bothSigners), ClientId: chainID, }, suite.now, @@ -102,8 +97,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), heightMinus1, &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now.Add(time.Minute), bothValSet, bothValSet, bothValSet, bothSigners), ClientId: chainID, }, suite.now, @@ -117,8 +112,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), heightMinus3, &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus3, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus3, suite.now.Add(time.Minute), bothValSet, bothValSet, suite.valSet, bothSigners), ClientId: chainID, }, suite.now, @@ -132,8 +127,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), heightMinus3, &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainIDRevision0, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainIDRevision0, int64(height.RevisionHeight+1), heightMinus3, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainIDRevision0, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainIDRevision0, int64(height.RevisionHeight+1), heightMinus3, suite.now.Add(time.Minute), bothValSet, bothValSet, suite.valSet, bothSigners), ClientId: chainID, }, suite.now, @@ -147,8 +142,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), heightMinus3, &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainIDRevision0, 3, heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainIDRevision0, 3, heightMinus3, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainIDRevision0, 3, heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainIDRevision0, 3, heightMinus3, suite.now.Add(time.Minute), bothValSet, bothValSet, suite.valSet, bothSigners), ClientId: chainID, }, suite.now, @@ -162,8 +157,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), heightMinus3, &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainIDRevision1, 1, heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainIDRevision1, 1, heightMinus3, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainIDRevision1, 1, heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainIDRevision1, 1, heightMinus3, suite.now.Add(time.Minute), bothValSet, bothValSet, suite.valSet, bothSigners), ClientId: chainID, }, suite.now, @@ -177,8 +172,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), height, &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, suite.valSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, suite.valSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, suite.valSet, bothSigners), ClientId: chainID, }, suite.now, @@ -192,8 +187,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), height, &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), ClientId: chainID, }, suite.now, @@ -207,8 +202,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), height, &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+3), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+3), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), ClientId: chainID, }, suite.now, @@ -222,8 +217,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), height, &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader("ethermint", int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader("ethermint", int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader("ethermint", int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader("ethermint", int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothValSet, bothSigners), ClientId: chainID, }, suite.now, @@ -237,8 +232,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), heightMinus3, &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, suite.valSet, bothSigners), ClientId: chainID, }, suite.now, @@ -252,8 +247,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), heightMinus3, &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus3, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus3, suite.now.Add(time.Minute), bothValSet, bothValSet, bothValSet, bothSigners), ClientId: chainID, }, suite.now, @@ -267,8 +262,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), height, &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothValSet, bothSigners), ClientId: chainID, }, suite.now, @@ -282,8 +277,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), height, &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothValSet, bothSigners), ClientId: chainID, }, suite.now, @@ -308,8 +303,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), height, &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now.Add(time.Minute), bothValSet, bothValSet, bothValSet, bothSigners), ClientId: chainID, }, suite.now, @@ -323,8 +318,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), height, &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothValSet, bothSigners), ClientId: chainID, }, suite.now.Add(trustingPeriod), @@ -338,8 +333,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), height, &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, suite.valSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, suite.valSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, suite.valSet, bothSigners), ClientId: chainID, }, suite.now, @@ -353,8 +348,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), height, &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, altValSet, bothValSet, altSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, altValSet, altValSet, bothValSet, altSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothValSet, bothSigners), ClientId: chainID, }, suite.now, @@ -368,8 +363,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), height, &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), altValSet, bothValSet, altSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), altValSet, altValSet, bothValSet, altSigners), ClientId: chainID, }, suite.now, @@ -383,8 +378,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), height, &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, altValSet, bothValSet, altSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), altValSet, bothValSet, altSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, altValSet, altValSet, bothValSet, altSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), altValSet, altValSet, bothValSet, altSigners), ClientId: chainID, }, suite.now, diff --git a/modules/light-clients/07-tendermint/types/misbehaviour_test.go b/modules/light-clients/07-tendermint/types/misbehaviour_test.go index 6b67d1b2945..fdd51df4e53 100644 --- a/modules/light-clients/07-tendermint/types/misbehaviour_test.go +++ b/modules/light-clients/07-tendermint/types/misbehaviour_test.go @@ -15,12 +15,11 @@ import ( ) func (suite *TendermintTestSuite) TestMisbehaviour() { - signers := []tmtypes.PrivValidator{suite.privVal} heightMinus1 := clienttypes.NewHeight(0, height.RevisionHeight-1) misbehaviour := &types.Misbehaviour{ Header1: suite.header, - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, suite.valSet, suite.valSet, signers), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, suite.valSet, suite.valSet, suite.valSet, suite.signers), ClientId: clientID, } @@ -36,18 +35,13 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { altVal := tmtypes.NewValidator(altPubKey, revisionHeight) - // Create bothValSet with both suite validator and altVal - bothValSet := tmtypes.NewValidatorSet(append(suite.valSet.Validators, altVal)) // Create alternative validator set with only altVal altValSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{altVal}) - signers := []tmtypes.PrivValidator{suite.privVal} - // Create signer array and ensure it is in same order as bothValSet - _, suiteVal := suite.valSet.GetByIndex(0) - bothSigners := ibctesting.CreateSortedSignerArray(altPrivVal, suite.privVal, altVal, suiteVal) + bothValSet, bothSigners := getBothSigners(suite, altVal, altPrivVal) - altSigners := []tmtypes.PrivValidator{altPrivVal} + altSignerArr := []tmtypes.PrivValidator{altPrivVal} heightMinus1 := clienttypes.NewHeight(0, height.RevisionHeight-1) @@ -61,7 +55,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { "valid fork misbehaviour, two headers at same height have different time", &types.Misbehaviour{ Header1: suite.header, - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now.Add(time.Minute), suite.valSet, suite.valSet, suite.valSet, suite.signers), ClientId: clientID, }, func(misbehaviour *types.Misbehaviour) error { return nil }, @@ -70,7 +64,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { { "valid time misbehaviour, both headers at different heights are at same time", &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+5), heightMinus1, suite.now, suite.valSet, suite.valSet, signers), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+5), heightMinus1, suite.now, suite.valSet, suite.valSet, suite.valSet, suite.signers), Header2: suite.header, ClientId: clientID, }, @@ -93,7 +87,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { "valid misbehaviour with different trusted headers", &types.Misbehaviour{ Header1: suite.header, - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), clienttypes.NewHeight(0, height.RevisionHeight-3), suite.now.Add(time.Minute), suite.valSet, bothValSet, signers), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), clienttypes.NewHeight(0, height.RevisionHeight-3), suite.now.Add(time.Minute), suite.valSet, suite.valSet, bothValSet, suite.signers), ClientId: clientID, }, func(misbehaviour *types.Misbehaviour) error { return nil }, @@ -102,7 +96,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { { "trusted height is 0 in Header1", &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), clienttypes.ZeroHeight(), suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), clienttypes.ZeroHeight(), suite.now.Add(time.Minute), suite.valSet, suite.valSet, suite.valSet, suite.signers), Header2: suite.header, ClientId: clientID, }, @@ -113,7 +107,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { "trusted height is 0 in Header2", &types.Misbehaviour{ Header1: suite.header, - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), clienttypes.ZeroHeight(), suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), clienttypes.ZeroHeight(), suite.now.Add(time.Minute), suite.valSet, suite.valSet, suite.valSet, suite.signers), ClientId: clientID, }, func(misbehaviour *types.Misbehaviour) error { return nil }, @@ -122,7 +116,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { { "trusted valset is nil in Header1", &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now.Add(time.Minute), suite.valSet, nil, signers), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now.Add(time.Minute), suite.valSet, suite.valSet, nil, suite.signers), Header2: suite.header, ClientId: clientID, }, @@ -133,7 +127,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { "trusted valset is nil in Header2", &types.Misbehaviour{ Header1: suite.header, - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now.Add(time.Minute), suite.valSet, nil, signers), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now.Add(time.Minute), suite.valSet, suite.valSet, nil, suite.signers), ClientId: clientID, }, func(misbehaviour *types.Misbehaviour) error { return nil }, @@ -143,7 +137,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { "invalid client ID ", &types.Misbehaviour{ Header1: suite.header, - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, suite.valSet, suite.valSet, signers), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, suite.valSet, suite.valSet, suite.valSet, suite.signers), ClientId: "GAIA", }, func(misbehaviour *types.Misbehaviour) error { return nil }, @@ -153,7 +147,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { "chainIDs do not match", &types.Misbehaviour{ Header1: suite.header, - Header2: suite.chainA.CreateTMClientHeader("ethermint", int64(height.RevisionHeight), heightMinus1, suite.now, suite.valSet, suite.valSet, signers), + Header2: suite.chainA.CreateTMClientHeader("ethermint", int64(height.RevisionHeight), heightMinus1, suite.now, suite.valSet, suite.valSet, suite.valSet, suite.signers), ClientId: clientID, }, func(misbehaviour *types.Misbehaviour) error { return nil }, @@ -163,7 +157,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { "header2 height is greater", &types.Misbehaviour{ Header1: suite.header, - Header2: suite.chainA.CreateTMClientHeader(chainID, 6, clienttypes.NewHeight(0, height.RevisionHeight+1), suite.now, suite.valSet, suite.valSet, signers), + Header2: suite.chainA.CreateTMClientHeader(chainID, 6, clienttypes.NewHeight(0, height.RevisionHeight+1), suite.now, suite.valSet, suite.valSet, suite.valSet, suite.signers), ClientId: clientID, }, func(misbehaviour *types.Misbehaviour) error { return nil }, @@ -172,7 +166,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { { "header 1 doesn't have 2/3 majority", &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, bothValSet, suite.valSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, bothValSet, bothValSet, suite.valSet, bothSigners), Header2: suite.header, ClientId: clientID, }, @@ -184,7 +178,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { return err } - tmCommit, err := tmtypes.MakeCommit(*blockID, int64(misbehaviour.Header2.GetHeight().GetRevisionHeight()), misbehaviour.Header1.Commit.Round, wrongVoteSet, altSigners, suite.now) + tmCommit, err := tmtypes.MakeCommit(*blockID, int64(misbehaviour.Header2.GetHeight().GetRevisionHeight()), misbehaviour.Header1.Commit.Round, wrongVoteSet, altSignerArr, suite.now) misbehaviour.Header1.Commit = tmCommit.ToProto() return err }, @@ -194,7 +188,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { "header 2 doesn't have 2/3 majority", &types.Misbehaviour{ Header1: suite.header, - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, bothValSet, suite.valSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, bothValSet, bothValSet, suite.valSet, bothSigners), ClientId: clientID, }, func(misbehaviour *types.Misbehaviour) error { @@ -205,7 +199,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { return err } - tmCommit, err := tmtypes.MakeCommit(*blockID, int64(misbehaviour.Header2.GetHeight().GetRevisionHeight()), misbehaviour.Header2.Commit.Round, wrongVoteSet, altSigners, suite.now) + tmCommit, err := tmtypes.MakeCommit(*blockID, int64(misbehaviour.Header2.GetHeight().GetRevisionHeight()), misbehaviour.Header2.Commit.Round, wrongVoteSet, altSignerArr, suite.now) misbehaviour.Header2.Commit = tmCommit.ToProto() return err }, @@ -215,7 +209,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { "validators sign off on wrong commit", &types.Misbehaviour{ Header1: suite.header, - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, bothValSet, suite.valSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, bothValSet, bothValSet, suite.valSet, bothSigners), ClientId: clientID, }, func(misbehaviour *types.Misbehaviour) error { diff --git a/modules/light-clients/07-tendermint/types/tendermint_test.go b/modules/light-clients/07-tendermint/types/tendermint_test.go index 7c1996abfa8..898a48efe72 100644 --- a/modules/light-clients/07-tendermint/types/tendermint_test.go +++ b/modules/light-clients/07-tendermint/types/tendermint_test.go @@ -48,6 +48,7 @@ type TendermintTestSuite struct { cdc codec.Codec privVal tmtypes.PrivValidator valSet *tmtypes.ValidatorSet + signers map[string]tmtypes.PrivValidator valsHash tmbytes.HexBytes header *ibctmtypes.Header now time.Time @@ -84,22 +85,27 @@ func (suite *TendermintTestSuite) SetupTest() { heightMinus1 := clienttypes.NewHeight(0, height.RevisionHeight-1) val := tmtypes.NewValidator(pubKey, 10) + suite.signers = make(map[string]tmtypes.PrivValidator) + suite.signers[val.Address.String()] = suite.privVal suite.valSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{val}) suite.valsHash = suite.valSet.Hash() - suite.header = suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) + suite.header = suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, suite.valSet, suite.valSet, suite.valSet, suite.signers) suite.ctx = app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 1, Time: suite.now}) } -func getSuiteSigners(suite *TendermintTestSuite) []tmtypes.PrivValidator { - return []tmtypes.PrivValidator{suite.privVal} +func getAltSigners(altVal *tmtypes.Validator, altPrivVal tmtypes.PrivValidator) map[string]tmtypes.PrivValidator { + return map[string]tmtypes.PrivValidator{altVal.Address.String(): altPrivVal} } -func getBothSigners(suite *TendermintTestSuite, altVal *tmtypes.Validator, altPrivVal tmtypes.PrivValidator) (*tmtypes.ValidatorSet, []tmtypes.PrivValidator) { +func getBothSigners(suite *TendermintTestSuite, altVal *tmtypes.Validator, altPrivVal tmtypes.PrivValidator) (*tmtypes.ValidatorSet, map[string]tmtypes.PrivValidator) { // Create bothValSet with both suite validator and altVal. Would be valid update bothValSet := tmtypes.NewValidatorSet(append(suite.valSet.Validators, altVal)) // Create signer array and ensure it is in same order as bothValSet _, suiteVal := suite.valSet.GetByIndex(0) - bothSigners := ibctesting.CreateSortedSignerArray(altPrivVal, suite.privVal, altVal, suiteVal) + bothSigners := map[string]tmtypes.PrivValidator{ + suiteVal.Address.String(): suite.privVal, + altVal.Address.String(): altPrivVal, + } return bothValSet, bothSigners } diff --git a/modules/light-clients/07-tendermint/types/update_test.go b/modules/light-clients/07-tendermint/types/update_test.go index 77c993dff72..379ccba5e00 100644 --- a/modules/light-clients/07-tendermint/types/update_test.go +++ b/modules/light-clients/07-tendermint/types/update_test.go @@ -22,8 +22,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { newHeader *types.Header currentTime time.Time bothValSet *tmtypes.ValidatorSet - signers []tmtypes.PrivValidator - bothSigners []tmtypes.PrivValidator + bothSigners map[string]tmtypes.PrivValidator ) // Setup different validators and signers for testing different types of updates @@ -42,7 +41,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { altVal := tmtypes.NewValidator(altPubKey, revisionHeight) // Create alternative validator set with only altVal, invalid update (too much change in valSet) altValSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{altVal}) - altSigners := []tmtypes.PrivValidator{altPrivVal} + altSigners := getAltSigners(altVal, altPrivVal) testCases := []struct { name string @@ -55,7 +54,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { setup: func(suite *TendermintTestSuite) { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, signers) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) currentTime = suite.now }, expFrozen: false, @@ -66,7 +65,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { setup: func(suite *TendermintTestSuite) { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus5.RevisionHeight), height, suite.headerTime, bothValSet, suite.valSet, bothSigners) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus5.RevisionHeight), height, suite.headerTime, bothValSet, bothValSet, suite.valSet, bothSigners) currentTime = suite.now }, expFrozen: false, @@ -77,7 +76,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { setup: func(suite *TendermintTestSuite) { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), bothValSet.Hash()) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, bothValSet, bothValSet, bothSigners) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, bothValSet, bothValSet, bothValSet, bothSigners) currentTime = suite.now }, expFrozen: false, @@ -89,7 +88,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) consStateHeight = heightMinus3 - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightMinus1.RevisionHeight), heightMinus3, suite.headerTime, bothValSet, suite.valSet, bothSigners) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightMinus1.RevisionHeight), heightMinus3, suite.headerTime, bothValSet, bothValSet, suite.valSet, bothSigners) currentTime = suite.now }, expFrozen: false, @@ -101,7 +100,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { clientState = types.NewClientState(chainIDRevision1, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) consStateHeight = heightMinus3 - newHeader = suite.chainA.CreateTMClientHeader(chainIDRevision0, int64(height.RevisionHeight), heightMinus3, suite.headerTime, bothValSet, suite.valSet, bothSigners) + newHeader = suite.chainA.CreateTMClientHeader(chainIDRevision0, int64(height.RevisionHeight), heightMinus3, suite.headerTime, bothValSet, bothValSet, suite.valSet, bothSigners) currentTime = suite.now }, expPass: true, @@ -111,7 +110,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { setup: func(suite *TendermintTestSuite) { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, heightPlus1, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, signers) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) currentTime = suite.now ctx := suite.chainA.GetContext().WithBlockTime(currentTime) // Store the header's consensus state in client store before UpdateClient call @@ -125,7 +124,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { setup: func(suite *TendermintTestSuite) { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, heightPlus1, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, signers) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) currentTime = suite.now ctx := suite.chainA.GetContext().WithBlockTime(currentTime) // Change the consensus state of header and store in client store to create a conflict @@ -143,7 +142,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { // create an intermediate consensus state with the same time as the newHeader to create a time violation. // header time is after client time consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus5.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, signers) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus5.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) currentTime = suite.now prevConsensusState := types.NewConsensusState(suite.headerTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) ctx := suite.chainA.GetContext().WithBlockTime(currentTime) @@ -161,7 +160,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { // create the next consensus state with the same time as the intermediate newHeader to create a time violation. // header time is after clientTime consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, signers) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) currentTime = suite.now nextConsensusState := types.NewConsensusState(suite.headerTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) ctx := suite.chainA.GetContext().WithBlockTime(currentTime) @@ -177,7 +176,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { setup: func(suite *TendermintTestSuite) { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader("ethermint", int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, signers) + newHeader = suite.chainA.CreateTMClientHeader("ethermint", int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) currentTime = suite.now }, expFrozen: false, @@ -188,7 +187,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { setup: func(suite *TendermintTestSuite) { clientState = types.NewClientState(chainIDRevision0, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainIDRevision1, 1, height, suite.headerTime, suite.valSet, suite.valSet, signers) + newHeader = suite.chainA.CreateTMClientHeader(chainIDRevision1, 1, height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) currentTime = suite.now }, expPass: false, @@ -198,7 +197,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { setup: func(suite *TendermintTestSuite) { clientState = types.NewClientState(chainIDRevision1, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(1, 1), commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainIDRevision1, 3, height, suite.headerTime, suite.valSet, suite.valSet, signers) + newHeader = suite.chainA.CreateTMClientHeader(chainIDRevision1, 3, height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) currentTime = suite.now }, expFrozen: false, @@ -209,7 +208,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { setup: func(suite *TendermintTestSuite) { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, bothValSet, suite.valSet, bothSigners) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, bothValSet, bothValSet, suite.valSet, bothSigners) currentTime = suite.now }, expFrozen: false, @@ -220,7 +219,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { setup: func(suite *TendermintTestSuite) { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), bothValSet.Hash()) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, bothValSet, signers) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, bothValSet, suite.signers) currentTime = suite.now }, expFrozen: false, @@ -231,7 +230,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { setup: func(suite *TendermintTestSuite) { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus5.RevisionHeight), height, suite.headerTime, altValSet, suite.valSet, altSigners) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus5.RevisionHeight), height, suite.headerTime, altValSet, altValSet, suite.valSet, altSigners) currentTime = suite.now }, expFrozen: false, @@ -242,7 +241,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { setup: func(suite *TendermintTestSuite) { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus5.RevisionHeight), height, suite.headerTime, bothValSet, bothValSet, bothSigners) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus5.RevisionHeight), height, suite.headerTime, bothValSet, bothValSet, bothValSet, bothSigners) currentTime = suite.now }, expFrozen: false, @@ -253,7 +252,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { setup: func(suite *TendermintTestSuite) { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, signers) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) // make current time pass trusting period from last timestamp on clientstate currentTime = suite.now.Add(trustingPeriod) }, @@ -265,7 +264,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { setup: func(suite *TendermintTestSuite) { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.now.Add(time.Minute), suite.valSet, suite.valSet, suite.valSet, suite.signers) currentTime = suite.now }, expFrozen: false, @@ -276,7 +275,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { setup: func(suite *TendermintTestSuite) { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.clientTime, suite.valSet, suite.valSet, signers) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.clientTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) currentTime = suite.now }, expFrozen: false, @@ -287,7 +286,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { setup: func(suite *TendermintTestSuite) { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, signers) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) // cause new header to fail validatebasic by changing commit height to mismatch header height newHeader.SignedHeader.Commit.Height = revisionHeight - 1 currentTime = suite.now @@ -301,7 +300,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(height.RevisionNumber, heightPlus5.RevisionHeight), commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) // Make new header at height less than latest client state - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightMinus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, signers) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightMinus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) currentTime = suite.now }, expFrozen: false, @@ -314,12 +313,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { suite.Run(fmt.Sprintf("Case: %s", tc.name), func() { suite.SetupTest() // reset metadata writes // Create bothValSet with both suite validator and altVal. Would be valid update - bothValSet = tmtypes.NewValidatorSet(append(suite.valSet.Validators, altVal)) - signers = []tmtypes.PrivValidator{suite.privVal} - - // Create signer array and ensure it is in same order as bothValSet - _, suiteVal := suite.valSet.GetByIndex(0) - bothSigners = ibctesting.CreateSortedSignerArray(altPrivVal, suite.privVal, altVal, suiteVal) + bothValSet, bothSigners = getBothSigners(suite, altVal, altPrivVal) consStateHeight = height // must be explicitly changed // setup test diff --git a/testing/app.go b/testing/app.go index 3c6a14ed453..1cde8614b6d 100644 --- a/testing/app.go +++ b/testing/app.go @@ -99,8 +99,6 @@ func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs bondDenom := stakingGenesis.Params.BondDenom - totalSupply := sdk.NewCoins() - // add bonded amount to bonded pool module account balances = append(balances, banktypes.Balance{ Address: authtypes.NewModuleAddress(stakingtypes.BondedPoolName).String(), @@ -112,7 +110,7 @@ func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs genesisState[stakingtypes.ModuleName] = app.AppCodec().MustMarshalJSON(&stakingGenesis) // update total supply - bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, totalSupply, []banktypes.Metadata{}) + bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, sdk.NewCoins(), []banktypes.Metadata{}) genesisState[banktypes.ModuleName] = app.AppCodec().MustMarshalJSON(bankGenesis) stateBytes, err := json.MarshalIndent(genesisState, "", " ") diff --git a/testing/chain.go b/testing/chain.go index 28382a6c463..9e248312bc8 100644 --- a/testing/chain.go +++ b/testing/chain.go @@ -1,7 +1,6 @@ package ibctesting import ( - "bytes" "fmt" "testing" "time" @@ -60,8 +59,15 @@ type TestChain struct { TxConfig client.TxConfig Codec codec.BinaryCodec - Vals *tmtypes.ValidatorSet - Signers []tmtypes.PrivValidator + Vals *tmtypes.ValidatorSet + NextVals *tmtypes.ValidatorSet + + // Signers is a map from validator address to the PrivValidator + // The map is converted into an array that is the same order as the validators right before signing commit + // This ensures that signers will always be in correct order even as validator powers change. + // If a test adds a new validator after chain creation, then the signer map must be updated to include + // the new PrivValidator entry. + Signers map[string]tmtypes.PrivValidator // autogenerated sender private key SenderPrivKey cryptotypes.PrivKey @@ -83,9 +89,9 @@ type TestChain struct { // NOTE: to use a custom sender privkey and account for testing purposes, replace and modify this // constructor function. // -// CONTRACT: Validator and signer array must be provided in the order expected by Tendermint. +// CONTRACT: Validator array must be provided in the order expected by Tendermint. // i.e. sorted first by power and then lexicographically by address. -func NewTestChainWithValSet(t *testing.T, coord *Coordinator, chainID string, valSet *tmtypes.ValidatorSet, signers []tmtypes.PrivValidator) *TestChain { +func NewTestChainWithValSet(t *testing.T, coord *Coordinator, chainID string, valSet *tmtypes.ValidatorSet, signers map[string]tmtypes.PrivValidator) *TestChain { genAccs := []authtypes.GenesisAccount{} genBals := []banktypes.Balance{} senderAccs := []SenderAccount{} @@ -135,6 +141,7 @@ func NewTestChainWithValSet(t *testing.T, coord *Coordinator, chainID string, va TxConfig: txConfig, Codec: app.AppCodec(), Vals: valSet, + NextVals: valSet, Signers: signers, SenderPrivKey: senderAccs[0].SenderPrivKey, SenderAccount: senderAccs[0].SenderAccount, @@ -169,13 +176,7 @@ func NewTestChain(t *testing.T, coord *Coordinator, chainID string) *TestChain { // or, if equal, by address lexical order valSet := tmtypes.NewValidatorSet(validators) - // create signers indexed by the valSet validators's order - signers := []tmtypes.PrivValidator{} - for _, val := range valSet.Validators { - signers = append(signers, signersByAddress[val.PubKey.Address().String()]) - } - - return NewTestChainWithValSet(t, coord, chainID, valSet, signers) + return NewTestChainWithValSet(t, coord, chainID, valSet, signersByAddress) } // GetContext returns the current context for the application. @@ -261,13 +262,24 @@ func (chain *TestChain) QueryConsensusStateProof(clientID string) ([]byte, clien // NextBlock sets the last header to the current header and increments the current header to be // at the next block height. It does not update the time as that is handled by the Coordinator. -// -// CONTRACT: this function must only be called after app.Commit() occurs +// It will call Endblock and Commit and apply the validator set changes to the next validators +// of the next block being created. This follows the Tendermint protocol of applying valset changes +// returned on block `n` to the validators of block `n+2`. +// It calls BeginBlock with the new block created before returning. func (chain *TestChain) NextBlock() { + res := chain.App.EndBlock(abci.RequestEndBlock{Height: chain.CurrentHeader.Height}) + + chain.App.Commit() + // set the last header to the current header // use nil trusted fields chain.LastHeader = chain.CurrentTMClientHeader() + // val set changes returned from previous block get applied to the next validators + // of this block. See tendermint spec for details. + chain.Vals = chain.NextVals + chain.NextVals = ApplyValSetChanges(chain.T, chain.Vals, res.ValidatorUpdates) + // increment the current header chain.CurrentHeader = tmproto.Header{ ChainID: chain.ChainID, @@ -277,7 +289,7 @@ func (chain *TestChain) NextBlock() { // chains. Time: chain.CurrentHeader.Time, ValidatorsHash: chain.Vals.Hash(), - NextValidatorsHash: chain.Vals.Hash(), + NextValidatorsHash: chain.NextVals.Hash(), } chain.App.BeginBlock(abci.RequestBeginBlock{Header: chain.CurrentHeader}) @@ -311,7 +323,7 @@ func (chain *TestChain) SendMsgs(msgs ...sdk.Msg) (*sdk.Result, error) { return nil, err } - // SignAndDeliver calls app.Commit() + // NextBlock calls app.Commit() chain.NextBlock() // increment sequence for successful transaction execution @@ -423,12 +435,12 @@ func (chain *TestChain) ExpireClient(amount time.Duration) { // CurrentTMClientHeader creates a TM header using the current header parameters // on the chain. The trusted fields in the header are set to nil. func (chain *TestChain) CurrentTMClientHeader() *ibctmtypes.Header { - return chain.CreateTMClientHeader(chain.ChainID, chain.CurrentHeader.Height, clienttypes.Height{}, chain.CurrentHeader.Time, chain.Vals, nil, chain.Signers) + return chain.CreateTMClientHeader(chain.ChainID, chain.CurrentHeader.Height, clienttypes.Height{}, chain.CurrentHeader.Time, chain.Vals, chain.NextVals, nil, chain.Signers) } // CreateTMClientHeader creates a TM header to update the TM client. Args are passed in to allow // caller flexibility to use params that differ from the chain. -func (chain *TestChain) CreateTMClientHeader(chainID string, blockHeight int64, trustedHeight clienttypes.Height, timestamp time.Time, tmValSet, tmTrustedVals *tmtypes.ValidatorSet, signers []tmtypes.PrivValidator) *ibctmtypes.Header { +func (chain *TestChain) CreateTMClientHeader(chainID string, blockHeight int64, trustedHeight clienttypes.Height, timestamp time.Time, tmValSet, nextVals, tmTrustedVals *tmtypes.ValidatorSet, signers map[string]tmtypes.PrivValidator) *ibctmtypes.Header { var ( valSet *tmproto.ValidatorSet trustedVals *tmproto.ValidatorSet @@ -436,6 +448,7 @@ func (chain *TestChain) CreateTMClientHeader(chainID string, blockHeight int64, require.NotNil(chain.T, tmValSet) vsetHash := tmValSet.Hash() + nextValHash := nextVals.Hash() tmHeader := tmtypes.Header{ Version: tmprotoversion.Consensus{Block: tmversion.BlockProtocol, App: 2}, @@ -446,7 +459,7 @@ func (chain *TestChain) CreateTMClientHeader(chainID string, blockHeight int64, LastCommitHash: chain.App.LastCommitID().Hash, DataHash: tmhash.Sum([]byte("data_hash")), ValidatorsHash: vsetHash, - NextValidatorsHash: vsetHash, + NextValidatorsHash: nextValHash, ConsensusHash: tmhash.Sum([]byte("consensus_hash")), AppHash: chain.CurrentHeader.AppHash, LastResultsHash: tmhash.Sum([]byte("last_results_hash")), @@ -458,7 +471,15 @@ func (chain *TestChain) CreateTMClientHeader(chainID string, blockHeight int64, blockID := MakeBlockID(hhash, 3, tmhash.Sum([]byte("part_set"))) voteSet := tmtypes.NewVoteSet(chainID, blockHeight, 1, tmproto.PrecommitType, tmValSet) - commit, err := tmtypes.MakeCommit(blockID, blockHeight, 1, voteSet, signers, timestamp) + // MakeCommit expects a signer array in the same order as the validator array. + // Thus we iterate over the ordered validator set and construct a signer array + // from the signer map in the same order. + var signerArr []tmtypes.PrivValidator + for _, v := range tmValSet.Validators { + signerArr = append(signerArr, signers[v.Address.String()]) + } + + commit, err := tmtypes.MakeCommit(blockID, blockHeight, 1, voteSet, signerArr, timestamp) require.NoError(chain.T, err) signedHeader := &tmproto.SignedHeader{ @@ -497,26 +518,6 @@ func MakeBlockID(hash []byte, partSetSize uint32, partSetHash []byte) tmtypes.Bl } } -// CreateSortedSignerArray takes two PrivValidators, and the corresponding Validator structs -// (including voting power). It returns a signer array of PrivValidators that matches the -// sorting of ValidatorSet. -// The sorting is first by .VotingPower (descending), with secondary index of .Address (ascending). -func CreateSortedSignerArray(altPrivVal, suitePrivVal tmtypes.PrivValidator, - altVal, suiteVal *tmtypes.Validator, -) []tmtypes.PrivValidator { - switch { - case altVal.VotingPower > suiteVal.VotingPower: - return []tmtypes.PrivValidator{altPrivVal, suitePrivVal} - case altVal.VotingPower < suiteVal.VotingPower: - return []tmtypes.PrivValidator{suitePrivVal, altPrivVal} - default: - if bytes.Compare(altVal.Address, suiteVal.Address) == -1 { - return []tmtypes.PrivValidator{altPrivVal, suitePrivVal} - } - return []tmtypes.PrivValidator{suitePrivVal, altPrivVal} - } -} - // CreatePortCapability binds and claims a capability for the given portID if it does not // already exist. This function will fail testing on any resulting error. // NOTE: only creation of a capability for a transfer or mock port is supported @@ -534,8 +535,6 @@ func (chain *TestChain) CreatePortCapability(scopedKeeper capabilitykeeper.Scope require.NoError(chain.T, err) } - chain.App.Commit() - chain.NextBlock() } @@ -562,8 +561,6 @@ func (chain *TestChain) CreateChannelCapability(scopedKeeper capabilitykeeper.Sc require.NoError(chain.T, err) } - chain.App.Commit() - chain.NextBlock() } diff --git a/testing/chain_test.go b/testing/chain_test.go index 2b77cb75a7d..64ddc6c751e 100644 --- a/testing/chain_test.go +++ b/testing/chain_test.go @@ -4,44 +4,35 @@ import ( "testing" "github.com/stretchr/testify/require" - tmtypes "github.com/tendermint/tendermint/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/types" ibctesting "github.com/cosmos/ibc-go/v3/testing" - "github.com/cosmos/ibc-go/v3/testing/mock" ) -func TestCreateSortedSignerArray(t *testing.T) { - privVal1 := mock.NewPV() - pubKey1, err := privVal1.GetPubKey() - require.NoError(t, err) +func TestChangeValSet(t *testing.T) { + coord := ibctesting.NewCoordinator(t, 2) + chainA := coord.GetChain(ibctesting.GetChainID(1)) + chainB := coord.GetChain(ibctesting.GetChainID(2)) - privVal2 := mock.NewPV() - pubKey2, err := privVal2.GetPubKey() - require.NoError(t, err) + path := ibctesting.NewPath(chainA, chainB) + coord.Setup(path) - validator1 := tmtypes.NewValidator(pubKey1, 1) - validator2 := tmtypes.NewValidator(pubKey2, 2) + amount, ok := sdk.NewIntFromString("10000000000000000000") + require.True(t, ok) + amount2, ok := sdk.NewIntFromString("30000000000000000000") + require.True(t, ok) - expected := []tmtypes.PrivValidator{privVal2, privVal1} + val := chainA.App.GetStakingKeeper().GetValidators(chainA.GetContext(), 4) - actual := ibctesting.CreateSortedSignerArray(privVal1, privVal2, validator1, validator2) - require.Equal(t, expected, actual) + chainA.App.GetStakingKeeper().Delegate(chainA.GetContext(), chainA.SenderAccounts[1].SenderAccount.GetAddress(), + amount, types.Unbonded, val[1], true) + chainA.App.GetStakingKeeper().Delegate(chainA.GetContext(), chainA.SenderAccounts[3].SenderAccount.GetAddress(), + amount2, types.Unbonded, val[3], true) - // swap order - actual = ibctesting.CreateSortedSignerArray(privVal2, privVal1, validator2, validator1) - require.Equal(t, expected, actual) + coord.CommitBlock(chainA) - // smaller address - validator1.Address = []byte{1} - validator2.Address = []byte{2} - validator2.VotingPower = 1 - - expected = []tmtypes.PrivValidator{privVal1, privVal2} - - actual = ibctesting.CreateSortedSignerArray(privVal1, privVal2, validator1, validator2) - require.Equal(t, expected, actual) - - // swap order - actual = ibctesting.CreateSortedSignerArray(privVal2, privVal1, validator2, validator1) - require.Equal(t, expected, actual) + // verify that update clients works even after validator update goes into effect + path.EndpointB.UpdateClient() + path.EndpointB.UpdateClient() } diff --git a/testing/coordinator.go b/testing/coordinator.go index 217c257473a..4c0ebc78463 100644 --- a/testing/coordinator.go +++ b/testing/coordinator.go @@ -181,7 +181,7 @@ func GetChainID(index int) string { // CONTRACT: the passed in list of indexes must not contain duplicates func (coord *Coordinator) CommitBlock(chains ...*TestChain) { for _, chain := range chains { - chain.App.Commit() + chain.NextBlock() } coord.IncrementTime() @@ -191,7 +191,6 @@ func (coord *Coordinator) CommitBlock(chains ...*TestChain) { func (coord *Coordinator) CommitNBlocks(chain *TestChain, n uint64) { for i := uint64(0); i < n; i++ { chain.App.BeginBlock(abci.RequestBeginBlock{Header: chain.CurrentHeader}) - chain.App.Commit() chain.NextBlock() coord.IncrementTime() } diff --git a/testing/simapp/test_helpers.go b/testing/simapp/test_helpers.go index 11dcd85b515..1df1714d8a7 100644 --- a/testing/simapp/test_helpers.go +++ b/testing/simapp/test_helpers.go @@ -234,6 +234,8 @@ func CheckBalance(t *testing.T, app *SimApp, addr sdk.AccAddress, balances sdk.C // SignAndDeliver signs and delivers a transaction. No simulation occurs as the // ibc testing package causes checkState and deliverState to diverge in block time. +// +// CONTRACT: BeginBlock must be called before this function. func SignAndDeliver( t *testing.T, txCfg client.TxConfig, app *bam.BaseApp, header tmproto.Header, msgs []sdk.Msg, chainID string, accNums, accSeqs []uint64, expSimPass, expPass bool, priv ...cryptotypes.PrivKey, @@ -251,8 +253,7 @@ func SignAndDeliver( ) require.NoError(t, err) - // Simulate a sending a transaction and committing a block - app.BeginBlock(abci.RequestBeginBlock{Header: header}) + // Simulate a sending a transaction gInfo, res, err := app.Deliver(txCfg.TxEncoder(), tx) if expPass { @@ -263,9 +264,6 @@ func SignAndDeliver( require.Nil(t, res) } - app.EndBlock(abci.RequestEndBlock{}) - app.Commit() - return gInfo, res, err } diff --git a/testing/utils.go b/testing/utils.go new file mode 100644 index 00000000000..f9f64bf72bd --- /dev/null +++ b/testing/utils.go @@ -0,0 +1,23 @@ +package ibctesting + +import ( + "testing" + + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + tmtypes "github.com/tendermint/tendermint/types" +) + +// ApplyValSetChanges takes in tmtypes.ValidatorSet and []abci.ValidatorUpdate and will return a new tmtypes.ValidatorSet which has the +// provided validator updates applied to the provided validator set. +func ApplyValSetChanges(t *testing.T, valSet *tmtypes.ValidatorSet, valUpdates []abci.ValidatorUpdate) *tmtypes.ValidatorSet { + updates, err := tmtypes.PB2TM.ValidatorUpdates(valUpdates) + require.NoError(t, err) + + // must copy since validator set will mutate with UpdateWithChangeSet + newVals := valSet.Copy() + err = newVals.UpdateWithChangeSet(updates) + require.NoError(t, err) + + return newVals +} From 5e3bac03b109e030ae80cb39c26db609581f772d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?colin=20axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Thu, 24 Mar 2022 15:29:29 +0100 Subject: [PATCH 14/71] 02-client-refactor: rename update to UpdateState for 07-tendermint (#1117) * rename update to UpdateState rename 07-tendermint update function to UpdateState add pruneOldestConsensusState function add check for duplicate update * fix: duplicate update check was performing incorrect logic * update godoc * add UpdateState tests * update godoc * chore: fix code spacing Co-authored-by: Sean King --- .../07-tendermint/types/update.go | 112 ++++++++------ .../07-tendermint/types/update_test.go | 143 ++++++++++++++++++ 2 files changed, 210 insertions(+), 45 deletions(-) diff --git a/modules/light-clients/07-tendermint/types/update.go b/modules/light-clients/07-tendermint/types/update.go index 2699ebc2cc9..7497307ae84 100644 --- a/modules/light-clients/07-tendermint/types/update.go +++ b/modules/light-clients/07-tendermint/types/update.go @@ -28,14 +28,6 @@ import ( // - header timestamp is past the trusting period in relation to the consensus state // - header timestamp is less than or equal to the consensus state timestamp // -// UpdateClient may be used to either create a consensus state for: -// - a future height greater than the latest client state height -// - a past height that was skipped during bisection -// If we are updating to a past height, a consensus state is created for that height to be persisted in client store -// If we are updating to a future height, the consensus state is created and the client state is updated to reflect -// the new latest height -// UpdateClient must only be used to update within a single revision, thus header revision number and trusted height's revision -// number must be the same. To update to a new revision, use a separate upgrade path // Tendermint client validity checking uses the bisection algorithm described // in the [Tendermint spec](https://github.com/tendermint/spec/blob/master/spec/consensus/light-client.md). // @@ -45,10 +37,6 @@ import ( // 2. Any valid update that breaks time monotonicity with respect to its neighboring consensus states is evidence of misbehaviour and will freeze client. // Misbehaviour sets frozen height to {0, 1} since it is only used as a boolean value (zero or non-zero). // -// Pruning: -// UpdateClient will additionally retrieve the earliest consensus state for this clientID and check if it is expired. If it is, -// that consensus state will be pruned from store along with all associated metadata. This will prevent the client store from -// becoming bloated with expired consensus states that can no longer be used for updates and packet verification. func (cs ClientState) CheckHeaderAndUpdateState( ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, header exported.ClientMessage, @@ -110,35 +98,10 @@ func (cs ClientState) CheckHeaderAndUpdateState( return &cs, consState, nil } - // Check the earliest consensus state to see if it is expired, if so then set the prune height - // so that we can delete consensus state and all associated metadata. - var ( - pruneHeight exported.Height - pruneError error - ) - pruneCb := func(height exported.Height) bool { - consState, err := GetConsensusState(clientStore, cdc, height) - // this error should never occur - if err != nil { - pruneError = err - return true - } - if cs.IsExpired(consState.Timestamp, ctx.BlockTime()) { - pruneHeight = height - } - return true - } - IterateConsensusStateAscending(clientStore, pruneCb) - if pruneError != nil { - return nil, nil, pruneError - } - // if pruneHeight is set, delete consensus state and metadata - if pruneHeight != nil { - deleteConsensusState(clientStore, pruneHeight) - deleteConsensusMetadata(clientStore, pruneHeight) + newClientState, consensusState, err := cs.UpdateState(ctx, cdc, clientStore, tmHeader) + if err != nil { + return nil, nil, err } - - newClientState, consensusState := update(ctx, clientStore, &cs, tmHeader) return newClientState, consensusState, nil } @@ -244,11 +207,32 @@ func checkValidity( return nil } -// update the consensus state from a new header and set processed time metadata -func update(ctx sdk.Context, clientStore sdk.KVStore, clientState *ClientState, header *Header) (*ClientState, *ConsensusState) { +// UpdateState may be used to either create a consensus state for: +// - a future height greater than the latest client state height +// - a past height that was skipped during bisection +// If we are updating to a past height, a consensus state is created for that height to be persisted in client store +// If we are updating to a future height, the consensus state is created and the client state is updated to reflect +// the new latest height +// UpdateState must only be used to update within a single revision, thus header revision number and trusted height's revision +// number must be the same. To update to a new revision, use a separate upgrade path +// UpdateState will prune the oldest consensus state if it is expired. +func (cs ClientState) UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg exported.ClientMessage) (*ClientState, *ConsensusState, error) { + header, ok := clientMsg.(*Header) + if !ok { + return nil, nil, sdkerrors.Wrapf(clienttypes.ErrInvalidClientType, "expected type %T, got %T", &Header{}, header) + } + + // check for duplicate update + if consensusState, _ := GetConsensusState(clientStore, cdc, header.GetHeight()); consensusState != nil { + // perform no-op + return &cs, consensusState, nil + } + + cs.pruneOldestConsensusState(ctx, cdc, clientStore) + height := header.GetHeight().(clienttypes.Height) - if height.GT(clientState.LatestHeight) { - clientState.LatestHeight = height + if height.GT(cs.LatestHeight) { + cs.LatestHeight = height } consensusState := &ConsensusState{ Timestamp: header.GetTime(), @@ -259,5 +243,43 @@ func update(ctx sdk.Context, clientStore sdk.KVStore, clientState *ClientState, // set metadata for this consensus state setConsensusMetadata(ctx, clientStore, header.GetHeight()) - return clientState, consensusState + return &cs, consensusState, nil +} + +// pruneOldestConsensusState will retrieve the earliest consensus state for this clientID and check if it is expired. If it is, +// that consensus state will be pruned from store along with all associated metadata. This will prevent the client store from +// becoming bloated with expired consensus states that can no longer be used for updates and packet verification. +func (cs ClientState) pruneOldestConsensusState(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore) { + // Check the earliest consensus state to see if it is expired, if so then set the prune height + // so that we can delete consensus state and all associated metadata. + var ( + pruneHeight exported.Height + pruneError error + ) + + pruneCb := func(height exported.Height) bool { + consState, err := GetConsensusState(clientStore, cdc, height) + // this error should never occur + if err != nil { + pruneError = err + return true + } + + if cs.IsExpired(consState.Timestamp, ctx.BlockTime()) { + pruneHeight = height + } + + return true + } + + IterateConsensusStateAscending(clientStore, pruneCb) + if pruneError != nil { + panic(pruneError) + } + + // if pruneHeight is set, delete consensus state and metadata + if pruneHeight != nil { + deleteConsensusState(clientStore, pruneHeight) + deleteConsensusMetadata(clientStore, pruneHeight) + } } diff --git a/modules/light-clients/07-tendermint/types/update_test.go b/modules/light-clients/07-tendermint/types/update_test.go index 379ccba5e00..1fd5a5253d1 100644 --- a/modules/light-clients/07-tendermint/types/update_test.go +++ b/modules/light-clients/07-tendermint/types/update_test.go @@ -367,6 +367,149 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { } } +func (suite *TendermintTestSuite) TestUpdateState() { + var ( + path *ibctesting.Path + clientMessage exported.ClientMessage + pruneHeight clienttypes.Height + updatedClientState *types.ClientState // TODO: retrieve from state after 'UpdateState' call + updatedConsensusState *types.ConsensusState // TODO: retrieve from state after 'UpdateState' call + ) + + testCases := []struct { + name string + malleate func() + expResult func() + expPass bool + }{ + { + "success with height later than latest height", func() { + suite.Require().True(path.EndpointA.GetClientState().GetLatestHeight().LT(clientMessage.GetHeight())) + }, + func() { + suite.Require().True(path.EndpointA.GetClientState().GetLatestHeight().LT(updatedClientState.GetLatestHeight())) // new update, updated client state should have changed + }, true, + }, + { + "success with height earlier than latest height", func() { + // commit a block so the pre-created ClientMessage + // isn't used to update the client to a newer height + suite.coordinator.CommitBlock(suite.chainB) + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + suite.Require().True(path.EndpointA.GetClientState().GetLatestHeight().GT(clientMessage.GetHeight())) + }, + func() { + suite.Require().Equal(path.EndpointA.GetClientState(), updatedClientState) // fill in height, no change to client state + }, true, + }, + { + "success with duplicate header", func() { + // update client in advance + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + // use the same header which just updated the client + clientMessage, err = path.EndpointA.Chain.ConstructUpdateTMClientHeader(path.EndpointA.Counterparty.Chain, path.EndpointA.ClientID) + suite.Require().NoError(err) + suite.Require().Equal(path.EndpointA.GetClientState().GetLatestHeight(), clientMessage.GetHeight()) + }, + func() { + suite.Require().Equal(path.EndpointA.GetClientState(), updatedClientState) + suite.Require().Equal(path.EndpointA.GetConsensusState(clientMessage.GetHeight()), updatedConsensusState) + }, true, + }, + { + "success with pruned consensus state", func() { + // this height will be expired and pruned + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + pruneHeight = path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + // Increment the time by a week + suite.coordinator.IncrementTimeBy(7 * 24 * time.Hour) + + // create the consensus state that can be used as trusted height for next update + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + // Increment the time by another week, then update the client. + // This will cause the first two consensus states to become expired. + suite.coordinator.IncrementTimeBy(7 * 24 * time.Hour) + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + // ensure counterparty state is committed + suite.coordinator.CommitBlock(suite.chainB) + clientMessage, err = path.EndpointA.Chain.ConstructUpdateTMClientHeader(path.EndpointA.Counterparty.Chain, path.EndpointA.ClientID) + suite.Require().NoError(err) + }, + func() { + suite.Require().True(path.EndpointA.GetClientState().GetLatestHeight().LT(updatedClientState.GetLatestHeight())) // new update, updated client state should have changed + + // ensure consensus state was pruned + _, found := path.EndpointA.Chain.GetConsensusState(path.EndpointA.ClientID, pruneHeight) + suite.Require().False(found) + }, true, + }, + { + "invalid ClientMessage type", func() { + clientMessage = &types.Misbehaviour{} + }, + func() {}, false, + }, + } + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + suite.SetupTest() // reset + pruneHeight = clienttypes.ZeroHeight() + path = ibctesting.NewPath(suite.chainA, suite.chainB) + + err := path.EndpointA.CreateClient() + suite.Require().NoError(err) + + // ensure counterparty state is committed + suite.coordinator.CommitBlock(suite.chainB) + clientMessage, err = path.EndpointA.Chain.ConstructUpdateTMClientHeader(path.EndpointA.Counterparty.Chain, path.EndpointA.ClientID) + suite.Require().NoError(err) + + tc.malleate() + + clientState := path.EndpointA.GetClientState() + + // TODO: remove casting when 'UpdateState' is an interface function. + tmClientState, ok := clientState.(*types.ClientState) + suite.Require().True(ok) + + clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + updatedClientState, updatedConsensusState, err = tmClientState.UpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, clientMessage) + + if tc.expPass { + suite.Require().NoError(err) + + header := clientMessage.(*types.Header) + expConsensusState := &types.ConsensusState{ + Timestamp: header.GetTime(), + Root: commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), + NextValidatorsHash: header.Header.NextValidatorsHash, + } + suite.Require().Equal(expConsensusState, updatedConsensusState) + + } else { + suite.Require().Error(err) + suite.Require().Nil(updatedClientState) + suite.Require().Nil(updatedConsensusState) + + } + + // perform custom checks + tc.expResult() + }) + } +} + func (suite *TendermintTestSuite) TestPruneConsensusState() { // create path and setup clients path := ibctesting.NewPath(suite.chainA, suite.chainB) From efbc5a1d9cb17b90dcce4748b223e046453f9ace Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Thu, 24 Mar 2022 16:07:50 +0100 Subject: [PATCH 15/71] chore: adding UpdateStateOnMisbehaviour to 07-tendermint (#1168) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * adding UpdateStateOnMisbehaviour with basic test * removing redundant return values * removing unnecessary CommitBlock in test func * Update modules/light-clients/07-tendermint/types/update.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> --- .../07-tendermint/types/update.go | 9 ++++ .../07-tendermint/types/upgrade_test.go | 51 +++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/modules/light-clients/07-tendermint/types/update.go b/modules/light-clients/07-tendermint/types/update.go index 7497307ae84..69e6936ac4f 100644 --- a/modules/light-clients/07-tendermint/types/update.go +++ b/modules/light-clients/07-tendermint/types/update.go @@ -13,6 +13,7 @@ import ( clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/exported" ) @@ -283,3 +284,11 @@ func (cs ClientState) pruneOldestConsensusState(ctx sdk.Context, cdc codec.Binar deleteConsensusMetadata(clientStore, pruneHeight) } } + +// UpdateStateOnMisbehaviour updates state upon misbehaviour, freezing the ClientState. This method should only be called when misbehaviour is detected +// as it does not perform any misbehaviour checks. +func (cs ClientState) UpdateStateOnMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore) { + cs.FrozenHeight = FrozenHeight + + clientStore.Set(host.ClientStateKey(), clienttypes.MustMarshalClientState(cdc, &cs)) +} diff --git a/modules/light-clients/07-tendermint/types/upgrade_test.go b/modules/light-clients/07-tendermint/types/upgrade_test.go index 112d3366cda..aaa8289f864 100644 --- a/modules/light-clients/07-tendermint/types/upgrade_test.go +++ b/modules/light-clients/07-tendermint/types/upgrade_test.go @@ -5,6 +5,7 @@ import ( clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/exported" "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" ibctesting "github.com/cosmos/ibc-go/v3/testing" @@ -481,3 +482,53 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { } } } + +func (suite *TendermintTestSuite) TestUpdateStateOnMisbehaviour() { + var ( + path *ibctesting.Path + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() {}, + true, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + // reset suite to create fresh application state + suite.SetupTest() + path = ibctesting.NewPath(suite.chainA, suite.chainB) + + err := path.EndpointA.CreateClient() + suite.Require().NoError(err) + + tc.malleate() + + clientState := path.EndpointA.GetClientState() + clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + + // TODO: remove casting when 'UpdateState' is an interface function. + tmClientState, ok := clientState.(*types.ClientState) + suite.Require().True(ok) + + tmClientState.UpdateStateOnMisbehaviour(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore) + + if tc.expPass { + clientStateBz := clientStore.Get(host.ClientStateKey()) + suite.Require().NotEmpty(clientStateBz) + + newClientState := clienttypes.MustUnmarshalClientState(suite.chainA.Codec, clientStateBz) + suite.Require().Equal(frozenHeight, newClientState.(*types.ClientState).FrozenHeight) + } + }) + } +} From b0dd49d92b98d7f1a082f279ce6c7a0cf71c37b5 Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Mon, 28 Mar 2022 16:36:07 +0200 Subject: [PATCH 16/71] chore: add solomachine client storage (#1144) * WIP: solomachine UpdateState now handles storage of ClientState and ConsensusState * applying suggestions for review * removing unnecessary consensus state storage * updating tests to use store in favour of return values --- .../types/misbehaviour_handle.go | 10 ++-- .../06-solomachine/types/update.go | 22 ++++++-- .../06-solomachine/types/update_test.go | 56 +++++++++++++------ 3 files changed, 59 insertions(+), 29 deletions(-) diff --git a/modules/light-clients/06-solomachine/types/misbehaviour_handle.go b/modules/light-clients/06-solomachine/types/misbehaviour_handle.go index 171ad08e5f0..eb97bc252fa 100644 --- a/modules/light-clients/06-solomachine/types/misbehaviour_handle.go +++ b/modules/light-clients/06-solomachine/types/misbehaviour_handle.go @@ -34,12 +34,12 @@ func (cs ClientState) CheckMisbehaviourAndUpdateState( // misbehaviour.ValidateBasic which is called by the 02-client keeper. // verify first signature - if err := verifySignatureAndData(cdc, cs, soloMisbehaviour, soloMisbehaviour.SignatureOne); err != nil { + if err := cs.verifySignatureAndData(cdc, soloMisbehaviour, soloMisbehaviour.SignatureOne); err != nil { return nil, sdkerrors.Wrap(err, "failed to verify signature one") } // verify second signature - if err := verifySignatureAndData(cdc, cs, soloMisbehaviour, soloMisbehaviour.SignatureTwo); err != nil { + if err := cs.verifySignatureAndData(cdc, soloMisbehaviour, soloMisbehaviour.SignatureTwo); err != nil { return nil, sdkerrors.Wrap(err, "failed to verify signature two") } @@ -50,7 +50,7 @@ func (cs ClientState) CheckMisbehaviourAndUpdateState( // verifySignatureAndData verifies that the currently registered public key has signed // over the provided data and that the data is valid. The data is valid if it can be // unmarshaled into the specified data type. -func verifySignatureAndData(cdc codec.BinaryCodec, clientState ClientState, misbehaviour *Misbehaviour, sigAndData *SignatureAndData) error { +func (cs ClientState) verifySignatureAndData(cdc codec.BinaryCodec, misbehaviour *Misbehaviour, sigAndData *SignatureAndData) error { // do not check misbehaviour timestamp since we want to allow processing of past misbehaviour @@ -62,7 +62,7 @@ func verifySignatureAndData(cdc codec.BinaryCodec, clientState ClientState, misb data, err := MisbehaviourSignBytes( cdc, misbehaviour.Sequence, sigAndData.Timestamp, - clientState.ConsensusState.Diversifier, + cs.ConsensusState.Diversifier, sigAndData.DataType, sigAndData.Data, ) @@ -75,7 +75,7 @@ func verifySignatureAndData(cdc codec.BinaryCodec, clientState ClientState, misb return err } - publicKey, err := clientState.ConsensusState.GetPubKey() + publicKey, err := cs.ConsensusState.GetPubKey() if err != nil { return err } diff --git a/modules/light-clients/06-solomachine/types/update.go b/modules/light-clients/06-solomachine/types/update.go index d04d93c36af..b6dfc44eac1 100644 --- a/modules/light-clients/06-solomachine/types/update.go +++ b/modules/light-clients/06-solomachine/types/update.go @@ -6,6 +6,7 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/exported" ) @@ -89,12 +90,12 @@ func (cs ClientState) verifyMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, // NOTE: a check that the misbehaviour message data are not equal is done by // misbehaviour.ValidateBasic which is called by the 02-client keeper. // verify first signature - if err := verifySignatureAndData(cdc, cs, misbehaviour, misbehaviour.SignatureOne); err != nil { + if err := cs.verifySignatureAndData(cdc, misbehaviour, misbehaviour.SignatureOne); err != nil { return sdkerrors.Wrap(err, "failed to verify signature one") } // verify second signature - if err := verifySignatureAndData(cdc, cs, misbehaviour, misbehaviour.SignatureTwo); err != nil { + if err := cs.verifySignatureAndData(cdc, misbehaviour, misbehaviour.SignatureTwo); err != nil { return sdkerrors.Wrap(err, "failed to verify signature two") } @@ -103,7 +104,12 @@ func (cs ClientState) verifyMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, // UpdateState updates the consensus state to the new public key and an incremented sequence. func (cs ClientState) UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg exported.ClientMessage) (exported.ClientState, exported.ConsensusState, error) { - smHeader := clientMsg.(*Header) + smHeader, ok := clientMsg.(*Header) + if !ok { + return nil, nil, sdkerrors.Wrapf(clienttypes.ErrInvalidClientType, "expected %T got %T", Header{}, clientMsg) + } + + // create new solomachine ConsensusState consensusState := &ConsensusState{ PublicKey: smHeader.NewPublicKey, Diversifier: smHeader.NewDiversifier, @@ -112,6 +118,9 @@ func (cs ClientState) UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, client cs.Sequence++ cs.ConsensusState = consensusState + + clientStore.Set(host.ClientStateKey(), clienttypes.MustMarshalClientState(cdc, &cs)) + return &cs, consensusState, nil } @@ -126,9 +135,10 @@ func (cs ClientState) CheckForMisbehaviour(_ sdk.Context, _ codec.BinaryCodec, _ // UpdateStateOnMisbehaviour updates state upon misbehaviour. This method should only be called on misbehaviour // as it does not perform any misbehaviour checks. -func (cs ClientState) UpdateStateOnMisbehaviour( - _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, // prematurely include args for self storage of consensus state -) (*ClientState, exported.ConsensusState, error) { +func (cs ClientState) UpdateStateOnMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore) (*ClientState, exported.ConsensusState, error) { cs.IsFrozen = true + + clientStore.Set(host.ClientStateKey(), clienttypes.MustMarshalClientState(cdc, &cs)) + return &cs, cs.ConsensusState, nil } diff --git a/modules/light-clients/06-solomachine/types/update_test.go b/modules/light-clients/06-solomachine/types/update_test.go index 56f620b15aa..53b6f014890 100644 --- a/modules/light-clients/06-solomachine/types/update_test.go +++ b/modules/light-clients/06-solomachine/types/update_test.go @@ -4,6 +4,8 @@ import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" + clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/exported" "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types" ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" @@ -594,30 +596,43 @@ func (suite *SoloMachineTestSuite) TestUpdateState() { }, true, }, + { + "invalid type misbehaviour", + func() { + clientState = solomachine.ClientState() + clientMsg = solomachine.CreateMisbehaviour() + }, + false, + }, } for _, tc := range testCases { tc := tc suite.Run(tc.name, func() { - // setup test - tc.setup() + tc.setup() // setup test + // TODO: remove casting when 'UpdateState' is an interface function. clientState, ok := clientState.(*types.ClientState) - if ok { - cs, consensusState, err := clientState.UpdateState(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, clientMsg) - - if tc.expPass { - suite.Require().NoError(err) - suite.Require().Equal(clientMsg.(*types.Header).NewPublicKey, cs.(*types.ClientState).ConsensusState.PublicKey) - suite.Require().Equal(false, cs.(*types.ClientState).IsFrozen) - suite.Require().Equal(clientMsg.(*types.Header).Sequence+1, cs.(*types.ClientState).Sequence) - suite.Require().Equal(consensusState, cs.(*types.ClientState).ConsensusState) - } else { - suite.Require().Error(err) - suite.Require().Nil(clientState) - suite.Require().Nil(consensusState) - } + suite.Require().True(ok) + + _, _, err := clientState.UpdateState(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, clientMsg) + + if tc.expPass { + suite.Require().NoError(err) + + clientStateBz := suite.store.Get(host.ClientStateKey()) + suite.Require().NotEmpty(clientStateBz) + + newClientState := clienttypes.MustUnmarshalClientState(suite.chainA.Codec, clientStateBz) + + suite.Require().False(newClientState.(*types.ClientState).IsFrozen) + suite.Require().Equal(clientMsg.(*types.Header).Sequence+1, newClientState.(*types.ClientState).Sequence) + suite.Require().Equal(clientMsg.(*types.Header).NewPublicKey, newClientState.(*types.ClientState).ConsensusState.PublicKey) + suite.Require().Equal(clientMsg.(*types.Header).NewDiversifier, newClientState.(*types.ClientState).ConsensusState.Diversifier) + suite.Require().Equal(clientMsg.(*types.Header).Timestamp, newClientState.(*types.ClientState).ConsensusState.Timestamp) + } else { + suite.Require().Error(err) } }) @@ -697,10 +712,15 @@ func (suite *SoloMachineTestSuite) TestUpdateStateOnMisbehaviour() { tc.malleate() - cs, _, _ := clientState.UpdateStateOnMisbehaviour(suite.chainA.GetContext(), suite.chainA.Codec, suite.store) + clientState.UpdateStateOnMisbehaviour(suite.chainA.GetContext(), suite.chainA.Codec, suite.store) if tc.expPass { - suite.Require().True(cs.IsFrozen) + clientStateBz := suite.store.Get(host.ClientStateKey()) + suite.Require().NotEmpty(clientStateBz) + + newClientState := clienttypes.MustUnmarshalClientState(suite.chainA.Codec, clientStateBz) + + suite.Require().True(newClientState.(*types.ClientState).IsFrozen) } }) } From 17209f76b2217425216f5b521f3a4b037f65681f Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Mon, 28 Mar 2022 17:40:11 +0200 Subject: [PATCH 17/71] refactor: adding CheckForMisbehaviour to 07-tendermint client (#1163) * adding CheckForMisbehaviour to tendermint ClientState * adding initial testcases for CheckForMisbehaviour * updating tests * updating tests * cleaning up code comments * updating godocs * fixing logic and updating tests * removing Misbehaviour verification and tests * fixing code structure after merge conflict --- .../types/misbehaviour_handle_test.go | 2 +- .../07-tendermint/types/update.go | 45 ++++ .../07-tendermint/types/update_test.go | 194 ++++++++++++++++++ .../07-tendermint/types/upgrade_test.go | 51 ----- 4 files changed, 240 insertions(+), 52 deletions(-) diff --git a/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go b/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go index 93178f57816..ef92046b9a8 100644 --- a/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go +++ b/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go @@ -40,7 +40,7 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { height1 clienttypes.Height consensusState2 exported.ConsensusState height2 clienttypes.Height - misbehaviour exported.ClientMessage + misbehaviour exported.ClientMessage timestamp time.Time expPass bool }{ diff --git a/modules/light-clients/07-tendermint/types/update.go b/modules/light-clients/07-tendermint/types/update.go index 69e6936ac4f..18b4a2daa50 100644 --- a/modules/light-clients/07-tendermint/types/update.go +++ b/modules/light-clients/07-tendermint/types/update.go @@ -285,6 +285,51 @@ func (cs ClientState) pruneOldestConsensusState(ctx sdk.Context, cdc codec.Binar } } +// CheckForMisbehaviour detects duplicate height misbehaviour and BFT time violation misbehaviour +func (cs ClientState) CheckForMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, msg exported.ClientMessage) bool { + switch msg := msg.(type) { + case *Header: + tmHeader := msg + consState := tmHeader.ConsensusState() + + // Check if the Client store already has a consensus state for the header's height + // If the consensus state exists, and it matches the header then we return early + // since header has already been submitted in a previous UpdateClient. + prevConsState, _ := GetConsensusState(clientStore, cdc, tmHeader.GetHeight()) + if prevConsState != nil { + // This header has already been submitted and the necessary state is already stored + // in client store, thus we can return early without further validation. + if reflect.DeepEqual(prevConsState, tmHeader.ConsensusState()) { + return false + } + + // A consensus state already exists for this height, but it does not match the provided header. + // The assumption is that Header has already been validated. Thus we can return true as misbehaviour is present + return true + } + + // Check that consensus state timestamps are monotonic + prevCons, prevOk := GetPreviousConsensusState(clientStore, cdc, tmHeader.GetHeight()) + nextCons, nextOk := GetNextConsensusState(clientStore, cdc, tmHeader.GetHeight()) + // if previous consensus state exists, check consensus state time is greater than previous consensus state time + // if previous consensus state is not before current consensus state return true + if prevOk && !prevCons.Timestamp.Before(consState.Timestamp) { + return true + } + // if next consensus state exists, check consensus state time is less than next consensus state time + // if next consensus state is not after current consensus state return true + if nextOk && !nextCons.Timestamp.After(consState.Timestamp) { + return true + } + case *Misbehaviour: + // The correctness of Misbehaviour ClientMessage types is ensured by calling VerifyClientMessage prior to this function + // Thus, here we can return true, as ClientMessage is of type Misbehaviour + return true + } + + return false +} + // UpdateStateOnMisbehaviour updates state upon misbehaviour, freezing the ClientState. This method should only be called when misbehaviour is detected // as it does not perform any misbehaviour checks. func (cs ClientState) UpdateStateOnMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore) { diff --git a/modules/light-clients/07-tendermint/types/update_test.go b/modules/light-clients/07-tendermint/types/update_test.go index 1fd5a5253d1..7216f48d33f 100644 --- a/modules/light-clients/07-tendermint/types/update_test.go +++ b/modules/light-clients/07-tendermint/types/update_test.go @@ -8,6 +8,7 @@ import ( clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/exported" types "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" ibctesting "github.com/cosmos/ibc-go/v3/testing" @@ -591,3 +592,196 @@ func (suite *TendermintTestSuite) TestPruneConsensusState() { consKey = types.GetIterationKey(clientStore, expiredHeight) suite.Require().Equal(expectedConsKey, consKey, "iteration key incorrectly pruned") } + +func (suite *TendermintTestSuite) TestCheckForMisbehaviour() { + var ( + path *ibctesting.Path + clientMessage exported.ClientMessage + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "valid update no misbehaviour", + func() {}, + false, + }, + { + "consensus state already exists, already updated", + func() { + header, ok := clientMessage.(*types.Header) + suite.Require().True(ok) + + consensusState := &types.ConsensusState{ + Timestamp: header.GetTime(), + Root: commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), + NextValidatorsHash: header.Header.NextValidatorsHash, + } + + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), path.EndpointA.ClientID, clientMessage.GetHeight(), consensusState) + }, + false, + }, + { + "consensus state already exists, app hash mismatch", + func() { + header, ok := clientMessage.(*types.Header) + suite.Require().True(ok) + + consensusState := &types.ConsensusState{ + Timestamp: header.GetTime(), + Root: commitmenttypes.NewMerkleRoot([]byte{}), // empty bytes + NextValidatorsHash: header.Header.NextValidatorsHash, + } + + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), path.EndpointA.ClientID, clientMessage.GetHeight(), consensusState) + }, + true, + }, + { + "previous consensus state exists and header time is before previous consensus state time", + func() { + header, ok := clientMessage.(*types.Header) + suite.Require().True(ok) + + // offset header timestamp before previous consensus state timestamp + header.Header.Time = header.GetTime().Add(-time.Hour) + }, + true, + }, + { + "next consensus state exists and header time is after next consensus state time", + func() { + header, ok := clientMessage.(*types.Header) + suite.Require().True(ok) + + // commit block and update client, adding a new consensus state + suite.coordinator.CommitBlock(suite.chainB) + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + // increase timestamp of current header + header.Header.Time = header.Header.Time.Add(time.Hour) + }, + true, + }, + { + "valid fork misbehaviour returns true", + func() { + header1, err := path.EndpointA.Chain.ConstructUpdateTMClientHeader(path.EndpointA.Counterparty.Chain, path.EndpointA.ClientID) + suite.Require().NoError(err) + + // commit block and update client + suite.coordinator.CommitBlock(suite.chainB) + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + header2, err := path.EndpointA.Chain.ConstructUpdateTMClientHeader(path.EndpointA.Counterparty.Chain, path.EndpointA.ClientID) + suite.Require().NoError(err) + + // assign the same height, each header will have a different commit hash + header1.Header.Height = header2.Header.Height + + clientMessage = &types.Misbehaviour{ + Header1: header1, + Header2: header2, + ClientId: path.EndpointA.ClientID, + } + }, + true, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + // reset suite to create fresh application state + suite.SetupTest() + path = ibctesting.NewPath(suite.chainA, suite.chainB) + + err := path.EndpointA.CreateClient() + suite.Require().NoError(err) + + // ensure counterparty state is committed + suite.coordinator.CommitBlock(suite.chainB) + clientMessage, err = path.EndpointA.Chain.ConstructUpdateTMClientHeader(path.EndpointA.Counterparty.Chain, path.EndpointA.ClientID) + suite.Require().NoError(err) + + tc.malleate() + + clientState := path.EndpointA.GetClientState() + + // TODO: remove casting when 'UpdateState' is an interface function. + tmClientState, ok := clientState.(*types.ClientState) + suite.Require().True(ok) + + clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + + foundMisbehaviour := tmClientState.CheckForMisbehaviour( + suite.chainA.GetContext(), + suite.chainA.App.AppCodec(), + clientStore, // pass in clientID prefixed clientStore + clientMessage, + ) + + if tc.expPass { + suite.Require().True(foundMisbehaviour) + } else { + suite.Require().False(foundMisbehaviour) + } + }) + } +} + +func (suite *TendermintTestSuite) TestUpdateStateOnMisbehaviour() { + var ( + path *ibctesting.Path + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() {}, + true, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + // reset suite to create fresh application state + suite.SetupTest() + path = ibctesting.NewPath(suite.chainA, suite.chainB) + + err := path.EndpointA.CreateClient() + suite.Require().NoError(err) + + tc.malleate() + + clientState := path.EndpointA.GetClientState() + clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + + // TODO: remove casting when 'UpdateState' is an interface function. + tmClientState, ok := clientState.(*types.ClientState) + suite.Require().True(ok) + + tmClientState.UpdateStateOnMisbehaviour(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore) + + if tc.expPass { + clientStateBz := clientStore.Get(host.ClientStateKey()) + suite.Require().NotEmpty(clientStateBz) + + newClientState := clienttypes.MustUnmarshalClientState(suite.chainA.Codec, clientStateBz) + suite.Require().Equal(frozenHeight, newClientState.(*types.ClientState).FrozenHeight) + } + }) + } +} diff --git a/modules/light-clients/07-tendermint/types/upgrade_test.go b/modules/light-clients/07-tendermint/types/upgrade_test.go index aaa8289f864..112d3366cda 100644 --- a/modules/light-clients/07-tendermint/types/upgrade_test.go +++ b/modules/light-clients/07-tendermint/types/upgrade_test.go @@ -5,7 +5,6 @@ import ( clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/exported" "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" ibctesting "github.com/cosmos/ibc-go/v3/testing" @@ -482,53 +481,3 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { } } } - -func (suite *TendermintTestSuite) TestUpdateStateOnMisbehaviour() { - var ( - path *ibctesting.Path - ) - - testCases := []struct { - name string - malleate func() - expPass bool - }{ - { - "success", - func() {}, - true, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - // reset suite to create fresh application state - suite.SetupTest() - path = ibctesting.NewPath(suite.chainA, suite.chainB) - - err := path.EndpointA.CreateClient() - suite.Require().NoError(err) - - tc.malleate() - - clientState := path.EndpointA.GetClientState() - clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - - // TODO: remove casting when 'UpdateState' is an interface function. - tmClientState, ok := clientState.(*types.ClientState) - suite.Require().True(ok) - - tmClientState.UpdateStateOnMisbehaviour(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore) - - if tc.expPass { - clientStateBz := clientStore.Get(host.ClientStateKey()) - suite.Require().NotEmpty(clientStateBz) - - newClientState := clienttypes.MustUnmarshalClientState(suite.chainA.Codec, clientStateBz) - suite.Require().Equal(frozenHeight, newClientState.(*types.ClientState).FrozenHeight) - } - }) - } -} From fadd9d078042e458babd4c0404f26b8af40a9e49 Mon Sep 17 00:00:00 2001 From: Sean King Date: Mon, 28 Mar 2022 18:24:31 +0200 Subject: [PATCH 18/71] 02-client refactor: Adding VerifyClientMessage helper fn (#1119) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: Adding VerifyClientMessage helper fn to ClientState * refactor: creating verifyHeader priv fn and respective test * refactor: adding initial test cases * refactor: add more test cases * nit: move fns * remove clientState var * refactor: adding different val set test case * refactor: add test case for header with next height and diff validator set * refactor: adding remaining test cases * chore: uncomment previous tests: * fix: chainA -> chainB * chore: comment * refactor: remove consState from api + fix tests * refactor: add verifyHeader to clientState * fix: incorret trusted validators for concensus state test * Update modules/light-clients/07-tendermint/types/update_test.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * chore: add comment * fix: params * refactor: remove timestamp from api * refactor: switch and type * fix: remove height+1 * 02-client refactor: add tests for verifyMisbehaviour (#1166) * refactor: move misbehaviour validation into verifyMisbehaviour function * begin writing misbehaviour tests * fix misbehaviour test * continue adding misbehaviour test cases * add more test cases to verifyMisbehaviour test * add changing validator set tests * finish rest of tests except revision height testing * Update modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go Co-authored-by: Damian Nolan * add back misbehaviour type assertion Co-authored-by: Damian Nolan Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> Co-authored-by: Damian Nolan --- .../types/misbehaviour_handle.go | 62 ++- .../types/misbehaviour_handle_test.go | 370 +++++++++++++++ .../07-tendermint/types/update.go | 55 ++- .../07-tendermint/types/update_test.go | 439 ++++++++++-------- 4 files changed, 678 insertions(+), 248 deletions(-) diff --git a/modules/light-clients/07-tendermint/types/misbehaviour_handle.go b/modules/light-clients/07-tendermint/types/misbehaviour_handle.go index 98878fbbbe8..932829aa56b 100644 --- a/modules/light-clients/07-tendermint/types/misbehaviour_handle.go +++ b/modules/light-clients/07-tendermint/types/misbehaviour_handle.go @@ -32,47 +32,62 @@ func (cs ClientState) CheckMisbehaviourAndUpdateState( return nil, sdkerrors.Wrapf(clienttypes.ErrInvalidClientType, "expected type %T, got %T", misbehaviour, &Misbehaviour{}) } - // The status of the client is checked in 02-client + if err := cs.VerifyClientMessage(ctx, clientStore, cdc, tmMisbehaviour); err != nil { + return nil, err + } + + cs.FrozenHeight = FrozenHeight + + return &cs, nil +} + +// verifyMisbehaviour determines whether or not two conflicting +// headers at the same height would have convinced the light client. +// +// NOTE: consensusState1 is the trusted consensus state that corresponds to the TrustedHeight +// of misbehaviour.Header1 +// Similarly, consensusState2 is the trusted consensus state that corresponds +// to misbehaviour.Header2 +// Misbehaviour sets frozen height to {0, 1} since it is only used as a boolean value (zero or non-zero). +func (cs *ClientState) verifyMisbehaviour(ctx sdk.Context, clientStore sdk.KVStore, cdc codec.BinaryCodec, misbehaviour *Misbehaviour) error { // if heights are equal check that this is valid misbehaviour of a fork // otherwise if heights are unequal check that this is valid misbehavior of BFT time violation - if tmMisbehaviour.Header1.GetHeight().EQ(tmMisbehaviour.Header2.GetHeight()) { - blockID1, err := tmtypes.BlockIDFromProto(&tmMisbehaviour.Header1.SignedHeader.Commit.BlockID) + if misbehaviour.Header1.GetHeight().EQ(misbehaviour.Header2.GetHeight()) { + blockID1, err := tmtypes.BlockIDFromProto(&misbehaviour.Header1.SignedHeader.Commit.BlockID) if err != nil { - return nil, sdkerrors.Wrap(err, "invalid block ID from header 1 in misbehaviour") + return sdkerrors.Wrap(err, "invalid block ID from header 1 in misbehaviour") } - blockID2, err := tmtypes.BlockIDFromProto(&tmMisbehaviour.Header2.SignedHeader.Commit.BlockID) + + blockID2, err := tmtypes.BlockIDFromProto(&misbehaviour.Header2.SignedHeader.Commit.BlockID) if err != nil { - return nil, sdkerrors.Wrap(err, "invalid block ID from header 2 in misbehaviour") + return sdkerrors.Wrap(err, "invalid block ID from header 2 in misbehaviour") } // Ensure that Commit Hashes are different if bytes.Equal(blockID1.Hash, blockID2.Hash) { - return nil, sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "headers block hashes are equal") + return sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "headers block hashes are equal") } + } else { // Header1 is at greater height than Header2, therefore Header1 time must be less than or equal to // Header2 time in order to be valid misbehaviour (violation of monotonic time). - if tmMisbehaviour.Header1.SignedHeader.Header.Time.After(tmMisbehaviour.Header2.SignedHeader.Header.Time) { - return nil, sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "headers are not at same height and are monotonically increasing") + if misbehaviour.Header1.SignedHeader.Header.Time.After(misbehaviour.Header2.SignedHeader.Header.Time) { + return sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "headers are not at same height and are monotonically increasing") } } // Regardless of the type of misbehaviour, ensure that both headers are valid and would have been accepted by light-client // Retrieve trusted consensus states for each Header in misbehaviour - // and unmarshal from clientStore - - // Get consensus bytes from clientStore - tmConsensusState1, err := GetConsensusState(clientStore, cdc, tmMisbehaviour.Header1.TrustedHeight) + tmConsensusState1, err := GetConsensusState(clientStore, cdc, misbehaviour.Header1.TrustedHeight) if err != nil { - return nil, sdkerrors.Wrapf(err, "could not get trusted consensus state from clientStore for Header1 at TrustedHeight: %s", tmMisbehaviour.Header1) + return sdkerrors.Wrapf(err, "could not get trusted consensus state from clientStore for Header1 at TrustedHeight: %s", misbehaviour.Header1.TrustedHeight) } - // Get consensus bytes from clientStore - tmConsensusState2, err := GetConsensusState(clientStore, cdc, tmMisbehaviour.Header2.TrustedHeight) + tmConsensusState2, err := GetConsensusState(clientStore, cdc, misbehaviour.Header2.TrustedHeight) if err != nil { - return nil, sdkerrors.Wrapf(err, "could not get trusted consensus state from clientStore for Header2 at TrustedHeight: %s", tmMisbehaviour.Header2) + return sdkerrors.Wrapf(err, "could not get trusted consensus state from clientStore for Header2 at TrustedHeight: %s", misbehaviour.Header2.TrustedHeight) } // Check the validity of the two conflicting headers against their respective @@ -81,19 +96,17 @@ func (cs ClientState) CheckMisbehaviourAndUpdateState( // misbehaviour.ValidateBasic by the client keeper and msg.ValidateBasic // by the base application. if err := checkMisbehaviourHeader( - &cs, tmConsensusState1, tmMisbehaviour.Header1, ctx.BlockTime(), + cs, tmConsensusState1, misbehaviour.Header1, ctx.BlockTime(), ); err != nil { - return nil, sdkerrors.Wrap(err, "verifying Header1 in Misbehaviour failed") + return sdkerrors.Wrap(err, "verifying Header1 in Misbehaviour failed") } if err := checkMisbehaviourHeader( - &cs, tmConsensusState2, tmMisbehaviour.Header2, ctx.BlockTime(), + cs, tmConsensusState2, misbehaviour.Header2, ctx.BlockTime(), ); err != nil { - return nil, sdkerrors.Wrap(err, "verifying Header2 in Misbehaviour failed") + return sdkerrors.Wrap(err, "verifying Header2 in Misbehaviour failed") } - cs.FrozenHeight = FrozenHeight - - return &cs, nil + return nil } // checkMisbehaviourHeader checks that a Header in Misbehaviour is valid misbehaviour given @@ -101,7 +114,6 @@ func (cs ClientState) CheckMisbehaviourAndUpdateState( func checkMisbehaviourHeader( clientState *ClientState, consState *ConsensusState, header *Header, currentTimestamp time.Time, ) error { - tmTrustedValset, err := tmtypes.ValidatorSetFromProto(header.TrustedValidators) if err != nil { return sdkerrors.Wrap(err, "trusted validator set is not tendermint validator set type") diff --git a/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go b/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go index ef92046b9a8..4657bae4d34 100644 --- a/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go +++ b/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go @@ -10,7 +10,9 @@ import ( clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v3/modules/core/exported" + smtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types" "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + ibctesting "github.com/cosmos/ibc-go/v3/testing" ibctestingmock "github.com/cosmos/ibc-go/v3/testing/mock" ) @@ -423,3 +425,371 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { }) } } + +func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { + // Setup different validators and signers for testing different types of updates + altPrivVal := ibctestingmock.NewPV() + altPubKey, err := altPrivVal.GetPubKey() + suite.Require().NoError(err) + + // create modified heights to use for test-cases + altVal := tmtypes.NewValidator(altPubKey, 100) + + // Create alternative validator set with only altVal, invalid update (too much change in valSet) + altValSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{altVal}) + altSigners := getAltSigners(altVal, altPrivVal) + + var ( + path *ibctesting.Path + misbehaviour exported.ClientMessage + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "valid fork misbehaviour", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + misbehaviour = &types.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, + true, + }, + { + "valid time misbehaviour", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + misbehaviour = &types.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+3, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, + true, + }, + { + "valid time misbehaviour, header 1 time stricly less than header 2 time", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + misbehaviour = &types.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+3, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Hour), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, + true, + }, + { + "valid misbehavior at height greater than last consensusState", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + misbehaviour = &types.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+1, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+1, trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, true, + }, + { + "valid misbehaviour with different trusted heights", func() { + trustedHeight1 := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals1, found := suite.chainB.GetValsAtHeight(int64(trustedHeight1.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + trustedHeight2 := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals2, found := suite.chainB.GetValsAtHeight(int64(trustedHeight2.RevisionHeight) + 1) + suite.Require().True(found) + + misbehaviour = &types.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight1, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals1, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight2, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals2, suite.chainB.Signers), + } + + }, + true, + }, + /* + { + + "valid misbehaviour at a previous revision", + types.NewClientState(chainIDRevision1, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(1, 1), commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + heightMinus1, + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), + heightMinus3, + &types.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(chainIDRevision0, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainIDRevision0, int64(height.RevisionHeight+1), heightMinus3, suite.now.Add(time.Minute), bothValSet, bothValSet, suite.valSet, bothSigners), + ClientId: chainID, + }, + suite.now, + true, + }, + { + "valid misbehaviour at a future revision", + types.NewClientState(chainIDRevision0, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + heightMinus1, + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), + heightMinus3, + &types.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(chainIDRevision0, 3, heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainIDRevision0, 3, heightMinus3, suite.now.Add(time.Minute), bothValSet, bothValSet, suite.valSet, bothSigners), + ClientId: chainID, + }, + suite.now, + true, + }, + { + "valid misbehaviour with trusted heights at a previous revision", + types.NewClientState(chainIDRevision1, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(1, 1), commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + heightMinus1, + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), + heightMinus3, + &types.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(chainIDRevision1, 1, heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainIDRevision1, 1, heightMinus3, suite.now.Add(time.Minute), bothValSet, bothValSet, suite.valSet, bothSigners), + ClientId: chainID, + }, + suite.now, + true, + }, + */ + { + "consensus state's valset hash different from misbehaviour should still pass", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + // Create bothValSet with both suite validator and altVal + bothValSet := tmtypes.NewValidatorSet(append(suite.chainB.Vals.Validators, altValSet.Proposer)) + bothSigners := suite.chainB.Signers + bothSigners[altValSet.Proposer.Address.String()] = altPrivVal + + misbehaviour = &types.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), bothValSet, suite.chainB.NextVals, trustedVals, bothSigners), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, bothValSet, suite.chainB.NextVals, trustedVals, bothSigners), + } + }, true, + }, + { + "invalid fork misbehaviour: identical headers", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + misbehaviourHeader := suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers) + misbehaviour = &types.Misbehaviour{ + Header1: misbehaviourHeader, + Header2: misbehaviourHeader, + } + }, false, + }, + { + "invalid time misbehaviour: monotonically increasing time", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + misbehaviour = &types.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+3, trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, false, + }, + { + "invalid misbehaviour: misbehaviour from different chain", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + misbehaviour = &types.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader("evmos", int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader("evmos", int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + + }, false, + }, + { + "misbehaviour trusted validators does not match validator hash in trusted consensus state", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + misbehaviour = &types.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, altValSet, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, altValSet, suite.chainB.Signers), + } + }, false, + }, + { + "trusted consensus state does not exist", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + misbehaviour = &types.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight.Increment().(clienttypes.Height), suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, false, + }, + { + "invalid tendermint misbehaviour", func() { + misbehaviour = &smtypes.Misbehaviour{} + }, false, + }, + { + "trusting period expired", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + suite.chainA.ExpireClient(path.EndpointA.ClientConfig.(*ibctesting.TendermintConfig).TrustingPeriod) + + misbehaviour = &types.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, false, + }, + { + "header 1 valset has too much change", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + misbehaviour = &types.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), altValSet, suite.chainB.NextVals, trustedVals, altSigners), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, false, + }, + { + "header 2 valset has too much change", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + misbehaviour = &types.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, altValSet, suite.chainB.NextVals, trustedVals, altSigners), + } + }, false, + }, + { + "both header 1 and header 2 valsets have too much change", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + misbehaviour = &types.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), altValSet, suite.chainB.NextVals, trustedVals, altSigners), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, altValSet, suite.chainB.NextVals, trustedVals, altSigners), + } + }, false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + path = ibctesting.NewPath(suite.chainA, suite.chainB) + + err := path.EndpointA.CreateClient() + suite.Require().NoError(err) + + clientState := path.EndpointA.GetClientState() + + // TODO: remove casting when `VerifyClientMessage` is apart of ClientState interface + tmClientState, ok := clientState.(*types.ClientState) + suite.Require().True(ok) + + tc.malleate() + + clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + + err = tmClientState.VerifyClientMessage(suite.chainA.GetContext(), clientStore, suite.chainA.App.AppCodec(), misbehaviour) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} diff --git a/modules/light-clients/07-tendermint/types/update.go b/modules/light-clients/07-tendermint/types/update.go index 18b4a2daa50..0862447a4b3 100644 --- a/modules/light-clients/07-tendermint/types/update.go +++ b/modules/light-clients/07-tendermint/types/update.go @@ -3,7 +3,6 @@ package types import ( "bytes" "reflect" - "time" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -65,15 +64,7 @@ func (cs ClientState) CheckHeaderAndUpdateState( conflictingHeader = true } - // get consensus state from clientStore - trustedConsState, err := GetConsensusState(clientStore, cdc, tmHeader.TrustedHeight) - if err != nil { - return nil, nil, sdkerrors.Wrapf( - err, "could not get consensus state from clientstore at TrustedHeight: %s", tmHeader.TrustedHeight, - ) - } - - if err := checkValidity(&cs, trustedConsState, tmHeader, ctx.BlockTime()); err != nil { + if err := cs.VerifyClientMessage(ctx, clientStore, cdc, tmHeader); err != nil { return nil, nil, err } @@ -126,12 +117,41 @@ func checkTrustedHeader(header *Header, consState *ConsensusState) error { return nil } -// checkValidity checks if the Tendermint header is valid. -// CONTRACT: consState.Height == header.TrustedHeight -func checkValidity( - clientState *ClientState, consState *ConsensusState, - header *Header, currentTimestamp time.Time, +// VerifyClientMessage checks if the clientMessage is of type Header or Misbehaviour and verifies the message +func (cs *ClientState) VerifyClientMessage( + ctx sdk.Context, clientStore sdk.KVStore, cdc codec.BinaryCodec, + clientMsg exported.ClientMessage, ) error { + switch msg := clientMsg.(type) { + case *Header: + return cs.verifyHeader(ctx, clientStore, cdc, msg) + case *Misbehaviour: + return cs.verifyMisbehaviour(ctx, clientStore, cdc, msg) + default: + return clienttypes.ErrInvalidClientType + } +} + +// verifyHeader returns an error if: +// - the client or header provided are not parseable to tendermint types +// - the header is invalid +// - header height is less than or equal to the trusted header height +// - header revision is not equal to trusted header revision +// - header valset commit verification fails +// - header timestamp is past the trusting period in relation to the consensus state +// - header timestamp is less than or equal to the consensus state timestamp +func (cs *ClientState) verifyHeader( + ctx sdk.Context, clientStore sdk.KVStore, cdc codec.BinaryCodec, + header *Header, +) error { + currentTimestamp := ctx.BlockTime() + + // Retrieve trusted consensus states for each Header in misbehaviour + consState, err := GetConsensusState(clientStore, cdc, header.TrustedHeight) + if err != nil { + return sdkerrors.Wrapf(err, "could not get trusted consensus state from clientStore for Header at TrustedHeight: %s", header.TrustedHeight) + } + if err := checkTrustedHeader(header, consState); err != nil { return err } @@ -169,7 +189,7 @@ func checkValidity( ) } - chainID := clientState.GetChainID() + chainID := cs.GetChainID() // If chainID is in revision format, then set revision number of chainID with the revision number // of the header we are verifying // This is useful if the update is at a previous revision rather than an update to the latest revision @@ -200,11 +220,12 @@ func checkValidity( err = light.Verify( &signedHeader, tmTrustedValidators, tmSignedHeader, tmValidatorSet, - clientState.TrustingPeriod, currentTimestamp, clientState.MaxClockDrift, clientState.TrustLevel.ToTendermint(), + cs.TrustingPeriod, currentTimestamp, cs.MaxClockDrift, cs.TrustLevel.ToTendermint(), ) if err != nil { return sdkerrors.Wrap(err, "failed to verify header") } + return nil } diff --git a/modules/light-clients/07-tendermint/types/update_test.go b/modules/light-clients/07-tendermint/types/update_test.go index 7216f48d33f..a6ae04c0897 100644 --- a/modules/light-clients/07-tendermint/types/update_test.go +++ b/modules/light-clients/07-tendermint/types/update_test.go @@ -4,15 +4,15 @@ import ( "fmt" "time" - tmtypes "github.com/tendermint/tendermint/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/exported" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" types "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" ibctesting "github.com/cosmos/ibc-go/v3/testing" ibctestingmock "github.com/cosmos/ibc-go/v3/testing/mock" + tmtypes "github.com/tendermint/tendermint/types" ) func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { @@ -35,14 +35,13 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { // create modified heights to use for test-cases heightPlus1 := clienttypes.NewHeight(height.RevisionNumber, height.RevisionHeight+1) - heightMinus1 := clienttypes.NewHeight(height.RevisionNumber, height.RevisionHeight-1) + // heightPlus5 := clienttypes.NewHeight(height.RevisionNumber, height.RevisionHeight+5) + // heightMinus1 := clienttypes.NewHeight(height.RevisionNumber, height.RevisionHeight-1) heightMinus3 := clienttypes.NewHeight(height.RevisionNumber, height.RevisionHeight-3) - heightPlus5 := clienttypes.NewHeight(height.RevisionNumber, height.RevisionHeight+5) - altVal := tmtypes.NewValidator(altPubKey, revisionHeight) // Create alternative validator set with only altVal, invalid update (too much change in valSet) - altValSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{altVal}) - altSigners := getAltSigners(altVal, altPrivVal) + // altValSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{altVal}) + // altSigners := getAltSigners(altVal, altPrivVal) testCases := []struct { name string @@ -50,51 +49,6 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { expFrozen bool expPass bool }{ - { - name: "successful update with next height and same validator set", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) - currentTime = suite.now - }, - expFrozen: false, - expPass: true, - }, - { - name: "successful update with future height and different validator set", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus5.RevisionHeight), height, suite.headerTime, bothValSet, bothValSet, suite.valSet, bothSigners) - currentTime = suite.now - }, - expFrozen: false, - expPass: true, - }, - { - name: "successful update with next height and different validator set", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), bothValSet.Hash()) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, bothValSet, bothValSet, bothValSet, bothSigners) - currentTime = suite.now - }, - expFrozen: false, - expPass: true, - }, - { - name: "successful update for a previous height", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - consStateHeight = heightMinus3 - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightMinus1.RevisionHeight), heightMinus3, suite.headerTime, bothValSet, bothValSet, suite.valSet, bothSigners) - currentTime = suite.now - }, - expFrozen: false, - expPass: true, - }, { name: "successful update for a previous revision", setup: func(suite *TendermintTestSuite) { @@ -120,69 +74,6 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { expFrozen: false, expPass: true, }, - { - name: "misbehaviour detection: header conflicts with existing consensus state", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, heightPlus1, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) - currentTime = suite.now - ctx := suite.chainA.GetContext().WithBlockTime(currentTime) - // Change the consensus state of header and store in client store to create a conflict - conflictConsState := newHeader.ConsensusState() - conflictConsState.Root = commitmenttypes.NewMerkleRoot([]byte("conflicting apphash")) - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(ctx, clientID, heightPlus1, conflictConsState) - }, - expFrozen: true, - expPass: true, - }, - { - name: "misbehaviour detection: previous consensus state time is not before header time. time monotonicity violation", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - // create an intermediate consensus state with the same time as the newHeader to create a time violation. - // header time is after client time - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus5.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) - currentTime = suite.now - prevConsensusState := types.NewConsensusState(suite.headerTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - ctx := suite.chainA.GetContext().WithBlockTime(currentTime) - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(ctx, clientID, heightPlus1, prevConsensusState) - clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, clientID) - types.SetIterationKey(clientStore, heightPlus1) - }, - expFrozen: true, - expPass: true, - }, - { - name: "misbehaviour detection: next consensus state time is not after header time. time monotonicity violation", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - // create the next consensus state with the same time as the intermediate newHeader to create a time violation. - // header time is after clientTime - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) - currentTime = suite.now - nextConsensusState := types.NewConsensusState(suite.headerTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - ctx := suite.chainA.GetContext().WithBlockTime(currentTime) - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(ctx, clientID, heightPlus5, nextConsensusState) - clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, clientID) - types.SetIterationKey(clientStore, heightPlus5) - }, - expFrozen: true, - expPass: true, - }, - { - name: "unsuccessful update with incorrect header chain-id", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader("ethermint", int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) - currentTime = suite.now - }, - expFrozen: false, - expPass: false, - }, { name: "unsuccessful update to a future revision", setup: func(suite *TendermintTestSuite) { @@ -204,50 +95,6 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { expFrozen: false, expPass: false, }, - { - name: "unsuccessful update with next height: update header mismatches nextValSetHash", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, bothValSet, bothValSet, suite.valSet, bothSigners) - currentTime = suite.now - }, - expFrozen: false, - expPass: false, - }, - { - name: "unsuccessful update with next height: update header mismatches different nextValSetHash", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), bothValSet.Hash()) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, bothValSet, suite.signers) - currentTime = suite.now - }, - expFrozen: false, - expPass: false, - }, - { - name: "unsuccessful update with future height: too much change in validator set", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus5.RevisionHeight), height, suite.headerTime, altValSet, altValSet, suite.valSet, altSigners) - currentTime = suite.now - }, - expFrozen: false, - expPass: false, - }, - { - name: "unsuccessful updates, passed in incorrect trusted validators for given consensus state", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus5.RevisionHeight), height, suite.headerTime, bothValSet, bothValSet, bothValSet, bothSigners) - currentTime = suite.now - }, - expFrozen: false, - expPass: false, - }, { name: "unsuccessful update: trusting period has passed since last client timestamp", setup: func(suite *TendermintTestSuite) { @@ -260,53 +107,6 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { expFrozen: false, expPass: false, }, - { - name: "unsuccessful update: header timestamp is past current timestamp", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.now.Add(time.Minute), suite.valSet, suite.valSet, suite.valSet, suite.signers) - currentTime = suite.now - }, - expFrozen: false, - expPass: false, - }, - { - name: "unsuccessful update: header timestamp is not past last client timestamp", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.clientTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) - currentTime = suite.now - }, - expFrozen: false, - expPass: false, - }, - { - name: "header basic validation failed", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) - // cause new header to fail validatebasic by changing commit height to mismatch header height - newHeader.SignedHeader.Commit.Height = revisionHeight - 1 - currentTime = suite.now - }, - expFrozen: false, - expPass: false, - }, - { - name: "header height < consensus height", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(height.RevisionNumber, heightPlus5.RevisionHeight), commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - // Make new header at height less than latest client state - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightMinus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) - currentTime = suite.now - }, - expFrozen: false, - expPass: false, - }, } for i, tc := range testCases { @@ -368,6 +168,233 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { } } +func (suite *TendermintTestSuite) TestVerifyHeader() { + var ( + path *ibctesting.Path + header *ibctmtypes.Header + ) + + // Setup different validators and signers for testing different types of updates + altPrivVal := ibctestingmock.NewPV() + altPubKey, err := altPrivVal.GetPubKey() + suite.Require().NoError(err) + + revisionHeight := int64(height.RevisionHeight) + + // create modified heights to use for test-cases + altVal := tmtypes.NewValidator(altPubKey, 100) + // Create alternative validator set with only altVal, invalid update (too much change in valSet) + altValSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{altVal}) + altSigners := getAltSigners(altVal, altPrivVal) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + name: "success", + malleate: func() {}, + expPass: true, + }, + { + name: "successful verify header for header with a previous height", + malleate: func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + // passing the CurrentHeader.Height as the block height as it will become a previous height once we commit N blocks + header = suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers) + + // commit some blocks so that the created Header now has a previous height as the BlockHeight + suite.coordinator.CommitNBlocks(suite.chainB, 5) + + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + }, + expPass: true, + }, + { + name: "successful verify header: header with future height and different validator set", + malleate: func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + // Create bothValSet with both suite validator and altVal + bothValSet := tmtypes.NewValidatorSet(append(suite.chainB.Vals.Validators, altVal)) + bothSigners := suite.chainB.Signers + bothSigners[altVal.Address.String()] = altPrivVal + + header = suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+5, trustedHeight, suite.chainB.CurrentHeader.Time, bothValSet, suite.chainB.NextVals, trustedVals, bothSigners) + }, + expPass: true, + }, + { + name: "successful verify header: header with next height and different validator set", + malleate: func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + // Create bothValSet with both suite validator and altVal + bothValSet := tmtypes.NewValidatorSet(append(suite.chainB.Vals.Validators, altVal)) + bothSigners := suite.chainB.Signers + bothSigners[altVal.Address.String()] = altPrivVal + + header = suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, bothValSet, suite.chainB.NextVals, trustedVals, bothSigners) + }, + expPass: true, + }, + { + name: "unsuccessful updates, passed in incorrect trusted validators for given consensus state", + malleate: func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + // Create bothValSet with both suite validator and altVal + bothValSet := tmtypes.NewValidatorSet(append(suite.chainB.Vals.Validators, altVal)) + bothSigners := suite.chainB.Signers + bothSigners[altVal.Address.String()] = altPrivVal + + header = suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+1, trustedHeight, suite.chainB.CurrentHeader.Time, bothValSet, bothValSet, bothValSet, bothSigners) + }, + expPass: false, + }, + { + name: "unsuccessful verify header with next height: update header mismatches nextValSetHash", + malleate: func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + // this will err as altValSet.Hash() != consState.NextValidatorsHash + header = suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+1, trustedHeight, suite.chainB.CurrentHeader.Time, altValSet, altValSet, trustedVals, altSigners) + }, + expPass: false, + }, + { + name: "unsuccessful update with future height: too much change in validator set", + malleate: func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + header = suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+1, trustedHeight, suite.chainB.CurrentHeader.Time, altValSet, altValSet, trustedVals, altSigners) + }, + expPass: false, + }, + { + name: "unsuccessful verify header: header height revision and trusted height revision mismatch", + malleate: func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + header = suite.chainB.CreateTMClientHeader(chainIDRevision1, 3, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers) + }, + expPass: false, + }, + { + name: "unsuccessful verify header: header height < consensus height", + malleate: func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + heightMinus1 := clienttypes.NewHeight(trustedHeight.RevisionNumber, trustedHeight.RevisionHeight-1) + + // Make new header at height less than latest client state + header = suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(heightMinus1.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers) + }, + expPass: false, + }, + { + name: "unsuccessful verify header: header basic validation failed", + malleate: func() { + // cause header to fail validatebasic by changing commit height to mismatch header height + header.SignedHeader.Commit.Height = revisionHeight - 1 + }, + expPass: false, + }, + { + name: "unsuccessful verify header: header timestamp is not past last client timestamp", + malleate: func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight)) + suite.Require().True(found) + + header = suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+1, trustedHeight, suite.chainB.CurrentHeader.Time.Add(-time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers) + }, + expPass: false, + }, + { + name: "unsuccessful verify header: header with incorrect header chain-id", + malleate: func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight)) + suite.Require().True(found) + + header = suite.chainB.CreateTMClientHeader(chainID, suite.chainB.CurrentHeader.Height+1, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers) + }, + expPass: false, + }, + { + name: "unsuccessful update: trusting period has passed since last client timestamp", + malleate: func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight)) + suite.Require().True(found) + + header = suite.chainA.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+1, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers) + + suite.chainB.ExpireClient(ibctesting.TrustingPeriod) + }, + expPass: false, + }, + } + + for _, tc := range testCases { + tc := tc + suite.SetupTest() + path = ibctesting.NewPath(suite.chainA, suite.chainB) + + err := path.EndpointA.CreateClient() + suite.Require().NoError(err) + + // ensure counterparty state is committed + suite.coordinator.CommitBlock(suite.chainB) + header, err = path.EndpointA.Chain.ConstructUpdateTMClientHeader(path.EndpointA.Counterparty.Chain, path.EndpointA.ClientID) + suite.Require().NoError(err) + + clientState := path.EndpointA.GetClientState() + + clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + + tc.malleate() + + tmClientState, ok := clientState.(*types.ClientState) + suite.Require().True(ok) + + err = tmClientState.VerifyClientMessage(suite.chainA.GetContext(), clientStore, suite.chainA.App.AppCodec(), header) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + } +} + func (suite *TendermintTestSuite) TestUpdateState() { var ( path *ibctesting.Path From c2602a551fc3983740940dc4b40626e68111654e Mon Sep 17 00:00:00 2001 From: Sean King Date: Wed, 30 Mar 2022 10:14:04 +0200 Subject: [PATCH 19/71] refactor: removing GetRoot from ConsensusState interface (#1186) * refactor: removing GetRoot from ConsensusState interface * refactor: remove unnecessary GetRoot definitions * chore: changelog --- CHANGELOG.md | 1 + modules/core/02-client/legacy/v100/solomachine.go | 5 ----- modules/core/exported/client.go | 4 ---- .../light-clients/06-solomachine/types/consensus_state.go | 5 ----- .../06-solomachine/types/consensus_state_test.go | 1 - 5 files changed, 1 insertion(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c302b4c759e..5651a3d7ebd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### State Machine Breaking ### Improvements +* [\#1186](https://github.com/cosmos/ibc-go/pull/1186/files) Removing `GetRoot` function from ConsensusState interface in `02-client`. `GetRoot` is unused by core IBC. ### Features diff --git a/modules/core/02-client/legacy/v100/solomachine.go b/modules/core/02-client/legacy/v100/solomachine.go index c9814439902..28901dd7709 100644 --- a/modules/core/02-client/legacy/v100/solomachine.go +++ b/modules/core/02-client/legacy/v100/solomachine.go @@ -197,11 +197,6 @@ func (cs ConsensusState) GetTimestamp() uint64 { panic("legacy solo machine is deprecated!") } -// GetRoot panics! -func (cs ConsensusState) GetRoot() exported.Root { - panic("legacy solo machine is deprecated!") -} - // ValidateBasic panics! func (cs ConsensusState) ValidateBasic() error { panic("legacy solo machine is deprecated!") diff --git a/modules/core/exported/client.go b/modules/core/exported/client.go index 39095aff28f..c1f30ce0804 100644 --- a/modules/core/exported/client.go +++ b/modules/core/exported/client.go @@ -185,10 +185,6 @@ type ConsensusState interface { ClientType() string // Consensus kind - // GetRoot returns the commitment root of the consensus state, - // which is used for key-value pair verification. - GetRoot() Root - // GetTimestamp returns the timestamp (in nanoseconds) of the consensus state GetTimestamp() uint64 diff --git a/modules/light-clients/06-solomachine/types/consensus_state.go b/modules/light-clients/06-solomachine/types/consensus_state.go index 3012f91a567..61b15b65882 100644 --- a/modules/light-clients/06-solomachine/types/consensus_state.go +++ b/modules/light-clients/06-solomachine/types/consensus_state.go @@ -22,11 +22,6 @@ func (cs ConsensusState) GetTimestamp() uint64 { return cs.Timestamp } -// GetRoot returns nil since solo machines do not have roots. -func (cs ConsensusState) GetRoot() exported.Root { - return nil -} - // GetPubKey unmarshals the public key into a cryptotypes.PubKey type. // An error is returned if the public key is nil or the cached value // is not a PubKey. diff --git a/modules/light-clients/06-solomachine/types/consensus_state_test.go b/modules/light-clients/06-solomachine/types/consensus_state_test.go index 33e200c5461..5b2b29cadcd 100644 --- a/modules/light-clients/06-solomachine/types/consensus_state_test.go +++ b/modules/light-clients/06-solomachine/types/consensus_state_test.go @@ -11,7 +11,6 @@ func (suite *SoloMachineTestSuite) TestConsensusState() { suite.Require().Equal(exported.Solomachine, consensusState.ClientType()) suite.Require().Equal(suite.solomachine.Time, consensusState.GetTimestamp()) - suite.Require().Nil(consensusState.GetRoot()) } func (suite *SoloMachineTestSuite) TestConsensusStateValidateBasic() { From 40183b48403ccf2685ecf223fe2d23d96bddbb30 Mon Sep 17 00:00:00 2001 From: Sean King Date: Wed, 30 Mar 2022 14:35:05 +0200 Subject: [PATCH 20/71] Adding VerifyClientMessage to ClientState interface (#1196) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: adding VerifyClientMessage to ClientState interface * fix: legacy * remove todo + fix test * Update modules/core/exported/client.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * Update modules/core/02-client/legacy/v100/solomachine.go Co-authored-by: Damian Nolan * chore: changelog Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> Co-authored-by: Damian Nolan --- CHANGELOG.md | 1 + modules/core/02-client/legacy/v100/solomachine.go | 7 +++++++ modules/core/exported/client.go | 4 +++- .../07-tendermint/types/misbehaviour_handle.go | 2 +- .../07-tendermint/types/misbehaviour_handle_test.go | 6 +----- modules/light-clients/07-tendermint/types/update.go | 4 ++-- modules/light-clients/07-tendermint/types/update_test.go | 5 +---- modules/light-clients/09-localhost/types/client_state.go | 9 +++++++++ 8 files changed, 25 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5651a3d7ebd..9d746fb889c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Improvements * [\#1186](https://github.com/cosmos/ibc-go/pull/1186/files) Removing `GetRoot` function from ConsensusState interface in `02-client`. `GetRoot` is unused by core IBC. +* (modules/core/02-client) [\#1196](https://github.com/cosmos/ibc-go/pull/1196) Adding VerifyClientMessage to ClientState interface. ### Features diff --git a/modules/core/02-client/legacy/v100/solomachine.go b/modules/core/02-client/legacy/v100/solomachine.go index 28901dd7709..9888814dee4 100644 --- a/modules/core/02-client/legacy/v100/solomachine.go +++ b/modules/core/02-client/legacy/v100/solomachine.go @@ -88,6 +88,13 @@ func (cs ClientState) ExportMetadata(_ sdk.KVStore) []exported.GenesisMetadata { panic("legacy solo machine is deprecated!") } +// VerifyClientMessage panics! +func (cs *ClientState) VerifyClientMessage( + _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientMessage, +) error { + panic("legacy solo machine is deprecated!") +} + // CheckHeaderAndUpdateState panics! func (cs *ClientState) CheckHeaderAndUpdateState( _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientMessage, diff --git a/modules/core/exported/client.go b/modules/core/exported/client.go index c1f30ce0804..00ebc1e418c 100644 --- a/modules/core/exported/client.go +++ b/modules/core/exported/client.go @@ -56,8 +56,10 @@ type ClientState interface { // Genesis function ExportMetadata(sdk.KVStore) []GenesisMetadata - // Update and Misbehaviour functions + // VerifyClientMessage verifies a ClientMessage. A ClientMessage could be a Header, Misbehaviour, or batch update. + VerifyClientMessage(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg ClientMessage) error + // Update and Misbehaviour functions CheckHeaderAndUpdateState(sdk.Context, codec.BinaryCodec, sdk.KVStore, ClientMessage) (ClientState, ConsensusState, error) CheckMisbehaviourAndUpdateState(sdk.Context, codec.BinaryCodec, sdk.KVStore, ClientMessage) (ClientState, error) CheckSubstituteAndUpdateState(ctx sdk.Context, cdc codec.BinaryCodec, subjectClientStore, substituteClientStore sdk.KVStore, substituteClient ClientState) (ClientState, error) diff --git a/modules/light-clients/07-tendermint/types/misbehaviour_handle.go b/modules/light-clients/07-tendermint/types/misbehaviour_handle.go index 932829aa56b..7dce230b6f7 100644 --- a/modules/light-clients/07-tendermint/types/misbehaviour_handle.go +++ b/modules/light-clients/07-tendermint/types/misbehaviour_handle.go @@ -32,7 +32,7 @@ func (cs ClientState) CheckMisbehaviourAndUpdateState( return nil, sdkerrors.Wrapf(clienttypes.ErrInvalidClientType, "expected type %T, got %T", misbehaviour, &Misbehaviour{}) } - if err := cs.VerifyClientMessage(ctx, clientStore, cdc, tmMisbehaviour); err != nil { + if err := cs.VerifyClientMessage(ctx, cdc, clientStore, tmMisbehaviour); err != nil { return nil, err } diff --git a/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go b/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go index 4657bae4d34..62af23b2707 100644 --- a/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go +++ b/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go @@ -775,15 +775,11 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { clientState := path.EndpointA.GetClientState() - // TODO: remove casting when `VerifyClientMessage` is apart of ClientState interface - tmClientState, ok := clientState.(*types.ClientState) - suite.Require().True(ok) - tc.malleate() clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - err = tmClientState.VerifyClientMessage(suite.chainA.GetContext(), clientStore, suite.chainA.App.AppCodec(), misbehaviour) + err = clientState.VerifyClientMessage(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, misbehaviour) if tc.expPass { suite.Require().NoError(err) diff --git a/modules/light-clients/07-tendermint/types/update.go b/modules/light-clients/07-tendermint/types/update.go index 0862447a4b3..615ed14a381 100644 --- a/modules/light-clients/07-tendermint/types/update.go +++ b/modules/light-clients/07-tendermint/types/update.go @@ -64,7 +64,7 @@ func (cs ClientState) CheckHeaderAndUpdateState( conflictingHeader = true } - if err := cs.VerifyClientMessage(ctx, clientStore, cdc, tmHeader); err != nil { + if err := cs.VerifyClientMessage(ctx, cdc, clientStore, tmHeader); err != nil { return nil, nil, err } @@ -119,7 +119,7 @@ func checkTrustedHeader(header *Header, consState *ConsensusState) error { // VerifyClientMessage checks if the clientMessage is of type Header or Misbehaviour and verifies the message func (cs *ClientState) VerifyClientMessage( - ctx sdk.Context, clientStore sdk.KVStore, cdc codec.BinaryCodec, + ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg exported.ClientMessage, ) error { switch msg := clientMsg.(type) { diff --git a/modules/light-clients/07-tendermint/types/update_test.go b/modules/light-clients/07-tendermint/types/update_test.go index a6ae04c0897..3bc96b0fb93 100644 --- a/modules/light-clients/07-tendermint/types/update_test.go +++ b/modules/light-clients/07-tendermint/types/update_test.go @@ -382,10 +382,7 @@ func (suite *TendermintTestSuite) TestVerifyHeader() { tc.malleate() - tmClientState, ok := clientState.(*types.ClientState) - suite.Require().True(ok) - - err = tmClientState.VerifyClientMessage(suite.chainA.GetContext(), clientStore, suite.chainA.App.AppCodec(), header) + err = clientState.VerifyClientMessage(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, header) if tc.expPass { suite.Require().NoError(err) diff --git a/modules/light-clients/09-localhost/types/client_state.go b/modules/light-clients/09-localhost/types/client_state.go index 728e4ec5f12..1ac05f3af64 100644 --- a/modules/light-clients/09-localhost/types/client_state.go +++ b/modules/light-clients/09-localhost/types/client_state.go @@ -142,6 +142,15 @@ func (cs ClientState) VerifyUpgradeAndUpdateState( return sdkerrors.Wrap(clienttypes.ErrInvalidUpgradeClient, "cannot upgrade localhost client") } +// VerifyClientMessage +// TODO: localhost client will be removed +func (cs ClientState) VerifyClientMessage( + _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, + _ exported.ClientMessage, +) error { + return nil +} + // VerifyClientState verifies that the localhost client state is stored locally func (cs ClientState) VerifyClientState( store sdk.KVStore, cdc codec.BinaryCodec, From a4b3d096023907fdd982449d85d9ff35c09317a8 Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Wed, 30 Mar 2022 15:13:21 +0200 Subject: [PATCH 21/71] chore: CheckSubstituteAndUpdateState stores client state in lightclients impl (#1170) * set client state in store in lightclient * adding changelog entry --- CHANGELOG.md | 1 + modules/core/02-client/keeper/proposal.go | 1 - .../light-clients/06-solomachine/types/proposal_handle.go | 2 ++ .../06-solomachine/types/proposal_handle_test.go | 6 ++++++ .../light-clients/07-tendermint/types/proposal_handle.go | 1 + .../07-tendermint/types/proposal_handle_test.go | 5 +++++ 6 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d746fb889c..0bd1a631b45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Improvements * [\#1186](https://github.com/cosmos/ibc-go/pull/1186/files) Removing `GetRoot` function from ConsensusState interface in `02-client`. `GetRoot` is unused by core IBC. * (modules/core/02-client) [\#1196](https://github.com/cosmos/ibc-go/pull/1196) Adding VerifyClientMessage to ClientState interface. +* (modules/core/02-client) [\#1170](https://github.com/cosmos/ibc-go/pull/1170) Updating `ClientUpdateProposal` to set client state in lightclient implementations `CheckSubstituteAndUpdateState` methods. ### Features diff --git a/modules/core/02-client/keeper/proposal.go b/modules/core/02-client/keeper/proposal.go index ef0bf043e50..71bfc7f833a 100644 --- a/modules/core/02-client/keeper/proposal.go +++ b/modules/core/02-client/keeper/proposal.go @@ -53,7 +53,6 @@ func (k Keeper) ClientUpdateProposal(ctx sdk.Context, p *types.ClientUpdatePropo if err != nil { return err } - k.SetClientState(ctx, p.SubjectClientId, clientState) k.Logger(ctx).Info("client updated after governance proposal passed", "client-id", p.SubjectClientId, "height", clientState.GetLatestHeight().String()) diff --git a/modules/light-clients/06-solomachine/types/proposal_handle.go b/modules/light-clients/06-solomachine/types/proposal_handle.go index a28bc27c398..456129537f4 100644 --- a/modules/light-clients/06-solomachine/types/proposal_handle.go +++ b/modules/light-clients/06-solomachine/types/proposal_handle.go @@ -60,5 +60,7 @@ func (cs ClientState) CheckSubstituteAndUpdateState( clientState.ConsensusState = substituteClientState.ConsensusState clientState.IsFrozen = false + setClientState(subjectClientStore, cdc, clientState) + return clientState, nil } diff --git a/modules/light-clients/06-solomachine/types/proposal_handle_test.go b/modules/light-clients/06-solomachine/types/proposal_handle_test.go index f52bbffde44..fbb814c0791 100644 --- a/modules/light-clients/06-solomachine/types/proposal_handle_test.go +++ b/modules/light-clients/06-solomachine/types/proposal_handle_test.go @@ -1,6 +1,8 @@ package types_test import ( + clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/exported" "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types" ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" @@ -78,6 +80,10 @@ func (suite *SoloMachineTestSuite) TestCheckSubstituteAndUpdateState() { suite.Require().Equal(substituteClientState.(*types.ClientState).ConsensusState, updatedClient.(*types.ClientState).ConsensusState) suite.Require().Equal(substituteClientState.(*types.ClientState).Sequence, updatedClient.(*types.ClientState).Sequence) suite.Require().Equal(false, updatedClient.(*types.ClientState).IsFrozen) + + // ensure updated client state is set in store + bz := subjectClientStore.Get(host.ClientStateKey()) + suite.Require().Equal(clienttypes.MustMarshalClientState(suite.chainA.Codec, updatedClient), bz) } else { suite.Require().Error(err) suite.Require().Nil(updatedClient) diff --git a/modules/light-clients/07-tendermint/types/proposal_handle.go b/modules/light-clients/07-tendermint/types/proposal_handle.go index bfa7f242e92..a87f9576567 100644 --- a/modules/light-clients/07-tendermint/types/proposal_handle.go +++ b/modules/light-clients/07-tendermint/types/proposal_handle.go @@ -87,6 +87,7 @@ func (cs ClientState) CheckSubstituteAndUpdateState( // no validation is necessary since the substitute is verified to be Active // in 02-client. + setClientState(subjectClientStore, cdc, &cs) return &cs, nil } diff --git a/modules/light-clients/07-tendermint/types/proposal_handle_test.go b/modules/light-clients/07-tendermint/types/proposal_handle_test.go index 822ec16e014..3793bd43332 100644 --- a/modules/light-clients/07-tendermint/types/proposal_handle_test.go +++ b/modules/light-clients/07-tendermint/types/proposal_handle_test.go @@ -4,6 +4,7 @@ import ( "time" clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/exported" "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" ibctesting "github.com/cosmos/ibc-go/v3/testing" @@ -299,6 +300,10 @@ func (suite *TendermintTestSuite) TestCheckSubstituteAndUpdateState() { suite.Require().Equal(expectedIterationKey, subjectIterationKey) suite.Require().Equal(newChainID, updatedClient.(*types.ClientState).ChainId) + + // ensure updated client state is set in store + bz := subjectClientStore.Get(host.ClientStateKey()) + suite.Require().Equal(clienttypes.MustMarshalClientState(suite.chainA.Codec, updatedClient), bz) } else { suite.Require().Error(err) suite.Require().Nil(updatedClient) From 2e2bfab6a714144f02a986fbfe5b6ced1c02f737 Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Wed, 30 Mar 2022 15:21:54 +0200 Subject: [PATCH 22/71] chore: 07-tendermint set client/consensus states in UpdateState (#1199) * adding storage ops to 07-tendermint UpdateState, updating tests * use clientMessage.GetHeight() as per review suggestions * updating tests to obtain previous client/consensus state in malleate --- .../07-tendermint/types/proposal_handle.go | 2 +- .../07-tendermint/types/store.go | 4 +- .../07-tendermint/types/update.go | 4 +- .../07-tendermint/types/update_test.go | 38 +++++++++++-------- .../07-tendermint/types/upgrade.go | 2 +- 5 files changed, 30 insertions(+), 20 deletions(-) diff --git a/modules/light-clients/07-tendermint/types/proposal_handle.go b/modules/light-clients/07-tendermint/types/proposal_handle.go index a87f9576567..c01ec6fd8b7 100644 --- a/modules/light-clients/07-tendermint/types/proposal_handle.go +++ b/modules/light-clients/07-tendermint/types/proposal_handle.go @@ -67,7 +67,7 @@ func (cs ClientState) CheckSubstituteAndUpdateState( return nil, sdkerrors.Wrap(err, "unable to retrieve latest consensus state for substitute client") } - SetConsensusState(subjectClientStore, cdc, consensusState, height) + setConsensusState(subjectClientStore, cdc, consensusState, height) // set metadata stored for the substitute consensus state processedHeight, found := GetProcessedHeight(substituteClientStore, height) diff --git a/modules/light-clients/07-tendermint/types/store.go b/modules/light-clients/07-tendermint/types/store.go index 33c2386bb78..f2c954cc029 100644 --- a/modules/light-clients/07-tendermint/types/store.go +++ b/modules/light-clients/07-tendermint/types/store.go @@ -50,8 +50,8 @@ func setClientState(clientStore sdk.KVStore, cdc codec.BinaryCodec, clientState clientStore.Set(key, val) } -// SetConsensusState stores the consensus state at the given height. -func SetConsensusState(clientStore sdk.KVStore, cdc codec.BinaryCodec, consensusState *ConsensusState, height exported.Height) { +// setConsensusState stores the consensus state at the given height. +func setConsensusState(clientStore sdk.KVStore, cdc codec.BinaryCodec, consensusState *ConsensusState, height exported.Height) { key := host.ConsensusStateKey(height) val := clienttypes.MustMarshalConsensusState(cdc, consensusState) clientStore.Set(key, val) diff --git a/modules/light-clients/07-tendermint/types/update.go b/modules/light-clients/07-tendermint/types/update.go index 615ed14a381..0a25cbc2518 100644 --- a/modules/light-clients/07-tendermint/types/update.go +++ b/modules/light-clients/07-tendermint/types/update.go @@ -262,7 +262,9 @@ func (cs ClientState) UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, client NextValidatorsHash: header.Header.NextValidatorsHash, } - // set metadata for this consensus state + // set client state, consensus state and asssociated metadata + setClientState(clientStore, cdc, &cs) + setConsensusState(clientStore, cdc, consensusState, header.GetHeight()) setConsensusMetadata(ctx, clientStore, header.GetHeight()) return &cs, consensusState, nil diff --git a/modules/light-clients/07-tendermint/types/update_test.go b/modules/light-clients/07-tendermint/types/update_test.go index 3bc96b0fb93..4f9648bd74a 100644 --- a/modules/light-clients/07-tendermint/types/update_test.go +++ b/modules/light-clients/07-tendermint/types/update_test.go @@ -4,6 +4,7 @@ import ( "fmt" "time" + sdk "github.com/cosmos/cosmos-sdk/types" clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" host "github.com/cosmos/ibc-go/v3/modules/core/24-host" @@ -394,11 +395,12 @@ func (suite *TendermintTestSuite) TestVerifyHeader() { func (suite *TendermintTestSuite) TestUpdateState() { var ( - path *ibctesting.Path - clientMessage exported.ClientMessage - pruneHeight clienttypes.Height - updatedClientState *types.ClientState // TODO: retrieve from state after 'UpdateState' call - updatedConsensusState *types.ConsensusState // TODO: retrieve from state after 'UpdateState' call + path *ibctesting.Path + clientMessage exported.ClientMessage + clientStore sdk.KVStore + pruneHeight clienttypes.Height + prevClientState exported.ClientState + prevConsensusState exported.ConsensusState ) testCases := []struct { @@ -412,7 +414,7 @@ func (suite *TendermintTestSuite) TestUpdateState() { suite.Require().True(path.EndpointA.GetClientState().GetLatestHeight().LT(clientMessage.GetHeight())) }, func() { - suite.Require().True(path.EndpointA.GetClientState().GetLatestHeight().LT(updatedClientState.GetLatestHeight())) // new update, updated client state should have changed + suite.Require().True(path.EndpointA.GetClientState().GetLatestHeight().EQ(clientMessage.GetHeight())) // new update, updated client state should have changed }, true, }, { @@ -424,9 +426,11 @@ func (suite *TendermintTestSuite) TestUpdateState() { suite.Require().NoError(err) suite.Require().True(path.EndpointA.GetClientState().GetLatestHeight().GT(clientMessage.GetHeight())) + + prevClientState = path.EndpointA.GetClientState() }, func() { - suite.Require().Equal(path.EndpointA.GetClientState(), updatedClientState) // fill in height, no change to client state + suite.Require().Equal(path.EndpointA.GetClientState(), prevClientState) // fill in height, no change to client state }, true, }, { @@ -439,10 +443,13 @@ func (suite *TendermintTestSuite) TestUpdateState() { clientMessage, err = path.EndpointA.Chain.ConstructUpdateTMClientHeader(path.EndpointA.Counterparty.Chain, path.EndpointA.ClientID) suite.Require().NoError(err) suite.Require().Equal(path.EndpointA.GetClientState().GetLatestHeight(), clientMessage.GetHeight()) + + prevClientState = path.EndpointA.GetClientState() + prevConsensusState = path.EndpointA.GetConsensusState(clientMessage.GetHeight()) }, func() { - suite.Require().Equal(path.EndpointA.GetClientState(), updatedClientState) - suite.Require().Equal(path.EndpointA.GetConsensusState(clientMessage.GetHeight()), updatedConsensusState) + suite.Require().Equal(path.EndpointA.GetClientState(), prevClientState) + suite.Require().Equal(path.EndpointA.GetConsensusState(clientMessage.GetHeight()), prevConsensusState) }, true, }, { @@ -471,7 +478,7 @@ func (suite *TendermintTestSuite) TestUpdateState() { suite.Require().NoError(err) }, func() { - suite.Require().True(path.EndpointA.GetClientState().GetLatestHeight().LT(updatedClientState.GetLatestHeight())) // new update, updated client state should have changed + suite.Require().True(path.EndpointA.GetClientState().GetLatestHeight().EQ(clientMessage.GetHeight())) // new update, updated client state should have changed // ensure consensus state was pruned _, found := path.EndpointA.Chain.GetConsensusState(path.EndpointA.ClientID, pruneHeight) @@ -508,8 +515,8 @@ func (suite *TendermintTestSuite) TestUpdateState() { tmClientState, ok := clientState.(*types.ClientState) suite.Require().True(ok) - clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - updatedClientState, updatedConsensusState, err = tmClientState.UpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, clientMessage) + clientStore = suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + _, _, err = tmClientState.UpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, clientMessage) if tc.expPass { suite.Require().NoError(err) @@ -520,13 +527,14 @@ func (suite *TendermintTestSuite) TestUpdateState() { Root: commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), NextValidatorsHash: header.Header.NextValidatorsHash, } + + bz := clientStore.Get(host.ConsensusStateKey(header.GetHeight())) + updatedConsensusState := clienttypes.MustUnmarshalConsensusState(suite.chainA.App.AppCodec(), bz) + suite.Require().Equal(expConsensusState, updatedConsensusState) } else { suite.Require().Error(err) - suite.Require().Nil(updatedClientState) - suite.Require().Nil(updatedConsensusState) - } // perform custom checks diff --git a/modules/light-clients/07-tendermint/types/upgrade.go b/modules/light-clients/07-tendermint/types/upgrade.go index 471769f0610..eab2e3681df 100644 --- a/modules/light-clients/07-tendermint/types/upgrade.go +++ b/modules/light-clients/07-tendermint/types/upgrade.go @@ -120,7 +120,7 @@ func (cs ClientState) VerifyUpgradeAndUpdateState( ) setClientState(clientStore, cdc, newClientState) - SetConsensusState(clientStore, cdc, newConsState, newClientState.LatestHeight) + setConsensusState(clientStore, cdc, newConsState, newClientState.LatestHeight) setConsensusMetadata(ctx, clientStore, tmUpgradeClient.LatestHeight) return nil From 18f1382d74fb447152dca13d95ac8a9fba7d8f83 Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Wed, 30 Mar 2022 15:58:56 +0200 Subject: [PATCH 23/71] chore: update 07-tendermint GetConsensusState to return bool over error (#1180) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * updating 07-tendermint GetConsensusState to return bool over error * panic on unmarshal and type assertion failure, update tests * updating to use MustUnmarshalConsensusState directly * Update modules/light-clients/07-tendermint/types/misbehaviour_handle.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * Update modules/light-clients/07-tendermint/types/misbehaviour_handle.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * removing comments and updating godoc Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> --- .../07-tendermint/types/client_state.go | 10 +++--- .../types/misbehaviour_handle.go | 12 +++---- .../07-tendermint/types/proposal_handle.go | 6 ++-- .../07-tendermint/types/store.go | 33 +++++-------------- .../07-tendermint/types/store_test.go | 25 ++++++++++---- .../07-tendermint/types/update.go | 12 +++---- .../07-tendermint/types/upgrade.go | 6 ++-- 7 files changed, 49 insertions(+), 55 deletions(-) diff --git a/modules/light-clients/07-tendermint/types/client_state.go b/modules/light-clients/07-tendermint/types/client_state.go index 51f826979fd..f0d0e79972f 100644 --- a/modules/light-clients/07-tendermint/types/client_state.go +++ b/modules/light-clients/07-tendermint/types/client_state.go @@ -76,8 +76,8 @@ func (cs ClientState) Status( } // get latest consensus state from clientStore to check for expiry - consState, err := GetConsensusState(clientStore, cdc, cs.GetLatestHeight()) - if err != nil { + consState, found := GetConsensusState(clientStore, cdc, cs.GetLatestHeight()) + if !found { // if the client state does not have an associated consensus state for its latest height // then it must be expired return exported.Expired @@ -574,9 +574,9 @@ func produceVerificationArgs( return commitmenttypes.MerkleProof{}, nil, sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "failed to unmarshal proof into commitment merkle proof") } - consensusState, err = GetConsensusState(store, cdc, height) - if err != nil { - return commitmenttypes.MerkleProof{}, nil, sdkerrors.Wrap(err, "please ensure the proof was constructed against a height that exists on the client") + consensusState, found := GetConsensusState(store, cdc, height) + if !found { + return commitmenttypes.MerkleProof{}, nil, sdkerrors.Wrap(clienttypes.ErrConsensusStateNotFound, "please ensure the proof was constructed against a height that exists on the client") } return merkleProof, consensusState, nil diff --git a/modules/light-clients/07-tendermint/types/misbehaviour_handle.go b/modules/light-clients/07-tendermint/types/misbehaviour_handle.go index 7dce230b6f7..71a8e5284f1 100644 --- a/modules/light-clients/07-tendermint/types/misbehaviour_handle.go +++ b/modules/light-clients/07-tendermint/types/misbehaviour_handle.go @@ -80,14 +80,14 @@ func (cs *ClientState) verifyMisbehaviour(ctx sdk.Context, clientStore sdk.KVSto // Regardless of the type of misbehaviour, ensure that both headers are valid and would have been accepted by light-client // Retrieve trusted consensus states for each Header in misbehaviour - tmConsensusState1, err := GetConsensusState(clientStore, cdc, misbehaviour.Header1.TrustedHeight) - if err != nil { - return sdkerrors.Wrapf(err, "could not get trusted consensus state from clientStore for Header1 at TrustedHeight: %s", misbehaviour.Header1.TrustedHeight) + tmConsensusState1, found := GetConsensusState(clientStore, cdc, misbehaviour.Header1.TrustedHeight) + if !found { + return sdkerrors.Wrapf(clienttypes.ErrConsensusStateNotFound, "could not get trusted consensus state from clientStore for Header1 at TrustedHeight: %s", misbehaviour.Header1.TrustedHeight) } - tmConsensusState2, err := GetConsensusState(clientStore, cdc, misbehaviour.Header2.TrustedHeight) - if err != nil { - return sdkerrors.Wrapf(err, "could not get trusted consensus state from clientStore for Header2 at TrustedHeight: %s", misbehaviour.Header2.TrustedHeight) + tmConsensusState2, found := GetConsensusState(clientStore, cdc, misbehaviour.Header2.TrustedHeight) + if !found { + return sdkerrors.Wrapf(clienttypes.ErrConsensusStateNotFound, "could not get trusted consensus state from clientStore for Header2 at TrustedHeight: %s", misbehaviour.Header2.TrustedHeight) } // Check the validity of the two conflicting headers against their respective diff --git a/modules/light-clients/07-tendermint/types/proposal_handle.go b/modules/light-clients/07-tendermint/types/proposal_handle.go index c01ec6fd8b7..563acce2c02 100644 --- a/modules/light-clients/07-tendermint/types/proposal_handle.go +++ b/modules/light-clients/07-tendermint/types/proposal_handle.go @@ -62,9 +62,9 @@ func (cs ClientState) CheckSubstituteAndUpdateState( // starting from initial height and ending on the latest height (inclusive) height := substituteClientState.GetLatestHeight() - consensusState, err := GetConsensusState(substituteClientStore, cdc, height) - if err != nil { - return nil, sdkerrors.Wrap(err, "unable to retrieve latest consensus state for substitute client") + consensusState, found := GetConsensusState(substituteClientStore, cdc, height) + if !found { + return nil, sdkerrors.Wrap(clienttypes.ErrConsensusStateNotFound, "unable to retrieve latest consensus state for substitute client") } setConsensusState(subjectClientStore, cdc, consensusState, height) diff --git a/modules/light-clients/07-tendermint/types/store.go b/modules/light-clients/07-tendermint/types/store.go index f2c954cc029..fabd29f3161 100644 --- a/modules/light-clients/07-tendermint/types/store.go +++ b/modules/light-clients/07-tendermint/types/store.go @@ -8,7 +8,6 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" host "github.com/cosmos/ibc-go/v3/modules/core/24-host" @@ -57,31 +56,16 @@ func setConsensusState(clientStore sdk.KVStore, cdc codec.BinaryCodec, consensus clientStore.Set(key, val) } -// GetConsensusState retrieves the consensus state from the client prefixed -// store. An error is returned if the consensus state does not exist. -func GetConsensusState(store sdk.KVStore, cdc codec.BinaryCodec, height exported.Height) (*ConsensusState, error) { +// GetConsensusState retrieves the consensus state from the client prefixed store. +// If the ConsensusState does not exist in state for the provided height a nil value and false boolean flag is returned +func GetConsensusState(store sdk.KVStore, cdc codec.BinaryCodec, height exported.Height) (*ConsensusState, bool) { bz := store.Get(host.ConsensusStateKey(height)) if bz == nil { - return nil, sdkerrors.Wrapf( - clienttypes.ErrConsensusStateNotFound, - "consensus state does not exist for height %s", height, - ) - } - - consensusStateI, err := clienttypes.UnmarshalConsensusState(cdc, bz) - if err != nil { - return nil, sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "unmarshal error: %v", err) - } - - consensusState, ok := consensusStateI.(*ConsensusState) - if !ok { - return nil, sdkerrors.Wrapf( - clienttypes.ErrInvalidConsensus, - "invalid consensus type %T, expected %T", consensusState, &ConsensusState{}, - ) + return nil, false } - return consensusState, nil + consensusStateI := clienttypes.MustUnmarshalConsensusState(cdc, bz) + return consensusStateI.(*ConsensusState), true } // deleteConsensusState deletes the consensus state at the given height @@ -299,9 +283,8 @@ func PruneAllExpiredConsensusStates( var heights []exported.Height pruneCb := func(height exported.Height) bool { - consState, err := GetConsensusState(clientStore, cdc, height) - // this error should never occur - if err != nil { + consState, found := GetConsensusState(clientStore, cdc, height) + if !found { // consensus state should always be found return true } diff --git a/modules/light-clients/07-tendermint/types/store_test.go b/modules/light-clients/07-tendermint/types/store_test.go index 22a8d069794..28877a49386 100644 --- a/modules/light-clients/07-tendermint/types/store_test.go +++ b/modules/light-clients/07-tendermint/types/store_test.go @@ -23,15 +23,16 @@ func (suite *TendermintTestSuite) TestGetConsensusState() { name string malleate func() expPass bool + expPanic bool }{ { - "success", func() {}, true, + "success", func() {}, true, false, }, { "consensus state not found", func() { // use height with no consensus state set height = height.(clienttypes.Height).Increment() - }, false, + }, false, false, }, { "not a consensus state interface", func() { @@ -39,7 +40,7 @@ func (suite *TendermintTestSuite) TestGetConsensusState() { store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) clientStateBz := suite.chainA.App.GetIBCKeeper().ClientKeeper.MustMarshalClientState(&types.ClientState{}) store.Set(host.ConsensusStateKey(height), clientStateBz) - }, false, + }, false, true, }, { "invalid consensus state (solomachine)", func() { @@ -47,7 +48,7 @@ func (suite *TendermintTestSuite) TestGetConsensusState() { store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) consensusStateBz := suite.chainA.App.GetIBCKeeper().ClientKeeper.MustMarshalConsensusState(&solomachinetypes.ConsensusState{}) store.Set(host.ConsensusStateKey(height), consensusStateBz) - }, false, + }, false, true, }, } @@ -64,16 +65,26 @@ func (suite *TendermintTestSuite) TestGetConsensusState() { tc.malleate() // change vars as necessary + if tc.expPanic { + suite.Require().Panics(func() { + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + types.GetConsensusState(store, suite.chainA.Codec, height) + }) + + return + } + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - consensusState, err := types.GetConsensusState(store, suite.chainA.Codec, height) + consensusState, found := types.GetConsensusState(store, suite.chainA.Codec, height) if tc.expPass { - suite.Require().NoError(err) + suite.Require().True(found) + expConsensusState, found := suite.chainA.GetConsensusState(path.EndpointA.ClientID, height) suite.Require().True(found) suite.Require().Equal(expConsensusState, consensusState) } else { - suite.Require().Error(err) + suite.Require().False(found) suite.Require().Nil(consensusState) } }) diff --git a/modules/light-clients/07-tendermint/types/update.go b/modules/light-clients/07-tendermint/types/update.go index 0a25cbc2518..ecd88312882 100644 --- a/modules/light-clients/07-tendermint/types/update.go +++ b/modules/light-clients/07-tendermint/types/update.go @@ -147,9 +147,9 @@ func (cs *ClientState) verifyHeader( currentTimestamp := ctx.BlockTime() // Retrieve trusted consensus states for each Header in misbehaviour - consState, err := GetConsensusState(clientStore, cdc, header.TrustedHeight) - if err != nil { - return sdkerrors.Wrapf(err, "could not get trusted consensus state from clientStore for Header at TrustedHeight: %s", header.TrustedHeight) + consState, found := GetConsensusState(clientStore, cdc, header.TrustedHeight) + if !found { + return sdkerrors.Wrapf(clienttypes.ErrConsensusStateNotFound, "could not get trusted consensus state from clientStore for Header at TrustedHeight: %s", header.TrustedHeight) } if err := checkTrustedHeader(header, consState); err != nil { @@ -282,10 +282,10 @@ func (cs ClientState) pruneOldestConsensusState(ctx sdk.Context, cdc codec.Binar ) pruneCb := func(height exported.Height) bool { - consState, err := GetConsensusState(clientStore, cdc, height) + consState, found := GetConsensusState(clientStore, cdc, height) // this error should never occur - if err != nil { - pruneError = err + if !found { + pruneError = sdkerrors.Wrapf(clienttypes.ErrConsensusStateNotFound, "failed to retrieve consensus state at height: %s", height) return true } diff --git a/modules/light-clients/07-tendermint/types/upgrade.go b/modules/light-clients/07-tendermint/types/upgrade.go index eab2e3681df..c3910aa8816 100644 --- a/modules/light-clients/07-tendermint/types/upgrade.go +++ b/modules/light-clients/07-tendermint/types/upgrade.go @@ -67,9 +67,9 @@ func (cs ClientState) VerifyUpgradeAndUpdateState( // Must prove against latest consensus state to ensure we are verifying against latest upgrade plan // This verifies that upgrade is intended for the provided revision, since committed client must exist // at this consensus state - consState, err := GetConsensusState(clientStore, cdc, lastHeight) - if err != nil { - return sdkerrors.Wrap(err, "could not retrieve consensus state for lastHeight") + consState, found := GetConsensusState(clientStore, cdc, lastHeight) + if !found { + return sdkerrors.Wrap(clienttypes.ErrConsensusStateNotFound, "could not retrieve consensus state for lastHeight") } // Verify client proof From 3c7358b1187023f84ffb014be87270cb8df839db Mon Sep 17 00:00:00 2001 From: Sean King Date: Thu, 31 Mar 2022 14:08:26 +0200 Subject: [PATCH 24/71] feat: adding UpdateStateOnMisbehaviour to ClientState interface (#1198) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: adding UpdateStateOnMisbehaviour to ClientState interface * Update modules/core/exported/client.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * fix: return values * chore: changelog Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> --- CHANGELOG.md | 2 ++ modules/core/02-client/legacy/v100/solomachine.go | 7 +++++++ modules/core/exported/client.go | 3 +++ modules/light-clients/06-solomachine/types/update.go | 7 +++---- modules/light-clients/09-localhost/types/client_state.go | 6 ++---- 5 files changed, 17 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bd1a631b45..ba95828957a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,8 +45,10 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Improvements * [\#1186](https://github.com/cosmos/ibc-go/pull/1186/files) Removing `GetRoot` function from ConsensusState interface in `02-client`. `GetRoot` is unused by core IBC. * (modules/core/02-client) [\#1196](https://github.com/cosmos/ibc-go/pull/1196) Adding VerifyClientMessage to ClientState interface. +* (modules/core/02-client) [\#1198](https://github.com/cosmos/ibc-go/pull/1198) Adding UpdateStateOnMisbehaviour to ClientState interface. * (modules/core/02-client) [\#1170](https://github.com/cosmos/ibc-go/pull/1170) Updating `ClientUpdateProposal` to set client state in lightclient implementations `CheckSubstituteAndUpdateState` methods. + ### Features ### Bug Fixes diff --git a/modules/core/02-client/legacy/v100/solomachine.go b/modules/core/02-client/legacy/v100/solomachine.go index 9888814dee4..24744c3fcbd 100644 --- a/modules/core/02-client/legacy/v100/solomachine.go +++ b/modules/core/02-client/legacy/v100/solomachine.go @@ -88,6 +88,13 @@ func (cs ClientState) ExportMetadata(_ sdk.KVStore) []exported.GenesisMetadata { panic("legacy solo machine is deprecated!") } +// UpdateStateOnMisbehaviour panics! +func (cs *ClientState) UpdateStateOnMisbehaviour( + _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, +) { + panic("legacy solo machine is deprecated!") +} + // VerifyClientMessage panics! func (cs *ClientState) VerifyClientMessage( _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientMessage, diff --git a/modules/core/exported/client.go b/modules/core/exported/client.go index 00ebc1e418c..8bc64f9106c 100644 --- a/modules/core/exported/client.go +++ b/modules/core/exported/client.go @@ -56,6 +56,9 @@ type ClientState interface { // Genesis function ExportMetadata(sdk.KVStore) []GenesisMetadata + // UpdateStateOnMisbehaviour should perform appropriate state changes on a client state given that misbehaviour has been detected and verified + UpdateStateOnMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore) + // VerifyClientMessage verifies a ClientMessage. A ClientMessage could be a Header, Misbehaviour, or batch update. VerifyClientMessage(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg ClientMessage) error diff --git a/modules/light-clients/06-solomachine/types/update.go b/modules/light-clients/06-solomachine/types/update.go index b6dfc44eac1..9379a622897 100644 --- a/modules/light-clients/06-solomachine/types/update.go +++ b/modules/light-clients/06-solomachine/types/update.go @@ -26,7 +26,8 @@ func (cs ClientState) CheckHeaderAndUpdateState( foundMisbehaviour := cs.CheckForMisbehaviour(ctx, cdc, clientStore, msg) if foundMisbehaviour { - return cs.UpdateStateOnMisbehaviour(ctx, cdc, clientStore) + cs.UpdateStateOnMisbehaviour(ctx, cdc, clientStore) + return &cs, cs.ConsensusState, nil } return cs.UpdateState(ctx, cdc, clientStore, msg) @@ -135,10 +136,8 @@ func (cs ClientState) CheckForMisbehaviour(_ sdk.Context, _ codec.BinaryCodec, _ // UpdateStateOnMisbehaviour updates state upon misbehaviour. This method should only be called on misbehaviour // as it does not perform any misbehaviour checks. -func (cs ClientState) UpdateStateOnMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore) (*ClientState, exported.ConsensusState, error) { +func (cs ClientState) UpdateStateOnMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore) { cs.IsFrozen = true clientStore.Set(host.ClientStateKey(), clienttypes.MustMarshalClientState(cdc, &cs)) - - return &cs, cs.ConsensusState, nil } diff --git a/modules/light-clients/09-localhost/types/client_state.go b/modules/light-clients/09-localhost/types/client_state.go index 1ac05f3af64..02031551f27 100644 --- a/modules/light-clients/09-localhost/types/client_state.go +++ b/modules/light-clients/09-localhost/types/client_state.go @@ -109,11 +109,9 @@ func (cs *ClientState) UpdateState( return cs, nil, nil } -// UpdateStateOnMisbehaviour returns an error (no misbehaviour case). +// UpdateStateOnMisbehaviour func (cs *ClientState) UpdateStateOnMisbehaviour( - _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientMessage, -) (*ClientState, error) { - return nil, sdkerrors.Wrapf(clienttypes.ErrUpdateClientFailed, "cannot update localhost client on misbehaviour") + _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore) { } // CheckMisbehaviourAndUpdateState implements ClientState From 5cf6528acdf1a38299422be8793bb547b07dcaf2 Mon Sep 17 00:00:00 2001 From: Sean King Date: Thu, 31 Mar 2022 14:23:48 +0200 Subject: [PATCH 25/71] refactor: remove localhost client implementation (#1187) * refactor: remove localhost light client implementation * chore: readding CreateLocalhost field * chore: readding createLocalhost field * chore: changelog * fix: BeginBlocker * fix: removing CreateLocalhost * chore: remove unused code * refactor: keeper tests * refactor: genesis type test add case for invalid client type * fix: add back tests * fix: remove unncessary if statement --- CHANGELOG.md | 1 + README.md | 2 - docs/OLD_README.md | 4 +- docs/ibc/integration.md | 11 - docs/ibc/proto-docs.md | 36 -- modules/core/02-client/abci.go | 13 +- modules/core/02-client/abci_test.go | 20 +- modules/core/02-client/genesis.go | 14 +- modules/core/02-client/keeper/client.go | 12 +- modules/core/02-client/keeper/client_test.go | 17 +- .../core/02-client/keeper/grpc_query_test.go | 8 +- modules/core/02-client/keeper/keeper_test.go | 29 +- modules/core/02-client/keeper/proposal.go | 7 +- .../core/02-client/keeper/proposal_test.go | 10 - modules/core/02-client/types/codec_test.go | 6 - modules/core/02-client/types/genesis.go | 4 - modules/core/02-client/types/genesis_test.go | 130 ++--- modules/core/02-client/types/msgs.go | 6 - modules/core/02-client/types/msgs_test.go | 15 +- modules/core/exported/client.go | 4 - modules/core/genesis.go | 2 +- modules/core/genesis_test.go | 25 +- modules/core/module.go | 5 +- modules/core/types/codec.go | 2 - modules/light-clients/09-localhost/doc.go | 5 - modules/light-clients/09-localhost/module.go | 10 - .../09-localhost/types/client_state.go | 375 ------------- .../09-localhost/types/client_state_test.go | 529 ------------------ .../light-clients/09-localhost/types/codec.go | 16 - .../09-localhost/types/errors.go | 10 - .../light-clients/09-localhost/types/keys.go | 6 - .../09-localhost/types/localhost.pb.go | 369 ------------ .../09-localhost/types/localhost_test.go | 43 -- .../lightclients/localhost/v1/localhost.proto | 18 - 34 files changed, 70 insertions(+), 1694 deletions(-) delete mode 100644 modules/light-clients/09-localhost/doc.go delete mode 100644 modules/light-clients/09-localhost/module.go delete mode 100644 modules/light-clients/09-localhost/types/client_state.go delete mode 100644 modules/light-clients/09-localhost/types/client_state_test.go delete mode 100644 modules/light-clients/09-localhost/types/codec.go delete mode 100644 modules/light-clients/09-localhost/types/errors.go delete mode 100644 modules/light-clients/09-localhost/types/keys.go delete mode 100644 modules/light-clients/09-localhost/types/localhost.pb.go delete mode 100644 modules/light-clients/09-localhost/types/localhost_test.go delete mode 100644 proto/ibc/lightclients/localhost/v1/localhost.proto diff --git a/CHANGELOG.md b/CHANGELOG.md index ba95828957a..7fb2e6ad9fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### State Machine Breaking ### Improvements +* (modules/light-clients/09-localhost) [\#1187](https://github.com/cosmos/ibc-go/pull/1187/) Removing localhost light client implementation as it is not functional. * [\#1186](https://github.com/cosmos/ibc-go/pull/1186/files) Removing `GetRoot` function from ConsensusState interface in `02-client`. `GetRoot` is unused by core IBC. * (modules/core/02-client) [\#1196](https://github.com/cosmos/ibc-go/pull/1196) Adding VerifyClientMessage to ClientState interface. * (modules/core/02-client) [\#1198](https://github.com/cosmos/ibc-go/pull/1198) Adding UpdateStateOnMisbehaviour to ClientState interface. diff --git a/README.md b/README.md index 5a3cf3b7e7e..61e1ab0c139 100644 --- a/README.md +++ b/README.md @@ -59,8 +59,6 @@ The Inter-Blockchain Communication protocol (IBC) allows blockchains to talk to 3.2 [ICS 06 Solo Machine](https://github.com/cosmos/ibc-go/tree/main/modules/light-clients/06-solomachine) -Note: The localhost client is currently non-functional. - ## Roadmap For an overview of upcoming changes to ibc-go take a look at the [roadmap](./docs/roadmap/roadmap.md). diff --git a/docs/OLD_README.md b/docs/OLD_README.md index 47bb2547732..df9cd52112a 100644 --- a/docs/OLD_README.md +++ b/docs/OLD_README.md @@ -28,8 +28,6 @@ For the general specification please refer to the [Interchain Standards](https:/ 3.2 [Tendermint Client](./../light-clients/07-tendermint/spec/README.md) - 3.3 [Localhost Client](./../light-clients/09-localhost/spec/README.md) - ## Implementation Details As stated above, the IBC implementation on the Cosmos SDK introduces some changes @@ -114,4 +112,4 @@ x/ibc │   └── 09-localhost/ └── testing/ ``` - \ No newline at end of file + diff --git a/docs/ibc/integration.md b/docs/ibc/integration.md index 09c1d2d2de9..92c542ca717 100644 --- a/docs/ibc/integration.md +++ b/docs/ibc/integration.md @@ -175,17 +175,6 @@ at each height during the `BeginBlock` call. The historical info is required to past historical info at any given height in order to verify the light client `ConsensusState` during the connection handhake. -The IBC module also has -[`BeginBlock`](https://github.com/cosmos/ibc-go/blob/main/modules/core/02-client/abci.go) logic as -well. This is optional as it is only required if your application uses the [localhost -client](https://github.com/cosmos/ibc/blob/master/spec/client/ics-009-loopback-client) to connect two -different modules from the same chain. - -::: tip -Only register the ibc module to the `SetOrderBeginBlockers` if your application will use the -localhost (_aka_ loopback) client. -::: - ```go // app.go func NewApp(...args) *App { diff --git a/docs/ibc/proto-docs.md b/docs/ibc/proto-docs.md index 569289dc01f..25819cf8270 100644 --- a/docs/ibc/proto-docs.md +++ b/docs/ibc/proto-docs.md @@ -217,9 +217,6 @@ - [ibc/core/types/v1/genesis.proto](#ibc/core/types/v1/genesis.proto) - [GenesisState](#ibc.core.types.v1.GenesisState) -- [ibc/lightclients/localhost/v1/localhost.proto](#ibc/lightclients/localhost/v1/localhost.proto) - - [ClientState](#ibc.lightclients.localhost.v1.ClientState) - - [ibc/lightclients/solomachine/v1/solomachine.proto](#ibc/lightclients/solomachine/v1/solomachine.proto) - [ChannelStateData](#ibc.lightclients.solomachine.v1.ChannelStateData) - [ClientState](#ibc.lightclients.solomachine.v1.ClientState) @@ -3242,39 +3239,6 @@ GenesisState defines the ibc module's genesis state. - - - - - - - - - - - -

Top

- -## ibc/lightclients/localhost/v1/localhost.proto - - - - - -### ClientState -ClientState defines a loopback (localhost) client. It requires (read-only) -access to keys outside the client prefix. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `chain_id` | [string](#string) | | self chain ID | -| `height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | self latest block height | - - - - - diff --git a/modules/core/02-client/abci.go b/modules/core/02-client/abci.go index aa13295e1c9..ec209b4fdd0 100644 --- a/modules/core/02-client/abci.go +++ b/modules/core/02-client/abci.go @@ -4,11 +4,10 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/ibc-go/v3/modules/core/02-client/keeper" - "github.com/cosmos/ibc-go/v3/modules/core/exported" ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" ) -// BeginBlocker updates an existing localhost client with the latest block height. +// BeginBlocker is used to perform IBC client upgrades func BeginBlocker(ctx sdk.Context, k keeper.Keeper) { plan, found := k.GetUpgradePlan(ctx) if found { @@ -29,14 +28,4 @@ func BeginBlocker(ctx sdk.Context, k keeper.Keeper) { k.SetUpgradedConsensusState(ctx, plan.Height, bz) } } - - _, found = k.GetClientState(ctx, exported.Localhost) - if !found { - return - } - - // update the localhost client with the latest block height - if err := k.UpdateClient(ctx, exported.Localhost, nil); err != nil { - panic(err) - } } diff --git a/modules/core/02-client/abci_test.go b/modules/core/02-client/abci_test.go index 80ebdb338ec..5d220f53a42 100644 --- a/modules/core/02-client/abci_test.go +++ b/modules/core/02-client/abci_test.go @@ -3,16 +3,14 @@ package client_test import ( "testing" - upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" "github.com/stretchr/testify/suite" abci "github.com/tendermint/tendermint/abci/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" client "github.com/cosmos/ibc-go/v3/modules/core/02-client" "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" - localhosttypes "github.com/cosmos/ibc-go/v3/modules/light-clients/09-localhost/types" ibctesting "github.com/cosmos/ibc-go/v3/testing" ) @@ -30,13 +28,6 @@ func (suite *ClientTestSuite) SetupTest() { suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1)) suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2)) - - // set localhost client - revision := types.ParseChainID(suite.chainA.GetContext().ChainID()) - localHostClient := localhosttypes.NewClientState( - suite.chainA.GetContext().ChainID(), types.NewHeight(revision, uint64(suite.chainA.GetContext().BlockHeight())), - ) - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), exported.Localhost, localHostClient) } func TestClientTestSuite(t *testing.T) { @@ -44,11 +35,6 @@ func TestClientTestSuite(t *testing.T) { } func (suite *ClientTestSuite) TestBeginBlocker() { - prevHeight := types.GetSelfHeight(suite.chainA.GetContext()) - - localHostClient := suite.chainA.GetClientState(exported.Localhost) - suite.Require().Equal(prevHeight, localHostClient.GetLatestHeight()) - for i := 0; i < 10; i++ { // increment height suite.coordinator.CommitBlock(suite.chainA, suite.chainB) @@ -56,10 +42,6 @@ func (suite *ClientTestSuite) TestBeginBlocker() { suite.Require().NotPanics(func() { client.BeginBlocker(suite.chainA.GetContext(), suite.chainA.App.GetIBCKeeper().ClientKeeper) }, "BeginBlocker shouldn't panic") - - localHostClient = suite.chainA.GetClientState(exported.Localhost) - suite.Require().Equal(prevHeight.Increment(), localHostClient.GetLatestHeight()) - prevHeight = localHostClient.GetLatestHeight().(types.Height) } } diff --git a/modules/core/02-client/genesis.go b/modules/core/02-client/genesis.go index 6ba20b22510..87deebba5d0 100644 --- a/modules/core/02-client/genesis.go +++ b/modules/core/02-client/genesis.go @@ -46,14 +46,9 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, gs types.GenesisState) { } k.SetNextClientSequence(ctx, gs.NextClientSequence) - - // NOTE: localhost creation is specifically disallowed for the time being. - // Issue: https://github.com/cosmos/cosmos-sdk/issues/7871 } // ExportGenesis returns the ibc client submodule's exported genesis. -// NOTE: CreateLocalhost should always be false on export since a -// created localhost will be included in the exported clients. func ExportGenesis(ctx sdk.Context, k keeper.Keeper) types.GenesisState { genClients := k.GetAllGenesisClients(ctx) clientsMetadata, err := k.GetAllClientMetadata(ctx, genClients) @@ -61,10 +56,11 @@ func ExportGenesis(ctx sdk.Context, k keeper.Keeper) types.GenesisState { panic(err) } return types.GenesisState{ - Clients: genClients, - ClientsMetadata: clientsMetadata, - ClientsConsensus: k.GetAllConsensusStates(ctx), - Params: k.GetParams(ctx), + Clients: genClients, + ClientsMetadata: clientsMetadata, + ClientsConsensus: k.GetAllConsensusStates(ctx), + Params: k.GetParams(ctx), + // Warning: CreateLocalhost is deprecated CreateLocalhost: false, NextClientSequence: k.GetNextClientSequence(ctx), } diff --git a/modules/core/02-client/keeper/client.go b/modules/core/02-client/keeper/client.go index bfadcda026e..fa68f896665 100644 --- a/modules/core/02-client/keeper/client.go +++ b/modules/core/02-client/keeper/client.go @@ -36,10 +36,7 @@ func (k Keeper) CreateClient( return "", err } - // check if consensus state is nil in case the created client is Localhost - if consensusState != nil { - k.SetClientConsensusState(ctx, clientID, clientState.GetLatestHeight(), consensusState) - } + k.SetClientConsensusState(ctx, clientID, clientState.GetLatestHeight(), consensusState) k.Logger(ctx).Info("client created at height", "client-id", clientID, "height", clientState.GetLatestHeight().String()) @@ -98,12 +95,7 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.C // Else the update was proof of misbehaviour and we must emit appropriate misbehaviour events. if status := newClientState.Status(ctx, clientStore, k.cdc); status != exported.Frozen { // if update is not misbehaviour then update the consensus state - // we don't set consensus state for localhost client - if header != nil && clientID != exported.Localhost { - k.SetClientConsensusState(ctx, clientID, header.GetHeight(), newConsensusState) - } else { - consensusHeight = types.GetSelfHeight(ctx) - } + k.SetClientConsensusState(ctx, clientID, header.GetHeight(), newConsensusState) k.Logger(ctx).Info("client state updated", "client-id", clientID, "height", consensusHeight.String()) diff --git a/modules/core/02-client/keeper/client_test.go b/modules/core/02-client/keeper/client_test.go index 424382a4d7f..9c441746755 100644 --- a/modules/core/02-client/keeper/client_test.go +++ b/modules/core/02-client/keeper/client_test.go @@ -12,8 +12,8 @@ import ( clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v3/modules/core/exported" + solomachinetypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types" ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" - localhosttypes "github.com/cosmos/ibc-go/v3/modules/light-clients/09-localhost/types" ibctesting "github.com/cosmos/ibc-go/v3/testing" ibctestingmock "github.com/cosmos/ibc-go/v3/testing/mock" ) @@ -25,7 +25,7 @@ func (suite *KeeperTestSuite) TestCreateClient() { expPass bool }{ {"success", ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), true}, - {"client type not supported", localhosttypes.NewClientState(testChainID, clienttypes.NewHeight(0, 1)), false}, + {"client type not supported", solomachinetypes.NewClientState(0, &solomachinetypes.ConsensusState{suite.solomachine.ConsensusState().PublicKey, suite.solomachine.Diversifier, suite.solomachine.Time}, false), false}, } for i, tc := range cases { @@ -252,19 +252,6 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { } } -func (suite *KeeperTestSuite) TestUpdateClientLocalhost() { - revision := types.ParseChainID(suite.chainA.ChainID) - var localhostClient exported.ClientState = localhosttypes.NewClientState(suite.chainA.ChainID, types.NewHeight(revision, uint64(suite.chainA.GetContext().BlockHeight()))) - - ctx := suite.chainA.GetContext().WithBlockHeight(suite.chainA.GetContext().BlockHeight() + 1) - err := suite.chainA.App.GetIBCKeeper().ClientKeeper.UpdateClient(ctx, exported.Localhost, nil) - suite.Require().NoError(err) - - clientState, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(ctx, exported.Localhost) - suite.Require().True(found) - suite.Require().Equal(localhostClient.GetLatestHeight().(types.Height).Increment(), clientState.GetLatestHeight()) -} - func (suite *KeeperTestSuite) TestUpgradeClient() { var ( path *ibctesting.Path diff --git a/modules/core/02-client/keeper/grpc_query_test.go b/modules/core/02-client/keeper/grpc_query_test.go index 5e393c33a97..60a5211733a 100644 --- a/modules/core/02-client/keeper/grpc_query_test.go +++ b/modules/core/02-client/keeper/grpc_query_test.go @@ -140,7 +140,7 @@ func (suite *KeeperTestSuite) TestQueryClientStates() { idcs := types.NewIdentifiedClientState(path1.EndpointA.ClientID, clientStateA1) idcs2 := types.NewIdentifiedClientState(path2.EndpointA.ClientID, clientStateA2) - // order is sorted by client id, localhost is last + // order is sorted by client id expClientStates = types.IdentifiedClientStates{idcs, idcs2}.Sort() req = &types.QueryClientStatesRequest{ Pagination: &query.PageRequest{ @@ -156,13 +156,7 @@ func (suite *KeeperTestSuite) TestQueryClientStates() { for _, tc := range testCases { suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { suite.SetupTest() // reset - expClientStates = nil - tc.malleate() - // always add localhost which is created by default in init genesis - localhostClientState := suite.chainA.GetClientState(exported.Localhost) - identifiedLocalhost := types.NewIdentifiedClientState(exported.Localhost, localhostClientState) - expClientStates = append(expClientStates, identifiedLocalhost) ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) diff --git a/modules/core/02-client/keeper/keeper_test.go b/modules/core/02-client/keeper/keeper_test.go index bbcc6dafde9..535b2f1685f 100644 --- a/modules/core/02-client/keeper/keeper_test.go +++ b/modules/core/02-client/keeper/keeper_test.go @@ -19,8 +19,8 @@ import ( "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v3/modules/core/exported" + solomachinetypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types" ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" - localhosttypes "github.com/cosmos/ibc-go/v3/modules/light-clients/09-localhost/types" ibctesting "github.com/cosmos/ibc-go/v3/testing" ibctestingmock "github.com/cosmos/ibc-go/v3/testing/mock" "github.com/cosmos/ibc-go/v3/testing/simapp" @@ -65,8 +65,8 @@ type KeeperTestSuite struct { privVal tmtypes.PrivValidator now time.Time past time.Time - - signers map[string]tmtypes.PrivValidator + solomachine *ibctesting.Solomachine + signers map[string]tmtypes.PrivValidator // TODO: deprecate queryClient types.QueryClient @@ -122,12 +122,7 @@ func (suite *KeeperTestSuite) SetupTest() { app.StakingKeeper.SetHistoricalInfo(suite.ctx, int64(i), &hi) } - // add localhost client - revision := types.ParseChainID(suite.chainA.ChainID) - localHostClient := localhosttypes.NewClientState( - suite.chainA.ChainID, types.NewHeight(revision, uint64(suite.chainA.GetContext().BlockHeight())), - ) - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), exported.Localhost, localHostClient) + suite.solomachine = ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachinesingle", "testing", 1) // TODO: deprecate queryHelper := baseapp.NewQueryServerTestHelper(suite.ctx, app.InterfaceRegistry()) @@ -177,11 +172,6 @@ func (suite *KeeperTestSuite) TestValidateSelfClient() { ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), nil, false, false), true, }, - { - "invalid client type", - localhosttypes.NewClientState(suite.chainA.ChainID, testClientHeight), - false, - }, { "frozen client", &ibctmtypes.ClientState{suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false}, @@ -197,6 +187,11 @@ func (suite *KeeperTestSuite) TestValidateSelfClient() { ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.NewHeight(0, uint64(suite.chainA.GetContext().BlockHeight())), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), false, }, + { + "invalid client type", + solomachinetypes.NewClientState(0, &solomachinetypes.ConsensusState{suite.solomachine.ConsensusState().PublicKey, suite.solomachine.Diversifier, suite.solomachine.Time}, false), + false, + }, { "invalid client revision", ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeightRevision1, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), @@ -256,11 +251,6 @@ func (suite KeeperTestSuite) TestGetAllGenesisClients() { expGenClients[i] = types.NewIdentifiedClientState(clientIDs[i], expClients[i]) } - // add localhost client - localHostClient, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), exported.Localhost) - suite.Require().True(found) - expGenClients = append(expGenClients, types.NewIdentifiedClientState(exported.Localhost, localHostClient)) - genClients := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetAllGenesisClients(suite.chainA.GetContext()) suite.Require().Equal(expGenClients.Sort(), genClients) @@ -287,7 +277,6 @@ func (suite KeeperTestSuite) TestGetAllGenesisMetadata() { genClients := []types.IdentifiedClientState{ types.NewIdentifiedClientState("07-tendermint-1", &ibctmtypes.ClientState{}), types.NewIdentifiedClientState("clientB", &ibctmtypes.ClientState{}), - types.NewIdentifiedClientState("clientC", &ibctmtypes.ClientState{}), types.NewIdentifiedClientState("clientD", &localhosttypes.ClientState{}), } suite.chainA.App.GetIBCKeeper().ClientKeeper.SetAllClientMetadata(suite.chainA.GetContext(), expectedGenMetadata) diff --git a/modules/core/02-client/keeper/proposal.go b/modules/core/02-client/keeper/proposal.go index 71bfc7f833a..ffe29f688cb 100644 --- a/modules/core/02-client/keeper/proposal.go +++ b/modules/core/02-client/keeper/proposal.go @@ -13,16 +13,11 @@ import ( // ClientUpdateProposal will retrieve the subject and substitute client. // A callback will occur to the subject client state with the client // prefixed store being provided for both the subject and the substitute client. -// The localhost client is not allowed to be modified with a proposal. The IBC -// client implementations are responsible for validating the parameters of the +// The IBC client implementations are responsible for validating the parameters of the // subtitute (enusring they match the subject's parameters) as well as copying // the necessary consensus states from the subtitute to the subject client // store. The substitute must be Active and the subject must not be Active. func (k Keeper) ClientUpdateProposal(ctx sdk.Context, p *types.ClientUpdateProposal) error { - if p.SubjectClientId == exported.Localhost || p.SubstituteClientId == exported.Localhost { - return sdkerrors.Wrap(types.ErrInvalidUpdateClientProposal, "cannot update localhost client with proposal") - } - subjectClientState, found := k.GetClientState(ctx, p.SubjectClientId) if !found { return sdkerrors.Wrapf(types.ErrClientNotFound, "subject client with ID %s", p.SubjectClientId) diff --git a/modules/core/02-client/keeper/proposal_test.go b/modules/core/02-client/keeper/proposal_test.go index bec5aa0a78a..0fbef636305 100644 --- a/modules/core/02-client/keeper/proposal_test.go +++ b/modules/core/02-client/keeper/proposal_test.go @@ -47,16 +47,6 @@ func (suite *KeeperTestSuite) TestClientUpdateProposal() { content = types.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, subject, substitute) }, true, }, - { - "cannot use localhost as subject", func() { - content = types.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, exported.Localhost, substitute) - }, false, - }, - { - "cannot use localhost as substitute", func() { - content = types.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, subject, exported.Localhost) - }, false, - }, { "cannot use solomachine as substitute for tendermint client", func() { solomachine := ibctesting.NewSolomachine(suite.T(), suite.cdc, "solo machine", "", 1) diff --git a/modules/core/02-client/types/codec_test.go b/modules/core/02-client/types/codec_test.go index b93773bebe5..197114a7be0 100644 --- a/modules/core/02-client/types/codec_test.go +++ b/modules/core/02-client/types/codec_test.go @@ -7,7 +7,6 @@ import ( commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v3/modules/core/exported" ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" - localhosttypes "github.com/cosmos/ibc-go/v3/modules/light-clients/09-localhost/types" ibctesting "github.com/cosmos/ibc-go/v3/testing" ) @@ -34,11 +33,6 @@ func (suite *TypesTestSuite) TestPackClientState() { ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), true, }, - { - "localhost client", - localhosttypes.NewClientState(chainID, clientHeight), - true, - }, { "nil", nil, diff --git a/modules/core/02-client/types/genesis.go b/modules/core/02-client/types/genesis.go index a272404054f..fce8a3d1b71 100644 --- a/modules/core/02-client/types/genesis.go +++ b/modules/core/02-client/types/genesis.go @@ -201,10 +201,6 @@ func (gs GenesisState) Validate() error { } - if gs.CreateLocalhost && !gs.Params.IsAllowedClient(exported.Localhost) { - return fmt.Errorf("localhost client is not registered on the allowlist") - } - if maxSequence != 0 && maxSequence >= gs.NextClientSequence { return fmt.Errorf("next client identifier sequence %d must be greater than the maximum sequence used in the provided client identifiers %d", gs.NextClientSequence, maxSequence) } diff --git a/modules/core/02-client/types/genesis_test.go b/modules/core/02-client/types/genesis_test.go index 6fc37070b31..1734fd482d2 100644 --- a/modules/core/02-client/types/genesis_test.go +++ b/modules/core/02-client/types/genesis_test.go @@ -9,18 +9,19 @@ import ( "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v3/modules/core/exported" + solomachinetypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types" ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" - localhosttypes "github.com/cosmos/ibc-go/v3/modules/light-clients/09-localhost/types" ibctesting "github.com/cosmos/ibc-go/v3/testing" ibctestingmock "github.com/cosmos/ibc-go/v3/testing/mock" ) const ( - chainID = "chainID" - tmClientID0 = "07-tendermint-0" - tmClientID1 = "07-tendermint-1" - invalidClientID = "myclient-0" - clientID = tmClientID0 + chainID = "chainID" + tmClientID0 = "07-tendermint-0" + tmClientID1 = "07-tendermint-1" + invalidClientID = "myclient-0" + soloMachineClientID = "06-solomachine-0" + clientID = tmClientID0 height = 10 ) @@ -78,9 +79,6 @@ func (suite *TypesTestSuite) TestValidateGenesis() { types.NewIdentifiedClientState( tmClientID0, ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), - types.NewIdentifiedClientState( - exported.Localhost+"-1", localhosttypes.NewClientState("chainID", clientHeight), - ), }, []types.ClientConsensusStates{ types.NewClientConsensusStates( @@ -104,21 +102,35 @@ func (suite *TypesTestSuite) TestValidateGenesis() { }, ), }, - types.NewParams(exported.Tendermint, exported.Localhost), + types.NewParams(exported.Tendermint), false, 2, ), expPass: true, }, { - name: "invalid clientid", + name: "invalid client type", genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - invalidClientID, ibctmtypes.NewClientState(chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + soloMachineClientID, ibctmtypes.NewClientState(chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), + types.NewIdentifiedClientState(tmClientID0, solomachinetypes.NewClientState(0, &solomachinetypes.ConsensusState{suite.solomachine.ConsensusState().PublicKey, suite.solomachine.Diversifier, suite.solomachine.Time}, false)), + }, + nil, + nil, + types.NewParams(exported.Tendermint), + false, + 0, + ), + expPass: false, + }, + { + name: "invalid clientid", + genState: types.NewGenesisState( + []types.IdentifiedClientState{ types.NewIdentifiedClientState( - exported.Localhost, localhosttypes.NewClientState("chainID", clientHeight), + invalidClientID, ibctmtypes.NewClientState(chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -141,23 +153,6 @@ func (suite *TypesTestSuite) TestValidateGenesis() { ), expPass: false, }, - { - name: "invalid client", - genState: types.NewGenesisState( - []types.IdentifiedClientState{ - types.NewIdentifiedClientState( - tmClientID0, ibctmtypes.NewClientState(chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), - ), - types.NewIdentifiedClientState(exported.Localhost, localhosttypes.NewClientState("chaindID", types.ZeroHeight())), - }, - nil, - nil, - types.NewParams(exported.Tendermint), - false, - 0, - ), - expPass: false, - }, { name: "consensus state client id does not match client id in genesis clients", genState: types.NewGenesisState( @@ -165,9 +160,6 @@ func (suite *TypesTestSuite) TestValidateGenesis() { types.NewIdentifiedClientState( tmClientID0, ibctmtypes.NewClientState(chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), - types.NewIdentifiedClientState( - exported.Localhost, localhosttypes.NewClientState("chaindID", clientHeight), - ), }, []types.ClientConsensusStates{ types.NewClientConsensusStates( @@ -196,9 +188,6 @@ func (suite *TypesTestSuite) TestValidateGenesis() { types.NewIdentifiedClientState( tmClientID0, ibctmtypes.NewClientState(chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), - types.NewIdentifiedClientState( - exported.Localhost, localhosttypes.NewClientState("chaindID", clientHeight), - ), }, []types.ClientConsensusStates{ types.NewClientConsensusStates( @@ -227,9 +216,6 @@ func (suite *TypesTestSuite) TestValidateGenesis() { types.NewIdentifiedClientState( tmClientID0, ibctmtypes.NewClientState(chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), - types.NewIdentifiedClientState( - exported.Localhost, localhosttypes.NewClientState("chaindID", clientHeight), - ), }, []types.ClientConsensusStates{ types.NewClientConsensusStates( @@ -258,9 +244,6 @@ func (suite *TypesTestSuite) TestValidateGenesis() { types.NewIdentifiedClientState( tmClientID0, ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), - types.NewIdentifiedClientState( - exported.Localhost, localhosttypes.NewClientState("chainID", clientHeight), - ), }, []types.ClientConsensusStates{ types.NewClientConsensusStates( @@ -289,9 +272,6 @@ func (suite *TypesTestSuite) TestValidateGenesis() { types.NewIdentifiedClientState( clientID, ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), - types.NewIdentifiedClientState( - exported.Localhost, localhosttypes.NewClientState("chainID", clientHeight), - ), }, []types.ClientConsensusStates{ types.NewClientConsensusStates( @@ -315,7 +295,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { }, ), }, - types.NewParams(exported.Tendermint, exported.Localhost), + types.NewParams(exported.Tendermint), false, 0, ), @@ -363,9 +343,6 @@ func (suite *TypesTestSuite) TestValidateGenesis() { types.NewIdentifiedClientState( tmClientID0, ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), - types.NewIdentifiedClientState( - exported.Localhost, localhosttypes.NewClientState("chainID", clientHeight), - ), }, []types.ClientConsensusStates{ types.NewClientConsensusStates( @@ -394,9 +371,6 @@ func (suite *TypesTestSuite) TestValidateGenesis() { types.NewIdentifiedClientState( tmClientID0, ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), - types.NewIdentifiedClientState( - exported.Localhost, localhosttypes.NewClientState("chainID", clientHeight), - ), }, []types.ClientConsensusStates{ types.NewClientConsensusStates( @@ -413,42 +387,11 @@ func (suite *TypesTestSuite) TestValidateGenesis() { }, nil, types.NewParams(" "), - true, + false, 0, ), expPass: false, }, - { - name: "localhost client not registered on allowlist", - genState: types.NewGenesisState( - []types.IdentifiedClientState{ - types.NewIdentifiedClientState( - tmClientID1, ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), - ), - types.NewIdentifiedClientState( - exported.Localhost+"-0", localhosttypes.NewClientState("chainID", clientHeight), - ), - }, - []types.ClientConsensusStates{ - types.NewClientConsensusStates( - tmClientID1, - []types.ConsensusStateWithHeight{ - types.NewConsensusStateWithHeight( - header.GetHeight().(types.Height), - ibctmtypes.NewConsensusState( - header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, - ), - ), - }, - ), - }, - nil, - types.NewParams(exported.Tendermint), - true, - 2, - ), - expPass: false, - }, { name: "next sequence too small", genState: types.NewGenesisState( @@ -457,7 +400,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { tmClientID0, ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), types.NewIdentifiedClientState( - exported.Localhost+"-1", localhosttypes.NewClientState("chainID", clientHeight), + tmClientID1, ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -474,7 +417,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { ), }, nil, - types.NewParams(exported.Tendermint, exported.Localhost), + types.NewParams(exported.Tendermint), false, 0, ), @@ -487,9 +430,6 @@ func (suite *TypesTestSuite) TestValidateGenesis() { types.NewIdentifiedClientState( "my-client", ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), - types.NewIdentifiedClientState( - exported.Localhost+"-1", localhosttypes.NewClientState("chainID", clientHeight), - ), }, []types.ClientConsensusStates{ types.NewClientConsensusStates( @@ -505,7 +445,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { ), }, nil, - types.NewParams(exported.Tendermint, exported.Localhost), + types.NewParams(exported.Tendermint), false, 5, ), @@ -514,14 +454,10 @@ func (suite *TypesTestSuite) TestValidateGenesis() { { name: "consensus state different than client state type", genState: types.NewGenesisState( - []types.IdentifiedClientState{ - types.NewIdentifiedClientState( - exported.Localhost+"-1", localhosttypes.NewClientState("chainID", clientHeight), - ), - }, + []types.IdentifiedClientState{}, []types.ClientConsensusStates{ types.NewClientConsensusStates( - exported.Localhost+"-1", + tmClientID0, []types.ConsensusStateWithHeight{ types.NewConsensusStateWithHeight( header.GetHeight().(types.Height), @@ -533,7 +469,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { ), }, nil, - types.NewParams(exported.Tendermint, exported.Localhost), + types.NewParams(exported.Tendermint), false, 5, ), diff --git a/modules/core/02-client/types/msgs.go b/modules/core/02-client/types/msgs.go index f1b8076c5b9..9d2d0f06d60 100644 --- a/modules/core/02-client/types/msgs.go +++ b/modules/core/02-client/types/msgs.go @@ -65,9 +65,6 @@ func (msg MsgCreateClient) ValidateBasic() error { if err := clientState.Validate(); err != nil { return err } - if clientState.ClientType() == exported.Localhost { - return sdkerrors.Wrap(ErrInvalidClient, "localhost client can only be created on chain initialization") - } consensusState, err := UnpackConsensusState(msg.ConsensusState) if err != nil { return err @@ -130,9 +127,6 @@ func (msg MsgUpdateClient) ValidateBasic() error { if err := header.ValidateBasic(); err != nil { return err } - if msg.ClientId == exported.Localhost { - return sdkerrors.Wrap(ErrInvalidClient, "localhost client is only updated on ABCI BeginBlock") - } return host.ClientIdentifierValidator(msg.ClientId) } diff --git a/modules/core/02-client/types/msgs_test.go b/modules/core/02-client/types/msgs_test.go index ce5ecd159cb..65dcb900ba0 100644 --- a/modules/core/02-client/types/msgs_test.go +++ b/modules/core/02-client/types/msgs_test.go @@ -9,7 +9,6 @@ import ( "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" solomachinetypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types" ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" ibctesting "github.com/cosmos/ibc-go/v3/testing" @@ -20,14 +19,16 @@ type TypesTestSuite struct { coordinator *ibctesting.Coordinator - chainA *ibctesting.TestChain - chainB *ibctesting.TestChain + chainA *ibctesting.TestChain + chainB *ibctesting.TestChain + solomachine *ibctesting.Solomachine } func (suite *TypesTestSuite) SetupTest() { suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1)) suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2)) + suite.solomachine = ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachinesingle", "testing", 1) } func TestTypesTestSuite(t *testing.T) { @@ -305,14 +306,6 @@ func (suite *TypesTestSuite) TestMsgUpdateClient_ValidateBasic() { }, false, }, - { - "unsupported - localhost", - func() { - msg, err = types.NewMsgUpdateClient(exported.Localhost, suite.chainA.CurrentTMClientHeader(), suite.chainA.SenderAccount.GetAddress().String()) - suite.Require().NoError(err) - }, - false, - }, } for _, tc := range cases { diff --git a/modules/core/exported/client.go b/modules/core/exported/client.go index 8bc64f9106c..225f668fdd7 100644 --- a/modules/core/exported/client.go +++ b/modules/core/exported/client.go @@ -19,10 +19,6 @@ const ( // Tendermint is used to indicate that the client uses the Tendermint Consensus Algorithm. Tendermint string = "07-tendermint" - // Localhost is the client type for a localhost client. It is also used as the clientID - // for the localhost client. - Localhost string = "09-localhost" - // Active is a status type of a client. An active client is allowed to be used. Active Status = "Active" diff --git a/modules/core/genesis.go b/modules/core/genesis.go index 9bc30a2d28f..47797440b39 100644 --- a/modules/core/genesis.go +++ b/modules/core/genesis.go @@ -12,7 +12,7 @@ import ( // InitGenesis initializes the ibc state from a provided genesis // state. -func InitGenesis(ctx sdk.Context, k keeper.Keeper, createLocalhost bool, gs *types.GenesisState) { +func InitGenesis(ctx sdk.Context, k keeper.Keeper, gs *types.GenesisState) { client.InitGenesis(ctx, k.ClientKeeper, gs.ClientGenesis) connection.InitGenesis(ctx, k.ConnectionKeeper, gs.ConnectionGenesis) channel.InitGenesis(ctx, k.ChannelKeeper, gs.ChannelGenesis) diff --git a/modules/core/genesis_test.go b/modules/core/genesis_test.go index aa921f7c19e..8ea981ff5fe 100644 --- a/modules/core/genesis_test.go +++ b/modules/core/genesis_test.go @@ -16,7 +16,6 @@ import ( "github.com/cosmos/ibc-go/v3/modules/core/exported" "github.com/cosmos/ibc-go/v3/modules/core/types" ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" - localhosttypes "github.com/cosmos/ibc-go/v3/modules/light-clients/09-localhost/types" ibctesting "github.com/cosmos/ibc-go/v3/testing" "github.com/cosmos/ibc-go/v3/testing/simapp" ) @@ -26,7 +25,6 @@ const ( clientID = "07-tendermint-0" connectionID2 = "connection-1" clientID2 = "07-tendermin-1" - localhostID = exported.Localhost + "-1" port1 = "firstport" port2 = "secondport" @@ -79,9 +77,6 @@ func (suite *IBCTestSuite) TestValidateGenesis() { clienttypes.NewIdentifiedClientState( clientID, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), - clienttypes.NewIdentifiedClientState( - localhostID, localhosttypes.NewClientState("chaindID", clientHeight), - ), }, []clienttypes.ClientConsensusStates{ clienttypes.NewClientConsensusStates( @@ -105,8 +100,8 @@ func (suite *IBCTestSuite) TestValidateGenesis() { }, ), }, - clienttypes.NewParams(exported.Tendermint, exported.Localhost), - true, + clienttypes.NewParams(exported.Tendermint), + false, 2, ), ConnectionGenesis: connectiontypes.NewGenesisState( @@ -159,9 +154,6 @@ func (suite *IBCTestSuite) TestValidateGenesis() { clienttypes.NewIdentifiedClientState( clientID, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), - clienttypes.NewIdentifiedClientState( - localhostID, localhosttypes.NewClientState("(chaindID)", clienttypes.ZeroHeight()), - ), }, nil, []clienttypes.IdentifiedGenesisMetadata{ @@ -243,9 +235,6 @@ func (suite *IBCTestSuite) TestInitGenesis() { clienttypes.NewIdentifiedClientState( clientID, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), - clienttypes.NewIdentifiedClientState( - exported.Localhost, localhosttypes.NewClientState("chaindID", clientHeight), - ), }, []clienttypes.ClientConsensusStates{ clienttypes.NewClientConsensusStates( @@ -269,8 +258,8 @@ func (suite *IBCTestSuite) TestInitGenesis() { }, ), }, - clienttypes.NewParams(exported.Tendermint, exported.Localhost), - true, + clienttypes.NewParams(exported.Tendermint), + false, 0, ), ConnectionGenesis: connectiontypes.NewGenesisState( @@ -320,7 +309,7 @@ func (suite *IBCTestSuite) TestInitGenesis() { app := simapp.Setup(false) suite.NotPanics(func() { - ibc.InitGenesis(app.BaseApp.NewContext(false, tmproto.Header{Height: 1}), *app.IBCKeeper, true, tc.genState) + ibc.InitGenesis(app.BaseApp.NewContext(false, tmproto.Header{Height: 1}), *app.IBCKeeper, tc.genState) }) } } @@ -355,7 +344,7 @@ func (suite *IBCTestSuite) TestExportGenesis() { // init genesis based on export suite.NotPanics(func() { - ibc.InitGenesis(suite.chainA.GetContext(), *suite.chainA.App.GetIBCKeeper(), true, gs) + ibc.InitGenesis(suite.chainA.GetContext(), *suite.chainA.App.GetIBCKeeper(), gs) }) suite.NotPanics(func() { @@ -366,7 +355,7 @@ func (suite *IBCTestSuite) TestExportGenesis() { // init genesis based on marshal and unmarshal suite.NotPanics(func() { - ibc.InitGenesis(suite.chainA.GetContext(), *suite.chainA.App.GetIBCKeeper(), true, gs) + ibc.InitGenesis(suite.chainA.GetContext(), *suite.chainA.App.GetIBCKeeper(), gs) }) }) } diff --git a/modules/core/module.go b/modules/core/module.go index 0cca3e37f1e..cab9fc8ab2e 100644 --- a/modules/core/module.go +++ b/modules/core/module.go @@ -93,9 +93,6 @@ func (AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) type AppModule struct { AppModuleBasic keeper *keeper.Keeper - - // create localhost by default - createLocalhost bool } // NewAppModule creates a new AppModule object @@ -149,7 +146,7 @@ func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, bz json.Ra if err != nil { panic(fmt.Sprintf("failed to unmarshal %s genesis state: %s", host.ModuleName, err)) } - InitGenesis(ctx, *am.keeper, am.createLocalhost, &gs) + InitGenesis(ctx, *am.keeper, &gs) return []abci.ValidatorUpdate{} } diff --git a/modules/core/types/codec.go b/modules/core/types/codec.go index 5caf105514e..8069c76715e 100644 --- a/modules/core/types/codec.go +++ b/modules/core/types/codec.go @@ -9,7 +9,6 @@ import ( commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" solomachinetypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types" ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" - localhosttypes "github.com/cosmos/ibc-go/v3/modules/light-clients/09-localhost/types" ) // RegisterInterfaces registers x/ibc interfaces into protobuf Any. @@ -19,6 +18,5 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) { channeltypes.RegisterInterfaces(registry) solomachinetypes.RegisterInterfaces(registry) ibctmtypes.RegisterInterfaces(registry) - localhosttypes.RegisterInterfaces(registry) commitmenttypes.RegisterInterfaces(registry) } diff --git a/modules/light-clients/09-localhost/doc.go b/modules/light-clients/09-localhost/doc.go deleted file mode 100644 index 40a0f060867..00000000000 --- a/modules/light-clients/09-localhost/doc.go +++ /dev/null @@ -1,5 +0,0 @@ -/* -Package localhost implements a concrete `ConsensusState`, `Header`, -`Misbehaviour` and `Equivocation` types for the loop-back client. -*/ -package localhost diff --git a/modules/light-clients/09-localhost/module.go b/modules/light-clients/09-localhost/module.go deleted file mode 100644 index e8d9eff55cf..00000000000 --- a/modules/light-clients/09-localhost/module.go +++ /dev/null @@ -1,10 +0,0 @@ -package localhost - -import ( - "github.com/cosmos/ibc-go/v3/modules/light-clients/09-localhost/types" -) - -// Name returns the IBC client name -func Name() string { - return types.SubModuleName -} diff --git a/modules/light-clients/09-localhost/types/client_state.go b/modules/light-clients/09-localhost/types/client_state.go deleted file mode 100644 index 02031551f27..00000000000 --- a/modules/light-clients/09-localhost/types/client_state.go +++ /dev/null @@ -1,375 +0,0 @@ -package types - -import ( - "bytes" - "encoding/binary" - "reflect" - "strings" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" -) - -var _ exported.ClientState = (*ClientState)(nil) - -// NewClientState creates a new ClientState instance -func NewClientState(chainID string, height clienttypes.Height) *ClientState { - return &ClientState{ - ChainId: chainID, - Height: height, - } -} - -// GetChainID returns an empty string -func (cs ClientState) GetChainID() string { - return cs.ChainId -} - -// ClientType is localhost. -func (cs ClientState) ClientType() string { - return exported.Localhost -} - -// GetLatestHeight returns the latest height stored. -func (cs ClientState) GetLatestHeight() exported.Height { - return cs.Height -} - -// Status always returns Active. The localhost status cannot be changed. -func (cs ClientState) Status(_ sdk.Context, _ sdk.KVStore, _ codec.BinaryCodec, -) exported.Status { - return exported.Active -} - -// Validate performs a basic validation of the client state fields. -func (cs ClientState) Validate() error { - if strings.TrimSpace(cs.ChainId) == "" { - return sdkerrors.Wrap(sdkerrors.ErrInvalidChainID, "chain id cannot be blank") - } - if cs.Height.RevisionHeight == 0 { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidHeight, "local revision height cannot be zero") - } - return nil -} - -// ZeroCustomFields returns the same client state since there are no custom fields in localhost -func (cs ClientState) ZeroCustomFields() exported.ClientState { - return &cs -} - -// Initialize ensures that initial consensus state for localhost is nil -func (cs ClientState) Initialize(_ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, consState exported.ConsensusState) error { - if consState != nil { - return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "initial consensus state for localhost must be nil.") - } - return nil -} - -// ExportMetadata is a no-op for localhost client -func (cs ClientState) ExportMetadata(_ sdk.KVStore) []exported.GenesisMetadata { - return nil -} - -// CheckHeaderAndUpdateState updates the localhost client. It only needs access to the context -func (cs *ClientState) CheckHeaderAndUpdateState( - ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, header exported.ClientMessage, -) (exported.ClientState, exported.ConsensusState, error) { - return cs.UpdateState(ctx, cdc, clientStore, header) -} - -// VerifyHeader is a no-op. -func (cs *ClientState) VerifyHeader( - _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientMessage, -) (exported.ClientState, exported.ConsensusState, error) { - return cs, nil, nil -} - -// CheckForMisbehaviour returns false. -func (cs *ClientState) CheckForMisbehaviour( - _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientMessage, -) (bool, error) { - return false, nil -} - -// UpdateState updates the localhost client. It only needs access to the context -func (cs *ClientState) UpdateState( - ctx sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientMessage, -) (exported.ClientState, exported.ConsensusState, error) { - // use the chain ID from context since the localhost client is from the running chain (i.e self). - cs.ChainId = ctx.ChainID() - revision := clienttypes.ParseChainID(cs.ChainId) - cs.Height = clienttypes.NewHeight(revision, uint64(ctx.BlockHeight())) - return cs, nil, nil -} - -// UpdateStateOnMisbehaviour -func (cs *ClientState) UpdateStateOnMisbehaviour( - _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore) { -} - -// CheckMisbehaviourAndUpdateState implements ClientState -// Since localhost is the client of the running chain, misbehaviour cannot be submitted to it -// Thus, CheckMisbehaviourAndUpdateState returns an error for localhost -func (cs ClientState) CheckMisbehaviourAndUpdateState( - _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientMessage, -) (exported.ClientState, error) { - return nil, sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "cannot submit misbehaviour to localhost client") -} - -// CheckSubstituteAndUpdateState returns an error. The localhost cannot be modified by -// proposals. -func (cs ClientState) CheckSubstituteAndUpdateState( - ctx sdk.Context, _ codec.BinaryCodec, _, _ sdk.KVStore, - _ exported.ClientState, -) (exported.ClientState, error) { - return nil, sdkerrors.Wrap(clienttypes.ErrUpdateClientFailed, "cannot update localhost client with a proposal") -} - -// VerifyUpgradeAndUpdateState returns an error since localhost cannot be upgraded -func (cs ClientState) VerifyUpgradeAndUpdateState( - _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, - _ exported.ClientState, _ exported.ConsensusState, _, _ []byte, -) error { - return sdkerrors.Wrap(clienttypes.ErrInvalidUpgradeClient, "cannot upgrade localhost client") -} - -// VerifyClientMessage -// TODO: localhost client will be removed -func (cs ClientState) VerifyClientMessage( - _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, - _ exported.ClientMessage, -) error { - return nil -} - -// VerifyClientState verifies that the localhost client state is stored locally -func (cs ClientState) VerifyClientState( - store sdk.KVStore, cdc codec.BinaryCodec, - _ exported.Height, _ exported.Prefix, _ string, _ []byte, clientState exported.ClientState, -) error { - path := host.KeyClientState - bz := store.Get([]byte(path)) - if bz == nil { - return sdkerrors.Wrapf(clienttypes.ErrFailedClientStateVerification, - "not found for path: %s", path) - } - - selfClient := clienttypes.MustUnmarshalClientState(cdc, bz) - - if !reflect.DeepEqual(selfClient, clientState) { - return sdkerrors.Wrapf(clienttypes.ErrFailedClientStateVerification, - "stored clientState != provided clientState: \n%v\n≠\n%v", - selfClient, clientState, - ) - } - return nil -} - -// VerifyClientConsensusState returns nil since a local host client does not store consensus -// states. -func (cs ClientState) VerifyClientConsensusState( - sdk.KVStore, codec.BinaryCodec, - exported.Height, string, exported.Height, exported.Prefix, - []byte, exported.ConsensusState, -) error { - return nil -} - -// VerifyConnectionState verifies a proof of the connection state of the -// specified connection end stored locally. -func (cs ClientState) VerifyConnectionState( - store sdk.KVStore, - cdc codec.BinaryCodec, - _ exported.Height, - _ exported.Prefix, - _ []byte, - connectionID string, - connectionEnd exported.ConnectionI, -) error { - path := host.ConnectionKey(connectionID) - bz := store.Get(path) - if bz == nil { - return sdkerrors.Wrapf(clienttypes.ErrFailedConnectionStateVerification, "not found for path %s", path) - } - - var prevConnection connectiontypes.ConnectionEnd - err := cdc.Unmarshal(bz, &prevConnection) - if err != nil { - return err - } - - if !reflect.DeepEqual(&prevConnection, connectionEnd) { - return sdkerrors.Wrapf( - clienttypes.ErrFailedConnectionStateVerification, - "connection end ≠ previous stored connection: \n%v\n≠\n%v", connectionEnd, prevConnection, - ) - } - - return nil -} - -// VerifyChannelState verifies a proof of the channel state of the specified -// channel end, under the specified port, stored on the local machine. -func (cs ClientState) VerifyChannelState( - store sdk.KVStore, - cdc codec.BinaryCodec, - _ exported.Height, - prefix exported.Prefix, - _ []byte, - portID, - channelID string, - channel exported.ChannelI, -) error { - path := host.ChannelKey(portID, channelID) - bz := store.Get(path) - if bz == nil { - return sdkerrors.Wrapf(clienttypes.ErrFailedChannelStateVerification, "not found for path %s", path) - } - - var prevChannel channeltypes.Channel - err := cdc.Unmarshal(bz, &prevChannel) - if err != nil { - return err - } - - if !reflect.DeepEqual(&prevChannel, channel) { - return sdkerrors.Wrapf( - clienttypes.ErrFailedChannelStateVerification, - "channel end ≠ previous stored channel: \n%v\n≠\n%v", channel, prevChannel, - ) - } - - return nil -} - -// VerifyPacketCommitment verifies a proof of an outgoing packet commitment at -// the specified port, specified channel, and specified sequence. -func (cs ClientState) VerifyPacketCommitment( - ctx sdk.Context, - store sdk.KVStore, - _ codec.BinaryCodec, - _ exported.Height, - _ uint64, - _ uint64, - _ exported.Prefix, - _ []byte, - portID, - channelID string, - sequence uint64, - commitmentBytes []byte, -) error { - path := host.PacketCommitmentKey(portID, channelID, sequence) - - data := store.Get(path) - if len(data) == 0 { - return sdkerrors.Wrapf(clienttypes.ErrFailedPacketCommitmentVerification, "not found for path %s", path) - } - - if !bytes.Equal(data, commitmentBytes) { - return sdkerrors.Wrapf( - clienttypes.ErrFailedPacketCommitmentVerification, - "commitment ≠ previous commitment: \n%X\n≠\n%X", commitmentBytes, data, - ) - } - - return nil -} - -// VerifyPacketAcknowledgement verifies a proof of an incoming packet -// acknowledgement at the specified port, specified channel, and specified sequence. -func (cs ClientState) VerifyPacketAcknowledgement( - ctx sdk.Context, - store sdk.KVStore, - _ codec.BinaryCodec, - _ exported.Height, - _ uint64, - _ uint64, - _ exported.Prefix, - _ []byte, - portID, - channelID string, - sequence uint64, - acknowledgement []byte, -) error { - path := host.PacketAcknowledgementKey(portID, channelID, sequence) - - data := store.Get(path) - if len(data) == 0 { - return sdkerrors.Wrapf(clienttypes.ErrFailedPacketAckVerification, "not found for path %s", path) - } - - if !bytes.Equal(data, acknowledgement) { - return sdkerrors.Wrapf( - clienttypes.ErrFailedPacketAckVerification, - "ak bytes ≠ previous ack: \n%X\n≠\n%X", acknowledgement, data, - ) - } - - return nil -} - -// VerifyPacketReceiptAbsence verifies a proof of the absence of an -// incoming packet receipt at the specified port, specified channel, and -// specified sequence. -func (cs ClientState) VerifyPacketReceiptAbsence( - ctx sdk.Context, - store sdk.KVStore, - _ codec.BinaryCodec, - _ exported.Height, - _ uint64, - _ uint64, - _ exported.Prefix, - _ []byte, - portID, - channelID string, - sequence uint64, -) error { - path := host.PacketReceiptKey(portID, channelID, sequence) - - data := store.Get(path) - if data != nil { - return sdkerrors.Wrap(clienttypes.ErrFailedPacketReceiptVerification, "expected no packet receipt") - } - - return nil -} - -// VerifyNextSequenceRecv verifies a proof of the next sequence number to be -// received of the specified channel at the specified port. -func (cs ClientState) VerifyNextSequenceRecv( - ctx sdk.Context, - store sdk.KVStore, - _ codec.BinaryCodec, - _ exported.Height, - _ uint64, - _ uint64, - _ exported.Prefix, - _ []byte, - portID, - channelID string, - nextSequenceRecv uint64, -) error { - path := host.NextSequenceRecvKey(portID, channelID) - - data := store.Get(path) - if len(data) == 0 { - return sdkerrors.Wrapf(clienttypes.ErrFailedNextSeqRecvVerification, "not found for path %s", path) - } - - prevSequenceRecv := binary.BigEndian.Uint64(data) - if prevSequenceRecv != nextSequenceRecv { - return sdkerrors.Wrapf( - clienttypes.ErrFailedNextSeqRecvVerification, - "next sequence receive ≠ previous stored sequence (%d ≠ %d)", nextSequenceRecv, prevSequenceRecv, - ) - } - - return nil -} diff --git a/modules/light-clients/09-localhost/types/client_state_test.go b/modules/light-clients/09-localhost/types/client_state_test.go deleted file mode 100644 index a54cc8efe9a..00000000000 --- a/modules/light-clients/09-localhost/types/client_state_test.go +++ /dev/null @@ -1,529 +0,0 @@ -package types_test - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" - "github.com/cosmos/ibc-go/v3/modules/light-clients/09-localhost/types" -) - -const ( - testConnectionID = "connectionid" - testPortID = "testportid" - testChannelID = "testchannelid" - testSequence = 1 -) - -func (suite *LocalhostTestSuite) TestStatus() { - clientState := types.NewClientState("chainID", clienttypes.NewHeight(3, 10)) - - // localhost should always return active - status := clientState.Status(suite.ctx, nil, nil) - suite.Require().Equal(exported.Active, status) -} - -func (suite *LocalhostTestSuite) TestValidate() { - testCases := []struct { - name string - clientState *types.ClientState - expPass bool - }{ - { - name: "valid client", - clientState: types.NewClientState("chainID", clienttypes.NewHeight(3, 10)), - expPass: true, - }, - { - name: "invalid chain id", - clientState: types.NewClientState(" ", clienttypes.NewHeight(3, 10)), - expPass: false, - }, - { - name: "invalid height", - clientState: types.NewClientState("chainID", clienttypes.ZeroHeight()), - expPass: false, - }, - } - - for _, tc := range testCases { - err := tc.clientState.Validate() - if tc.expPass { - suite.Require().NoError(err, tc.name) - } else { - suite.Require().Error(err, tc.name) - } - } -} - -func (suite *LocalhostTestSuite) TestInitialize() { - testCases := []struct { - name string - consState exported.ConsensusState - expPass bool - }{ - { - "valid initialization", - nil, - true, - }, - { - "invalid consenus state", - &ibctmtypes.ConsensusState{}, - false, - }, - } - - clientState := types.NewClientState("chainID", clienttypes.NewHeight(3, 10)) - - for _, tc := range testCases { - err := clientState.Initialize(suite.ctx, suite.cdc, suite.store, tc.consState) - - if tc.expPass { - suite.Require().NoError(err, "valid testcase: %s failed", tc.name) - } else { - suite.Require().Error(err, "invalid testcase: %s passed", tc.name) - } - } -} - -func (suite *LocalhostTestSuite) TestVerifyClientState() { - clientState := types.NewClientState("chainID", clientHeight) - invalidClient := types.NewClientState("chainID", clienttypes.NewHeight(0, 12)) - - testCases := []struct { - name string - clientState *types.ClientState - malleate func() - counterparty *types.ClientState - expPass bool - }{ - { - name: "proof verification success", - clientState: clientState, - malleate: func() { - bz := clienttypes.MustMarshalClientState(suite.cdc, clientState) - suite.store.Set(host.ClientStateKey(), bz) - }, - counterparty: clientState, - expPass: true, - }, - { - name: "proof verification failed: invalid client", - clientState: clientState, - malleate: func() { - bz := clienttypes.MustMarshalClientState(suite.cdc, clientState) - suite.store.Set(host.ClientStateKey(), bz) - }, - counterparty: invalidClient, - expPass: false, - }, - { - name: "proof verification failed: client not stored", - clientState: clientState, - malleate: func() {}, - counterparty: clientState, - expPass: false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() - tc.malleate() - - err := tc.clientState.VerifyClientState( - suite.store, suite.cdc, clienttypes.NewHeight(0, 10), nil, "", []byte{}, tc.counterparty, - ) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } - -} - -func (suite *LocalhostTestSuite) TestVerifyClientConsensusState() { - clientState := types.NewClientState("chainID", clientHeight) - err := clientState.VerifyClientConsensusState( - nil, nil, nil, "", nil, nil, nil, nil, - ) - suite.Require().NoError(err) -} - -func (suite *LocalhostTestSuite) TestCheckHeaderAndUpdateState() { - clientState := types.NewClientState("chainID", clientHeight) - cs, _, err := clientState.CheckHeaderAndUpdateState(suite.ctx, nil, nil, nil) - suite.Require().NoError(err) - suite.Require().Equal(uint64(0), cs.GetLatestHeight().GetRevisionNumber()) - suite.Require().Equal(suite.ctx.BlockHeight(), int64(cs.GetLatestHeight().GetRevisionHeight())) - suite.Require().Equal(suite.ctx.BlockHeader().ChainID, clientState.ChainId) -} - -func (suite *LocalhostTestSuite) TestMisbehaviourAndUpdateState() { - clientState := types.NewClientState("chainID", clientHeight) - cs, err := clientState.CheckMisbehaviourAndUpdateState(suite.ctx, nil, nil, nil) - suite.Require().Error(err) - suite.Require().Nil(cs) -} - -func (suite *LocalhostTestSuite) TestProposedHeaderAndUpdateState() { - clientState := types.NewClientState("chainID", clientHeight) - cs, err := clientState.CheckSubstituteAndUpdateState(suite.ctx, nil, nil, nil, nil) - suite.Require().Error(err) - suite.Require().Nil(cs) -} - -func (suite *LocalhostTestSuite) TestVerifyConnectionState() { - counterparty := connectiontypes.NewCounterparty("clientB", testConnectionID, commitmenttypes.NewMerklePrefix([]byte("ibc"))) - conn1 := connectiontypes.NewConnectionEnd(connectiontypes.OPEN, "clientA", counterparty, []*connectiontypes.Version{connectiontypes.NewVersion("1", nil)}, 0) - conn2 := connectiontypes.NewConnectionEnd(connectiontypes.OPEN, "clientA", counterparty, []*connectiontypes.Version{connectiontypes.NewVersion("2", nil)}, 0) - - testCases := []struct { - name string - clientState *types.ClientState - malleate func() - connection connectiontypes.ConnectionEnd - expPass bool - }{ - { - name: "proof verification success", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() { - bz, err := suite.cdc.Marshal(&conn1) - suite.Require().NoError(err) - suite.store.Set(host.ConnectionKey(testConnectionID), bz) - }, - connection: conn1, - expPass: true, - }, - { - name: "proof verification failed: connection not stored", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() {}, - connection: conn1, - expPass: false, - }, - { - name: "proof verification failed: unmarshal error", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() { - suite.store.Set(host.ConnectionKey(testConnectionID), []byte("connection")) - }, - connection: conn1, - expPass: false, - }, - { - name: "proof verification failed: different connection stored", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() { - bz, err := suite.cdc.Marshal(&conn2) - suite.Require().NoError(err) - suite.store.Set(host.ConnectionKey(testConnectionID), bz) - }, - connection: conn1, - expPass: false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() - tc.malleate() - - err := tc.clientState.VerifyConnectionState( - suite.store, suite.cdc, clientHeight, nil, []byte{}, testConnectionID, &tc.connection, - ) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} - -func (suite *LocalhostTestSuite) TestVerifyChannelState() { - counterparty := channeltypes.NewCounterparty(testPortID, testChannelID) - ch1 := channeltypes.NewChannel(channeltypes.OPEN, channeltypes.ORDERED, counterparty, []string{testConnectionID}, "1.0.0") - ch2 := channeltypes.NewChannel(channeltypes.OPEN, channeltypes.ORDERED, counterparty, []string{testConnectionID}, "2.0.0") - - testCases := []struct { - name string - clientState *types.ClientState - malleate func() - channel channeltypes.Channel - expPass bool - }{ - { - name: "proof verification success", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() { - bz, err := suite.cdc.Marshal(&ch1) - suite.Require().NoError(err) - suite.store.Set(host.ChannelKey(testPortID, testChannelID), bz) - }, - channel: ch1, - expPass: true, - }, - { - name: "proof verification failed: channel not stored", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() {}, - channel: ch1, - expPass: false, - }, - { - name: "proof verification failed: unmarshal failed", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() { - suite.store.Set(host.ChannelKey(testPortID, testChannelID), []byte("channel")) - - }, - channel: ch1, - expPass: false, - }, - { - name: "proof verification failed: different channel stored", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() { - bz, err := suite.cdc.Marshal(&ch2) - suite.Require().NoError(err) - suite.store.Set(host.ChannelKey(testPortID, testChannelID), bz) - - }, - channel: ch1, - expPass: false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() - tc.malleate() - - err := tc.clientState.VerifyChannelState( - suite.store, suite.cdc, clientHeight, nil, []byte{}, testPortID, testChannelID, &tc.channel, - ) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} - -func (suite *LocalhostTestSuite) TestVerifyPacketCommitment() { - testCases := []struct { - name string - clientState *types.ClientState - malleate func() - commitment []byte - expPass bool - }{ - { - name: "proof verification success", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() { - suite.store.Set( - host.PacketCommitmentKey(testPortID, testChannelID, testSequence), []byte("commitment"), - ) - }, - commitment: []byte("commitment"), - expPass: true, - }, - { - name: "proof verification failed: different commitment stored", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() { - suite.store.Set( - host.PacketCommitmentKey(testPortID, testChannelID, testSequence), []byte("different"), - ) - }, - commitment: []byte("commitment"), - expPass: false, - }, - { - name: "proof verification failed: no commitment stored", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() {}, - commitment: []byte{}, - expPass: false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() - tc.malleate() - - err := tc.clientState.VerifyPacketCommitment( - suite.ctx, suite.store, suite.cdc, clientHeight, 0, 0, nil, []byte{}, testPortID, testChannelID, testSequence, tc.commitment, - ) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} - -func (suite *LocalhostTestSuite) TestVerifyPacketAcknowledgement() { - testCases := []struct { - name string - clientState *types.ClientState - malleate func() - ack []byte - expPass bool - }{ - { - name: "proof verification success", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() { - suite.store.Set( - host.PacketAcknowledgementKey(testPortID, testChannelID, testSequence), []byte("acknowledgement"), - ) - }, - ack: []byte("acknowledgement"), - expPass: true, - }, - { - name: "proof verification failed: different ack stored", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() { - suite.store.Set( - host.PacketAcknowledgementKey(testPortID, testChannelID, testSequence), []byte("different"), - ) - }, - ack: []byte("acknowledgement"), - expPass: false, - }, - { - name: "proof verification failed: no commitment stored", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() {}, - ack: []byte{}, - expPass: false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() - tc.malleate() - - err := tc.clientState.VerifyPacketAcknowledgement( - suite.ctx, suite.store, suite.cdc, clientHeight, 0, 0, nil, []byte{}, testPortID, testChannelID, testSequence, tc.ack, - ) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} - -func (suite *LocalhostTestSuite) TestVerifyPacketReceiptAbsence() { - clientState := types.NewClientState("chainID", clientHeight) - - err := clientState.VerifyPacketReceiptAbsence( - suite.ctx, suite.store, suite.cdc, clientHeight, 0, 0, nil, nil, testPortID, testChannelID, testSequence, - ) - - suite.Require().NoError(err, "receipt absence failed") - - suite.store.Set(host.PacketReceiptKey(testPortID, testChannelID, testSequence), []byte("receipt")) - - err = clientState.VerifyPacketReceiptAbsence( - suite.ctx, suite.store, suite.cdc, clientHeight, 0, 0, nil, nil, testPortID, testChannelID, testSequence, - ) - suite.Require().Error(err, "receipt exists in store") -} - -func (suite *LocalhostTestSuite) TestVerifyNextSeqRecv() { - nextSeqRecv := uint64(5) - - testCases := []struct { - name string - clientState *types.ClientState - malleate func() - nextSeqRecv uint64 - expPass bool - }{ - { - name: "proof verification success", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() { - suite.store.Set( - host.NextSequenceRecvKey(testPortID, testChannelID), - sdk.Uint64ToBigEndian(nextSeqRecv), - ) - }, - nextSeqRecv: nextSeqRecv, - expPass: true, - }, - { - name: "proof verification failed: different nextSeqRecv stored", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() { - suite.store.Set( - host.NextSequenceRecvKey(testPortID, testChannelID), - sdk.Uint64ToBigEndian(3), - ) - }, - nextSeqRecv: nextSeqRecv, - expPass: false, - }, - { - name: "proof verification failed: no nextSeqRecv stored", - clientState: types.NewClientState("chainID", clientHeight), - malleate: func() {}, - nextSeqRecv: nextSeqRecv, - expPass: false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() - tc.malleate() - - err := tc.clientState.VerifyNextSequenceRecv( - suite.ctx, suite.store, suite.cdc, clientHeight, 0, 0, nil, []byte{}, testPortID, testChannelID, nextSeqRecv, - ) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} diff --git a/modules/light-clients/09-localhost/types/codec.go b/modules/light-clients/09-localhost/types/codec.go deleted file mode 100644 index a1f48114bd4..00000000000 --- a/modules/light-clients/09-localhost/types/codec.go +++ /dev/null @@ -1,16 +0,0 @@ -package types - -import ( - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - - "github.com/cosmos/ibc-go/v3/modules/core/exported" -) - -// RegisterInterfaces register the ibc interfaces submodule implementations to protobuf -// Any. -func RegisterInterfaces(registry codectypes.InterfaceRegistry) { - registry.RegisterImplementations( - (*exported.ClientState)(nil), - &ClientState{}, - ) -} diff --git a/modules/light-clients/09-localhost/types/errors.go b/modules/light-clients/09-localhost/types/errors.go deleted file mode 100644 index 57ad7c1f6a6..00000000000 --- a/modules/light-clients/09-localhost/types/errors.go +++ /dev/null @@ -1,10 +0,0 @@ -package types - -import ( - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" -) - -// Localhost sentinel errors -var ( - ErrConsensusStatesNotStored = sdkerrors.Register(SubModuleName, 2, "localhost does not store consensus states") -) diff --git a/modules/light-clients/09-localhost/types/keys.go b/modules/light-clients/09-localhost/types/keys.go deleted file mode 100644 index 2fe7c7e48f5..00000000000 --- a/modules/light-clients/09-localhost/types/keys.go +++ /dev/null @@ -1,6 +0,0 @@ -package types - -const ( - // SubModuleName for the localhost (loopback) client - SubModuleName = "localhost" -) diff --git a/modules/light-clients/09-localhost/types/localhost.pb.go b/modules/light-clients/09-localhost/types/localhost.pb.go deleted file mode 100644 index 60fecb51822..00000000000 --- a/modules/light-clients/09-localhost/types/localhost.pb.go +++ /dev/null @@ -1,369 +0,0 @@ -// Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: ibc/lightclients/localhost/v1/localhost.proto - -package types - -import ( - fmt "fmt" - types "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" - io "io" - math "math" - math_bits "math/bits" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package - -// ClientState defines a loopback (localhost) client. It requires (read-only) -// access to keys outside the client prefix. -type ClientState struct { - // self chain ID - ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty" yaml:"chain_id"` - // self latest block height - Height types.Height `protobuf:"bytes,2,opt,name=height,proto3" json:"height"` -} - -func (m *ClientState) Reset() { *m = ClientState{} } -func (m *ClientState) String() string { return proto.CompactTextString(m) } -func (*ClientState) ProtoMessage() {} -func (*ClientState) Descriptor() ([]byte, []int) { - return fileDescriptor_acd9f5b22d41bf6d, []int{0} -} -func (m *ClientState) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ClientState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ClientState.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 *ClientState) XXX_Merge(src proto.Message) { - xxx_messageInfo_ClientState.Merge(m, src) -} -func (m *ClientState) XXX_Size() int { - return m.Size() -} -func (m *ClientState) XXX_DiscardUnknown() { - xxx_messageInfo_ClientState.DiscardUnknown(m) -} - -var xxx_messageInfo_ClientState proto.InternalMessageInfo - -func init() { - proto.RegisterType((*ClientState)(nil), "ibc.lightclients.localhost.v1.ClientState") -} - -func init() { - proto.RegisterFile("ibc/lightclients/localhost/v1/localhost.proto", fileDescriptor_acd9f5b22d41bf6d) -} - -var fileDescriptor_acd9f5b22d41bf6d = []byte{ - // 288 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0xcd, 0x4c, 0x4a, 0xd6, - 0xcf, 0xc9, 0x4c, 0xcf, 0x28, 0x49, 0xce, 0xc9, 0x4c, 0xcd, 0x2b, 0x29, 0xd6, 0xcf, 0xc9, 0x4f, - 0x4e, 0xcc, 0xc9, 0xc8, 0x2f, 0x2e, 0xd1, 0x2f, 0x33, 0x44, 0x70, 0xf4, 0x0a, 0x8a, 0xf2, 0x4b, - 0xf2, 0x85, 0x64, 0x33, 0x93, 0x92, 0xf5, 0x90, 0x95, 0xeb, 0x21, 0x54, 0x94, 0x19, 0x4a, 0x89, - 0xa4, 0xe7, 0xa7, 0xe7, 0x83, 0x55, 0xea, 0x83, 0x58, 0x10, 0x4d, 0x52, 0xf2, 0x20, 0x3b, 0x92, - 0xf3, 0x8b, 0x52, 0xf5, 0x21, 0x9a, 0x40, 0x06, 0x43, 0x58, 0x10, 0x05, 0x4a, 0xb5, 0x5c, 0xdc, - 0xce, 0x60, 0x7e, 0x70, 0x49, 0x62, 0x49, 0xaa, 0x90, 0x1e, 0x17, 0x47, 0x72, 0x46, 0x62, 0x66, - 0x5e, 0x7c, 0x66, 0x8a, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0xa7, 0x93, 0xf0, 0xa7, 0x7b, 0xf2, 0xfc, - 0x95, 0x89, 0xb9, 0x39, 0x56, 0x4a, 0x30, 0x19, 0xa5, 0x20, 0x76, 0x30, 0xd3, 0x33, 0x45, 0xc8, - 0x82, 0x8b, 0x2d, 0x23, 0x15, 0xe4, 0x26, 0x09, 0x26, 0x05, 0x46, 0x0d, 0x6e, 0x23, 0x29, 0x3d, - 0x90, 0x2b, 0x41, 0x16, 0xea, 0x41, 0xad, 0x29, 0x33, 0xd4, 0xf3, 0x00, 0xab, 0x70, 0x62, 0x39, - 0x71, 0x4f, 0x9e, 0x21, 0x08, 0xaa, 0xde, 0x8a, 0xa5, 0x63, 0x81, 0x3c, 0x83, 0x53, 0xdc, 0x89, - 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, 0x1c, 0xc3, - 0x85, 0xc7, 0x72, 0x0c, 0x37, 0x1e, 0xcb, 0x31, 0x44, 0xb9, 0xa4, 0x67, 0x96, 0x64, 0x94, 0x26, - 0xe9, 0x25, 0xe7, 0xe7, 0xea, 0x27, 0xe7, 0x17, 0xe7, 0xe6, 0x17, 0xeb, 0x67, 0x26, 0x25, 0xeb, - 0xa6, 0xe7, 0xeb, 0x97, 0x19, 0xeb, 0xe7, 0xe6, 0xa7, 0x94, 0xe6, 0xa4, 0x16, 0x43, 0x42, 0x4f, - 0x17, 0x16, 0x7c, 0x06, 0x96, 0xba, 0x88, 0x10, 0x2c, 0xa9, 0x2c, 0x48, 0x2d, 0x4e, 0x62, 0x03, - 0xfb, 0xd2, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0xd1, 0xbf, 0x6a, 0xbf, 0x6c, 0x01, 0x00, 0x00, -} - -func (m *ClientState) 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 *ClientState) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *ClientState) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - { - size, err := m.Height.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintLocalhost(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 - if len(m.ChainId) > 0 { - i -= len(m.ChainId) - copy(dAtA[i:], m.ChainId) - i = encodeVarintLocalhost(dAtA, i, uint64(len(m.ChainId))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func encodeVarintLocalhost(dAtA []byte, offset int, v uint64) int { - offset -= sovLocalhost(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ - } - dAtA[offset] = uint8(v) - return base -} -func (m *ClientState) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.ChainId) - if l > 0 { - n += 1 + l + sovLocalhost(uint64(l)) - } - l = m.Height.Size() - n += 1 + l + sovLocalhost(uint64(l)) - return n -} - -func sovLocalhost(x uint64) (n int) { - return (math_bits.Len64(x|1) + 6) / 7 -} -func sozLocalhost(x uint64) (n int) { - return sovLocalhost(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (m *ClientState) 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 ErrIntOverflowLocalhost - } - 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: ClientState: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ClientState: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowLocalhost - } - 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 ErrInvalidLengthLocalhost - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthLocalhost - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ChainId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowLocalhost - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthLocalhost - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthLocalhost - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if err := m.Height.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipLocalhost(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthLocalhost - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func skipLocalhost(dAtA []byte) (n int, err error) { - l := len(dAtA) - iNdEx := 0 - depth := 0 - for iNdEx < l { - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowLocalhost - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - wireType := int(wire & 0x7) - switch wireType { - case 0: - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowLocalhost - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - iNdEx++ - if dAtA[iNdEx-1] < 0x80 { - break - } - } - case 1: - iNdEx += 8 - case 2: - var length int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowLocalhost - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - length |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if length < 0 { - return 0, ErrInvalidLengthLocalhost - } - iNdEx += length - case 3: - depth++ - case 4: - if depth == 0 { - return 0, ErrUnexpectedEndOfGroupLocalhost - } - depth-- - case 5: - iNdEx += 4 - default: - return 0, fmt.Errorf("proto: illegal wireType %d", wireType) - } - if iNdEx < 0 { - return 0, ErrInvalidLengthLocalhost - } - if depth == 0 { - return iNdEx, nil - } - } - return 0, io.ErrUnexpectedEOF -} - -var ( - ErrInvalidLengthLocalhost = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowLocalhost = fmt.Errorf("proto: integer overflow") - ErrUnexpectedEndOfGroupLocalhost = fmt.Errorf("proto: unexpected end of group") -) diff --git a/modules/light-clients/09-localhost/types/localhost_test.go b/modules/light-clients/09-localhost/types/localhost_test.go deleted file mode 100644 index ee8507c4efc..00000000000 --- a/modules/light-clients/09-localhost/types/localhost_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package types_test - -import ( - "testing" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/stretchr/testify/suite" - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - "github.com/cosmos/ibc-go/v3/testing/simapp" -) - -const ( - height = 4 -) - -var ( - clientHeight = clienttypes.NewHeight(0, 10) -) - -type LocalhostTestSuite struct { - suite.Suite - - cdc codec.Codec - ctx sdk.Context - store sdk.KVStore -} - -func (suite *LocalhostTestSuite) SetupTest() { - isCheckTx := false - app := simapp.Setup(isCheckTx) - - suite.cdc = app.AppCodec() - suite.ctx = app.BaseApp.NewContext(isCheckTx, tmproto.Header{Height: 1, ChainID: "ibc-chain"}) - suite.store = app.IBCKeeper.ClientKeeper.ClientStore(suite.ctx, exported.Localhost) -} - -func TestLocalhostTestSuite(t *testing.T) { - suite.Run(t, new(LocalhostTestSuite)) -} diff --git a/proto/ibc/lightclients/localhost/v1/localhost.proto b/proto/ibc/lightclients/localhost/v1/localhost.proto deleted file mode 100644 index 43056801542..00000000000 --- a/proto/ibc/lightclients/localhost/v1/localhost.proto +++ /dev/null @@ -1,18 +0,0 @@ -syntax = "proto3"; - -package ibc.lightclients.localhost.v1; - -option go_package = "github.com/cosmos/ibc-go/v3/modules/light-clients/09-localhost/types"; - -import "gogoproto/gogo.proto"; -import "ibc/core/client/v1/client.proto"; - -// ClientState defines a loopback (localhost) client. It requires (read-only) -// access to keys outside the client prefix. -message ClientState { - option (gogoproto.goproto_getters) = false; - // self chain ID - string chain_id = 1 [(gogoproto.moretags) = "yaml:\"chain_id\""]; - // self latest block height - ibc.core.client.v1.Height height = 2 [(gogoproto.nullable) = false]; -} From eb48e546cea26e94ac3c3137c4d814373e49bfac Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Thu, 31 Mar 2022 15:07:23 +0200 Subject: [PATCH 26/71] chore: adding UpdateState to ClientState interface (#1206) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * adding UpdateState to ClientState interface and updating surrounds * updating godoc * adding changelog entry * Update modules/core/exported/client.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * fixing typo in changelog Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> --- CHANGELOG.md | 1 + .../core/02-client/legacy/v100/solomachine.go | 5 +++++ modules/core/exported/client.go | 4 ++++ .../06-solomachine/types/update.go | 14 ++++++++++---- .../06-solomachine/types/update_test.go | 6 +----- .../light-clients/07-tendermint/types/update.go | 17 ++++++++++------- .../07-tendermint/types/update_test.go | 6 +----- 7 files changed, 32 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fb2e6ad9fb..c970fd44340 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (modules/core/02-client) [\#1196](https://github.com/cosmos/ibc-go/pull/1196) Adding VerifyClientMessage to ClientState interface. * (modules/core/02-client) [\#1198](https://github.com/cosmos/ibc-go/pull/1198) Adding UpdateStateOnMisbehaviour to ClientState interface. * (modules/core/02-client) [\#1170](https://github.com/cosmos/ibc-go/pull/1170) Updating `ClientUpdateProposal` to set client state in lightclient implementations `CheckSubstituteAndUpdateState` methods. +* (modules/core/exported) [\#1206](https://github.com/cosmos/ibc-go/pull/1206) Adding new method `UpdateState` to `ClientState` interface. ### Features diff --git a/modules/core/02-client/legacy/v100/solomachine.go b/modules/core/02-client/legacy/v100/solomachine.go index 24744c3fcbd..3696c89a458 100644 --- a/modules/core/02-client/legacy/v100/solomachine.go +++ b/modules/core/02-client/legacy/v100/solomachine.go @@ -102,6 +102,11 @@ func (cs *ClientState) VerifyClientMessage( panic("legacy solo machine is deprecated!") } +// UpdateState panis! +func (cs *ClientState) UpdateState(_ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientMessage) error { + panic("legacy solo machine is deprecated!") +} + // CheckHeaderAndUpdateState panics! func (cs *ClientState) CheckHeaderAndUpdateState( _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientMessage, diff --git a/modules/core/exported/client.go b/modules/core/exported/client.go index 225f668fdd7..9490e633984 100644 --- a/modules/core/exported/client.go +++ b/modules/core/exported/client.go @@ -58,6 +58,10 @@ type ClientState interface { // VerifyClientMessage verifies a ClientMessage. A ClientMessage could be a Header, Misbehaviour, or batch update. VerifyClientMessage(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg ClientMessage) error + // UpdateState updates and stores as necessary any associated information for an IBC client, such as the ClientState and corresponding ConsensusState. + // An error is returned if ClientMessage is of type Misbehaviour + UpdateState(sdk.Context, codec.BinaryCodec, sdk.KVStore, ClientMessage) error + // Update and Misbehaviour functions CheckHeaderAndUpdateState(sdk.Context, codec.BinaryCodec, sdk.KVStore, ClientMessage) (ClientState, ConsensusState, error) CheckMisbehaviourAndUpdateState(sdk.Context, codec.BinaryCodec, sdk.KVStore, ClientMessage) (ClientState, error) diff --git a/modules/light-clients/06-solomachine/types/update.go b/modules/light-clients/06-solomachine/types/update.go index 9379a622897..2bf3ea64f35 100644 --- a/modules/light-clients/06-solomachine/types/update.go +++ b/modules/light-clients/06-solomachine/types/update.go @@ -30,7 +30,13 @@ func (cs ClientState) CheckHeaderAndUpdateState( return &cs, cs.ConsensusState, nil } - return cs.UpdateState(ctx, cdc, clientStore, msg) + if err := cs.UpdateState(ctx, cdc, clientStore, msg); err != nil { + return nil, nil, err + } + + newClientState := clienttypes.MustUnmarshalClientState(cdc, clientStore.Get(host.ClientStateKey())).(*ClientState) + + return newClientState, newClientState.ConsensusState, nil } // VerifyClientMessage introspects the provided ClientMessage and checks its validity @@ -104,10 +110,10 @@ func (cs ClientState) verifyMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, } // UpdateState updates the consensus state to the new public key and an incremented sequence. -func (cs ClientState) UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg exported.ClientMessage) (exported.ClientState, exported.ConsensusState, error) { +func (cs ClientState) UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg exported.ClientMessage) error { smHeader, ok := clientMsg.(*Header) if !ok { - return nil, nil, sdkerrors.Wrapf(clienttypes.ErrInvalidClientType, "expected %T got %T", Header{}, clientMsg) + return sdkerrors.Wrapf(clienttypes.ErrInvalidClientType, "expected %T got %T", Header{}, clientMsg) } // create new solomachine ConsensusState @@ -122,7 +128,7 @@ func (cs ClientState) UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, client clientStore.Set(host.ClientStateKey(), clienttypes.MustMarshalClientState(cdc, &cs)) - return &cs, consensusState, nil + return nil } // CheckForMisbehaviour returns true for type Misbehaviour (passed VerifyClientMessage check), otherwise returns false diff --git a/modules/light-clients/06-solomachine/types/update_test.go b/modules/light-clients/06-solomachine/types/update_test.go index 53b6f014890..0078c4718df 100644 --- a/modules/light-clients/06-solomachine/types/update_test.go +++ b/modules/light-clients/06-solomachine/types/update_test.go @@ -612,11 +612,7 @@ func (suite *SoloMachineTestSuite) TestUpdateState() { suite.Run(tc.name, func() { tc.setup() // setup test - // TODO: remove casting when 'UpdateState' is an interface function. - clientState, ok := clientState.(*types.ClientState) - suite.Require().True(ok) - - _, _, err := clientState.UpdateState(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, clientMsg) + err := clientState.UpdateState(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, clientMsg) if tc.expPass { suite.Require().NoError(err) diff --git a/modules/light-clients/07-tendermint/types/update.go b/modules/light-clients/07-tendermint/types/update.go index ecd88312882..5b854730992 100644 --- a/modules/light-clients/07-tendermint/types/update.go +++ b/modules/light-clients/07-tendermint/types/update.go @@ -90,11 +90,14 @@ func (cs ClientState) CheckHeaderAndUpdateState( return &cs, consState, nil } - newClientState, consensusState, err := cs.UpdateState(ctx, cdc, clientStore, tmHeader) - if err != nil { + if err := cs.UpdateState(ctx, cdc, clientStore, tmHeader); err != nil { return nil, nil, err } - return newClientState, consensusState, nil + + newClientState := clienttypes.MustUnmarshalClientState(cdc, clientStore.Get(host.ClientStateKey())) + newConsensusState := clienttypes.MustUnmarshalConsensusState(cdc, clientStore.Get(host.ConsensusStateKey(header.GetHeight()))) + + return newClientState, newConsensusState, nil } // checkTrustedHeader checks that consensus state matches trusted fields of Header @@ -238,16 +241,16 @@ func (cs *ClientState) verifyHeader( // UpdateState must only be used to update within a single revision, thus header revision number and trusted height's revision // number must be the same. To update to a new revision, use a separate upgrade path // UpdateState will prune the oldest consensus state if it is expired. -func (cs ClientState) UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg exported.ClientMessage) (*ClientState, *ConsensusState, error) { +func (cs ClientState) UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg exported.ClientMessage) error { header, ok := clientMsg.(*Header) if !ok { - return nil, nil, sdkerrors.Wrapf(clienttypes.ErrInvalidClientType, "expected type %T, got %T", &Header{}, header) + return sdkerrors.Wrapf(clienttypes.ErrInvalidClientType, "expected type %T, got %T", &Header{}, header) } // check for duplicate update if consensusState, _ := GetConsensusState(clientStore, cdc, header.GetHeight()); consensusState != nil { // perform no-op - return &cs, consensusState, nil + return nil } cs.pruneOldestConsensusState(ctx, cdc, clientStore) @@ -267,7 +270,7 @@ func (cs ClientState) UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, client setConsensusState(clientStore, cdc, consensusState, header.GetHeight()) setConsensusMetadata(ctx, clientStore, header.GetHeight()) - return &cs, consensusState, nil + return nil } // pruneOldestConsensusState will retrieve the earliest consensus state for this clientID and check if it is expired. If it is, diff --git a/modules/light-clients/07-tendermint/types/update_test.go b/modules/light-clients/07-tendermint/types/update_test.go index 4f9648bd74a..bdc38e2486c 100644 --- a/modules/light-clients/07-tendermint/types/update_test.go +++ b/modules/light-clients/07-tendermint/types/update_test.go @@ -511,12 +511,8 @@ func (suite *TendermintTestSuite) TestUpdateState() { clientState := path.EndpointA.GetClientState() - // TODO: remove casting when 'UpdateState' is an interface function. - tmClientState, ok := clientState.(*types.ClientState) - suite.Require().True(ok) - clientStore = suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - _, _, err = tmClientState.UpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, clientMessage) + err = clientState.UpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, clientMessage) if tc.expPass { suite.Require().NoError(err) From d2be6d5aa6a5fc77c9f8c78f14417444860c4e3a Mon Sep 17 00:00:00 2001 From: Sean King Date: Thu, 31 Mar 2022 16:31:36 +0200 Subject: [PATCH 27/71] feat: adding CheckForMisbehaviour to ClientState interface (#1197) * feat: adding CheckForMisbehaviour to ClientState interface * fix: comment * nit: fix todo * chore: changelog --- CHANGELOG.md | 2 +- modules/core/02-client/legacy/v100/solomachine.go | 5 +++++ modules/core/exported/client.go | 3 +++ modules/light-clients/07-tendermint/types/update_test.go | 7 +------ 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c970fd44340..68e425a272d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,9 +48,9 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (modules/core/02-client) [\#1196](https://github.com/cosmos/ibc-go/pull/1196) Adding VerifyClientMessage to ClientState interface. * (modules/core/02-client) [\#1198](https://github.com/cosmos/ibc-go/pull/1198) Adding UpdateStateOnMisbehaviour to ClientState interface. * (modules/core/02-client) [\#1170](https://github.com/cosmos/ibc-go/pull/1170) Updating `ClientUpdateProposal` to set client state in lightclient implementations `CheckSubstituteAndUpdateState` methods. +* (modules/core/02-client) [\#1197](https://github.com/cosmos/ibc-go/pull/1197) Adding `CheckForMisbehaviour` to `ClientState` interface. * (modules/core/exported) [\#1206](https://github.com/cosmos/ibc-go/pull/1206) Adding new method `UpdateState` to `ClientState` interface. - ### Features ### Bug Fixes diff --git a/modules/core/02-client/legacy/v100/solomachine.go b/modules/core/02-client/legacy/v100/solomachine.go index 3696c89a458..2ef2cc19eac 100644 --- a/modules/core/02-client/legacy/v100/solomachine.go +++ b/modules/core/02-client/legacy/v100/solomachine.go @@ -88,6 +88,11 @@ func (cs ClientState) ExportMetadata(_ sdk.KVStore) []exported.GenesisMetadata { panic("legacy solo machine is deprecated!") } +// CheckForMisbehaviour panics! +func (cs ClientState) CheckForMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, msg exported.ClientMessage) bool { + panic("legacy solo machine is deprecated!") +} + // UpdateStateOnMisbehaviour panics! func (cs *ClientState) UpdateStateOnMisbehaviour( _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, diff --git a/modules/core/exported/client.go b/modules/core/exported/client.go index 9490e633984..643c254cbeb 100644 --- a/modules/core/exported/client.go +++ b/modules/core/exported/client.go @@ -49,6 +49,9 @@ type ClientState interface { // Clients must return their status. Only Active clients are allowed to process packets. Status(ctx sdk.Context, clientStore sdk.KVStore, cdc codec.BinaryCodec) Status + // Checks for evidence of a misbehaviour in Header or Misbehaviour type + CheckForMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, msg ClientMessage) bool + // Genesis function ExportMetadata(sdk.KVStore) []GenesisMetadata diff --git a/modules/light-clients/07-tendermint/types/update_test.go b/modules/light-clients/07-tendermint/types/update_test.go index bdc38e2486c..788ccdb2bad 100644 --- a/modules/light-clients/07-tendermint/types/update_test.go +++ b/modules/light-clients/07-tendermint/types/update_test.go @@ -741,14 +741,9 @@ func (suite *TendermintTestSuite) TestCheckForMisbehaviour() { tc.malleate() clientState := path.EndpointA.GetClientState() - - // TODO: remove casting when 'UpdateState' is an interface function. - tmClientState, ok := clientState.(*types.ClientState) - suite.Require().True(ok) - clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - foundMisbehaviour := tmClientState.CheckForMisbehaviour( + foundMisbehaviour := clientState.CheckForMisbehaviour( suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, // pass in clientID prefixed clientStore From e2f37b8ea65e88d789d1909ec02ffe3420336bd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?colin=20axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Fri, 1 Apr 2022 12:51:01 +0200 Subject: [PATCH 28/71] Replace CheckHeaderAndUpdateState with new ClientState functions (#1208) * refactor: replace CheckHeaderAndUpdateState with VerifyClientMessage, CheckForMisbehaviour, UpdateStateOnMisbehaviour, and UpdateState * add changelog entry * fix tests --- CHANGELOG.md | 2 + modules/core/02-client/keeper/client.go | 80 ++++++++----------- modules/core/02-client/keeper/events.go | 10 +-- .../core/02-client/legacy/v100/solomachine.go | 2 +- modules/core/exported/client.go | 4 +- .../06-solomachine/types/update.go | 4 +- .../06-solomachine/types/update_test.go | 2 +- .../07-tendermint/types/update.go | 2 +- .../07-tendermint/types/update_test.go | 6 +- 9 files changed, 49 insertions(+), 63 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68e425a272d..b7beb37f789 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,8 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### State Machine Breaking ### Improvements + +* (02-client) [\#1208](https://github.com/cosmos/ibc-go/pull/1208) Replace `CheckHeaderAndUpdateState` usage in 02-client with calls to `VerifyClientMessage`, `CheckForMisbehaviour`, `UpdateStateOnMisbehaviour` and `UpdateState`. * (modules/light-clients/09-localhost) [\#1187](https://github.com/cosmos/ibc-go/pull/1187/) Removing localhost light client implementation as it is not functional. * [\#1186](https://github.com/cosmos/ibc-go/pull/1186/files) Removing `GetRoot` function from ConsensusState interface in `02-client`. `GetRoot` is unused by core IBC. * (modules/core/02-client) [\#1196](https://github.com/cosmos/ibc-go/pull/1196) Adding VerifyClientMessage to ClientState interface. diff --git a/modules/core/02-client/keeper/client.go b/modules/core/02-client/keeper/client.go index fa68f896665..eb1fc87e354 100644 --- a/modules/core/02-client/keeper/client.go +++ b/modules/core/02-client/keeper/client.go @@ -54,7 +54,7 @@ func (k Keeper) CreateClient( } // UpdateClient updates the consensus state and the state root from a provided header. -func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.ClientMessage) error { +func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, clientMsg exported.ClientMessage) error { clientState, found := k.GetClientState(ctx, clientID) if !found { return sdkerrors.Wrapf(types.ErrClientNotFound, "cannot update client with ID %s", clientID) @@ -66,54 +66,21 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.C return sdkerrors.Wrapf(types.ErrClientNotActive, "cannot update client (%s) with status %s", clientID, status) } - // Any writes made in CheckHeaderAndUpdateState are persisted on both valid updates and misbehaviour updates. - // Light client implementations are responsible for writing the correct metadata (if any) in either case. - newClientState, newConsensusState, err := clientState.CheckHeaderAndUpdateState(ctx, k.cdc, clientStore, header) - if err != nil { - return sdkerrors.Wrapf(err, "cannot update client with ID %s", clientID) - } - - // emit the full header in events - var ( - headerStr string - consensusHeight exported.Height - ) - if header != nil { - // Marshal the Header as an Any and encode the resulting bytes to hex. - // This prevents the event value from containing invalid UTF-8 characters - // which may cause data to be lost when JSON encoding/decoding. - headerStr = hex.EncodeToString(types.MustMarshalClientMessage(k.cdc, header)) - // set default consensus height with header height - consensusHeight = header.GetHeight() - + if err := clientState.VerifyClientMessage(ctx, k.cdc, clientStore, clientMsg); err != nil { + return err } - // set new client state regardless of if update is valid update or misbehaviour - k.SetClientState(ctx, clientID, newClientState) - // If client state is not frozen after clientState CheckHeaderAndUpdateState, - // then update was valid. Write the update state changes, and set new consensus state. - // Else the update was proof of misbehaviour and we must emit appropriate misbehaviour events. - if status := newClientState.Status(ctx, clientStore, k.cdc); status != exported.Frozen { - // if update is not misbehaviour then update the consensus state - k.SetClientConsensusState(ctx, clientID, header.GetHeight(), newConsensusState) - - k.Logger(ctx).Info("client state updated", "client-id", clientID, "height", consensusHeight.String()) + // Marshal the ClientMessage as an Any and encode the resulting bytes to hex. + // This prevents the event value from containing invalid UTF-8 characters + // which may cause data to be lost when JSON encoding/decoding. + clientMsgStr := hex.EncodeToString(types.MustMarshalClientMessage(k.cdc, clientMsg)) - defer func() { - telemetry.IncrCounterWithLabels( - []string{"ibc", "client", "update"}, - 1, - []metrics.Label{ - telemetry.NewLabel(types.LabelClientType, clientState.ClientType()), - telemetry.NewLabel(types.LabelClientID, clientID), - telemetry.NewLabel(types.LabelUpdateType, "msg"), - }, - ) - }() + // set default consensus height with header height + consensusHeight := clientMsg.GetHeight() - // emitting events in the keeper emits for both begin block and handler client updates - EmitUpdateClientEvent(ctx, clientID, newClientState, consensusHeight, headerStr) - } else { + foundMisbehaviour := clientState.CheckForMisbehaviour(ctx, k.cdc, clientStore, clientMsg) + if foundMisbehaviour { + clientState.UpdateStateOnMisbehaviour(ctx, k.cdc, clientStore, clientMsg) k.Logger(ctx).Info("client frozen due to misbehaviour", "client-id", clientID) @@ -129,9 +96,30 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.C ) }() - EmitSubmitMisbehaviourEventOnUpdate(ctx, clientID, newClientState, consensusHeight, headerStr) + EmitSubmitMisbehaviourEventOnUpdate(ctx, clientID, clientState.ClientType(), consensusHeight, clientMsgStr) + + return nil } + clientState.UpdateState(ctx, k.cdc, clientStore, clientMsg) + + k.Logger(ctx).Info("client state updated", "client-id", clientID, "height", consensusHeight.String()) + + defer func() { + telemetry.IncrCounterWithLabels( + []string{"ibc", "client", "update"}, + 1, + []metrics.Label{ + telemetry.NewLabel(types.LabelClientType, clientState.ClientType()), + telemetry.NewLabel(types.LabelClientID, clientID), + telemetry.NewLabel(types.LabelUpdateType, "msg"), + }, + ) + }() + + // emitting events in the keeper emits for both begin block and handler client updates + EmitUpdateClientEvent(ctx, clientID, clientState.ClientType(), consensusHeight, clientMsgStr) + return nil } diff --git a/modules/core/02-client/keeper/events.go b/modules/core/02-client/keeper/events.go index ff8ae1c3acd..4e2f38941a0 100644 --- a/modules/core/02-client/keeper/events.go +++ b/modules/core/02-client/keeper/events.go @@ -24,14 +24,14 @@ func EmitCreateClientEvent(ctx sdk.Context, clientID string, clientState exporte } // EmitUpdateClientEvent emits an update client event -func EmitUpdateClientEvent(ctx sdk.Context, clientID string, clientState exported.ClientState, consensusHeight exported.Height, headerStr string) { +func EmitUpdateClientEvent(ctx sdk.Context, clientID string, clientType string, consensusHeight exported.Height, clientMsgStr string) { ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( types.EventTypeUpdateClient, sdk.NewAttribute(types.AttributeKeyClientID, clientID), - sdk.NewAttribute(types.AttributeKeyClientType, clientState.ClientType()), + sdk.NewAttribute(types.AttributeKeyClientType, clientType), sdk.NewAttribute(types.AttributeKeyConsensusHeight, consensusHeight.String()), - sdk.NewAttribute(types.AttributeKeyHeader, headerStr), + sdk.NewAttribute(types.AttributeKeyHeader, clientMsgStr), ), sdk.NewEvent( sdk.EventTypeMessage, @@ -80,12 +80,12 @@ func EmitSubmitMisbehaviourEvent(ctx sdk.Context, clientID string, clientState e } // EmitSubmitMisbehaviourEventOnUpdate emits a client misbehaviour event on a client update event -func EmitSubmitMisbehaviourEventOnUpdate(ctx sdk.Context, clientID string, clientState exported.ClientState, consensusHeight exported.Height, headerStr string) { +func EmitSubmitMisbehaviourEventOnUpdate(ctx sdk.Context, clientID string, clientType string, consensusHeight exported.Height, headerStr string) { ctx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypeSubmitMisbehaviour, sdk.NewAttribute(types.AttributeKeyClientID, clientID), - sdk.NewAttribute(types.AttributeKeyClientType, clientState.ClientType()), + sdk.NewAttribute(types.AttributeKeyClientType, clientType), sdk.NewAttribute(types.AttributeKeyConsensusHeight, consensusHeight.String()), sdk.NewAttribute(types.AttributeKeyHeader, headerStr), ), diff --git a/modules/core/02-client/legacy/v100/solomachine.go b/modules/core/02-client/legacy/v100/solomachine.go index 2ef2cc19eac..19e5459206c 100644 --- a/modules/core/02-client/legacy/v100/solomachine.go +++ b/modules/core/02-client/legacy/v100/solomachine.go @@ -95,7 +95,7 @@ func (cs ClientState) CheckForMisbehaviour(ctx sdk.Context, cdc codec.BinaryCode // UpdateStateOnMisbehaviour panics! func (cs *ClientState) UpdateStateOnMisbehaviour( - _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, + _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientMessage, ) { panic("legacy solo machine is deprecated!") } diff --git a/modules/core/exported/client.go b/modules/core/exported/client.go index 643c254cbeb..5fa9b8b4ca2 100644 --- a/modules/core/exported/client.go +++ b/modules/core/exported/client.go @@ -56,12 +56,12 @@ type ClientState interface { ExportMetadata(sdk.KVStore) []GenesisMetadata // UpdateStateOnMisbehaviour should perform appropriate state changes on a client state given that misbehaviour has been detected and verified - UpdateStateOnMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore) + UpdateStateOnMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg ClientMessage) // VerifyClientMessage verifies a ClientMessage. A ClientMessage could be a Header, Misbehaviour, or batch update. VerifyClientMessage(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg ClientMessage) error - // UpdateState updates and stores as necessary any associated information for an IBC client, such as the ClientState and corresponding ConsensusState. + // UpdateState updates and stores as necessary any associated information for an IBC client, such as the ClientState and corresponding ConsensusState. // An error is returned if ClientMessage is of type Misbehaviour UpdateState(sdk.Context, codec.BinaryCodec, sdk.KVStore, ClientMessage) error diff --git a/modules/light-clients/06-solomachine/types/update.go b/modules/light-clients/06-solomachine/types/update.go index 2bf3ea64f35..49dc6ebddfb 100644 --- a/modules/light-clients/06-solomachine/types/update.go +++ b/modules/light-clients/06-solomachine/types/update.go @@ -26,7 +26,7 @@ func (cs ClientState) CheckHeaderAndUpdateState( foundMisbehaviour := cs.CheckForMisbehaviour(ctx, cdc, clientStore, msg) if foundMisbehaviour { - cs.UpdateStateOnMisbehaviour(ctx, cdc, clientStore) + cs.UpdateStateOnMisbehaviour(ctx, cdc, clientStore, msg) return &cs, cs.ConsensusState, nil } @@ -142,7 +142,7 @@ func (cs ClientState) CheckForMisbehaviour(_ sdk.Context, _ codec.BinaryCodec, _ // UpdateStateOnMisbehaviour updates state upon misbehaviour. This method should only be called on misbehaviour // as it does not perform any misbehaviour checks. -func (cs ClientState) UpdateStateOnMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore) { +func (cs ClientState) UpdateStateOnMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, _ exported.ClientMessage) { cs.IsFrozen = true clientStore.Set(host.ClientStateKey(), clienttypes.MustMarshalClientState(cdc, &cs)) diff --git a/modules/light-clients/06-solomachine/types/update_test.go b/modules/light-clients/06-solomachine/types/update_test.go index 0078c4718df..e4d33e2e7f6 100644 --- a/modules/light-clients/06-solomachine/types/update_test.go +++ b/modules/light-clients/06-solomachine/types/update_test.go @@ -708,7 +708,7 @@ func (suite *SoloMachineTestSuite) TestUpdateStateOnMisbehaviour() { tc.malleate() - clientState.UpdateStateOnMisbehaviour(suite.chainA.GetContext(), suite.chainA.Codec, suite.store) + clientState.UpdateStateOnMisbehaviour(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, nil) if tc.expPass { clientStateBz := suite.store.Get(host.ClientStateKey()) diff --git a/modules/light-clients/07-tendermint/types/update.go b/modules/light-clients/07-tendermint/types/update.go index 5b854730992..6fe7c6c5910 100644 --- a/modules/light-clients/07-tendermint/types/update.go +++ b/modules/light-clients/07-tendermint/types/update.go @@ -358,7 +358,7 @@ func (cs ClientState) CheckForMisbehaviour(ctx sdk.Context, cdc codec.BinaryCode // UpdateStateOnMisbehaviour updates state upon misbehaviour, freezing the ClientState. This method should only be called when misbehaviour is detected // as it does not perform any misbehaviour checks. -func (cs ClientState) UpdateStateOnMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore) { +func (cs ClientState) UpdateStateOnMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, _ exported.ClientMessage) { cs.FrozenHeight = FrozenHeight clientStore.Set(host.ClientStateKey(), clienttypes.MustMarshalClientState(cdc, &cs)) diff --git a/modules/light-clients/07-tendermint/types/update_test.go b/modules/light-clients/07-tendermint/types/update_test.go index 788ccdb2bad..b903319a0e7 100644 --- a/modules/light-clients/07-tendermint/types/update_test.go +++ b/modules/light-clients/07-tendermint/types/update_test.go @@ -792,11 +792,7 @@ func (suite *TendermintTestSuite) TestUpdateStateOnMisbehaviour() { clientState := path.EndpointA.GetClientState() clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - // TODO: remove casting when 'UpdateState' is an interface function. - tmClientState, ok := clientState.(*types.ClientState) - suite.Require().True(ok) - - tmClientState.UpdateStateOnMisbehaviour(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore) + clientState.UpdateStateOnMisbehaviour(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, nil) if tc.expPass { clientStateBz := clientStore.Get(host.ClientStateKey()) From 8a9978c7fb016f55eb52e439eae279c7217f47c7 Mon Sep 17 00:00:00 2001 From: Sean King Date: Mon, 4 Apr 2022 15:57:05 +0200 Subject: [PATCH 29/71] refactor: removing CheckHeaderAndUpdateState from ClientState (#1210) * refactor: removing CheckHeaderAndUpdateState from ClientState interface & light client implementations * chore: fix changelog --- CHANGELOG.md | 3 +- modules/core/exported/client.go | 1 - .../06-solomachine/types/update.go | 29 --- .../06-solomachine/types/update_test.go | 171 -------------- .../07-tendermint/types/update.go | 84 ------- .../07-tendermint/types/update_test.go | 215 +++++------------- 6 files changed, 63 insertions(+), 440 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7beb37f789..ef8f4ae43c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,13 +44,14 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Improvements -* (02-client) [\#1208](https://github.com/cosmos/ibc-go/pull/1208) Replace `CheckHeaderAndUpdateState` usage in 02-client with calls to `VerifyClientMessage`, `CheckForMisbehaviour`, `UpdateStateOnMisbehaviour` and `UpdateState`. +* (modules/core/02-client) [\#1208](https://github.com/cosmos/ibc-go/pull/1208) Replace `CheckHeaderAndUpdateState` usage in 02-client with calls to `VerifyClientMessage`, `CheckForMisbehaviour`, `UpdateStateOnMisbehaviour` and `UpdateState`. * (modules/light-clients/09-localhost) [\#1187](https://github.com/cosmos/ibc-go/pull/1187/) Removing localhost light client implementation as it is not functional. * [\#1186](https://github.com/cosmos/ibc-go/pull/1186/files) Removing `GetRoot` function from ConsensusState interface in `02-client`. `GetRoot` is unused by core IBC. * (modules/core/02-client) [\#1196](https://github.com/cosmos/ibc-go/pull/1196) Adding VerifyClientMessage to ClientState interface. * (modules/core/02-client) [\#1198](https://github.com/cosmos/ibc-go/pull/1198) Adding UpdateStateOnMisbehaviour to ClientState interface. * (modules/core/02-client) [\#1170](https://github.com/cosmos/ibc-go/pull/1170) Updating `ClientUpdateProposal` to set client state in lightclient implementations `CheckSubstituteAndUpdateState` methods. * (modules/core/02-client) [\#1197](https://github.com/cosmos/ibc-go/pull/1197) Adding `CheckForMisbehaviour` to `ClientState` interface. +* (modules/core/02-client) [\#1195](https://github.com/cosmos/ibc-go/pull/1210) Removing `CheckHeaderAndUpdateState` from `ClientState` interface & associated light client implementations. * (modules/core/exported) [\#1206](https://github.com/cosmos/ibc-go/pull/1206) Adding new method `UpdateState` to `ClientState` interface. ### Features diff --git a/modules/core/exported/client.go b/modules/core/exported/client.go index 5fa9b8b4ca2..de69fb5b1f6 100644 --- a/modules/core/exported/client.go +++ b/modules/core/exported/client.go @@ -66,7 +66,6 @@ type ClientState interface { UpdateState(sdk.Context, codec.BinaryCodec, sdk.KVStore, ClientMessage) error // Update and Misbehaviour functions - CheckHeaderAndUpdateState(sdk.Context, codec.BinaryCodec, sdk.KVStore, ClientMessage) (ClientState, ConsensusState, error) CheckMisbehaviourAndUpdateState(sdk.Context, codec.BinaryCodec, sdk.KVStore, ClientMessage) (ClientState, error) CheckSubstituteAndUpdateState(ctx sdk.Context, cdc codec.BinaryCodec, subjectClientStore, substituteClientStore sdk.KVStore, substituteClient ClientState) (ClientState, error) diff --git a/modules/light-clients/06-solomachine/types/update.go b/modules/light-clients/06-solomachine/types/update.go index 49dc6ebddfb..0fc5c76544f 100644 --- a/modules/light-clients/06-solomachine/types/update.go +++ b/modules/light-clients/06-solomachine/types/update.go @@ -10,35 +10,6 @@ import ( "github.com/cosmos/ibc-go/v3/modules/core/exported" ) -// CheckHeaderAndUpdateState checks if the provided header is valid and updates -// the consensus state if appropriate. It returns an error if: -// - the header provided is not parseable to a solo machine header -// - the header sequence does not match the current sequence -// - the header timestamp is less than the consensus state timestamp -// - the currently registered public key did not provide the update signature -func (cs ClientState) CheckHeaderAndUpdateState( - ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, - msg exported.ClientMessage, -) (exported.ClientState, exported.ConsensusState, error) { - if err := cs.VerifyClientMessage(ctx, cdc, clientStore, msg); err != nil { - return nil, nil, err - } - - foundMisbehaviour := cs.CheckForMisbehaviour(ctx, cdc, clientStore, msg) - if foundMisbehaviour { - cs.UpdateStateOnMisbehaviour(ctx, cdc, clientStore, msg) - return &cs, cs.ConsensusState, nil - } - - if err := cs.UpdateState(ctx, cdc, clientStore, msg); err != nil { - return nil, nil, err - } - - newClientState := clienttypes.MustUnmarshalClientState(cdc, clientStore.Get(host.ClientStateKey())).(*ClientState) - - return newClientState, newClientState.ConsensusState, nil -} - // VerifyClientMessage introspects the provided ClientMessage and checks its validity // A Solomachine Header is considered valid if the currently registered public key has signed over the new public key with the correct sequence // A Solomachine Misbehaviour is considered valid if duplicate signatures of the current public key are found on two different messages at a given sequence diff --git a/modules/light-clients/06-solomachine/types/update_test.go b/modules/light-clients/06-solomachine/types/update_test.go index e4d33e2e7f6..7e3acaf0637 100644 --- a/modules/light-clients/06-solomachine/types/update_test.go +++ b/modules/light-clients/06-solomachine/types/update_test.go @@ -12,177 +12,6 @@ import ( ibctesting "github.com/cosmos/ibc-go/v3/testing" ) -func (suite *SoloMachineTestSuite) TestCheckHeaderAndUpdateState() { - var ( - clientState exported.ClientState - header exported.ClientMessage - ) - - // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - testCases := []struct { - name string - setup func() - expPass bool - }{ - { - "successful update", - func() { - clientState = solomachine.ClientState() - header = solomachine.CreateHeader() - }, - true, - }, - { - "wrong client state type", - func() { - clientState = &ibctmtypes.ClientState{} - header = solomachine.CreateHeader() - }, - false, - }, - { - "invalid header type", - func() { - clientState = solomachine.ClientState() - header = &ibctmtypes.Header{} - }, - false, - }, - { - "wrong sequence in header", - func() { - clientState = solomachine.ClientState() - // store in temp before assigning to interface type - h := solomachine.CreateHeader() - h.Sequence++ - header = h - }, - false, - }, - { - "invalid header Signature", - func() { - clientState = solomachine.ClientState() - h := solomachine.CreateHeader() - h.Signature = suite.GetInvalidProof() - header = h - }, false, - }, - { - "invalid timestamp in header", - func() { - clientState = solomachine.ClientState() - h := solomachine.CreateHeader() - h.Timestamp-- - header = h - }, false, - }, - { - "signature uses wrong sequence", - func() { - clientState = solomachine.ClientState() - solomachine.Sequence++ - header = solomachine.CreateHeader() - }, - false, - }, - { - "signature uses new pubkey to sign", - func() { - // store in temp before assinging to interface type - cs := solomachine.ClientState() - h := solomachine.CreateHeader() - - publicKey, err := codectypes.NewAnyWithValue(solomachine.PublicKey) - suite.NoError(err) - - data := &types.HeaderData{ - NewPubKey: publicKey, - NewDiversifier: h.NewDiversifier, - } - - dataBz, err := suite.chainA.Codec.Marshal(data) - suite.Require().NoError(err) - - // generate invalid signature - signBytes := &types.SignBytes{ - Sequence: cs.Sequence, - Timestamp: solomachine.Time, - Diversifier: solomachine.Diversifier, - DataType: types.CLIENT, - Data: dataBz, - } - - signBz, err := suite.chainA.Codec.Marshal(signBytes) - suite.Require().NoError(err) - - sig := solomachine.GenerateSignature(signBz) - suite.Require().NoError(err) - h.Signature = sig - - clientState = cs - header = h - - }, - false, - }, - { - "signature signs over old pubkey", - func() { - // store in temp before assinging to interface type - cs := solomachine.ClientState() - oldPubKey := solomachine.PublicKey - h := solomachine.CreateHeader() - - // generate invalid signature - data := append(sdk.Uint64ToBigEndian(cs.Sequence), oldPubKey.Bytes()...) - sig := solomachine.GenerateSignature(data) - h.Signature = sig - - clientState = cs - header = h - }, - false, - }, - { - "consensus state public key is nil", - func() { - cs := solomachine.ClientState() - cs.ConsensusState.PublicKey = nil - clientState = cs - header = solomachine.CreateHeader() - }, - false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - // setup test - tc.setup() - - clientState, consensusState, err := clientState.CheckHeaderAndUpdateState(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, header) - - if tc.expPass { - suite.Require().NoError(err) - suite.Require().Equal(header.(*types.Header).NewPublicKey, clientState.(*types.ClientState).ConsensusState.PublicKey) - suite.Require().Equal(false, clientState.(*types.ClientState).IsFrozen) - suite.Require().Equal(header.(*types.Header).Sequence+1, clientState.(*types.ClientState).Sequence) - suite.Require().Equal(consensusState, clientState.(*types.ClientState).ConsensusState) - } else { - suite.Require().Error(err) - suite.Require().Nil(clientState) - suite.Require().Nil(consensusState) - } - }) - } - } -} - func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { var ( clientMsg exported.ClientMessage diff --git a/modules/light-clients/07-tendermint/types/update.go b/modules/light-clients/07-tendermint/types/update.go index 6fe7c6c5910..18849096bba 100644 --- a/modules/light-clients/07-tendermint/types/update.go +++ b/modules/light-clients/07-tendermint/types/update.go @@ -16,90 +16,6 @@ import ( "github.com/cosmos/ibc-go/v3/modules/core/exported" ) -// CheckHeaderAndUpdateState checks if the provided header is valid, and if valid it will: -// create the consensus state for the header.Height -// and update the client state if the header height is greater than the latest client state height -// It returns an error if: -// - the client or header provided are not parseable to tendermint types -// - the header is invalid -// - header height is less than or equal to the trusted header height -// - header revision is not equal to trusted header revision -// - header valset commit verification fails -// - header timestamp is past the trusting period in relation to the consensus state -// - header timestamp is less than or equal to the consensus state timestamp -// -// Tendermint client validity checking uses the bisection algorithm described -// in the [Tendermint spec](https://github.com/tendermint/spec/blob/master/spec/consensus/light-client.md). -// -// Misbehaviour Detection: -// UpdateClient will detect implicit misbehaviour by enforcing certain invariants on any new update call and will return a frozen client. -// 1. Any valid update that creates a different consensus state for an already existing height is evidence of misbehaviour and will freeze client. -// 2. Any valid update that breaks time monotonicity with respect to its neighboring consensus states is evidence of misbehaviour and will freeze client. -// Misbehaviour sets frozen height to {0, 1} since it is only used as a boolean value (zero or non-zero). -// -func (cs ClientState) CheckHeaderAndUpdateState( - ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, - header exported.ClientMessage, -) (exported.ClientState, exported.ConsensusState, error) { - tmHeader, ok := header.(*Header) - if !ok { - return nil, nil, sdkerrors.Wrapf( - clienttypes.ErrInvalidHeader, "expected type %T, got %T", &Header{}, header, - ) - } - - // Check if the Client store already has a consensus state for the header's height - // If the consensus state exists, and it matches the header then we return early - // since header has already been submitted in a previous UpdateClient. - var conflictingHeader bool - prevConsState, _ := GetConsensusState(clientStore, cdc, header.GetHeight()) - if prevConsState != nil { - // This header has already been submitted and the necessary state is already stored - // in client store, thus we can return early without further validation. - if reflect.DeepEqual(prevConsState, tmHeader.ConsensusState()) { - return &cs, prevConsState, nil - } - // A consensus state already exists for this height, but it does not match the provided header. - // Thus, we must check that this header is valid, and if so we will freeze the client. - conflictingHeader = true - } - - if err := cs.VerifyClientMessage(ctx, cdc, clientStore, tmHeader); err != nil { - return nil, nil, err - } - - consState := tmHeader.ConsensusState() - // Header is different from existing consensus state and also valid, so freeze the client and return - if conflictingHeader { - cs.FrozenHeight = FrozenHeight - return &cs, consState, nil - } - // Check that consensus state timestamps are monotonic - prevCons, prevOk := GetPreviousConsensusState(clientStore, cdc, header.GetHeight()) - nextCons, nextOk := GetNextConsensusState(clientStore, cdc, header.GetHeight()) - // if previous consensus state exists, check consensus state time is greater than previous consensus state time - // if previous consensus state is not before current consensus state, freeze the client and return. - if prevOk && !prevCons.Timestamp.Before(consState.Timestamp) { - cs.FrozenHeight = FrozenHeight - return &cs, consState, nil - } - // if next consensus state exists, check consensus state time is less than next consensus state time - // if next consensus state is not after current consensus state, freeze the client and return. - if nextOk && !nextCons.Timestamp.After(consState.Timestamp) { - cs.FrozenHeight = FrozenHeight - return &cs, consState, nil - } - - if err := cs.UpdateState(ctx, cdc, clientStore, tmHeader); err != nil { - return nil, nil, err - } - - newClientState := clienttypes.MustUnmarshalClientState(cdc, clientStore.Get(host.ClientStateKey())) - newConsensusState := clienttypes.MustUnmarshalConsensusState(cdc, clientStore.Get(host.ConsensusStateKey(header.GetHeight()))) - - return newClientState, newConsensusState, nil -} - // checkTrustedHeader checks that consensus state matches trusted fields of Header func checkTrustedHeader(header *Header, consState *ConsensusState) error { tmTrustedValidators, err := tmtypes.ValidatorSetFromProto(header.TrustedValidators) diff --git a/modules/light-clients/07-tendermint/types/update_test.go b/modules/light-clients/07-tendermint/types/update_test.go index b903319a0e7..022e6d1ba36 100644 --- a/modules/light-clients/07-tendermint/types/update_test.go +++ b/modules/light-clients/07-tendermint/types/update_test.go @@ -1,7 +1,6 @@ package types_test import ( - "fmt" "time" sdk "github.com/cosmos/cosmos-sdk/types" @@ -16,159 +15,6 @@ import ( tmtypes "github.com/tendermint/tendermint/types" ) -func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { - var ( - clientState *types.ClientState - consensusState *types.ConsensusState - consStateHeight clienttypes.Height - newHeader *types.Header - currentTime time.Time - bothValSet *tmtypes.ValidatorSet - bothSigners map[string]tmtypes.PrivValidator - ) - - // Setup different validators and signers for testing different types of updates - altPrivVal := ibctestingmock.NewPV() - altPubKey, err := altPrivVal.GetPubKey() - suite.Require().NoError(err) - - revisionHeight := int64(height.RevisionHeight) - - // create modified heights to use for test-cases - heightPlus1 := clienttypes.NewHeight(height.RevisionNumber, height.RevisionHeight+1) - // heightPlus5 := clienttypes.NewHeight(height.RevisionNumber, height.RevisionHeight+5) - // heightMinus1 := clienttypes.NewHeight(height.RevisionNumber, height.RevisionHeight-1) - heightMinus3 := clienttypes.NewHeight(height.RevisionNumber, height.RevisionHeight-3) - altVal := tmtypes.NewValidator(altPubKey, revisionHeight) - // Create alternative validator set with only altVal, invalid update (too much change in valSet) - // altValSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{altVal}) - // altSigners := getAltSigners(altVal, altPrivVal) - - testCases := []struct { - name string - setup func(*TendermintTestSuite) - expFrozen bool - expPass bool - }{ - { - name: "successful update for a previous revision", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainIDRevision1, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - consStateHeight = heightMinus3 - newHeader = suite.chainA.CreateTMClientHeader(chainIDRevision0, int64(height.RevisionHeight), heightMinus3, suite.headerTime, bothValSet, bothValSet, suite.valSet, bothSigners) - currentTime = suite.now - }, - expPass: true, - }, - { - name: "successful update with identical header to a previous update", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, heightPlus1, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) - currentTime = suite.now - ctx := suite.chainA.GetContext().WithBlockTime(currentTime) - // Store the header's consensus state in client store before UpdateClient call - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(ctx, clientID, heightPlus1, newHeader.ConsensusState()) - }, - expFrozen: false, - expPass: true, - }, - { - name: "unsuccessful update to a future revision", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainIDRevision0, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainIDRevision1, 1, height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) - currentTime = suite.now - }, - expPass: false, - }, - { - name: "unsuccessful update: header height revision and trusted height revision mismatch", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainIDRevision1, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(1, 1), commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainIDRevision1, 3, height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) - currentTime = suite.now - }, - expFrozen: false, - expPass: false, - }, - { - name: "unsuccessful update: trusting period has passed since last client timestamp", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) - // make current time pass trusting period from last timestamp on clientstate - currentTime = suite.now.Add(trustingPeriod) - }, - expFrozen: false, - expPass: false, - }, - } - - for i, tc := range testCases { - tc := tc - suite.Run(fmt.Sprintf("Case: %s", tc.name), func() { - suite.SetupTest() // reset metadata writes - // Create bothValSet with both suite validator and altVal. Would be valid update - bothValSet, bothSigners = getBothSigners(suite, altVal, altPrivVal) - - consStateHeight = height // must be explicitly changed - // setup test - tc.setup(suite) - - // Set current timestamp in context - ctx := suite.chainA.GetContext().WithBlockTime(currentTime) - - // Set trusted consensus state in client store - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(ctx, clientID, consStateHeight, consensusState) - - height := newHeader.GetHeight() - expectedConsensus := &types.ConsensusState{ - Timestamp: newHeader.GetTime(), - Root: commitmenttypes.NewMerkleRoot(newHeader.Header.GetAppHash()), - NextValidatorsHash: newHeader.Header.NextValidatorsHash, - } - - newClientState, consensusState, err := clientState.CheckHeaderAndUpdateState( - ctx, - suite.cdc, - suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), clientID), // pass in clientID prefixed clientStore - newHeader, - ) - - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - - suite.Require().Equal(tc.expFrozen, !newClientState.(*types.ClientState).FrozenHeight.IsZero(), "client state status is unexpected after update") - - // further writes only happen if update is not misbehaviour - if !tc.expFrozen { - // Determine if clientState should be updated or not - // TODO: check the entire Height struct once GetLatestHeight returns clienttypes.Height - if height.GT(clientState.LatestHeight) { - // Header Height is greater than clientState latest Height, clientState should be updated with header.GetHeight() - suite.Require().Equal(height, newClientState.GetLatestHeight(), "clientstate height did not update") - } else { - // Update will add past consensus state, clientState should not be updated at all - suite.Require().Equal(clientState.LatestHeight, newClientState.GetLatestHeight(), "client state height updated for past header") - } - - suite.Require().Equal(expectedConsensus, consensusState, "valid test case %d failed: %s", i, tc.name) - } - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - suite.Require().Nil(newClientState, "invalid test case %d passed: %s", i, tc.name) - suite.Require().Nil(consensusState, "invalid test case %d passed: %s", i, tc.name) - } - }) - } -} - func (suite *TendermintTestSuite) TestVerifyHeader() { var ( path *ibctesting.Path @@ -362,6 +208,67 @@ func (suite *TendermintTestSuite) TestVerifyHeader() { }, expPass: false, }, + // TODO: add revision tests after helper function to upgrade chain/client + /* + { + name: "successful update for a previous revision", + setup: func(suite *TendermintTestSuite) { + clientState = types.NewClientState(chainIDRevision1, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) + consStateHeight = heightMinus3 + newHeader = suite.chainA.CreateTMClientHeader(chainIDRevision0, int64(height.RevisionHeight), heightMinus3, suite.headerTime, bothValSet, bothValSet, suite.valSet, bothSigners) + currentTime = suite.now + }, + expPass: true, + }, + { + name: "successful update with identical header to a previous update", + setup: func(suite *TendermintTestSuite) { + clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, heightPlus1, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) + currentTime = suite.now + ctx := suite.chainA.GetContext().WithBlockTime(currentTime) + // Store the header's consensus state in client store before UpdateClient call + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(ctx, clientID, heightPlus1, newHeader.ConsensusState()) + }, + expFrozen: false, + expPass: true, + }, + { + name: "unsuccessful update to a future revision", + setup: func(suite *TendermintTestSuite) { + clientState = types.NewClientState(chainIDRevision0, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) + newHeader = suite.chainA.CreateTMClientHeader(chainIDRevision1, 1, height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) + currentTime = suite.now + }, + expPass: false, + }, + { + name: "unsuccessful update: header height revision and trusted height revision mismatch", + setup: func(suite *TendermintTestSuite) { + clientState = types.NewClientState(chainIDRevision1, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(1, 1), commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) + newHeader = suite.chainA.CreateTMClientHeader(chainIDRevision1, 3, height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) + currentTime = suite.now + }, + expFrozen: false, + expPass: false, + }, + { + name: "unsuccessful update: trusting period has passed since last client timestamp", + setup: func(suite *TendermintTestSuite) { + clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) + // make current time pass trusting period from last timestamp on clientstate + currentTime = suite.now.Add(trustingPeriod) + }, + expFrozen: false, + expPass: false, + }, + */ } for _, tc := range testCases { From c43af667aef35a3dffadc8af2f6fa1d4263d7312 Mon Sep 17 00:00:00 2001 From: Sean King Date: Mon, 4 Apr 2022 16:30:28 +0200 Subject: [PATCH 30/71] refactor: routing MsgSubmitMisbehaviour to UpdateClient keeper fn (#1188) * refactor: routing MsgSubmitMisbehaviour to UpdateClient keeper fn * chore: updating changelog and comment for deprecated notice * fix: channel proto file linter issue + adding deprecated flag to MsgSubmitMisbehavior * chore: fix changelog --- CHANGELOG.md | 2 + docs/ibc/proto-docs.md | 13 +- modules/core/02-client/types/tx.pb.go | 86 ++++++------ modules/core/04-channel/types/tx.pb.go | 174 ++++++++++++------------- modules/core/keeper/msg_server.go | 6 +- proto/ibc/core/channel/v1/tx.proto | 6 +- proto/ibc/core/client/v1/tx.proto | 7 +- 7 files changed, 151 insertions(+), 143 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef8f4ae43c1..e1a1568ee2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,8 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### State Machine Breaking ### Improvements +* (modules/core/02-client) [\#1188](https://github.com/cosmos/ibc-go/pull/1188/files) Routing `MsgSubmitMisbehaviour` to `UpdateClient` keeper function. Deprecating `SubmitMisbehaviour` endpoint. + * (modules/core/02-client) [\#1208](https://github.com/cosmos/ibc-go/pull/1208) Replace `CheckHeaderAndUpdateState` usage in 02-client with calls to `VerifyClientMessage`, `CheckForMisbehaviour`, `UpdateStateOnMisbehaviour` and `UpdateState`. * (modules/light-clients/09-localhost) [\#1187](https://github.com/cosmos/ibc-go/pull/1187/) Removing localhost light client implementation as it is not functional. diff --git a/docs/ibc/proto-docs.md b/docs/ibc/proto-docs.md index 25819cf8270..cb9fc7a1e67 100644 --- a/docs/ibc/proto-docs.md +++ b/docs/ibc/proto-docs.md @@ -2045,9 +2045,9 @@ ResponseResultType defines the possible outcomes of the execution of a message | Name | Number | Description | | ---- | ------ | ----------- | -| RESPONSE_RESULT_UNSPECIFIED | 0 | Default zero value enumeration | -| RESPONSE_RESULT_NOOP | 1 | The message did not call the IBC application callbacks (because, for example, the packet had already been relayed) | -| RESPONSE_RESULT_SUCCESS | 2 | The message was executed successfully | +| RESPONSE_RESULT_TYPE_UNSPECIFIED | 0 | Default zero value enumeration | +| RESPONSE_RESULT_TYPE_NOOP | 1 | The message did not call the IBC application callbacks (because, for example, the packet had already been relayed) | +| RESPONSE_RESULT_TYPE_SUCCESS | 2 | The message was executed successfully | @@ -2472,13 +2472,14 @@ MsgCreateClientResponse defines the Msg/CreateClient response type. ### MsgSubmitMisbehaviour MsgSubmitMisbehaviour defines an sdk.Msg type that submits Evidence for light client misbehaviour. +Warning: DEPRECATED | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `client_id` | [string](#string) | | client unique identifier | -| `misbehaviour` | [google.protobuf.Any](#google.protobuf.Any) | | misbehaviour used for freezing the light client | -| `signer` | [string](#string) | | signer address | +| `client_id` | [string](#string) | | **Deprecated.** client unique identifier | +| `misbehaviour` | [google.protobuf.Any](#google.protobuf.Any) | | **Deprecated.** misbehaviour used for freezing the light client | +| `signer` | [string](#string) | | **Deprecated.** signer address | diff --git a/modules/core/02-client/types/tx.pb.go b/modules/core/02-client/types/tx.pb.go index a9eec55a353..97bf12ae73f 100644 --- a/modules/core/02-client/types/tx.pb.go +++ b/modules/core/02-client/types/tx.pb.go @@ -281,13 +281,14 @@ var xxx_messageInfo_MsgUpgradeClientResponse proto.InternalMessageInfo // MsgSubmitMisbehaviour defines an sdk.Msg type that submits Evidence for // light client misbehaviour. +// Warning: DEPRECATED type MsgSubmitMisbehaviour struct { // client unique identifier - ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` + ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` // Deprecated: Do not use. // misbehaviour used for freezing the light client - Misbehaviour *types.Any `protobuf:"bytes,2,opt,name=misbehaviour,proto3" json:"misbehaviour,omitempty"` + Misbehaviour *types.Any `protobuf:"bytes,2,opt,name=misbehaviour,proto3" json:"misbehaviour,omitempty"` // Deprecated: Do not use. // signer address - Signer string `protobuf:"bytes,3,opt,name=signer,proto3" json:"signer,omitempty"` + Signer string `protobuf:"bytes,3,opt,name=signer,proto3" json:"signer,omitempty"` // Deprecated: Do not use. } func (m *MsgSubmitMisbehaviour) Reset() { *m = MsgSubmitMisbehaviour{} } @@ -375,45 +376,46 @@ func init() { func init() { proto.RegisterFile("ibc/core/client/v1/tx.proto", fileDescriptor_cb5dc4651eb49a04) } var fileDescriptor_cb5dc4651eb49a04 = []byte{ - // 601 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0x3f, 0x6f, 0xd3, 0x4e, - 0x18, 0x8e, 0x9b, 0xdf, 0x2f, 0x6a, 0xae, 0x81, 0x56, 0x26, 0xb4, 0xa9, 0xab, 0xda, 0x91, 0xe9, - 0x10, 0x44, 0x7b, 0x47, 0xd2, 0xa5, 0xea, 0x46, 0x3a, 0x31, 0x44, 0x02, 0x57, 0x0c, 0xb0, 0x04, - 0xff, 0xb9, 0x5e, 0x4e, 0xc4, 0xbe, 0xc8, 0x67, 0x47, 0xe4, 0x1b, 0x30, 0x32, 0xf0, 0x01, 0x2a, - 0x06, 0x3e, 0x0b, 0x63, 0x07, 0x06, 0xa6, 0xa8, 0x4a, 0x16, 0xe6, 0x7c, 0x02, 0x14, 0x9f, 0x13, - 0x62, 0x37, 0x8e, 0x22, 0xfe, 0x6c, 0x3e, 0xbf, 0xcf, 0x3d, 0xcf, 0xfb, 0xf8, 0x79, 0xcf, 0x07, - 0x0e, 0xa8, 0x65, 0x23, 0x9b, 0xf9, 0x18, 0xd9, 0x5d, 0x8a, 0xbd, 0x00, 0xf5, 0xeb, 0x28, 0x78, - 0x0f, 0x7b, 0x3e, 0x0b, 0x98, 0x2c, 0x53, 0xcb, 0x86, 0xd3, 0x22, 0x14, 0x45, 0xd8, 0xaf, 0x2b, - 0x65, 0xc2, 0x08, 0x8b, 0xca, 0x68, 0xfa, 0x24, 0x90, 0xca, 0x3e, 0x61, 0x8c, 0x74, 0x31, 0x8a, - 0x56, 0x56, 0x78, 0x85, 0x4c, 0x6f, 0x20, 0x4a, 0xfa, 0xad, 0x04, 0xb6, 0x5b, 0x9c, 0x5c, 0xf8, - 0xd8, 0x0c, 0xf0, 0x45, 0xc4, 0x23, 0xbf, 0x00, 0x25, 0xc1, 0xd8, 0xe6, 0x81, 0x19, 0xe0, 0x8a, - 0x54, 0x95, 0x6a, 0x5b, 0x8d, 0x32, 0x14, 0x2c, 0x70, 0xc6, 0x02, 0x9f, 0x79, 0x83, 0xe6, 0xde, - 0x64, 0xa8, 0x3d, 0x18, 0x98, 0x6e, 0xf7, 0x5c, 0x5f, 0xdc, 0xa3, 0x1b, 0x5b, 0x62, 0x79, 0x39, - 0x5d, 0xc9, 0xaf, 0xc1, 0xb6, 0xcd, 0x3c, 0x8e, 0x3d, 0x1e, 0xf2, 0x98, 0x74, 0x63, 0x05, 0xa9, - 0x32, 0x19, 0x6a, 0xbb, 0x31, 0x69, 0x72, 0x9b, 0x6e, 0xdc, 0x9f, 0xbf, 0x11, 0xd4, 0xbb, 0xa0, - 0xc0, 0x29, 0xf1, 0xb0, 0x5f, 0xc9, 0x57, 0xa5, 0x5a, 0xd1, 0x88, 0x57, 0xe7, 0x9b, 0x1f, 0xae, - 0xb5, 0xdc, 0x8f, 0x6b, 0x2d, 0xa7, 0xef, 0x83, 0xbd, 0x94, 0x43, 0x03, 0xf3, 0xde, 0x94, 0x45, - 0xff, 0x24, 0xdc, 0xbf, 0xea, 0x39, 0xbf, 0xdc, 0xd7, 0x41, 0x31, 0x76, 0x42, 0x9d, 0xc8, 0x7a, - 0xb1, 0x59, 0x9e, 0x0c, 0xb5, 0x9d, 0x84, 0x49, 0xea, 0xe8, 0xc6, 0xa6, 0x78, 0x7e, 0xee, 0xc8, - 0xc7, 0xa0, 0xd0, 0xc1, 0xa6, 0x83, 0xfd, 0x55, 0xae, 0x8c, 0x18, 0xb3, 0x76, 0xc7, 0x8b, 0x5d, - 0xcd, 0x3b, 0xfe, 0x96, 0x07, 0x3b, 0x51, 0x8d, 0xf8, 0xa6, 0xf3, 0x07, 0x2d, 0xa7, 0x33, 0xde, - 0xf8, 0x17, 0x19, 0xe7, 0xff, 0x52, 0xc6, 0x2f, 0x41, 0xb9, 0xe7, 0x33, 0x76, 0xd5, 0x0e, 0x85, - 0xed, 0xb6, 0xd0, 0xad, 0xfc, 0x57, 0x95, 0x6a, 0xa5, 0xa6, 0x36, 0x19, 0x6a, 0x07, 0x82, 0x69, - 0x19, 0x4a, 0x37, 0xe4, 0xe8, 0x75, 0xf2, 0x93, 0xbd, 0x03, 0x87, 0x29, 0x70, 0xaa, 0xf7, 0xff, - 0x23, 0xee, 0xda, 0x64, 0xa8, 0x1d, 0x2d, 0xe5, 0x4e, 0xf7, 0xac, 0x24, 0x44, 0xb2, 0x66, 0xb4, - 0x90, 0x91, 0xb8, 0x02, 0x2a, 0xe9, 0x54, 0xe7, 0x91, 0x7f, 0x91, 0xc0, 0xc3, 0x16, 0x27, 0x97, - 0xa1, 0xe5, 0xd2, 0xa0, 0x45, 0xb9, 0x85, 0x3b, 0x66, 0x9f, 0xb2, 0xd0, 0xff, 0x9d, 0xdc, 0xcf, - 0x40, 0xc9, 0x5d, 0xa0, 0x58, 0x39, 0xb0, 0x09, 0xe4, 0x1a, 0x63, 0xab, 0x81, 0xc3, 0xa5, 0x7d, - 0xce, 0x9c, 0x34, 0x3e, 0xe7, 0x41, 0xbe, 0xc5, 0x89, 0xfc, 0x16, 0x94, 0x12, 0x3f, 0x9c, 0x47, - 0xf0, 0xee, 0xaf, 0x0c, 0xa6, 0xce, 0xac, 0xf2, 0x64, 0x0d, 0xd0, 0x4c, 0x69, 0xaa, 0x90, 0x38, - 0xd4, 0x59, 0x0a, 0x8b, 0xa0, 0x4c, 0x85, 0x65, 0x07, 0x51, 0xb6, 0xc1, 0xbd, 0xe4, 0x44, 0x1d, - 0x65, 0xee, 0x5e, 0x40, 0x29, 0xc7, 0xeb, 0xa0, 0xe6, 0x22, 0x3e, 0x90, 0x97, 0xc4, 0xfe, 0x38, - 0x83, 0xe3, 0x2e, 0x54, 0xa9, 0xaf, 0x0d, 0x9d, 0x69, 0x36, 0x8d, 0xaf, 0x23, 0x55, 0xba, 0x19, - 0xa9, 0xd2, 0xed, 0x48, 0x95, 0x3e, 0x8e, 0xd5, 0xdc, 0xcd, 0x58, 0xcd, 0x7d, 0x1f, 0xab, 0xb9, - 0x37, 0x67, 0x84, 0x06, 0x9d, 0xd0, 0x82, 0x36, 0x73, 0x91, 0xcd, 0xb8, 0xcb, 0x38, 0xa2, 0x96, - 0x7d, 0x42, 0x18, 0xea, 0x9f, 0x22, 0x97, 0x39, 0x61, 0x17, 0x73, 0x71, 0x5b, 0x3d, 0x6d, 0x9c, - 0xc4, 0x17, 0x56, 0x30, 0xe8, 0x61, 0x6e, 0x15, 0xa2, 0xb9, 0x3a, 0xfd, 0x19, 0x00, 0x00, 0xff, - 0xff, 0x64, 0xe7, 0xce, 0xe6, 0xd0, 0x06, 0x00, 0x00, + // 617 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0xbf, 0x6e, 0xd3, 0x40, + 0x1c, 0x8e, 0x1b, 0x88, 0xda, 0x6b, 0xa0, 0x95, 0x09, 0xa9, 0xeb, 0xaa, 0x76, 0x64, 0x3a, 0x04, + 0xd1, 0xda, 0x24, 0x59, 0x50, 0xc5, 0x42, 0x3a, 0x31, 0x44, 0x02, 0x57, 0x0c, 0xb0, 0x04, 0xff, + 0xb9, 0x5e, 0x4e, 0xc4, 0xbe, 0xc8, 0x67, 0x47, 0xe4, 0x0d, 0x18, 0x19, 0x78, 0x80, 0x8a, 0x27, + 0xe0, 0x31, 0x18, 0x3b, 0x30, 0x30, 0x45, 0x55, 0xb2, 0x30, 0xe7, 0x09, 0x50, 0x7c, 0x4e, 0xb0, + 0x1d, 0x3b, 0x8a, 0x04, 0x6c, 0x3e, 0xff, 0xbe, 0xfb, 0xbe, 0xdf, 0xe7, 0xef, 0x77, 0x3e, 0x70, + 0x84, 0x4d, 0x4b, 0xb3, 0x88, 0x07, 0x35, 0xab, 0x8f, 0xa1, 0xeb, 0x6b, 0xc3, 0x86, 0xe6, 0x7f, + 0x54, 0x07, 0x1e, 0xf1, 0x09, 0xcf, 0x63, 0xd3, 0x52, 0xe7, 0x45, 0x95, 0x15, 0xd5, 0x61, 0x43, + 0xac, 0x20, 0x82, 0x48, 0x58, 0xd6, 0xe6, 0x4f, 0x0c, 0x29, 0x1e, 0x22, 0x42, 0x50, 0x1f, 0x6a, + 0xe1, 0xca, 0x0c, 0xae, 0x34, 0xc3, 0x1d, 0xb1, 0x92, 0x72, 0xcb, 0x81, 0xbd, 0x0e, 0x45, 0x17, + 0x1e, 0x34, 0x7c, 0x78, 0x11, 0xf2, 0xf0, 0xaf, 0x40, 0x99, 0x31, 0x76, 0xa9, 0x6f, 0xf8, 0x50, + 0xe0, 0x6a, 0x5c, 0x7d, 0xb7, 0x59, 0x51, 0x19, 0x8b, 0xba, 0x60, 0x51, 0x5f, 0xb8, 0xa3, 0xf6, + 0xc1, 0x6c, 0x2c, 0x3f, 0x18, 0x19, 0x4e, 0xff, 0x5c, 0x89, 0xef, 0x51, 0xf4, 0x5d, 0xb6, 0xbc, + 0x9c, 0xaf, 0xf8, 0xb7, 0x60, 0xcf, 0x22, 0x2e, 0x85, 0x2e, 0x0d, 0x68, 0x44, 0xba, 0xb5, 0x86, + 0x54, 0x9c, 0x8d, 0xe5, 0x6a, 0x44, 0x9a, 0xdc, 0xa6, 0xe8, 0xf7, 0x97, 0x6f, 0x18, 0x75, 0x15, + 0x94, 0x28, 0x46, 0x2e, 0xf4, 0x84, 0x62, 0x8d, 0xab, 0xef, 0xe8, 0xd1, 0xea, 0x7c, 0xfb, 0xd3, + 0xb5, 0x5c, 0xf8, 0x75, 0x2d, 0x17, 0x94, 0x43, 0x70, 0x90, 0x72, 0xa8, 0x43, 0x3a, 0x98, 0xb3, + 0x28, 0x5f, 0x98, 0xfb, 0x37, 0x03, 0xfb, 0x8f, 0xfb, 0x06, 0xd8, 0x89, 0x9c, 0x60, 0x3b, 0xb4, + 0xbe, 0xd3, 0xae, 0xcc, 0xc6, 0xf2, 0x7e, 0xc2, 0x24, 0xb6, 0x15, 0x7d, 0x9b, 0x3d, 0xbf, 0xb4, + 0xf9, 0x53, 0x50, 0xea, 0x41, 0xc3, 0x86, 0xde, 0x3a, 0x57, 0x7a, 0x84, 0xd9, 0xb8, 0xe3, 0x78, + 0x57, 0xcb, 0x8e, 0x7f, 0x14, 0xc1, 0x7e, 0x58, 0x43, 0x9e, 0x61, 0xff, 0x45, 0xcb, 0xe9, 0x8c, + 0xb7, 0xfe, 0x47, 0xc6, 0xc5, 0x7f, 0x94, 0xf1, 0x6b, 0x50, 0x19, 0x78, 0x84, 0x5c, 0x75, 0x03, + 0x66, 0xbb, 0xcb, 0x74, 0x85, 0x3b, 0x35, 0xae, 0x5e, 0x6e, 0xcb, 0xb3, 0xb1, 0x7c, 0xc4, 0x98, + 0xb2, 0x50, 0x8a, 0xce, 0x87, 0xaf, 0x93, 0x9f, 0xec, 0x03, 0x38, 0x4e, 0x81, 0x53, 0xbd, 0xdf, + 0x0d, 0xb9, 0xeb, 0xb3, 0xb1, 0x7c, 0x92, 0xc9, 0x9d, 0xee, 0x59, 0x4c, 0x88, 0xe4, 0xcd, 0x68, + 0x29, 0x27, 0x71, 0x11, 0x08, 0xe9, 0x54, 0x97, 0x91, 0x7f, 0xe3, 0xc0, 0xc3, 0x0e, 0x45, 0x97, + 0x81, 0xe9, 0x60, 0xbf, 0x83, 0xa9, 0x09, 0x7b, 0xc6, 0x10, 0x93, 0xc0, 0xe3, 0x5b, 0xab, 0xb9, + 0x57, 0xb3, 0x72, 0x17, 0xb8, 0x58, 0xf2, 0xcf, 0x41, 0xd9, 0x89, 0x91, 0xac, 0x4d, 0x7e, 0x4b, + 0xe0, 0xf4, 0x04, 0x9a, 0x17, 0x93, 0xc3, 0x1b, 0x22, 0x56, 0xed, 0xc8, 0xe0, 0x38, 0xb3, 0xe3, + 0x85, 0xa7, 0xe6, 0xd7, 0x22, 0x28, 0x76, 0x28, 0xe2, 0xdf, 0x83, 0x72, 0xe2, 0xd7, 0xf3, 0x48, + 0x5d, 0xfd, 0xa9, 0xa9, 0xa9, 0xd3, 0x2b, 0x3e, 0xd9, 0x00, 0xb4, 0x50, 0x9a, 0x2b, 0x24, 0x8e, + 0x77, 0x9e, 0x42, 0x1c, 0x94, 0xab, 0x90, 0x75, 0x24, 0x79, 0x0b, 0xdc, 0x4b, 0xce, 0xd6, 0x49, + 0xee, 0xee, 0x18, 0x4a, 0x3c, 0xdd, 0x04, 0xb5, 0x14, 0xf1, 0x00, 0x9f, 0x31, 0x00, 0x8f, 0x73, + 0x38, 0x56, 0xa1, 0x62, 0x63, 0x63, 0xe8, 0x42, 0xb3, 0xad, 0x7f, 0x9f, 0x48, 0xdc, 0xcd, 0x44, + 0xe2, 0x6e, 0x27, 0x12, 0xf7, 0x79, 0x2a, 0x15, 0x6e, 0xa6, 0x52, 0xe1, 0xe7, 0x54, 0x2a, 0xbc, + 0x7b, 0x86, 0xb0, 0xdf, 0x0b, 0x4c, 0xd5, 0x22, 0x8e, 0x66, 0x11, 0xea, 0x10, 0xaa, 0x61, 0xd3, + 0x3a, 0x43, 0x44, 0x1b, 0xb6, 0x34, 0x87, 0xd8, 0x41, 0x1f, 0x52, 0x76, 0x6f, 0x3d, 0x6d, 0x9e, + 0x45, 0x57, 0x97, 0x3f, 0x1a, 0x40, 0x6a, 0x96, 0xc2, 0xf9, 0x6a, 0xfd, 0x0e, 0x00, 0x00, 0xff, + 0xff, 0x47, 0x75, 0x66, 0xe0, 0xda, 0x06, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/modules/core/04-channel/types/tx.pb.go b/modules/core/04-channel/types/tx.pb.go index e497cf802b1..8f9ebe6ec63 100644 --- a/modules/core/04-channel/types/tx.pb.go +++ b/modules/core/04-channel/types/tx.pb.go @@ -42,15 +42,15 @@ const ( ) var ResponseResultType_name = map[int32]string{ - 0: "RESPONSE_RESULT_UNSPECIFIED", - 1: "RESPONSE_RESULT_NOOP", - 2: "RESPONSE_RESULT_SUCCESS", + 0: "RESPONSE_RESULT_TYPE_UNSPECIFIED", + 1: "RESPONSE_RESULT_TYPE_NOOP", + 2: "RESPONSE_RESULT_TYPE_SUCCESS", } var ResponseResultType_value = map[string]int32{ - "RESPONSE_RESULT_UNSPECIFIED": 0, - "RESPONSE_RESULT_NOOP": 1, - "RESPONSE_RESULT_SUCCESS": 2, + "RESPONSE_RESULT_TYPE_UNSPECIFIED": 0, + "RESPONSE_RESULT_TYPE_NOOP": 1, + "RESPONSE_RESULT_TYPE_SUCCESS": 2, } func (x ResponseResultType) String() string { @@ -902,87 +902,87 @@ func init() { func init() { proto.RegisterFile("ibc/core/channel/v1/tx.proto", fileDescriptor_bc4637e0ac3fc7b7) } var fileDescriptor_bc4637e0ac3fc7b7 = []byte{ - // 1267 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0x4b, 0x6f, 0xdb, 0x46, - 0x10, 0xd6, 0xcb, 0xb2, 0x3d, 0x72, 0x6d, 0x99, 0xf2, 0x43, 0xa1, 0x62, 0x51, 0xe5, 0x21, 0x11, - 0x5c, 0x44, 0xf2, 0x23, 0x40, 0x11, 0xa3, 0x40, 0x61, 0xa9, 0x0a, 0x6a, 0xb4, 0x7e, 0x80, 0xb2, - 0x7b, 0x70, 0x8b, 0x0a, 0x12, 0xb5, 0x91, 0x09, 0x49, 0x5c, 0x95, 0xa4, 0x94, 0xe8, 0x1f, 0x04, - 0x3e, 0xe5, 0x6c, 0xc0, 0x40, 0x8a, 0x1e, 0x7b, 0x48, 0x7f, 0x46, 0x8e, 0x39, 0xb5, 0x45, 0x0f, - 0x42, 0x61, 0x5f, 0x7a, 0xd6, 0x2f, 0x28, 0xb8, 0x5c, 0x52, 0x94, 0x44, 0xc2, 0x74, 0x62, 0x3b, - 0xb9, 0xed, 0xce, 0x7c, 0x3b, 0x3b, 0xfb, 0x7d, 0xc3, 0x7d, 0x10, 0xee, 0x4b, 0x15, 0x31, 0x2b, - 0x62, 0x05, 0x65, 0xc5, 0x93, 0xb2, 0x2c, 0xa3, 0x46, 0xb6, 0xb3, 0x9e, 0xd5, 0x5e, 0x64, 0x5a, - 0x0a, 0xd6, 0x30, 0x13, 0x93, 0x2a, 0x62, 0x46, 0xf7, 0x66, 0xa8, 0x37, 0xd3, 0x59, 0x67, 0x17, - 0x6a, 0xb8, 0x86, 0x89, 0x3f, 0xab, 0xb7, 0x0c, 0x28, 0xcb, 0x0d, 0x02, 0x35, 0x24, 0x24, 0x6b, - 0x7a, 0x1c, 0xa3, 0x45, 0x01, 0x9f, 0x3b, 0xcd, 0x64, 0x86, 0x25, 0x10, 0xfe, 0x57, 0x3f, 0x30, - 0xbb, 0x6a, 0x2d, 0x6f, 0x18, 0xf7, 0x5b, 0x48, 0xde, 0x91, 0x25, 0x8d, 0xf9, 0x02, 0x26, 0x5b, - 0x58, 0xd1, 0x4a, 0x52, 0x35, 0xee, 0x4f, 0xf9, 0xd3, 0xd3, 0x39, 0xa6, 0xdf, 0xe3, 0x66, 0xbb, - 0xe5, 0x66, 0x63, 0x8b, 0xa7, 0x0e, 0x5e, 0x08, 0xeb, 0xad, 0x9d, 0x2a, 0xf3, 0x15, 0x4c, 0xd2, - 0xa0, 0xf1, 0x40, 0xca, 0x9f, 0x8e, 0x6c, 0xdc, 0xcf, 0x38, 0x2c, 0x22, 0x43, 0xe7, 0xc8, 0x85, - 0xde, 0xf6, 0x38, 0x9f, 0x60, 0x0e, 0x61, 0x96, 0x20, 0xac, 0x4a, 0x35, 0x19, 0x29, 0xf1, 0xa0, - 0x3e, 0x93, 0x40, 0x7b, 0x5b, 0x53, 0x2f, 0x5f, 0x73, 0xbe, 0xff, 0x5e, 0x73, 0x3e, 0x5e, 0x00, - 0x76, 0x3c, 0x45, 0x01, 0xa9, 0x2d, 0x2c, 0xab, 0x88, 0x79, 0x0c, 0x40, 0x43, 0x0d, 0xb2, 0x5d, - 0xec, 0xf7, 0xb8, 0x79, 0x23, 0xdb, 0x81, 0x8f, 0x17, 0xa6, 0x69, 0x67, 0xa7, 0xca, 0xff, 0x19, - 0x84, 0xf9, 0xe1, 0xa0, 0x87, 0x4a, 0xf7, 0x7a, 0xcb, 0xde, 0x83, 0x58, 0x4b, 0x41, 0x1d, 0x09, - 0xb7, 0xd5, 0x92, 0x2d, 0x83, 0x00, 0x19, 0x98, 0xec, 0xf7, 0x38, 0x96, 0x0e, 0x1c, 0x07, 0xf1, - 0xc2, 0xbc, 0x69, 0xcd, 0x9b, 0x29, 0xd9, 0x69, 0x0c, 0x5e, 0x9f, 0x46, 0x01, 0x16, 0x44, 0xdc, - 0x96, 0x35, 0xa4, 0xb4, 0xca, 0x8a, 0xd6, 0x2d, 0x75, 0x90, 0xa2, 0x4a, 0x58, 0x8e, 0x87, 0x48, - 0x3a, 0x5c, 0xbf, 0xc7, 0x25, 0x28, 0x21, 0x0e, 0x28, 0x5e, 0x88, 0xd9, 0xcd, 0x3f, 0x18, 0x56, - 0x9d, 0xda, 0x96, 0x82, 0xf1, 0xb3, 0x92, 0x24, 0x4b, 0x5a, 0x7c, 0x22, 0xe5, 0x4f, 0xcf, 0xd8, - 0xa9, 0x1d, 0xf8, 0x78, 0x61, 0x9a, 0x74, 0x48, 0xed, 0x1c, 0xc3, 0x8c, 0xe1, 0x39, 0x41, 0x52, - 0xed, 0x44, 0x8b, 0x87, 0xc9, 0x62, 0x58, 0xdb, 0x62, 0x8c, 0x1a, 0xed, 0xac, 0x67, 0xbe, 0x25, - 0x88, 0x5c, 0x42, 0x5f, 0x4a, 0xbf, 0xc7, 0xc5, 0xec, 0x71, 0x8d, 0xd1, 0xbc, 0x10, 0x21, 0x5d, - 0x03, 0x69, 0x2b, 0x96, 0x49, 0x97, 0x62, 0x49, 0xc0, 0xbd, 0x31, 0x5d, 0xcd, 0x5a, 0xe1, 0xff, - 0x1a, 0x53, 0x7d, 0x5b, 0xac, 0x5f, 0x4f, 0xf5, 0xe1, 0x72, 0x0b, 0x78, 0x2b, 0x37, 0xe6, 0x18, - 0x96, 0x87, 0x78, 0xb7, 0x85, 0x20, 0x55, 0x9f, 0xe3, 0xfb, 0x3d, 0x2e, 0xe9, 0x20, 0x90, 0x3d, - 0xde, 0xa2, 0xdd, 0x33, 0xa8, 0x9b, 0xdb, 0x50, 0x7e, 0x1d, 0x0c, 0x41, 0x4b, 0x9a, 0xd2, 0xa5, - 0xc2, 0x2f, 0xf4, 0x7b, 0x5c, 0xd4, 0x2e, 0x90, 0xa6, 0x74, 0x79, 0x61, 0x8a, 0xb4, 0xf5, 0x6f, - 0xe7, 0x13, 0x93, 0x7d, 0x5b, 0xac, 0x5b, 0xb2, 0xff, 0x1e, 0x80, 0xc5, 0x61, 0x6f, 0x1e, 0xcb, - 0xcf, 0x24, 0xa5, 0x79, 0x17, 0xd2, 0x5b, 0x54, 0x96, 0xc5, 0x3a, 0x11, 0xdb, 0x81, 0xca, 0xb2, - 0x58, 0x37, 0xa9, 0xd4, 0x0b, 0x72, 0x94, 0xca, 0xd0, 0xad, 0x50, 0x39, 0xe1, 0x42, 0x25, 0x07, - 0x2b, 0x8e, 0x64, 0x59, 0x74, 0x9e, 0xf9, 0x21, 0x36, 0x40, 0xe4, 0x1b, 0x58, 0x45, 0xd7, 0x3f, - 0x34, 0xde, 0x8f, 0xcc, 0xab, 0x0f, 0x8b, 0x15, 0x48, 0x38, 0xe4, 0x66, 0xe5, 0xfe, 0x26, 0x00, - 0x4b, 0x23, 0xfe, 0x3b, 0xac, 0x85, 0xe1, 0x0d, 0x35, 0xf8, 0x9e, 0x1b, 0xea, 0xdd, 0x96, 0x43, - 0x0a, 0x92, 0xce, 0x84, 0x59, 0x9c, 0xbe, 0x0a, 0xc0, 0x67, 0xbb, 0x6a, 0x4d, 0x40, 0x62, 0xe7, - 0xa0, 0x2c, 0xd6, 0x91, 0xc6, 0x3c, 0x81, 0x70, 0x8b, 0xb4, 0x08, 0x93, 0x91, 0x8d, 0x84, 0xe3, - 0x49, 0x66, 0x80, 0xe9, 0x41, 0x46, 0x07, 0x30, 0x4f, 0x21, 0x6a, 0xa4, 0x2b, 0xe2, 0x66, 0x53, - 0xd2, 0x9a, 0x48, 0xd6, 0x08, 0xbd, 0x33, 0xb9, 0x44, 0xbf, 0xc7, 0x2d, 0xdb, 0x17, 0x34, 0x40, - 0xf0, 0xc2, 0x1c, 0x31, 0xe5, 0x2d, 0xcb, 0x18, 0x69, 0xc1, 0x5b, 0x21, 0x2d, 0xe4, 0x42, 0xda, - 0xcf, 0x64, 0xc3, 0x19, 0x30, 0x62, 0xdd, 0x56, 0xbe, 0x86, 0xb0, 0x82, 0xd4, 0x76, 0xc3, 0x60, - 0x66, 0x76, 0xe3, 0xa1, 0x23, 0x33, 0x26, 0x5c, 0x20, 0xd0, 0xc3, 0x6e, 0x0b, 0x09, 0x74, 0xd8, - 0x56, 0x48, 0x9f, 0x83, 0xff, 0x27, 0x00, 0xb0, 0xab, 0xd6, 0x0e, 0xa5, 0x26, 0xc2, 0xed, 0x9b, - 0xe1, 0xbb, 0x2d, 0x2b, 0x48, 0x44, 0x52, 0x07, 0x55, 0xdd, 0xf8, 0x1e, 0x20, 0x4c, 0xbe, 0x8f, - 0x2c, 0xcb, 0xad, 0xf2, 0xfd, 0x1d, 0x30, 0x32, 0x7a, 0xa1, 0x95, 0x54, 0xf4, 0x4b, 0x1b, 0xc9, - 0x22, 0x2a, 0x29, 0x48, 0xec, 0x10, 0xee, 0x43, 0xb9, 0x95, 0x7e, 0x8f, 0xbb, 0x67, 0x44, 0x18, - 0xc7, 0xf0, 0x42, 0x54, 0x37, 0x16, 0xa9, 0x4d, 0xd7, 0xc3, 0x43, 0xc5, 0xff, 0x48, 0xae, 0xc4, - 0x94, 0xdb, 0x9b, 0x56, 0xee, 0xcc, 0xb8, 0x82, 0xd0, 0xe8, 0xfb, 0x32, 0xf9, 0xa2, 0x3e, 0x05, - 0x01, 0xbf, 0x84, 0x08, 0xfd, 0xac, 0xf4, 0x8c, 0xe8, 0xe6, 0xb4, 0xd4, 0xef, 0x71, 0xcc, 0xd0, - 0x37, 0xa7, 0x3b, 0x79, 0xc1, 0xd8, 0xc6, 0x8c, 0xdc, 0x6f, 0x73, 0x7b, 0x72, 0x56, 0x7e, 0xe2, - 0x43, 0x95, 0x0f, 0xbb, 0x28, 0x5f, 0x21, 0xb7, 0x88, 0x61, 0x6d, 0x6e, 0xba, 0x00, 0xfe, 0x08, - 0x90, 0xf2, 0xda, 0x16, 0xeb, 0x32, 0x7e, 0xde, 0x40, 0xd5, 0x1a, 0x22, 0xfb, 0xd5, 0x07, 0x54, - 0x40, 0x1a, 0xe6, 0xca, 0xc3, 0xd1, 0x8c, 0x02, 0x10, 0x46, 0xcd, 0x03, 0x8d, 0xf5, 0x81, 0x55, - 0x37, 0x8d, 0x89, 0xd3, 0xd4, 0x78, 0x5b, 0xef, 0x7c, 0xe4, 0x23, 0x48, 0x24, 0x0f, 0xc0, 0x11, - 0xc6, 0x6e, 0x58, 0x97, 0xd5, 0x33, 0x3f, 0x30, 0xe3, 0x20, 0x66, 0x0d, 0x12, 0x42, 0xa1, 0x78, - 0xb0, 0xbf, 0x57, 0x2c, 0x94, 0x84, 0x42, 0xf1, 0xe8, 0xfb, 0xc3, 0xd2, 0xd1, 0x5e, 0xf1, 0xa0, - 0x90, 0xdf, 0x79, 0xba, 0x53, 0xf8, 0x26, 0xea, 0x63, 0xe7, 0x4e, 0xcf, 0x53, 0x11, 0x9b, 0x89, - 0xe1, 0x61, 0x61, 0x74, 0xc4, 0xde, 0xfe, 0xfe, 0x41, 0xd4, 0xcf, 0x4e, 0x9d, 0x9e, 0xa7, 0x42, - 0x7a, 0x9b, 0x49, 0xc3, 0xf2, 0x28, 0xa6, 0x78, 0x94, 0xcf, 0x17, 0x8a, 0xc5, 0x68, 0x80, 0x8d, - 0x9c, 0x9e, 0xa7, 0x26, 0x69, 0x97, 0x0d, 0xbd, 0xfc, 0x2d, 0xe9, 0xdb, 0x78, 0x33, 0x05, 0xc1, - 0x5d, 0xb5, 0xc6, 0xd4, 0x61, 0x6e, 0xf4, 0xa9, 0xee, 0xbc, 0xdc, 0xf1, 0x07, 0x33, 0x9b, 0xf5, - 0x08, 0xb4, 0x88, 0x3d, 0x81, 0xd9, 0x91, 0xf7, 0xf1, 0x03, 0x0f, 0x21, 0x0e, 0x95, 0x2e, 0x9b, - 0xf1, 0x86, 0x73, 0x99, 0x49, 0xbf, 0x02, 0x7b, 0x99, 0x69, 0x5b, 0xac, 0x7b, 0x9a, 0xc9, 0xf6, - 0x14, 0x60, 0x34, 0x60, 0x1c, 0x9e, 0x01, 0xab, 0x1e, 0xa2, 0x50, 0x2c, 0xbb, 0xe1, 0x1d, 0x6b, - 0xcd, 0x2a, 0x43, 0x74, 0xec, 0xb6, 0x9c, 0xbe, 0x22, 0x8e, 0x85, 0x64, 0xd7, 0xbc, 0x22, 0xad, - 0xf9, 0x9e, 0x43, 0xcc, 0xf1, 0x86, 0xeb, 0x25, 0x90, 0xb9, 0xce, 0xcd, 0x6b, 0x80, 0xad, 0x89, - 0x7f, 0x02, 0xb0, 0x5d, 0x03, 0x79, 0xb7, 0x10, 0x03, 0x0c, 0xbb, 0x7a, 0x35, 0xc6, 0x8a, 0x5e, - 0x84, 0x49, 0xf3, 0xc6, 0xc3, 0xb9, 0x0d, 0xa3, 0x00, 0xf6, 0xe1, 0x15, 0x00, 0x7b, 0xed, 0x8d, - 0x1c, 0xc6, 0x0f, 0xae, 0x18, 0x4a, 0x71, 0xee, 0xb5, 0xe7, 0x72, 0x80, 0xd4, 0x61, 0x6e, 0x74, - 0xd7, 0x77, 0xcd, 0x72, 0x04, 0xe8, 0xfe, 0xf1, 0xba, 0xec, 0x8a, 0xb9, 0xe2, 0xdb, 0x8b, 0xa4, - 0xff, 0xdd, 0x45, 0xd2, 0xff, 0xef, 0x45, 0xd2, 0xff, 0xea, 0x32, 0xe9, 0x7b, 0x77, 0x99, 0xf4, - 0xfd, 0x7d, 0x99, 0xf4, 0x1d, 0x3f, 0xa9, 0x49, 0xda, 0x49, 0xbb, 0x92, 0x11, 0x71, 0x33, 0x2b, - 0x62, 0xb5, 0x89, 0xd5, 0xac, 0x54, 0x11, 0x1f, 0xd5, 0x70, 0xb6, 0xb3, 0x99, 0x6d, 0xe2, 0x6a, - 0xbb, 0x81, 0x54, 0xe3, 0xaf, 0xe1, 0xda, 0xe3, 0x47, 0xe6, 0x8f, 0x43, 0xad, 0xdb, 0x42, 0x6a, - 0x25, 0x4c, 0x7e, 0x1a, 0x6e, 0xfe, 0x1f, 0x00, 0x00, 0xff, 0xff, 0xd5, 0x99, 0xe1, 0x0c, 0xc3, - 0x14, 0x00, 0x00, + // 1275 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0xcd, 0x6e, 0xdb, 0x46, + 0x10, 0xd6, 0x5f, 0x64, 0x67, 0xe4, 0xda, 0x32, 0x65, 0x3b, 0x32, 0x65, 0x8b, 0x2a, 0x0f, 0xb1, + 0xe1, 0xc2, 0x52, 0x6c, 0xa7, 0x28, 0x62, 0x14, 0x28, 0x2c, 0x55, 0x41, 0x8d, 0xd6, 0x3f, 0xa0, + 0xe4, 0x02, 0x75, 0x8b, 0x0a, 0x12, 0xb5, 0x91, 0x09, 0x49, 0x5c, 0x95, 0xa4, 0x94, 0xe8, 0x0d, + 0x02, 0x9f, 0x72, 0x0e, 0x60, 0x20, 0x45, 0x4f, 0x45, 0x0f, 0xe9, 0x63, 0xe4, 0x98, 0x53, 0x5b, + 0xf4, 0x20, 0x14, 0xf6, 0xa5, 0x67, 0x3d, 0x41, 0xc1, 0xe5, 0x92, 0xa2, 0x24, 0x12, 0xa6, 0x13, + 0xdb, 0xcd, 0x6d, 0x77, 0xe6, 0xdb, 0xd9, 0xd9, 0xef, 0x1b, 0xee, 0x0f, 0x61, 0x49, 0xaa, 0x88, + 0x19, 0x11, 0x2b, 0x28, 0x23, 0x9e, 0x94, 0x65, 0x19, 0x35, 0x32, 0x9d, 0x8d, 0x8c, 0xf6, 0x2c, + 0xdd, 0x52, 0xb0, 0x86, 0x99, 0x98, 0x54, 0x11, 0xd3, 0xba, 0x37, 0x4d, 0xbd, 0xe9, 0xce, 0x06, + 0x3b, 0x57, 0xc3, 0x35, 0x4c, 0xfc, 0x19, 0xbd, 0x65, 0x40, 0x59, 0x6e, 0x10, 0xa8, 0x21, 0x21, + 0x59, 0xd3, 0xe3, 0x18, 0x2d, 0x0a, 0xf8, 0xd8, 0x69, 0x26, 0x33, 0x2c, 0x81, 0xf0, 0x3f, 0xfb, + 0x81, 0xd9, 0x53, 0x6b, 0x39, 0xc3, 0x78, 0xd0, 0x42, 0xf2, 0xae, 0x2c, 0x69, 0xcc, 0x27, 0x30, + 0xd1, 0xc2, 0x8a, 0x56, 0x92, 0xaa, 0x71, 0x7f, 0xca, 0xbf, 0x7a, 0x37, 0xcb, 0xf4, 0x7b, 0xdc, + 0x74, 0xb7, 0xdc, 0x6c, 0x6c, 0xf3, 0xd4, 0xc1, 0x0b, 0x61, 0xbd, 0xb5, 0x5b, 0x65, 0x3e, 0x87, + 0x09, 0x1a, 0x34, 0x1e, 0x48, 0xf9, 0x57, 0x23, 0x9b, 0x4b, 0x69, 0x87, 0x45, 0xa4, 0xe9, 0x1c, + 0xd9, 0xd0, 0x9b, 0x1e, 0xe7, 0x13, 0xcc, 0x21, 0xcc, 0x02, 0x84, 0x55, 0xa9, 0x26, 0x23, 0x25, + 0x1e, 0xd4, 0x67, 0x12, 0x68, 0x6f, 0x7b, 0xf2, 0xf9, 0x2b, 0xce, 0xf7, 0xef, 0x2b, 0xce, 0xc7, + 0x0b, 0xc0, 0x8e, 0xa7, 0x28, 0x20, 0xb5, 0x85, 0x65, 0x15, 0x31, 0x0f, 0x01, 0x68, 0xa8, 0x41, + 0xb6, 0xf3, 0xfd, 0x1e, 0x37, 0x6b, 0x64, 0x3b, 0xf0, 0xf1, 0xc2, 0x5d, 0xda, 0xd9, 0xad, 0xf2, + 0x7f, 0x04, 0x61, 0x76, 0x38, 0x68, 0x51, 0xe9, 0x5e, 0x6d, 0xd9, 0xfb, 0x10, 0x6b, 0x29, 0xa8, + 0x23, 0xe1, 0xb6, 0x5a, 0xb2, 0x65, 0x10, 0x20, 0x03, 0x93, 0xfd, 0x1e, 0xc7, 0xd2, 0x81, 0xe3, + 0x20, 0x5e, 0x98, 0x35, 0xad, 0x39, 0x33, 0x25, 0x3b, 0x8d, 0xc1, 0xab, 0xd3, 0x28, 0xc0, 0x9c, + 0x88, 0xdb, 0xb2, 0x86, 0x94, 0x56, 0x59, 0xd1, 0xba, 0xa5, 0x0e, 0x52, 0x54, 0x09, 0xcb, 0xf1, + 0x10, 0x49, 0x87, 0xeb, 0xf7, 0xb8, 0x04, 0x25, 0xc4, 0x01, 0xc5, 0x0b, 0x31, 0xbb, 0xf9, 0x5b, + 0xc3, 0xaa, 0x53, 0xdb, 0x52, 0x30, 0x7e, 0x52, 0x92, 0x64, 0x49, 0x8b, 0xdf, 0x49, 0xf9, 0x57, + 0xa7, 0xec, 0xd4, 0x0e, 0x7c, 0xbc, 0x70, 0x97, 0x74, 0x48, 0xed, 0x1c, 0xc3, 0x94, 0xe1, 0x39, + 0x41, 0x52, 0xed, 0x44, 0x8b, 0x87, 0xc9, 0x62, 0x58, 0xdb, 0x62, 0x8c, 0x1a, 0xed, 0x6c, 0xa4, + 0xbf, 0x22, 0x88, 0x6c, 0x42, 0x5f, 0x4a, 0xbf, 0xc7, 0xc5, 0xec, 0x71, 0x8d, 0xd1, 0xbc, 0x10, + 0x21, 0x5d, 0x03, 0x69, 0x2b, 0x96, 0x09, 0x97, 0x62, 0x49, 0xc0, 0xe2, 0x98, 0xae, 0x66, 0xad, + 0xf0, 0x7f, 0x8e, 0xa9, 0xbe, 0x23, 0xd6, 0xaf, 0xa6, 0xfa, 0x70, 0xb9, 0x05, 0xbc, 0x95, 0x1b, + 0x73, 0x0c, 0xf7, 0x86, 0x78, 0xb7, 0x85, 0x20, 0x55, 0x9f, 0xe5, 0xfb, 0x3d, 0x2e, 0xe9, 0x20, + 0x90, 0x3d, 0xde, 0xbc, 0xdd, 0x33, 0xa8, 0x9b, 0x9b, 0x50, 0x7e, 0x03, 0x0c, 0x41, 0x4b, 0x9a, + 0xd2, 0xa5, 0xc2, 0xcf, 0xf5, 0x7b, 0x5c, 0xd4, 0x2e, 0x90, 0xa6, 0x74, 0x79, 0x61, 0x92, 0xb4, + 0xf5, 0x6f, 0xe7, 0x03, 0x93, 0x7d, 0x47, 0xac, 0x5b, 0xb2, 0xff, 0x16, 0x80, 0xf9, 0x61, 0x6f, + 0x0e, 0xcb, 0x4f, 0x24, 0xa5, 0x79, 0x1b, 0xd2, 0x5b, 0x54, 0x96, 0xc5, 0x3a, 0x11, 0xdb, 0x81, + 0xca, 0xb2, 0x58, 0x37, 0xa9, 0xd4, 0x0b, 0x72, 0x94, 0xca, 0xd0, 0x8d, 0x50, 0x79, 0xc7, 0x85, + 0x4a, 0x0e, 0x96, 0x1d, 0xc9, 0xb2, 0xe8, 0x7c, 0xe9, 0x87, 0xd8, 0x00, 0x91, 0x6b, 0x60, 0x15, + 0x5d, 0xfd, 0xd0, 0x78, 0x37, 0x32, 0x2f, 0x3f, 0x2c, 0x96, 0x21, 0xe1, 0x90, 0x9b, 0x95, 0xfb, + 0xeb, 0x00, 0x2c, 0x8c, 0xf8, 0x6f, 0xb1, 0x16, 0x86, 0x37, 0xd4, 0xe0, 0x3b, 0x6e, 0xa8, 0xb7, + 0x5b, 0x0e, 0x29, 0x48, 0x3a, 0x13, 0x66, 0x71, 0xfa, 0x22, 0x00, 0x1f, 0xed, 0xa9, 0x35, 0x01, + 0x89, 0x9d, 0xc3, 0xb2, 0x58, 0x47, 0x1a, 0xf3, 0x08, 0xc2, 0x2d, 0xd2, 0x22, 0x4c, 0x46, 0x36, + 0x13, 0x8e, 0x27, 0x99, 0x01, 0xa6, 0x07, 0x19, 0x1d, 0xc0, 0x3c, 0x86, 0xa8, 0x91, 0xae, 0x88, + 0x9b, 0x4d, 0x49, 0x6b, 0x22, 0x59, 0x23, 0xf4, 0x4e, 0x65, 0x13, 0xfd, 0x1e, 0x77, 0xcf, 0xbe, + 0xa0, 0x01, 0x82, 0x17, 0x66, 0x88, 0x29, 0x67, 0x59, 0xc6, 0x48, 0x0b, 0xde, 0x08, 0x69, 0x21, + 0x17, 0xd2, 0x7e, 0x24, 0x1b, 0xce, 0x80, 0x11, 0xeb, 0xb6, 0xf2, 0x05, 0x84, 0x15, 0xa4, 0xb6, + 0x1b, 0x06, 0x33, 0xd3, 0x9b, 0x2b, 0x8e, 0xcc, 0x98, 0x70, 0x81, 0x40, 0x8b, 0xdd, 0x16, 0x12, + 0xe8, 0xb0, 0xed, 0x90, 0x3e, 0x07, 0xff, 0x77, 0x00, 0x60, 0x4f, 0xad, 0x15, 0xa5, 0x26, 0xc2, + 0xed, 0xeb, 0xe1, 0xbb, 0x2d, 0x2b, 0x48, 0x44, 0x52, 0x07, 0x55, 0xdd, 0xf8, 0x1e, 0x20, 0x4c, + 0xbe, 0x8f, 0x2c, 0xcb, 0x8d, 0xf2, 0xfd, 0x35, 0x30, 0x32, 0x7a, 0xa6, 0x95, 0x54, 0xf4, 0x53, + 0x1b, 0xc9, 0x22, 0x2a, 0x29, 0x48, 0xec, 0x10, 0xee, 0x43, 0xd9, 0xe5, 0x7e, 0x8f, 0x5b, 0x34, + 0x22, 0x8c, 0x63, 0x78, 0x21, 0xaa, 0x1b, 0x0b, 0xd4, 0xa6, 0xeb, 0xe1, 0xa1, 0xe2, 0xbf, 0x27, + 0x57, 0x62, 0xca, 0xed, 0x75, 0x2b, 0xf7, 0xd2, 0xb8, 0x82, 0xd0, 0xe8, 0x07, 0x32, 0xf9, 0xa2, + 0x3e, 0x04, 0x01, 0x3f, 0x83, 0x08, 0xfd, 0xac, 0xf4, 0x8c, 0xe8, 0xe6, 0xb4, 0xd0, 0xef, 0x71, + 0xcc, 0xd0, 0x37, 0xa7, 0x3b, 0x79, 0xc1, 0xd8, 0xc6, 0x8c, 0xdc, 0x6f, 0x72, 0x7b, 0x72, 0x56, + 0xfe, 0xce, 0xfb, 0x2a, 0x1f, 0x76, 0x51, 0xbe, 0x42, 0x6e, 0x11, 0xc3, 0xda, 0x5c, 0x77, 0x01, + 0xfc, 0x1e, 0x20, 0xe5, 0xb5, 0x23, 0xd6, 0x65, 0xfc, 0xb4, 0x81, 0xaa, 0x35, 0x44, 0xf6, 0xab, + 0xf7, 0xa8, 0x80, 0x55, 0x98, 0x29, 0x0f, 0x47, 0x33, 0x0a, 0x40, 0x18, 0x35, 0x0f, 0x34, 0xd6, + 0x07, 0x56, 0xdd, 0x34, 0x26, 0x4e, 0x53, 0xe3, 0x1d, 0xbd, 0xf3, 0x3f, 0x1f, 0x41, 0x22, 0x79, + 0x00, 0x8e, 0x30, 0x76, 0xcd, 0xba, 0xac, 0xfd, 0xea, 0x07, 0x66, 0x1c, 0xc4, 0x7c, 0x0a, 0x29, + 0x21, 0x5f, 0x38, 0x3c, 0xd8, 0x2f, 0xe4, 0x4b, 0x42, 0xbe, 0x70, 0xf4, 0x4d, 0xb1, 0x54, 0xfc, + 0xee, 0x30, 0x5f, 0x3a, 0xda, 0x2f, 0x1c, 0xe6, 0x73, 0xbb, 0x8f, 0x77, 0xf3, 0x5f, 0x46, 0x7d, + 0xec, 0xcc, 0xe9, 0x59, 0x2a, 0x62, 0x33, 0x31, 0x2b, 0xb0, 0xe8, 0x38, 0x6c, 0xff, 0xe0, 0xe0, + 0x30, 0xea, 0x67, 0x27, 0x4f, 0xcf, 0x52, 0x21, 0xbd, 0xcd, 0xac, 0xc3, 0x92, 0x23, 0xb0, 0x70, + 0x94, 0xcb, 0xe5, 0x0b, 0x85, 0x68, 0x80, 0x8d, 0x9c, 0x9e, 0xa5, 0x26, 0x68, 0x97, 0x0d, 0x3d, + 0xff, 0x25, 0xe9, 0xdb, 0x7c, 0x3d, 0x09, 0xc1, 0x3d, 0xb5, 0xc6, 0xd4, 0x61, 0x66, 0xf4, 0xe5, + 0xee, 0xbc, 0xfa, 0xf1, 0xf7, 0x33, 0x9b, 0xf1, 0x08, 0xb4, 0x78, 0x3e, 0x81, 0xe9, 0x91, 0xe7, + 0xf2, 0x7d, 0x0f, 0x21, 0x8a, 0x4a, 0x97, 0x4d, 0x7b, 0xc3, 0xb9, 0xcc, 0xa4, 0xdf, 0x88, 0xbd, + 0xcc, 0xb4, 0x23, 0xd6, 0x3d, 0xcd, 0x64, 0x7b, 0x19, 0x30, 0x1a, 0x30, 0x0e, 0xaf, 0x82, 0x35, + 0x0f, 0x51, 0x28, 0x96, 0xdd, 0xf4, 0x8e, 0xb5, 0x66, 0x95, 0x21, 0x3a, 0x76, 0x79, 0x5e, 0xbd, + 0x24, 0x8e, 0x85, 0x64, 0x1f, 0x78, 0x45, 0x5a, 0xf3, 0x3d, 0x85, 0x98, 0xe3, 0x85, 0xd7, 0x4b, + 0x20, 0x73, 0x9d, 0x5b, 0x57, 0x00, 0x5b, 0x13, 0xff, 0x00, 0x60, 0xbb, 0x15, 0xf2, 0x6e, 0x21, + 0x06, 0x18, 0x76, 0xed, 0x72, 0x8c, 0x15, 0xbd, 0x00, 0x13, 0xe6, 0x05, 0x88, 0x73, 0x1b, 0x46, + 0x01, 0xec, 0xca, 0x25, 0x00, 0x7b, 0xed, 0x8d, 0x9c, 0xcd, 0xf7, 0x2f, 0x19, 0x4a, 0x71, 0xee, + 0xb5, 0xe7, 0x72, 0x9e, 0xd4, 0x61, 0x66, 0xf4, 0x10, 0x70, 0xcd, 0x72, 0x04, 0xe8, 0xfe, 0xf1, + 0xba, 0x6c, 0x92, 0xd9, 0xc2, 0x9b, 0xf3, 0xa4, 0xff, 0xed, 0x79, 0xd2, 0xff, 0xcf, 0x79, 0xd2, + 0xff, 0xe2, 0x22, 0xe9, 0x7b, 0x7b, 0x91, 0xf4, 0xfd, 0x75, 0x91, 0xf4, 0x1d, 0x3f, 0xaa, 0x49, + 0xda, 0x49, 0xbb, 0x92, 0x16, 0x71, 0x33, 0x23, 0x62, 0xb5, 0x89, 0xd5, 0x8c, 0x54, 0x11, 0xd7, + 0x6b, 0x38, 0xd3, 0xd9, 0xca, 0x34, 0x71, 0xb5, 0xdd, 0x40, 0xaa, 0xf1, 0x13, 0xf1, 0xc1, 0xc3, + 0x75, 0xf3, 0x3f, 0xa2, 0xd6, 0x6d, 0x21, 0xb5, 0x12, 0x26, 0xff, 0x10, 0xb7, 0xfe, 0x0b, 0x00, + 0x00, 0xff, 0xff, 0xec, 0xba, 0x10, 0x62, 0xd2, 0x14, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/modules/core/keeper/msg_server.go b/modules/core/keeper/msg_server.go index 673978e053b..4893f82dd66 100644 --- a/modules/core/keeper/msg_server.go +++ b/modules/core/keeper/msg_server.go @@ -79,6 +79,8 @@ func (k Keeper) UpgradeClient(goCtx context.Context, msg *clienttypes.MsgUpgrade } // SubmitMisbehaviour defines a rpc handler method for MsgSubmitMisbehaviour. +// Warning: DEPRECATED +// This handler is redudant as `MsgUpdateClient` is now capable of handling both a Header and a Misbehaviour func (k Keeper) SubmitMisbehaviour(goCtx context.Context, msg *clienttypes.MsgSubmitMisbehaviour) (*clienttypes.MsgSubmitMisbehaviourResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) @@ -87,8 +89,8 @@ func (k Keeper) SubmitMisbehaviour(goCtx context.Context, msg *clienttypes.MsgSu return nil, err } - if err := k.ClientKeeper.CheckMisbehaviourAndUpdateState(ctx, msg.ClientId, misbehaviour); err != nil { - return nil, sdkerrors.Wrap(err, "failed to process misbehaviour for IBC client") + if err = k.ClientKeeper.UpdateClient(ctx, msg.ClientId, misbehaviour); err != nil { + return nil, err } return &clienttypes.MsgSubmitMisbehaviourResponse{}, nil diff --git a/proto/ibc/core/channel/v1/tx.proto b/proto/ibc/core/channel/v1/tx.proto index d4d6df1d5e9..15714173a1a 100644 --- a/proto/ibc/core/channel/v1/tx.proto +++ b/proto/ibc/core/channel/v1/tx.proto @@ -47,11 +47,11 @@ enum ResponseResultType { option (gogoproto.goproto_enum_prefix) = false; // Default zero value enumeration - RESPONSE_RESULT_UNSPECIFIED = 0 [(gogoproto.enumvalue_customname) = "UNSPECIFIED"]; + RESPONSE_RESULT_TYPE_UNSPECIFIED = 0 [(gogoproto.enumvalue_customname) = "UNSPECIFIED"]; // The message did not call the IBC application callbacks (because, for example, the packet had already been relayed) - RESPONSE_RESULT_NOOP = 1 [(gogoproto.enumvalue_customname) = "NOOP"]; + RESPONSE_RESULT_TYPE_NOOP = 1 [(gogoproto.enumvalue_customname) = "NOOP"]; // The message was executed successfully - RESPONSE_RESULT_SUCCESS = 2 [(gogoproto.enumvalue_customname) = "SUCCESS"]; + RESPONSE_RESULT_TYPE_SUCCESS = 2 [(gogoproto.enumvalue_customname) = "SUCCESS"]; } // MsgChannelOpenInit defines an sdk.Msg to initialize a channel handshake. It diff --git a/proto/ibc/core/client/v1/tx.proto b/proto/ibc/core/client/v1/tx.proto index 06dbfbd0dfe..51f03780c75 100644 --- a/proto/ibc/core/client/v1/tx.proto +++ b/proto/ibc/core/client/v1/tx.proto @@ -82,16 +82,17 @@ message MsgUpgradeClientResponse {} // MsgSubmitMisbehaviour defines an sdk.Msg type that submits Evidence for // light client misbehaviour. +// Warning: DEPRECATED message MsgSubmitMisbehaviour { option (gogoproto.equal) = false; option (gogoproto.goproto_getters) = false; // client unique identifier - string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\"", deprecated = true]; // misbehaviour used for freezing the light client - google.protobuf.Any misbehaviour = 2; + google.protobuf.Any misbehaviour = 2 [deprecated = true]; // signer address - string signer = 3; + string signer = 3 [deprecated = true]; } // MsgSubmitMisbehaviourResponse defines the Msg/SubmitMisbehaviour response From e249518062f5479a1b470b6338203d95094f0ae6 Mon Sep 17 00:00:00 2001 From: Sean King Date: Mon, 4 Apr 2022 17:05:09 +0200 Subject: [PATCH 31/71] refactor: removing CheckMisbehaviourAndUpdateState from ClientState interface (#1212) * refactor: removing CheckForMisbehaviourAndUpdateState from ClientState interface and associated client implemtations * chore: changelog --- CHANGELOG.md | 3 +- modules/core/02-client/keeper/client.go | 42 -- modules/core/02-client/keeper/client_test.go | 249 ----------- modules/core/exported/client.go | 1 - .../types/misbehaviour_handle.go | 43 -- .../types/misbehaviour_handle_test.go | 265 ----------- .../types/misbehaviour_handle.go | 29 -- .../types/misbehaviour_handle_test.go | 413 ------------------ 8 files changed, 1 insertion(+), 1044 deletions(-) delete mode 100644 modules/light-clients/06-solomachine/types/misbehaviour_handle_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index e1a1568ee2b..8e5bde9662d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,8 +44,6 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Improvements * (modules/core/02-client) [\#1188](https://github.com/cosmos/ibc-go/pull/1188/files) Routing `MsgSubmitMisbehaviour` to `UpdateClient` keeper function. Deprecating `SubmitMisbehaviour` endpoint. - - * (modules/core/02-client) [\#1208](https://github.com/cosmos/ibc-go/pull/1208) Replace `CheckHeaderAndUpdateState` usage in 02-client with calls to `VerifyClientMessage`, `CheckForMisbehaviour`, `UpdateStateOnMisbehaviour` and `UpdateState`. * (modules/light-clients/09-localhost) [\#1187](https://github.com/cosmos/ibc-go/pull/1187/) Removing localhost light client implementation as it is not functional. * [\#1186](https://github.com/cosmos/ibc-go/pull/1186/files) Removing `GetRoot` function from ConsensusState interface in `02-client`. `GetRoot` is unused by core IBC. @@ -54,6 +52,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (modules/core/02-client) [\#1170](https://github.com/cosmos/ibc-go/pull/1170) Updating `ClientUpdateProposal` to set client state in lightclient implementations `CheckSubstituteAndUpdateState` methods. * (modules/core/02-client) [\#1197](https://github.com/cosmos/ibc-go/pull/1197) Adding `CheckForMisbehaviour` to `ClientState` interface. * (modules/core/02-client) [\#1195](https://github.com/cosmos/ibc-go/pull/1210) Removing `CheckHeaderAndUpdateState` from `ClientState` interface & associated light client implementations. +* (modules/core/02-client) [\#1189](https://github.com/cosmos/ibc-go/pull/1212) Removing `CheckMisbehaviourAndUpdateState` from `ClientState` interface & associated light client implementations. * (modules/core/exported) [\#1206](https://github.com/cosmos/ibc-go/pull/1206) Adding new method `UpdateState` to `ClientState` interface. ### Features diff --git a/modules/core/02-client/keeper/client.go b/modules/core/02-client/keeper/client.go index eb1fc87e354..e702c31fe5c 100644 --- a/modules/core/02-client/keeper/client.go +++ b/modules/core/02-client/keeper/client.go @@ -161,45 +161,3 @@ func (k Keeper) UpgradeClient(ctx sdk.Context, clientID string, upgradedClient e return nil } - -// CheckMisbehaviourAndUpdateState checks for client misbehaviour and freezes the -// client if so. -func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, clientID string, misbehaviour exported.ClientMessage) error { - clientState, found := k.GetClientState(ctx, clientID) - if !found { - return sdkerrors.Wrapf(types.ErrClientNotFound, "cannot check misbehaviour for client with ID %s", clientID) - } - - clientStore := k.ClientStore(ctx, clientID) - - if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(types.ErrClientNotActive, "cannot process misbehaviour for client (%s) with status %s", clientID, status) - } - - if err := misbehaviour.ValidateBasic(); err != nil { - return err - } - - clientState, err := clientState.CheckMisbehaviourAndUpdateState(ctx, k.cdc, clientStore, misbehaviour) - if err != nil { - return err - } - - k.SetClientState(ctx, clientID, clientState) - k.Logger(ctx).Info("client frozen due to misbehaviour", "client-id", clientID) - - defer func() { - telemetry.IncrCounterWithLabels( - []string{"ibc", "client", "misbehaviour"}, - 1, - []metrics.Label{ - telemetry.NewLabel(types.LabelClientType, misbehaviour.ClientType()), - telemetry.NewLabel(types.LabelClientID, clientID), - }, - ) - }() - - EmitSubmitMisbehaviourEvent(ctx, clientID, clientState) - - return nil -} diff --git a/modules/core/02-client/keeper/client_test.go b/modules/core/02-client/keeper/client_test.go index 9c441746755..2061bb84a4a 100644 --- a/modules/core/02-client/keeper/client_test.go +++ b/modules/core/02-client/keeper/client_test.go @@ -6,7 +6,6 @@ import ( "time" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" @@ -15,7 +14,6 @@ import ( solomachinetypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types" ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" ibctesting "github.com/cosmos/ibc-go/v3/testing" - ibctestingmock "github.com/cosmos/ibc-go/v3/testing/mock" ) func (suite *KeeperTestSuite) TestCreateClient() { @@ -408,253 +406,6 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { } -func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() { - var ( - clientID string - err error - ) - - altPrivVal := ibctestingmock.NewPV() - altPubKey, err := altPrivVal.GetPubKey() - suite.Require().NoError(err) - altVal := tmtypes.NewValidator(altPubKey, 4) - - // Set valSet here with suite.valSet so it doesn't get reset on each testcase - valSet := suite.valSet - valsHash := valSet.Hash() - - // Create bothValSet with both suite validator and altVal - bothValSet := tmtypes.NewValidatorSet(append(suite.valSet.Validators, altVal)) - bothValsHash := bothValSet.Hash() - // Create alternative validator set with only altVal - altValSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{altVal}) - - // Create signer array and ensure it is in same order as bothValSet - _, suiteVal := suite.valSet.GetByIndex(0) - bothSigners := make(map[string]tmtypes.PrivValidator, 2) - bothSigners[suiteVal.Address.String()] = suite.privVal - bothSigners[altVal.Address.String()] = altPrivVal - - altSigners := make(map[string]tmtypes.PrivValidator, 1) - altSigners[altVal.Address.String()] = altPrivVal - - // Create valid Misbehaviour by making a duplicate header that signs over different block time - altTime := suite.ctx.BlockTime().Add(time.Minute) - - heightPlus3 := types.NewHeight(0, height+3) - heightPlus5 := types.NewHeight(0, height+5) - - testCases := []struct { - name string - misbehaviour *ibctmtypes.Misbehaviour - malleate func() error - expPass bool - }{ - { - "trusting period misbehavior should pass", - &ibctmtypes.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, altTime, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: clientID, - }, - func() error { - suite.consensusState.NextValidatorsHash = bothValsHash - clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) - clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) - - return err - }, - true, - }, - { - "time misbehavior should pass", - &ibctmtypes.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+5), testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, altTime, bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: clientID, - }, - func() error { - suite.consensusState.NextValidatorsHash = bothValsHash - clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) - clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) - - return err - }, - true, - }, - { - "misbehavior at later height should pass", - &ibctmtypes.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), testClientHeight, altTime, bothValSet, bothValSet, valSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, valSet, bothSigners), - ClientId: clientID, - }, - func() error { - suite.consensusState.NextValidatorsHash = valsHash - clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) - clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) - - // store intermediate consensus state to check that trustedHeight does not need to be highest consensus state before header height - intermediateConsState := &ibctmtypes.ConsensusState{ - Timestamp: suite.now.Add(time.Minute), - NextValidatorsHash: suite.chainB.Vals.Hash(), - } - suite.keeper.SetClientConsensusState(suite.ctx, clientID, heightPlus3, intermediateConsState) - - clientState.LatestHeight = heightPlus3 - suite.keeper.SetClientState(suite.ctx, clientID, clientState) - - return err - }, - true, - }, - { - "misbehavior at later height with different trusted heights should pass", - &ibctmtypes.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), testClientHeight, altTime, bothValSet, bothValSet, valSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), heightPlus3, suite.ctx.BlockTime(), bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: clientID, - }, - func() error { - suite.consensusState.NextValidatorsHash = valsHash - clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) - clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) - - // store trusted consensus state for Header2 - intermediateConsState := &ibctmtypes.ConsensusState{ - Timestamp: suite.now.Add(time.Minute), - NextValidatorsHash: bothValsHash, - } - suite.keeper.SetClientConsensusState(suite.ctx, clientID, heightPlus3, intermediateConsState) - - clientState.LatestHeight = heightPlus3 - suite.keeper.SetClientState(suite.ctx, clientID, clientState) - - return err - }, - true, - }, - { - "misbehavior ValidateBasic fails: misbehaviour height is at same height as trusted height", - &ibctmtypes.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight), testClientHeight, altTime, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight), testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: clientID, - }, - func() error { - suite.consensusState.NextValidatorsHash = bothValsHash - clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) - clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) - - return err - }, - false, - }, - { - "trusted ConsensusState1 not found", - &ibctmtypes.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), heightPlus3, altTime, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, valSet, bothSigners), - ClientId: clientID, - }, - func() error { - suite.consensusState.NextValidatorsHash = valsHash - clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) - clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) - // intermediate consensus state at height + 3 is not created - return err - }, - false, - }, - { - "trusted ConsensusState2 not found", - &ibctmtypes.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), testClientHeight, altTime, bothValSet, bothValSet, valSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), heightPlus3, suite.ctx.BlockTime(), bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: clientID, - }, - func() error { - suite.consensusState.NextValidatorsHash = valsHash - clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) - clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) - // intermediate consensus state at height + 3 is not created - return err - }, - false, - }, - { - "client state not found", - &ibctmtypes.Misbehaviour{}, - func() error { return nil }, - false, - }, - { - "client already is not active - client is frozen", - &ibctmtypes.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, altTime, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: clientID, - }, - func() error { - suite.consensusState.NextValidatorsHash = bothValsHash - clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) - clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) - - clientState.FrozenHeight = types.NewHeight(0, 1) - suite.keeper.SetClientState(suite.ctx, clientID, clientState) - - return err - }, - false, - }, - { - "misbehaviour check failed", - &ibctmtypes.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, altTime, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, suite.ctx.BlockTime(), altValSet, altValSet, bothValSet, altSigners), - ClientId: clientID, - }, - func() error { - clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) - if err != nil { - return err - } - clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) - - return err - }, - false, - }, - } - - for i, tc := range testCases { - tc := tc - i := i - - suite.Run(tc.name, func() { - suite.SetupTest() // reset - clientID = testClientID // must be explicitly changed - - err := tc.malleate() - suite.Require().NoError(err) - - tc.misbehaviour.ClientId = clientID - - err = suite.keeper.CheckMisbehaviourAndUpdateState(suite.ctx, clientID, tc.misbehaviour) - - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - - clientState, found := suite.keeper.GetClientState(suite.ctx, clientID) - suite.Require().True(found, "valid test case %d failed: %s", i, tc.name) - suite.Require().True(!clientState.(*ibctmtypes.ClientState).FrozenHeight.IsZero(), "valid test case %d failed: %s", i, tc.name) - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - } - }) - } -} - func (suite *KeeperTestSuite) TestUpdateClientEventEmission() { path := ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupClients(path) diff --git a/modules/core/exported/client.go b/modules/core/exported/client.go index de69fb5b1f6..44fc75a6169 100644 --- a/modules/core/exported/client.go +++ b/modules/core/exported/client.go @@ -66,7 +66,6 @@ type ClientState interface { UpdateState(sdk.Context, codec.BinaryCodec, sdk.KVStore, ClientMessage) error // Update and Misbehaviour functions - CheckMisbehaviourAndUpdateState(sdk.Context, codec.BinaryCodec, sdk.KVStore, ClientMessage) (ClientState, error) CheckSubstituteAndUpdateState(ctx sdk.Context, cdc codec.BinaryCodec, subjectClientStore, substituteClientStore sdk.KVStore, substituteClient ClientState) (ClientState, error) // Upgrade functions diff --git a/modules/light-clients/06-solomachine/types/misbehaviour_handle.go b/modules/light-clients/06-solomachine/types/misbehaviour_handle.go index eb97bc252fa..becf8a654a5 100644 --- a/modules/light-clients/06-solomachine/types/misbehaviour_handle.go +++ b/modules/light-clients/06-solomachine/types/misbehaviour_handle.go @@ -2,51 +2,8 @@ package types import ( "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" ) -// CheckMisbehaviourAndUpdateState determines whether or not the currently registered -// public key signed over two different messages with the same sequence. If this is true -// the client state is updated to a frozen status. -// NOTE: Misbehaviour is not tracked for previous public keys, a solo machine may update to -// a new public key before the misbehaviour is processed. Therefore, misbehaviour is data -// order processing dependent. -func (cs ClientState) CheckMisbehaviourAndUpdateState( - ctx sdk.Context, - cdc codec.BinaryCodec, - clientStore sdk.KVStore, - misbehaviour exported.ClientMessage, -) (exported.ClientState, error) { - - soloMisbehaviour, ok := misbehaviour.(*Misbehaviour) - if !ok { - return nil, sdkerrors.Wrapf( - clienttypes.ErrInvalidClientType, - "misbehaviour type %T, expected %T", misbehaviour, &Misbehaviour{}, - ) - } - - // NOTE: a check that the misbehaviour message data are not equal is done by - // misbehaviour.ValidateBasic which is called by the 02-client keeper. - - // verify first signature - if err := cs.verifySignatureAndData(cdc, soloMisbehaviour, soloMisbehaviour.SignatureOne); err != nil { - return nil, sdkerrors.Wrap(err, "failed to verify signature one") - } - - // verify second signature - if err := cs.verifySignatureAndData(cdc, soloMisbehaviour, soloMisbehaviour.SignatureTwo); err != nil { - return nil, sdkerrors.Wrap(err, "failed to verify signature two") - } - - cs.IsFrozen = true - return &cs, nil -} - // verifySignatureAndData verifies that the currently registered public key has signed // over the provided data and that the data is valid. The data is valid if it can be // unmarshaled into the specified data type. diff --git a/modules/light-clients/06-solomachine/types/misbehaviour_handle_test.go b/modules/light-clients/06-solomachine/types/misbehaviour_handle_test.go deleted file mode 100644 index 0fd4aa0edd9..00000000000 --- a/modules/light-clients/06-solomachine/types/misbehaviour_handle_test.go +++ /dev/null @@ -1,265 +0,0 @@ -package types_test - -import ( - "github.com/cosmos/ibc-go/v3/modules/core/exported" - "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" -) - -func (suite *SoloMachineTestSuite) TestCheckMisbehaviourAndUpdateState() { - var ( - clientState exported.ClientState - misbehaviour exported.ClientMessage - ) - - // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - testCases := []struct { - name string - setup func() - expPass bool - }{ - { - "valid misbehaviour", - func() { - clientState = solomachine.ClientState() - misbehaviour = solomachine.CreateMisbehaviour() - }, - true, - }, - { - "old misbehaviour is successful (timestamp is less than current consensus state)", - func() { - clientState = solomachine.ClientState() - solomachine.Time = solomachine.Time - 5 - misbehaviour = solomachine.CreateMisbehaviour() - }, true, - }, - { - "wrong client state type", - func() { - clientState = &ibctmtypes.ClientState{} - misbehaviour = solomachine.CreateMisbehaviour() - }, - false, - }, - { - "invalid misbehaviour type", - func() { - clientState = solomachine.ClientState() - misbehaviour = &ibctmtypes.Misbehaviour{} - }, - false, - }, - { - "invalid SignatureOne SignatureData", - func() { - clientState = solomachine.ClientState() - m := solomachine.CreateMisbehaviour() - - m.SignatureOne.Signature = suite.GetInvalidProof() - misbehaviour = m - }, false, - }, - { - "invalid SignatureTwo SignatureData", - func() { - clientState = solomachine.ClientState() - m := solomachine.CreateMisbehaviour() - - m.SignatureTwo.Signature = suite.GetInvalidProof() - misbehaviour = m - }, false, - }, - { - "invalid SignatureOne timestamp", - func() { - clientState = solomachine.ClientState() - m := solomachine.CreateMisbehaviour() - - m.SignatureOne.Timestamp = 1000000000000 - misbehaviour = m - }, false, - }, - { - "invalid SignatureTwo timestamp", - func() { - clientState = solomachine.ClientState() - m := solomachine.CreateMisbehaviour() - - m.SignatureTwo.Timestamp = 1000000000000 - misbehaviour = m - }, false, - }, - { - "invalid first signature data", - func() { - clientState = solomachine.ClientState() - - // store in temp before assigning to interface type - m := solomachine.CreateMisbehaviour() - - msg := []byte("DATA ONE") - signBytes := &types.SignBytes{ - Sequence: solomachine.Sequence + 1, - Timestamp: solomachine.Time, - Diversifier: solomachine.Diversifier, - DataType: types.CLIENT, - Data: msg, - } - - data, err := suite.chainA.Codec.Marshal(signBytes) - suite.Require().NoError(err) - - sig := solomachine.GenerateSignature(data) - - m.SignatureOne.Signature = sig - m.SignatureOne.Data = msg - misbehaviour = m - }, - false, - }, - { - "invalid second signature data", - func() { - clientState = solomachine.ClientState() - - // store in temp before assigning to interface type - m := solomachine.CreateMisbehaviour() - - msg := []byte("DATA TWO") - signBytes := &types.SignBytes{ - Sequence: solomachine.Sequence + 1, - Timestamp: solomachine.Time, - Diversifier: solomachine.Diversifier, - DataType: types.CLIENT, - Data: msg, - } - - data, err := suite.chainA.Codec.Marshal(signBytes) - suite.Require().NoError(err) - - sig := solomachine.GenerateSignature(data) - - m.SignatureTwo.Signature = sig - m.SignatureTwo.Data = msg - misbehaviour = m - }, - false, - }, - { - "wrong pubkey generates first signature", - func() { - clientState = solomachine.ClientState() - badMisbehaviour := solomachine.CreateMisbehaviour() - - // update public key to a new one - solomachine.CreateHeader() - m := solomachine.CreateMisbehaviour() - - // set SignatureOne to use the wrong signature - m.SignatureOne = badMisbehaviour.SignatureOne - misbehaviour = m - }, false, - }, - { - "wrong pubkey generates second signature", - func() { - clientState = solomachine.ClientState() - badMisbehaviour := solomachine.CreateMisbehaviour() - - // update public key to a new one - solomachine.CreateHeader() - m := solomachine.CreateMisbehaviour() - - // set SignatureTwo to use the wrong signature - m.SignatureTwo = badMisbehaviour.SignatureTwo - misbehaviour = m - }, false, - }, - - { - "signatures sign over different sequence", - func() { - clientState = solomachine.ClientState() - - // store in temp before assigning to interface type - m := solomachine.CreateMisbehaviour() - - // Signature One - msg := []byte("DATA ONE") - // sequence used is plus 1 - signBytes := &types.SignBytes{ - Sequence: solomachine.Sequence + 1, - Timestamp: solomachine.Time, - Diversifier: solomachine.Diversifier, - DataType: types.CLIENT, - Data: msg, - } - - data, err := suite.chainA.Codec.Marshal(signBytes) - suite.Require().NoError(err) - - sig := solomachine.GenerateSignature(data) - - m.SignatureOne.Signature = sig - m.SignatureOne.Data = msg - - // Signature Two - msg = []byte("DATA TWO") - // sequence used is minus 1 - - signBytes = &types.SignBytes{ - Sequence: solomachine.Sequence - 1, - Timestamp: solomachine.Time, - Diversifier: solomachine.Diversifier, - DataType: types.CLIENT, - Data: msg, - } - data, err = suite.chainA.Codec.Marshal(signBytes) - suite.Require().NoError(err) - - sig = solomachine.GenerateSignature(data) - - m.SignatureTwo.Signature = sig - m.SignatureTwo.Data = msg - - misbehaviour = m - - }, - false, - }, - { - "consensus state pubkey is nil", - func() { - cs := solomachine.ClientState() - cs.ConsensusState.PublicKey = nil - clientState = cs - misbehaviour = solomachine.CreateMisbehaviour() - }, - false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - // setup test - tc.setup() - - clientState, err := clientState.CheckMisbehaviourAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), suite.store, misbehaviour) - - if tc.expPass { - suite.Require().NoError(err) - suite.Require().True(clientState.(*types.ClientState).IsFrozen, "client not frozen") - } else { - suite.Require().Error(err) - suite.Require().Nil(clientState) - } - }) - } - } -} diff --git a/modules/light-clients/07-tendermint/types/misbehaviour_handle.go b/modules/light-clients/07-tendermint/types/misbehaviour_handle.go index 71a8e5284f1..a8a01c456d1 100644 --- a/modules/light-clients/07-tendermint/types/misbehaviour_handle.go +++ b/modules/light-clients/07-tendermint/types/misbehaviour_handle.go @@ -10,37 +10,8 @@ import ( tmtypes "github.com/tendermint/tendermint/types" clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" ) -// CheckMisbehaviourAndUpdateState determines whether or not two conflicting -// headers at the same height would have convinced the light client. -// -// NOTE: consensusState1 is the trusted consensus state that corresponds to the TrustedHeight -// of misbehaviour.Header1 -// Similarly, consensusState2 is the trusted consensus state that corresponds -// to misbehaviour.Header2 -// Misbehaviour sets frozen height to {0, 1} since it is only used as a boolean value (zero or non-zero). -func (cs ClientState) CheckMisbehaviourAndUpdateState( - ctx sdk.Context, - cdc codec.BinaryCodec, - clientStore sdk.KVStore, - misbehaviour exported.ClientMessage, -) (exported.ClientState, error) { - tmMisbehaviour, ok := misbehaviour.(*Misbehaviour) - if !ok { - return nil, sdkerrors.Wrapf(clienttypes.ErrInvalidClientType, "expected type %T, got %T", misbehaviour, &Misbehaviour{}) - } - - if err := cs.VerifyClientMessage(ctx, cdc, clientStore, tmMisbehaviour); err != nil { - return nil, err - } - - cs.FrozenHeight = FrozenHeight - - return &cs, nil -} - // verifyMisbehaviour determines whether or not two conflicting // headers at the same height would have convinced the light client. // diff --git a/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go b/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go index 62af23b2707..2fde6ae2920 100644 --- a/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go +++ b/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go @@ -1,14 +1,11 @@ package types_test import ( - "fmt" "time" - "github.com/tendermint/tendermint/crypto/tmhash" tmtypes "github.com/tendermint/tendermint/types" clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v3/modules/core/exported" smtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types" "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" @@ -16,416 +13,6 @@ import ( ibctestingmock "github.com/cosmos/ibc-go/v3/testing/mock" ) -func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { - altPrivVal := ibctestingmock.NewPV() - altPubKey, err := altPrivVal.GetPubKey() - suite.Require().NoError(err) - - altVal := tmtypes.NewValidator(altPubKey, 4) - - // Create alternative validator set with only altVal - altValSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{altVal}) - - // Create bothValSet with both suite validator and altVal - bothValSet, bothSigners := getBothSigners(suite, altVal, altPrivVal) - bothValsHash := bothValSet.Hash() - - altSigners := getAltSigners(altVal, altPrivVal) - - heightMinus1 := clienttypes.NewHeight(height.RevisionNumber, height.RevisionHeight-1) - heightMinus3 := clienttypes.NewHeight(height.RevisionNumber, height.RevisionHeight-3) - - testCases := []struct { - name string - clientState exported.ClientState - consensusState1 exported.ConsensusState - height1 clienttypes.Height - consensusState2 exported.ConsensusState - height2 clienttypes.Height - misbehaviour exported.ClientMessage - timestamp time.Time - expPass bool - }{ - { - "valid fork misbehaviour", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: chainID, - }, - suite.now, - true, - }, - { - "valid time misbehaviour", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+3), height, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: chainID, - }, - suite.now, - true, - }, - { - "valid time misbehaviour header 1 stricly less than header 2", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+3), height, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Hour), bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: chainID, - }, - suite.now, - true, - }, - { - "valid misbehavior at height greater than last consensusState", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - heightMinus1, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - heightMinus1, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now.Add(time.Minute), bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: chainID, - }, - suite.now, - true, - }, - { - "valid misbehaviour with different trusted heights", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - heightMinus1, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), - heightMinus3, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus3, suite.now.Add(time.Minute), bothValSet, bothValSet, suite.valSet, bothSigners), - ClientId: chainID, - }, - suite.now, - true, - }, - { - "valid misbehaviour at a previous revision", - types.NewClientState(chainIDRevision1, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(1, 1), commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - heightMinus1, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), - heightMinus3, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainIDRevision0, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainIDRevision0, int64(height.RevisionHeight+1), heightMinus3, suite.now.Add(time.Minute), bothValSet, bothValSet, suite.valSet, bothSigners), - ClientId: chainID, - }, - suite.now, - true, - }, - { - "valid misbehaviour at a future revision", - types.NewClientState(chainIDRevision0, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - heightMinus1, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), - heightMinus3, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainIDRevision0, 3, heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainIDRevision0, 3, heightMinus3, suite.now.Add(time.Minute), bothValSet, bothValSet, suite.valSet, bothSigners), - ClientId: chainID, - }, - suite.now, - true, - }, - { - "valid misbehaviour with trusted heights at a previous revision", - types.NewClientState(chainIDRevision1, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(1, 1), commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - heightMinus1, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), - heightMinus3, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainIDRevision1, 1, heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainIDRevision1, 1, heightMinus3, suite.now.Add(time.Minute), bothValSet, bothValSet, suite.valSet, bothSigners), - ClientId: chainID, - }, - suite.now, - true, - }, - { - "consensus state's valset hash different from misbehaviour should still pass", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), - height, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), - height, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, suite.valSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, suite.valSet, bothSigners), - ClientId: chainID, - }, - suite.now, - true, - }, - { - "invalid fork misbehaviour: identical headers", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: chainID, - }, - suite.now, - false, - }, - { - "invalid time misbehaviour: monotonically increasing time", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+3), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: chainID, - }, - suite.now, - false, - }, - { - "invalid misbehavior misbehaviour from different chain", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader("ethermint", int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader("ethermint", int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: chainID, - }, - suite.now, - false, - }, - { - "invalid misbehavior misbehaviour with trusted height different from trusted consensus state", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - heightMinus1, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), - heightMinus3, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, suite.valSet, bothSigners), - ClientId: chainID, - }, - suite.now, - false, - }, - { - "invalid misbehavior misbehaviour with trusted validators different from trusted consensus state", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - heightMinus1, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), - heightMinus3, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus3, suite.now.Add(time.Minute), bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: chainID, - }, - suite.now, - false, - }, - { - "already frozen client state", - &types.ClientState{FrozenHeight: clienttypes.NewHeight(0, 1)}, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: chainID, - }, - suite.now, - false, - }, - { - "trusted consensus state does not exist", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - nil, // consensus state for trusted height - 1 does not exist in store - clienttypes.Height{}, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: chainID, - }, - suite.now, - false, - }, - { - "invalid tendermint misbehaviour", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - nil, - suite.now, - false, - }, - { - "provided height > header height", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now.Add(time.Minute), bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: chainID, - }, - suite.now, - false, - }, - { - "trusting period expired", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(time.Time{}, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - heightMinus1, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: chainID, - }, - suite.now.Add(trustingPeriod), - false, - }, - { - "trusted validators is incorrect for given consensus state", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, suite.valSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, suite.valSet, bothSigners), - ClientId: chainID, - }, - suite.now, - false, - }, - { - "first valset has too much change", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, altValSet, altValSet, bothValSet, altSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothValSet, bothSigners), - ClientId: chainID, - }, - suite.now, - false, - }, - { - "second valset has too much change", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), altValSet, altValSet, bothValSet, altSigners), - ClientId: chainID, - }, - suite.now, - false, - }, - { - "both valsets have too much change", - types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - height, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now, altValSet, altValSet, bothValSet, altSigners), - Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+1), height, suite.now.Add(time.Minute), altValSet, altValSet, bothValSet, altSigners), - ClientId: chainID, - }, - suite.now, - false, - }, - } - - for i, tc := range testCases { - tc := tc - suite.Run(fmt.Sprintf("Case: %s", tc.name), func() { - // reset suite to create fresh application state - suite.SetupTest() - - // Set current timestamp in context - ctx := suite.chainA.GetContext().WithBlockTime(tc.timestamp) - - // Set trusted consensus states in client store - - if tc.consensusState1 != nil { - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(ctx, clientID, tc.height1, tc.consensusState1) - } - if tc.consensusState2 != nil { - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(ctx, clientID, tc.height2, tc.consensusState2) - } - - clientState, err := tc.clientState.CheckMisbehaviourAndUpdateState( - ctx, - suite.chainA.App.AppCodec(), - suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, clientID), // pass in clientID prefixed clientStore - tc.misbehaviour, - ) - - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - suite.Require().NotNil(clientState, "valid test case %d failed: %s", i, tc.name) - suite.Require().True(!clientState.(*types.ClientState).FrozenHeight.IsZero(), "valid test case %d failed: %s", i, tc.name) - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - suite.Require().Nil(clientState, "invalid test case %d passed: %s", i, tc.name) - } - }) - } -} - func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { // Setup different validators and signers for testing different types of updates altPrivVal := ibctestingmock.NewPV() From e91ee68dd01742097230929fc7a12791a0ae868d Mon Sep 17 00:00:00 2001 From: Charly Date: Mon, 25 Apr 2022 13:54:36 +0200 Subject: [PATCH 32/71] fix: rm AllowUpdateAfter... check (#1118) * update code & test * update proto and adr026 * update CHANGELOG * update cli docs * update broken milestone link --- CHANGELOG.md | 1 + .../adr-026-ibc-client-recovery-mechanisms.md | 13 +- docs/ibc/proposals.md | 3 +- docs/ibc/proto-docs.md | 4 +- docs/roadmap/roadmap.md | 2 +- .../07-tendermint/types/proposal_handle.go | 27 +--- .../types/proposal_handle_test.go | 102 +----------- .../07-tendermint/types/tendermint.pb.go | 146 +++++++++--------- .../tendermint/v1/tendermint.proto | 11 +- 9 files changed, 101 insertions(+), 208 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e5bde9662d..c2190232888 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### State Machine Breaking ### Improvements +* (modules/light-clients/07-tendermint) [\#1118](https://github.com/cosmos/ibc-go/pull/1118) Deprecating `AllowUpdateAfterExpiry and AllowUpdateAfterMisbehaviour`. See ADR-026 for context. * (modules/core/02-client) [\#1188](https://github.com/cosmos/ibc-go/pull/1188/files) Routing `MsgSubmitMisbehaviour` to `UpdateClient` keeper function. Deprecating `SubmitMisbehaviour` endpoint. * (modules/core/02-client) [\#1208](https://github.com/cosmos/ibc-go/pull/1208) Replace `CheckHeaderAndUpdateState` usage in 02-client with calls to `VerifyClientMessage`, `CheckForMisbehaviour`, `UpdateStateOnMisbehaviour` and `UpdateState`. * (modules/light-clients/09-localhost) [\#1187](https://github.com/cosmos/ibc-go/pull/1187/) Removing localhost light client implementation as it is not functional. diff --git a/docs/architecture/adr-026-ibc-client-recovery-mechanisms.md b/docs/architecture/adr-026-ibc-client-recovery-mechanisms.md index c40e3b08cd5..27bcb33e545 100644 --- a/docs/architecture/adr-026-ibc-client-recovery-mechanisms.md +++ b/docs/architecture/adr-026-ibc-client-recovery-mechanisms.md @@ -6,6 +6,7 @@ - 2020/08/06: Revisions per review & to reference version - 2021/01/15: Revision to support substitute clients for unfreezing - 2021/05/20: Revision to simplify consensus state copying, remove initial height +- 2022/04/08: Revision to deprecate AllowUpdateAfterExpiry and AllowUpdateAfterMisbehaviour ## Status @@ -35,21 +36,20 @@ Two-thirds of the validator set (the quorum for governance, module participation We elect not to deal with chains which have actually halted, which is necessarily Byzantine behaviour and in which case token recovery is not likely possible anyways (in-flight packets cannot be timed-out, but the relative impact of that is minor). 1. Require Tendermint light clients (ICS 07) to be created with the following additional flags - 1. `allow_governance_override_after_expiry` (boolean, default false) + 1. `allow_update_after_expiry` (boolean, default true). Note that this flag has been deprecated, it remains to signal intent but checks against this value will not be enforced. 1. Require Tendermint light clients (ICS 07) to expose the following additional internal query functions 1. `Expired() boolean`, which returns whether or not the client has passed the trusting period since the last update (in which case no headers can be validated) 1. Require Tendermint light clients (ICS 07) & solo machine clients (ICS 06) to be created with the following additional flags - 1. `allow_governance_override_after_misbehaviour` (boolean, default false) + 1. `allow_update_after_misbehaviour` (boolean, default true). Note that this flag has been deprecated, it remains to signal intent but checks against this value will not be enforced. 1. Require Tendermint light clients (ICS 07) to expose the following additional state mutation functions 1. `Unfreeze()`, which unfreezes a light client after misbehaviour and clears any frozen height previously set 1. Add a new governance proposal type, `ClientUpdateProposal`, in the `x/ibc` module 1. Extend the base `Proposal` with two client identifiers (`string`). 1. The first client identifier is the proposed client to be updated. This client must be either frozen or expired. 1. The second client is a substitute client. It carries all the state for the client which may be updated. It must have identitical client and chain parameters to the client which may be updated (except for latest height, frozen height, and chain-id). It should be continually updated during the voting period. - 1. If this governance proposal passes, the client on trial will be updated to the latest state of the substitute, if and only if: - 1. `allow_governance_override_after_expiry` is true and the client has expired (`Expired()` returns true) - 1. `allow_governance_override_after_misbehaviour` is true and the client has been frozen (`Frozen()` returns true) - 1. In this case, additionally, the client is unfrozen by calling `Unfreeze()` + 1. If this governance proposal passes, the client on trial will be updated to the latest state of the substitute. + + Previously, AllowUpdateAfterExpiry and AllowUpdateAfterMisbehaviour were used to signal the recovery options for an expired or frozen client, and governance proposals were not allowed to overwrite the client if these parameters were set to false. However, this has now been deprecated because a code migration can overwrite the client and consensus states regardless of the value of these parameters. If governance would vote to overwrite a client or consensus state, it is likely that governance would also willing to perform a code migration to do the same. Note that clients frozen due to misbehaviour must wait for the evidence to expire to avoid becoming refrozen. @@ -62,7 +62,6 @@ This ADR does not address planned upgrades, which are handled separately as per - Establishes a mechanism for client recovery in the case of expiry - Establishes a mechanism for client recovery in the case of misbehaviour -- Clients can elect to disallow this recovery mechanism if they do not wish to allow for it - Constructing an ClientUpdate Proposal is as difficult as creating a new client ### Negative diff --git a/docs/ibc/proposals.md b/docs/ibc/proposals.md index c2cb34860b2..f6bf351a705 100644 --- a/docs/ibc/proposals.md +++ b/docs/ibc/proposals.md @@ -46,7 +46,6 @@ See also the relevant documentation: [ADR-026, IBC client recovery mechanisms](. ### Preconditions - The chain is updated with ibc-go >= v1.1.0. -- Recovery parameters are set to `true` for the Tendermint light client (this determines if a governance proposal can be used). If the recovery parameters are set to `false`, recovery will require custom migration code. - The client identifier of an active client for the same counterparty chain. - The governance deposit. @@ -67,7 +66,7 @@ Check if the client is attached to the expected `chain-id`. For example, for an } ``` -The client is attached to the expected Akash `chain-id` and the recovery parameters (`allow_update_after_expiry` and `allow_update_after_misbehaviour`) are set to `true`. +The client is attached to the expected Akash `chain-id`. Note that although the parameters (`allow_update_after_expiry` and `allow_update_after_misbehaviour`) exist to signal intent, these parameters have been deprecated and will not enforce any checks on the revival of client. See ADR-026 for more context on this deprecation. ### Step 2 diff --git a/docs/ibc/proto-docs.md b/docs/ibc/proto-docs.md index cb9fc7a1e67..32b500639a3 100644 --- a/docs/ibc/proto-docs.md +++ b/docs/ibc/proto-docs.md @@ -3911,8 +3911,8 @@ and a possible frozen height. | `latest_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | Latest height the client was updated to | | `proof_specs` | [ics23.ProofSpec](#ics23.ProofSpec) | repeated | Proof specifications used in verifying counterparty state | | `upgrade_path` | [string](#string) | repeated | Path at which next upgraded client will be committed. Each element corresponds to the key for a single CommitmentProof in the chained proof. NOTE: ClientState must stored under `{upgradePath}/{upgradeHeight}/clientState` ConsensusState must be stored under `{upgradepath}/{upgradeHeight}/consensusState` For SDK chains using the default upgrade module, upgrade_path should be []string{"upgrade", "upgradedIBCState"}` | -| `allow_update_after_expiry` | [bool](#bool) | | This flag, when set to true, will allow governance to recover a client which has expired | -| `allow_update_after_misbehaviour` | [bool](#bool) | | This flag, when set to true, will allow governance to unfreeze a client whose chain has experienced a misbehaviour event | +| `allow_update_after_expiry` | [bool](#bool) | | **Deprecated.** allow_update_after_expiry is deprecated | +| `allow_update_after_misbehaviour` | [bool](#bool) | | **Deprecated.** allow_update_after_misbehaviour is deprecated | diff --git a/docs/roadmap/roadmap.md b/docs/roadmap/roadmap.md index e425e4630aa..0a4819b4bb1 100644 --- a/docs/roadmap/roadmap.md +++ b/docs/roadmap/roadmap.md @@ -59,7 +59,7 @@ During this quarter we will also probably release versions that bump the Cosmos ### H2 January -- [`v2.0.a`](https://github.com/cosmos/ibc-go/milestone/14) +- [`v2.0.a`](https://github.com/cosmos/ibc-go/milestone/11) - [`v3.0.0-beta1`](https://github.com/cosmos/ibc-go/milestone/12): Beta 1 release of `v3.0.0` including Interchain Accounts, an update of Golang from `v1.15` to `v1.17`, and some core improvements. This is a Go-API breaking release because of [#472](https://github.com/cosmos/ibc-go/issues/472) and [#675](https://github.com/cosmos/ibc-go/pull/675). ### H1 February diff --git a/modules/light-clients/07-tendermint/types/proposal_handle.go b/modules/light-clients/07-tendermint/types/proposal_handle.go index 563acce2c02..a938d181f65 100644 --- a/modules/light-clients/07-tendermint/types/proposal_handle.go +++ b/modules/light-clients/07-tendermint/types/proposal_handle.go @@ -12,18 +12,17 @@ import ( ) // CheckSubstituteAndUpdateState will try to update the client with the state of the -// substitute if and only if the proposal passes and one of the following conditions are -// satisfied: -// 1) AllowUpdateAfterMisbehaviour and Status() == Frozen -// 2) AllowUpdateAfterExpiry=true and Status() == Expired +// substitute. // +// AllowUpdateAfterMisbehaviour and AllowUpdateAfterExpiry have been deprecated. +// Please see ADR 026 for more information. +// // The following must always be true: // - The substitute client is the same type as the subject client // - The subject and substitute client states match in all parameters (expect frozen height, latest height, and chain-id) // // In case 1) before updating the client, the client will be unfrozen by resetting -// the FrozenHeight to the zero Height. If a client is frozen and AllowUpdateAfterMisbehaviour -// is set to true, the client will be unexpired even if AllowUpdateAfterExpiry is set to false. +// the FrozenHeight to the zero Height. func (cs ClientState) CheckSubstituteAndUpdateState( ctx sdk.Context, cdc codec.BinaryCodec, subjectClientStore, substituteClientStore sdk.KVStore, substituteClient exported.ClientState, @@ -39,23 +38,9 @@ func (cs ClientState) CheckSubstituteAndUpdateState( return nil, sdkerrors.Wrap(clienttypes.ErrInvalidSubstitute, "subject client state does not match substitute client state") } - switch cs.Status(ctx, subjectClientStore, cdc) { - - case exported.Frozen: - if !cs.AllowUpdateAfterMisbehaviour { - return nil, sdkerrors.Wrap(clienttypes.ErrUpdateClientFailed, "client is not allowed to be unfrozen") - } - + if cs.Status(ctx, subjectClientStore, cdc) == exported.Frozen { // unfreeze the client cs.FrozenHeight = clienttypes.ZeroHeight() - - case exported.Expired: - if !cs.AllowUpdateAfterExpiry { - return nil, sdkerrors.Wrap(clienttypes.ErrUpdateClientFailed, "client is not allowed to be unexpired") - } - - default: - return nil, sdkerrors.Wrap(clienttypes.ErrUpdateClientFailed, "client cannot be updated with proposal") } // copy consensus states and processed time from substitute to subject diff --git a/modules/light-clients/07-tendermint/types/proposal_handle_test.go b/modules/light-clients/07-tendermint/types/proposal_handle_test.go index 3793bd43332..8d7ca6c7330 100644 --- a/modules/light-clients/07-tendermint/types/proposal_handle_test.go +++ b/modules/light-clients/07-tendermint/types/proposal_handle_test.go @@ -82,111 +82,31 @@ func (suite *TendermintTestSuite) TestCheckSubstituteAndUpdateState() { expPass bool }{ { - name: "not allowed to be updated, not frozen or expired", - AllowUpdateAfterExpiry: false, - AllowUpdateAfterMisbehaviour: false, - FreezeClient: false, - ExpireClient: false, - expPass: false, - }, - { - name: "not allowed to be updated, client is frozen", - AllowUpdateAfterExpiry: false, - AllowUpdateAfterMisbehaviour: false, - FreezeClient: true, - ExpireClient: false, - expPass: false, - }, - { - name: "not allowed to be updated, client is expired", - AllowUpdateAfterExpiry: false, - AllowUpdateAfterMisbehaviour: false, - FreezeClient: false, - ExpireClient: true, - expPass: false, - }, - { - name: "not allowed to be updated, client is frozen and expired", + name: "PASS: update checks are deprecated, client is frozen and expired", AllowUpdateAfterExpiry: false, AllowUpdateAfterMisbehaviour: false, FreezeClient: true, ExpireClient: true, - expPass: false, - }, - { - name: "allowed to be updated only after misbehaviour, not frozen or expired", - AllowUpdateAfterExpiry: false, - AllowUpdateAfterMisbehaviour: true, - FreezeClient: false, - ExpireClient: false, - expPass: false, - }, - { - name: "allowed to be updated only after misbehaviour, client is expired", - AllowUpdateAfterExpiry: false, - AllowUpdateAfterMisbehaviour: true, - FreezeClient: false, - ExpireClient: true, - expPass: false, - }, - { - name: "allowed to be updated only after expiry, not frozen or expired", - AllowUpdateAfterExpiry: true, - AllowUpdateAfterMisbehaviour: false, - FreezeClient: false, - ExpireClient: false, - expPass: false, - }, - { - name: "allowed to be updated only after expiry, client is frozen", - AllowUpdateAfterExpiry: true, - AllowUpdateAfterMisbehaviour: false, - FreezeClient: true, - ExpireClient: false, - expPass: false, - }, - { - name: "PASS: allowed to be updated only after misbehaviour, client is frozen", - AllowUpdateAfterExpiry: false, - AllowUpdateAfterMisbehaviour: true, - FreezeClient: true, - ExpireClient: false, expPass: true, }, { - name: "PASS: allowed to be updated only after misbehaviour, client is frozen and expired", + name: "PASS: update checks are deprecated, not frozen or expired", AllowUpdateAfterExpiry: false, AllowUpdateAfterMisbehaviour: true, - FreezeClient: true, - ExpireClient: true, - expPass: true, - }, - { - name: "PASS: allowed to be updated only after expiry, client is expired", - AllowUpdateAfterExpiry: true, - AllowUpdateAfterMisbehaviour: false, FreezeClient: false, - ExpireClient: true, + ExpireClient: false, expPass: true, }, { - name: "allowed to be updated only after expiry, client is frozen and expired", + name: "PASS: update checks are deprecated, not frozen or expired", AllowUpdateAfterExpiry: true, AllowUpdateAfterMisbehaviour: false, - FreezeClient: true, - ExpireClient: true, - expPass: false, - }, - { - name: "allowed to be updated after expiry and misbehaviour, not frozen or expired", - AllowUpdateAfterExpiry: true, - AllowUpdateAfterMisbehaviour: true, FreezeClient: false, ExpireClient: false, - expPass: false, + expPass: true, }, { - name: "PASS: allowed to be updated after expiry and misbehaviour, client is frozen", + name: "PASS: update checks are deprecated, client is frozen", AllowUpdateAfterExpiry: true, AllowUpdateAfterMisbehaviour: true, FreezeClient: true, @@ -194,21 +114,13 @@ func (suite *TendermintTestSuite) TestCheckSubstituteAndUpdateState() { expPass: true, }, { - name: "PASS: allowed to be updated after expiry and misbehaviour, client is expired", + name: "PASS: update checks are deprecated, client is expired", AllowUpdateAfterExpiry: true, AllowUpdateAfterMisbehaviour: true, FreezeClient: false, ExpireClient: true, expPass: true, }, - { - name: "PASS: allowed to be updated after expiry and misbehaviour, client is frozen and expired", - AllowUpdateAfterExpiry: true, - AllowUpdateAfterMisbehaviour: true, - FreezeClient: true, - ExpireClient: true, - expPass: true, - }, } for _, tc := range testCases { diff --git a/modules/light-clients/07-tendermint/types/tendermint.pb.go b/modules/light-clients/07-tendermint/types/tendermint.pb.go index 7257f37e5a5..00d2cba20e1 100644 --- a/modules/light-clients/07-tendermint/types/tendermint.pb.go +++ b/modules/light-clients/07-tendermint/types/tendermint.pb.go @@ -59,12 +59,10 @@ type ClientState struct { // the default upgrade module, upgrade_path should be []string{"upgrade", // "upgradedIBCState"}` UpgradePath []string `protobuf:"bytes,9,rep,name=upgrade_path,json=upgradePath,proto3" json:"upgrade_path,omitempty" yaml:"upgrade_path"` - // This flag, when set to true, will allow governance to recover a client - // which has expired - AllowUpdateAfterExpiry bool `protobuf:"varint,10,opt,name=allow_update_after_expiry,json=allowUpdateAfterExpiry,proto3" json:"allow_update_after_expiry,omitempty" yaml:"allow_update_after_expiry"` - // This flag, when set to true, will allow governance to unfreeze a client - // whose chain has experienced a misbehaviour event - AllowUpdateAfterMisbehaviour bool `protobuf:"varint,11,opt,name=allow_update_after_misbehaviour,json=allowUpdateAfterMisbehaviour,proto3" json:"allow_update_after_misbehaviour,omitempty" yaml:"allow_update_after_misbehaviour"` + // allow_update_after_expiry is deprecated + AllowUpdateAfterExpiry bool `protobuf:"varint,10,opt,name=allow_update_after_expiry,json=allowUpdateAfterExpiry,proto3" json:"allow_update_after_expiry,omitempty" yaml:"allow_update_after_expiry"` // Deprecated: Do not use. + // allow_update_after_misbehaviour is deprecated + AllowUpdateAfterMisbehaviour bool `protobuf:"varint,11,opt,name=allow_update_after_misbehaviour,json=allowUpdateAfterMisbehaviour,proto3" json:"allow_update_after_misbehaviour,omitempty" yaml:"allow_update_after_misbehaviour"` // Deprecated: Do not use. } func (m *ClientState) Reset() { *m = ClientState{} } @@ -325,75 +323,75 @@ func init() { } var fileDescriptor_c6d6cf2b288949be = []byte{ - // 1079 bytes of a gzipped FileDescriptorProto + // 1078 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xcf, 0x6f, 0xe3, 0x44, - 0x14, 0x6e, 0xda, 0xb2, 0x4d, 0x26, 0xe9, 0x76, 0x31, 0xa5, 0x9b, 0x96, 0x6e, 0x1c, 0x19, 0xb4, - 0x44, 0x48, 0xb5, 0x49, 0x8a, 0x84, 0x54, 0x71, 0xc1, 0xdd, 0x45, 0x2d, 0x62, 0xa5, 0xca, 0xe5, - 0x87, 0x84, 0x84, 0xcc, 0xc4, 0x9e, 0x24, 0xa3, 0xb5, 0x3d, 0xc6, 0x33, 0x09, 0x2d, 0x7f, 0x01, - 0x9c, 0xd8, 0x23, 0xe2, 0xc4, 0x81, 0x3f, 0x66, 0x8f, 0x3d, 0x72, 0x32, 0xa8, 0xbd, 0x70, 0xce, - 0x91, 0x13, 0x9a, 0x1f, 0xb6, 0xa7, 0xd9, 0x2e, 0xd5, 0x72, 0x89, 0xe6, 0xbd, 0xf7, 0xbd, 0xef, - 0xcb, 0xbc, 0x79, 0xf3, 0xc6, 0xc0, 0xc1, 0xc3, 0xc0, 0x89, 0xf0, 0x78, 0xc2, 0x82, 0x08, 0xa3, - 0x84, 0x51, 0x87, 0xa1, 0x24, 0x44, 0x59, 0x8c, 0x13, 0xe6, 0xcc, 0xfa, 0x9a, 0x65, 0xa7, 0x19, - 0x61, 0xc4, 0xe8, 0xe0, 0x61, 0x60, 0xeb, 0x09, 0xb6, 0x06, 0x99, 0xf5, 0x77, 0xba, 0x5a, 0x3e, - 0x3b, 0x4f, 0x11, 0x75, 0x66, 0x30, 0xc2, 0x21, 0x64, 0x24, 0x93, 0x0c, 0x3b, 0xbb, 0x2f, 0x20, - 0xc4, 0xaf, 0x8a, 0xb6, 0xd2, 0x8c, 0x90, 0x51, 0x61, 0x75, 0xc6, 0x84, 0x8c, 0x23, 0xe4, 0x08, - 0x6b, 0x38, 0x1d, 0x39, 0xe1, 0x34, 0x83, 0x0c, 0x93, 0x44, 0xc5, 0xcd, 0xc5, 0x38, 0xc3, 0x31, - 0xa2, 0x0c, 0xc6, 0x69, 0x01, 0xe0, 0xfb, 0x0b, 0x48, 0x86, 0x1c, 0xf9, 0x77, 0xf9, 0x9e, 0xe4, - 0x4a, 0x01, 0xde, 0xad, 0x00, 0x24, 0x8e, 0x31, 0x8b, 0x0b, 0x50, 0x69, 0x29, 0xe0, 0xe6, 0x98, - 0x8c, 0x89, 0x58, 0x3a, 0x7c, 0x25, 0xbd, 0xd6, 0xdf, 0x6b, 0xa0, 0x79, 0x28, 0xf8, 0x4e, 0x19, - 0x64, 0xc8, 0xd8, 0x06, 0xf5, 0x60, 0x02, 0x71, 0xe2, 0xe3, 0xb0, 0x5d, 0xeb, 0xd6, 0x7a, 0x0d, - 0x6f, 0x4d, 0xd8, 0xc7, 0xa1, 0x81, 0x40, 0x93, 0x65, 0x53, 0xca, 0xfc, 0x08, 0xcd, 0x50, 0xd4, - 0x5e, 0xee, 0xd6, 0x7a, 0xcd, 0x41, 0xcf, 0xfe, 0xef, 0x7a, 0xda, 0x9f, 0x64, 0x30, 0xe0, 0x1b, - 0x76, 0x77, 0x9e, 0xe7, 0xe6, 0xd2, 0x3c, 0x37, 0x8d, 0x73, 0x18, 0x47, 0x07, 0x96, 0x46, 0x65, - 0x79, 0x40, 0x58, 0x9f, 0x71, 0xc3, 0x18, 0x81, 0x0d, 0x61, 0xe1, 0x64, 0xec, 0xa7, 0x28, 0xc3, - 0x24, 0x6c, 0xaf, 0x08, 0xa9, 0x6d, 0x5b, 0x16, 0xcb, 0x2e, 0x8a, 0x65, 0x3f, 0x52, 0xc5, 0x74, - 0x2d, 0xc5, 0xbd, 0xa5, 0x71, 0x57, 0xf9, 0xd6, 0x2f, 0x7f, 0x9a, 0x35, 0xef, 0x6e, 0xe1, 0x3d, - 0x11, 0x4e, 0x03, 0x83, 0x7b, 0xd3, 0x64, 0x48, 0x92, 0x50, 0x13, 0x5a, 0xbd, 0x4d, 0xe8, 0x6d, - 0x25, 0x74, 0x5f, 0x0a, 0x2d, 0x12, 0x48, 0xa5, 0x8d, 0xd2, 0xad, 0xa4, 0x10, 0xd8, 0x88, 0xe1, - 0x99, 0x1f, 0x44, 0x24, 0x78, 0xea, 0x87, 0x19, 0x1e, 0xb1, 0xf6, 0x6b, 0xaf, 0xb8, 0xa5, 0x85, - 0x7c, 0x29, 0xb4, 0x1e, 0xc3, 0xb3, 0x43, 0xee, 0x7c, 0xc4, 0x7d, 0xc6, 0x37, 0x60, 0x7d, 0x94, - 0x91, 0x1f, 0x50, 0xe2, 0x4f, 0x10, 0x3f, 0x90, 0xf6, 0x1d, 0x21, 0xb2, 0x23, 0x8e, 0x88, 0xb7, - 0x88, 0xad, 0x3a, 0x67, 0xd6, 0xb7, 0x8f, 0x04, 0xc2, 0xdd, 0x55, 0x2a, 0x9b, 0x52, 0xe5, 0x5a, - 0xba, 0xe5, 0xb5, 0xa4, 0x2d, 0xb1, 0x9c, 0x3e, 0x82, 0x0c, 0x51, 0x56, 0xd0, 0xaf, 0xbd, 0x2a, - 0xfd, 0xb5, 0x74, 0xcb, 0x6b, 0x49, 0x5b, 0xd1, 0x1f, 0x83, 0xa6, 0xb8, 0x3a, 0x3e, 0x4d, 0x51, - 0x40, 0xdb, 0xf5, 0xee, 0x4a, 0xaf, 0x39, 0xb8, 0x67, 0xe3, 0x80, 0x0e, 0xf6, 0xed, 0x13, 0x1e, - 0x39, 0x4d, 0x51, 0xe0, 0x6e, 0x55, 0x2d, 0xa4, 0xc1, 0x2d, 0x0f, 0xa4, 0x05, 0x84, 0x1a, 0x07, - 0xa0, 0x35, 0x4d, 0xc7, 0x19, 0x0c, 0x91, 0x9f, 0x42, 0x36, 0x69, 0x37, 0xba, 0x2b, 0xbd, 0x86, - 0x7b, 0x7f, 0x9e, 0x9b, 0x6f, 0xa8, 0x73, 0xd3, 0xa2, 0x96, 0xd7, 0x54, 0xe6, 0x09, 0x64, 0x13, - 0xc3, 0x07, 0xdb, 0x30, 0x8a, 0xc8, 0xf7, 0xfe, 0x34, 0x0d, 0x21, 0x43, 0x3e, 0x1c, 0x31, 0x94, - 0xf9, 0xe8, 0x2c, 0xc5, 0xd9, 0x79, 0x1b, 0x74, 0x6b, 0xbd, 0xba, 0xfb, 0xce, 0x3c, 0x37, 0xbb, - 0x92, 0xe8, 0xa5, 0x50, 0xcb, 0xdb, 0x12, 0xb1, 0x2f, 0x44, 0xe8, 0x63, 0x1e, 0x79, 0x2c, 0x02, - 0xc6, 0x77, 0xc0, 0xbc, 0x21, 0x2b, 0xc6, 0x74, 0x88, 0x26, 0x70, 0x86, 0xc9, 0x34, 0x6b, 0x37, - 0x85, 0xcc, 0x7b, 0xf3, 0xdc, 0x7c, 0xf8, 0x52, 0x19, 0x3d, 0xc1, 0xf2, 0x76, 0x17, 0xc5, 0x9e, - 0x68, 0xe1, 0x83, 0xd5, 0x1f, 0x7f, 0x33, 0x97, 0xac, 0xdf, 0x97, 0xc1, 0xdd, 0x43, 0x92, 0x50, - 0x94, 0xd0, 0x29, 0x95, 0xb7, 0xdd, 0x05, 0x8d, 0x72, 0xe0, 0x88, 0xeb, 0xce, 0x8f, 0x73, 0xb1, - 0x25, 0x3f, 0x2f, 0x10, 0x6e, 0x9d, 0x1f, 0xe7, 0x33, 0xde, 0x79, 0x55, 0x9a, 0xf1, 0x11, 0x58, - 0xcd, 0x08, 0x61, 0x6a, 0x1e, 0x58, 0x5a, 0x37, 0x54, 0x13, 0x68, 0xd6, 0xb7, 0x9f, 0xa0, 0xec, - 0x69, 0x84, 0x3c, 0x42, 0x98, 0xbb, 0xca, 0x69, 0x3c, 0x91, 0x65, 0xfc, 0x54, 0x03, 0x9b, 0x09, - 0x3a, 0x63, 0x7e, 0x39, 0x65, 0xa9, 0x3f, 0x81, 0x74, 0x22, 0xee, 0x7c, 0xcb, 0xfd, 0x6a, 0x9e, - 0x9b, 0x6f, 0xc9, 0x1a, 0xdc, 0x84, 0xb2, 0xfe, 0xc9, 0xcd, 0x0f, 0xc6, 0x98, 0x4d, 0xa6, 0x43, - 0x2e, 0xa7, 0xcf, 0x7e, 0x6d, 0x19, 0xe1, 0x21, 0x75, 0x86, 0xe7, 0x0c, 0x51, 0xfb, 0x08, 0x9d, - 0xb9, 0x7c, 0xe1, 0x19, 0x9c, 0xee, 0xcb, 0x92, 0xed, 0x08, 0xd2, 0x89, 0x2a, 0xd3, 0xcf, 0xcb, - 0xa0, 0xa5, 0x57, 0xcf, 0xd8, 0x07, 0x0d, 0xd9, 0xd8, 0xe5, 0x4c, 0x14, 0x4d, 0x78, 0x4f, 0xfe, - 0xad, 0x32, 0x64, 0xb5, 0x6b, 0x5e, 0x5d, 0x5a, 0xc7, 0xa1, 0x01, 0x41, 0x7d, 0x82, 0x60, 0x88, - 0x32, 0xbf, 0xaf, 0x2a, 0xf3, 0xf0, 0xb6, 0x49, 0x79, 0x24, 0xf0, 0x6e, 0xe7, 0x32, 0x37, 0xd7, - 0xe4, 0xba, 0x3f, 0xcf, 0xcd, 0x0d, 0x29, 0x53, 0x90, 0x59, 0xde, 0x9a, 0x5c, 0xf6, 0x35, 0x89, - 0x81, 0x9a, 0x90, 0xff, 0x43, 0x62, 0xf0, 0x82, 0xc4, 0xa0, 0x94, 0x18, 0xa8, 0x8a, 0xfc, 0xba, - 0x02, 0xee, 0x48, 0xb4, 0x01, 0xc1, 0x3a, 0xc5, 0xe3, 0x04, 0x85, 0xbe, 0x84, 0xa8, 0xa6, 0xe9, - 0xe8, 0x3a, 0xf2, 0x35, 0x3c, 0x15, 0x30, 0x25, 0xb8, 0x7b, 0x91, 0x9b, 0xb5, 0x6a, 0x0e, 0x5c, - 0xa3, 0xb0, 0xbc, 0x16, 0xd5, 0xb0, 0x7c, 0xcc, 0x94, 0xa7, 0xec, 0x53, 0x54, 0x34, 0xd6, 0x0d, - 0x12, 0xe5, 0xf1, 0x9d, 0x22, 0xe6, 0xb6, 0x2b, 0xfa, 0x6b, 0xe9, 0x96, 0xd7, 0x9a, 0x69, 0x38, - 0xe3, 0x5b, 0x20, 0x1f, 0x02, 0xa1, 0x2f, 0xc6, 0xd8, 0xca, 0xad, 0x63, 0xec, 0x81, 0x1a, 0x63, - 0x6f, 0x6a, 0xcf, 0x4b, 0x99, 0x6f, 0x79, 0xeb, 0xca, 0xa1, 0x06, 0x59, 0x04, 0x8c, 0x02, 0x51, - 0xb5, 0xab, 0x7a, 0x5a, 0x6e, 0xdb, 0xc5, 0x83, 0x79, 0x6e, 0x6e, 0x5f, 0x57, 0xa9, 0x38, 0x2c, - 0xef, 0x75, 0xe5, 0xac, 0x1a, 0xd7, 0xfa, 0x14, 0xd4, 0x8b, 0x27, 0xd6, 0xd8, 0x05, 0x8d, 0x64, - 0x1a, 0xa3, 0x8c, 0x47, 0xc4, 0xc9, 0xac, 0x7a, 0x95, 0xc3, 0xe8, 0x82, 0x66, 0x88, 0x12, 0x12, - 0xe3, 0x44, 0xc4, 0x97, 0x45, 0x5c, 0x77, 0xb9, 0xfe, 0xf3, 0xcb, 0x4e, 0xed, 0xe2, 0xb2, 0x53, - 0xfb, 0xeb, 0xb2, 0x53, 0x7b, 0x76, 0xd5, 0x59, 0xba, 0xb8, 0xea, 0x2c, 0xfd, 0x71, 0xd5, 0x59, - 0xfa, 0xfa, 0xb1, 0x76, 0xc9, 0x02, 0x42, 0x63, 0x42, 0xf9, 0x87, 0xd7, 0xde, 0x98, 0x38, 0xb3, - 0x7d, 0x27, 0x26, 0xe1, 0x34, 0x42, 0x54, 0x7e, 0x86, 0xed, 0x15, 0xdf, 0x61, 0xef, 0x7f, 0xb8, - 0xb7, 0xf8, 0xa1, 0x34, 0xbc, 0x23, 0x86, 0xca, 0xfe, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xfb, - 0x37, 0x2a, 0xdd, 0xb6, 0x09, 0x00, 0x00, + 0x14, 0x6e, 0xda, 0xb2, 0x4d, 0x26, 0xe9, 0x76, 0x31, 0xa5, 0x9b, 0x96, 0x6e, 0x1c, 0x19, 0xa9, + 0xe4, 0x40, 0x6d, 0x92, 0x22, 0x21, 0x55, 0x5c, 0x70, 0x77, 0x51, 0x8b, 0x58, 0xa9, 0x72, 0xf9, + 0x21, 0x21, 0x21, 0x33, 0xb1, 0x27, 0xc9, 0x68, 0x6d, 0x8f, 0xe5, 0x99, 0x84, 0x96, 0xbf, 0x00, + 0x4e, 0xec, 0x11, 0x71, 0xe2, 0xc0, 0x1f, 0xb3, 0xc7, 0x1e, 0x39, 0x19, 0xd4, 0x5e, 0x39, 0xe5, + 0xc8, 0x09, 0xcd, 0x0f, 0xdb, 0xd3, 0x6c, 0x97, 0x6a, 0xb9, 0x44, 0xf3, 0xde, 0xfb, 0xde, 0xf7, + 0x65, 0xe6, 0xbd, 0x79, 0x63, 0xe0, 0xe0, 0x61, 0xe0, 0x44, 0x78, 0x3c, 0x61, 0x41, 0x84, 0x51, + 0xc2, 0xa8, 0xc3, 0x50, 0x12, 0xa2, 0x2c, 0xc6, 0x09, 0x73, 0x66, 0x7d, 0xcd, 0xb2, 0xd3, 0x8c, + 0x30, 0x62, 0x74, 0xf0, 0x30, 0xb0, 0xf5, 0x04, 0x5b, 0x83, 0xcc, 0xfa, 0x3b, 0x5d, 0x2d, 0x9f, + 0x5d, 0xa4, 0x88, 0x3a, 0x33, 0x18, 0xe1, 0x10, 0x32, 0x92, 0x49, 0x86, 0x9d, 0xdd, 0x97, 0x10, + 0xe2, 0x57, 0x45, 0x5b, 0x69, 0x46, 0xc8, 0xa8, 0xb0, 0x3a, 0x63, 0x42, 0xc6, 0x11, 0x72, 0x84, + 0x35, 0x9c, 0x8e, 0x9c, 0x70, 0x9a, 0x41, 0x86, 0x49, 0xa2, 0xe2, 0xe6, 0x62, 0x9c, 0xe1, 0x18, + 0x51, 0x06, 0xe3, 0xb4, 0x00, 0xf0, 0xfd, 0x05, 0x24, 0x43, 0x8e, 0xfc, 0xbb, 0x7c, 0x4f, 0x72, + 0xa5, 0x00, 0xef, 0x55, 0x00, 0x12, 0xc7, 0x98, 0xc5, 0x05, 0xa8, 0xb4, 0x14, 0x70, 0x73, 0x4c, + 0xc6, 0x44, 0x2c, 0x1d, 0xbe, 0x92, 0x5e, 0xeb, 0xef, 0x35, 0xd0, 0x3c, 0x12, 0x7c, 0x67, 0x0c, + 0x32, 0x64, 0x6c, 0x83, 0x7a, 0x30, 0x81, 0x38, 0xf1, 0x71, 0xd8, 0xae, 0x75, 0x6b, 0xbd, 0x86, + 0xb7, 0x26, 0xec, 0x93, 0xd0, 0x40, 0xa0, 0xc9, 0xb2, 0x29, 0x65, 0x7e, 0x84, 0x66, 0x28, 0x6a, + 0x2f, 0x77, 0x6b, 0xbd, 0xe6, 0xa0, 0x67, 0xff, 0xf7, 0x79, 0xda, 0x9f, 0x66, 0x30, 0xe0, 0x1b, + 0x76, 0x77, 0x5e, 0xe4, 0xe6, 0xd2, 0x3c, 0x37, 0x8d, 0x0b, 0x18, 0x47, 0x87, 0x96, 0x46, 0x65, + 0x79, 0x40, 0x58, 0x9f, 0x73, 0xc3, 0x18, 0x81, 0x0d, 0x61, 0xe1, 0x64, 0xec, 0xa7, 0x28, 0xc3, + 0x24, 0x6c, 0xaf, 0x08, 0xa9, 0x6d, 0x5b, 0x1e, 0x96, 0x5d, 0x1c, 0x96, 0xfd, 0x58, 0x1d, 0xa6, + 0x6b, 0x29, 0xee, 0x2d, 0x8d, 0xbb, 0xca, 0xb7, 0x7e, 0xf9, 0xd3, 0xac, 0x79, 0xf7, 0x0b, 0xef, + 0xa9, 0x70, 0x1a, 0x18, 0x3c, 0x98, 0x26, 0x43, 0x92, 0x84, 0x9a, 0xd0, 0xea, 0x5d, 0x42, 0xef, + 0x2a, 0xa1, 0x87, 0x52, 0x68, 0x91, 0x40, 0x2a, 0x6d, 0x94, 0x6e, 0x25, 0x85, 0xc0, 0x46, 0x0c, + 0xcf, 0xfd, 0x20, 0x22, 0xc1, 0x33, 0x3f, 0xcc, 0xf0, 0x88, 0xb5, 0xdf, 0x78, 0xcd, 0x2d, 0x2d, + 0xe4, 0x4b, 0xa1, 0xf5, 0x18, 0x9e, 0x1f, 0x71, 0xe7, 0x63, 0xee, 0x33, 0xbe, 0x05, 0xeb, 0xa3, + 0x8c, 0xfc, 0x80, 0x12, 0x7f, 0x82, 0x78, 0x41, 0xda, 0xf7, 0x84, 0xc8, 0x8e, 0x28, 0x11, 0x6f, + 0x11, 0x5b, 0x75, 0xce, 0xac, 0x6f, 0x1f, 0x0b, 0x84, 0xbb, 0xab, 0x54, 0x36, 0xa5, 0xca, 0x8d, + 0x74, 0xcb, 0x6b, 0x49, 0x5b, 0x62, 0x39, 0x7d, 0x04, 0x19, 0xa2, 0xac, 0xa0, 0x5f, 0x7b, 0x5d, + 0xfa, 0x1b, 0xe9, 0x96, 0xd7, 0x92, 0xb6, 0xa2, 0x3f, 0x01, 0x4d, 0x71, 0x75, 0x7c, 0x9a, 0xa2, + 0x80, 0xb6, 0xeb, 0xdd, 0x95, 0x5e, 0x73, 0xf0, 0xc0, 0xc6, 0x01, 0x1d, 0x1c, 0xd8, 0xa7, 0x3c, + 0x72, 0x96, 0xa2, 0xc0, 0xdd, 0xaa, 0x5a, 0x48, 0x83, 0x5b, 0x1e, 0x48, 0x0b, 0x08, 0x35, 0x0e, + 0x41, 0x6b, 0x9a, 0x8e, 0x33, 0x18, 0x22, 0x3f, 0x85, 0x6c, 0xd2, 0x6e, 0x74, 0x57, 0x7a, 0x0d, + 0xf7, 0xe1, 0x3c, 0x37, 0xdf, 0x52, 0x75, 0xd3, 0xa2, 0x96, 0xd7, 0x54, 0xe6, 0x29, 0x64, 0x13, + 0x03, 0x82, 0x6d, 0x18, 0x45, 0xe4, 0x7b, 0x7f, 0x9a, 0x86, 0x90, 0x21, 0x1f, 0x8e, 0x18, 0xca, + 0x7c, 0x74, 0x9e, 0xe2, 0xec, 0xa2, 0x0d, 0xba, 0xb5, 0x5e, 0xdd, 0xdd, 0x9b, 0xe7, 0x66, 0x57, + 0x12, 0xbd, 0x12, 0x6a, 0xb5, 0x6b, 0xde, 0x96, 0x88, 0x7e, 0x29, 0x82, 0x9f, 0xf0, 0xd8, 0x13, + 0x11, 0x32, 0x28, 0x30, 0x6f, 0xc9, 0x8b, 0x31, 0x1d, 0xa2, 0x09, 0x9c, 0x61, 0x32, 0xcd, 0xda, + 0x4d, 0x21, 0xf4, 0xfe, 0x3c, 0x37, 0xf7, 0x5e, 0x29, 0xa4, 0x27, 0x70, 0xb9, 0xdd, 0x45, 0xb9, + 0xa7, 0x1a, 0xe0, 0x70, 0xf5, 0xc7, 0xdf, 0xcc, 0x25, 0xeb, 0xf7, 0x65, 0x70, 0xff, 0x88, 0x24, + 0x14, 0x25, 0x74, 0x4a, 0xe5, 0x8d, 0x77, 0x41, 0xa3, 0x1c, 0x3a, 0xe2, 0xca, 0xf3, 0x92, 0x2e, + 0xb6, 0xe5, 0x17, 0x05, 0xc2, 0xad, 0xf3, 0x92, 0x3e, 0xe7, 0xdd, 0x57, 0xa5, 0x19, 0x1f, 0x83, + 0xd5, 0x8c, 0x10, 0xa6, 0x66, 0x82, 0xa5, 0x75, 0x44, 0x35, 0x85, 0x66, 0x7d, 0xfb, 0x29, 0xca, + 0x9e, 0x45, 0xc8, 0x23, 0x84, 0xb9, 0xab, 0x9c, 0xc6, 0x13, 0x59, 0xc6, 0x4f, 0x35, 0xb0, 0x99, + 0xa0, 0x73, 0xe6, 0x97, 0x93, 0x96, 0xfa, 0x13, 0x48, 0x27, 0xe2, 0xde, 0xb7, 0xdc, 0xaf, 0xe7, + 0xb9, 0xf9, 0x8e, 0x3c, 0x85, 0xdb, 0x50, 0xd6, 0x3f, 0xb9, 0xf9, 0xe1, 0x18, 0xb3, 0xc9, 0x74, + 0xc8, 0xe5, 0xf4, 0xf9, 0xaf, 0x2d, 0x23, 0x3c, 0xa4, 0xce, 0xf0, 0x82, 0x21, 0x6a, 0x1f, 0xa3, + 0x73, 0x97, 0x2f, 0x3c, 0x83, 0xd3, 0x7d, 0x55, 0xb2, 0x1d, 0x43, 0x3a, 0x51, 0xc7, 0xf4, 0xf3, + 0x32, 0x68, 0xe9, 0xa7, 0x67, 0x1c, 0x80, 0x86, 0x6c, 0xee, 0x72, 0x2e, 0x8a, 0x46, 0x7c, 0x20, + 0xff, 0x56, 0x19, 0xe2, 0x65, 0xa8, 0x4b, 0xeb, 0x24, 0x34, 0x20, 0xa8, 0x4f, 0x10, 0x0c, 0x51, + 0xe6, 0xf7, 0xd5, 0xc9, 0xec, 0xdd, 0x35, 0x2d, 0x8f, 0x05, 0xde, 0xed, 0x5c, 0xe5, 0xe6, 0x9a, + 0x5c, 0xf7, 0xe7, 0xb9, 0xb9, 0x21, 0x65, 0x0a, 0x32, 0xcb, 0x5b, 0x93, 0xcb, 0xbe, 0x26, 0x31, + 0x50, 0x53, 0xf2, 0x7f, 0x48, 0x0c, 0x5e, 0x92, 0x18, 0x94, 0x12, 0x03, 0x75, 0x22, 0xbf, 0xae, + 0x80, 0x7b, 0x12, 0x6d, 0x40, 0xb0, 0x4e, 0xf1, 0x38, 0x41, 0xa1, 0x2f, 0x21, 0xaa, 0x69, 0x3a, + 0xba, 0x8e, 0x7c, 0x11, 0xcf, 0x04, 0x4c, 0x09, 0xee, 0x5e, 0xe6, 0x66, 0xad, 0x9a, 0x05, 0x37, + 0x28, 0x2c, 0xaf, 0x45, 0x35, 0x2c, 0x1f, 0x35, 0x65, 0x95, 0x7d, 0x8a, 0x8a, 0xc6, 0xba, 0x45, + 0xa2, 0x2c, 0xdf, 0x19, 0x62, 0x6e, 0xbb, 0xa2, 0xbf, 0x91, 0x6e, 0x79, 0xad, 0x99, 0x86, 0x33, + 0xbe, 0x03, 0xf2, 0x31, 0x10, 0xfa, 0x62, 0x94, 0xad, 0xdc, 0x39, 0xca, 0x1e, 0xa9, 0x51, 0xf6, + 0xb6, 0xf6, 0xc4, 0x94, 0xf9, 0x96, 0xb7, 0xae, 0x1c, 0x6a, 0x98, 0x45, 0xc0, 0x28, 0x10, 0x55, + 0xbb, 0xaa, 0xe7, 0xe5, 0xae, 0x5d, 0x3c, 0x9a, 0xe7, 0xe6, 0xf6, 0x4d, 0x95, 0x8a, 0xc3, 0xf2, + 0xde, 0x54, 0xce, 0xaa, 0x71, 0xad, 0xcf, 0x40, 0xbd, 0x78, 0x66, 0x8d, 0x5d, 0xd0, 0x48, 0xa6, + 0x31, 0xca, 0x78, 0x44, 0x54, 0x66, 0xd5, 0xab, 0x1c, 0x46, 0x17, 0x34, 0x43, 0x94, 0x90, 0x18, + 0x27, 0x22, 0xbe, 0x2c, 0xe2, 0xba, 0xcb, 0xf5, 0x5f, 0x5c, 0x75, 0x6a, 0x97, 0x57, 0x9d, 0xda, + 0x5f, 0x57, 0x9d, 0xda, 0xf3, 0xeb, 0xce, 0xd2, 0xe5, 0x75, 0x67, 0xe9, 0x8f, 0xeb, 0xce, 0xd2, + 0x37, 0x4f, 0xb4, 0x4b, 0x16, 0x10, 0x1a, 0x13, 0xca, 0x3f, 0xbe, 0xf6, 0xc7, 0xc4, 0x99, 0x1d, + 0x38, 0x31, 0x09, 0xa7, 0x11, 0xa2, 0xf2, 0x53, 0x6c, 0xbf, 0xf8, 0x16, 0xfb, 0xe0, 0xa3, 0xfd, + 0xc5, 0x8f, 0xa5, 0xe1, 0x3d, 0x31, 0x54, 0x0e, 0xfe, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xdd, 0xc8, + 0x3e, 0xfe, 0xba, 0x09, 0x00, 0x00, } func (m *ClientState) Marshal() (dAtA []byte, err error) { diff --git a/proto/ibc/lightclients/tendermint/v1/tendermint.proto b/proto/ibc/lightclients/tendermint/v1/tendermint.proto index 0b55a20e433..7221f6502fa 100644 --- a/proto/ibc/lightclients/tendermint/v1/tendermint.proto +++ b/proto/ibc/lightclients/tendermint/v1/tendermint.proto @@ -52,12 +52,11 @@ message ClientState { // "upgradedIBCState"}` repeated string upgrade_path = 9 [(gogoproto.moretags) = "yaml:\"upgrade_path\""]; - // This flag, when set to true, will allow governance to recover a client - // which has expired - bool allow_update_after_expiry = 10 [(gogoproto.moretags) = "yaml:\"allow_update_after_expiry\""]; - // This flag, when set to true, will allow governance to unfreeze a client - // whose chain has experienced a misbehaviour event - bool allow_update_after_misbehaviour = 11 [(gogoproto.moretags) = "yaml:\"allow_update_after_misbehaviour\""]; + // allow_update_after_expiry is deprecated + bool allow_update_after_expiry = 10 [deprecated = true, (gogoproto.moretags) = "yaml:\"allow_update_after_expiry\""]; + // allow_update_after_misbehaviour is deprecated + bool allow_update_after_misbehaviour = 11 + [deprecated = true, (gogoproto.moretags) = "yaml:\"allow_update_after_misbehaviour\""]; } // ConsensusState defines the consensus state from Tendermint. From e1ec9f4154ea39d768e12d470dffc3985493c521 Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Tue, 26 Apr 2022 11:31:07 +0200 Subject: [PATCH 33/71] chore: update 02-client-refactor branch with latest main (#1286) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: allow the mock module to be used multiple times as base ibc application in middleware stack (#892) ## Description Currently the `AppModule` assumes a single scoped keeper. This doesn't allow the mock module to be used as a base application for different middleware stack (ica stack, fee stack, etc) I broke the API because I think it is cleaner. If we want this to be non API breaking, I can try to readjust ref: #891 --- 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. - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * feat: adding Pack/Unpack acknowledgement helper fns (#895) * feat: adding Pack/Unpack acknowledgement helper fns * chore: changelog * fix: docs * Update modules/core/04-channel/types/codec.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * imp: support custom keys for testing (#893) * chore: add ParsePacketFromEvents testing helper function (#904) ## Description ref: #891 --- 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. - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * fix: correctly claim capability for mock module, handle genesis exports (#921) ## Description This contains two fixes: - the capability being claimed by the scoped keeper was incorrect (mock.ModuleName -> port ID) - the mock module wasn't accounting for non empty genesis state in capabilities (after genesis export, capability will create the bound ports so rebinding doesn't need to happen) closes: #XXXX --- 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. - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * docs: update migration docs for upgrade proposal in relation to ICS27 (#920) ## Description closes: #XXXX --- 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. - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * chore(ica): add trail of bits audit report (#903) * chore(ica): add trail of bits audit report * relocate the audit report for ICA Co-authored-by: Carlos Rodriguez * testing: adding multiple sender accounts for testing purposes (#935) * testing: adding multiple sender accounts for testing puproses * fix genesis setup (#936) * Update testing/chain.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * refactor: code hygiene * Update testing/chain.go Co-authored-by: Aditya * fix: setting totalySupply to empty * nit: CamelCase not UPPERCASE Co-authored-by: Aditya Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * Create test chain with multiple validators (#942) * testing: adding multiple sender accounts for testing puproses * fix genesis setup (#936) * Update testing/chain.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * refactor: code hygiene * Update testing/chain.go Co-authored-by: Aditya * multi validator commit taken from @saione * add function to pass custom valset * add changelog Co-authored-by: Sean King Co-authored-by: Sean King Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * add changelog entry for SDK bump * fix: classify client states without consensus states as expired (#941) ## Description closes: #850 --- 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. - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * chore: fix broken link (#972) * add backport actions for v1.3.x and v2.1.x (#958) * Revert "feat: adding Pack/Unpack acknowledgement helper fns (#895)" (#973) This reverts commit 843b459635da8cedd92945141c4efe3a762f305d. Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> * chore: update migration docs (#985) * chore: update migration docs * Update docs/migrations/v2-to-v3.md Co-authored-by: Damian Nolan Co-authored-by: Damian Nolan * chore: fix mispelled words (#991) * fix: remove go mod tidy from proto-gen script (#989) * bug: support base denoms with slashes (#978) * bug: support base denoms with slashes * add changelog entry Co-authored-by: Carlos Rodriguez * upgrade ics23 to v0.7 (#948) * upgrade ics23 to v0.7-rc * add changelog entry * update ics23 to final 0.7 Co-authored-by: Carlos Rodriguez * ibctesting: make `testing.T` public (#1020) * add changelog entry for #941 * fix package import (#1007) * feat: Add a function to initialize the ICS27 module via an upgrade proposal (#1037) ## Description closes: #1034 --- 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. - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * docs: add missing args to NewKeeper in integration docs (#1038) ## Description This add some missing arguments to `ibckeeper.NewKeeper` and `ibctransferkeeper.NewKeeper` in integration docs --- 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)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [x] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * small fixes for v2 to v3 migration (#1016) * small fixes for v2 to v3 migration * review comment * Update v2-to-v3.md * add store upgrade documentation Co-authored-by: Carlos Rodriguez * add missing slash * build(deps): bump actions/checkout from 2.4.0 to 3 (#1045) Bumps [actions/checkout](https://github.com/actions/checkout) from 2.4.0 to 3.
Release notes

Sourced from actions/checkout's releases.

v3.0.0

  • Update default runtime to node16
Changelog

Sourced from actions/checkout's changelog.

Changelog

v2.3.1

v2.3.0

v2.2.0

v2.1.1

  • Changes to support GHES (here and here)

v2.1.0

v2.0.0

v2 (beta)

  • Improved fetch performance
    • The default behavior now fetches only the SHA being checked-out
  • Script authenticated git commands
    • Persists with.token in the local git config
    • Enables your scripts to run authenticated git commands
    • Post-job cleanup removes the token
    • Coming soon: Opt out by setting with.persist-credentials to false
  • Creates a local branch
    • No longer detached HEAD when checking out a branch
    • A local branch is created with the corresponding upstream branch set
  • Improved layout

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=2.4.0&new-version=3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
* call packet.GetSequence() rather than passing the func as argument (#995) * Add counterpartyChannelID param to IBCModule.OnChanOpenAck (#1086) * add counterpartyChannelID param to IBCModule OnChanOpenAck() * change testing mock * change ica IBCModules ChannelOpenAck * change transfer IBCModules ChannelOpenAck * change core keeper ChannelOpenAck() * CHANGELOG.md * update v2-to-v3 migration doc * Update docs/migrations/v2-to-v3.md Co-authored-by: Carlos Rodriguez Co-authored-by: Carlos Rodriguez * fix mirgation docs (#1091) * fix: handle testing update client errors (#1094) * replace channel keeper with IBC keeper in AnteDecorator (#950) * replace channel keeper with IBC keeper in AnteDecorator and pass message to rpc handler * fix error checking condition * fix for proper way of getting go context * refactor tests for ante handler * review comment * review comments and some fixes * review comments * execute message for update client as well * add migration Co-authored-by: Carlos Rodriguez * add backport rules for v1.4.x and v2.2.x (#1085) * ibctesting: custom voting power reduction for testing (#939) * ibctesting: custom voting power reduction for testing * changelog * fix * make T public * fix * revert changes * fix test * build(deps): bump github.com/spf13/cobra from 1.3.0 to 1.4.0 (#1105) * build(deps): bump google.golang.org/grpc from 1.44.0 to 1.45.0 (#1098) Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.44.0 to 1.45.0. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.44.0...v1.45.0) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix: adjust InitModule to account for empty controller and host keepers (#1120) ## Description closes: #XXXX --- 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//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` - [x] Re-reviewed `Files changed` in the Github PR explorer - [x] Review `Codecov Report` in the comment section below once CI passes * Merge pull request from GHSA-j658-c98j-fww4 Co-authored-by: Carlos Rodriguez * fixes for the documentation about handling ack for SDK <= 0.45 (#1122) * fixes for documentation * review comment Co-authored-by: Carlos Rodriguez * Allow testing to update ValidatorSet (#1003) * testing: adding multiple sender accounts for testing puproses * fix genesis setup (#936) * Update testing/chain.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * refactor: code hygiene * Update testing/chain.go Co-authored-by: Aditya * multi validator commit taken from @saione * add function to pass custom valset * create simplest failing test * progress * fix changevalset test * fix errors in tendermint package * fix client types test * fix client keeper * fix cap functions * fix genesis core tests * fix ica tests * fix doc * CHANGELOG * replace signer array with signer map * add documentation * fix merge * documentation * ordered signer array doc * add new delegation and comment to change valset test Co-authored-by: Sean King Co-authored-by: Sean King Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> Co-authored-by: Carlos Rodriguez * imp: create codeql-analysis action (#1128) ## Description Noticed that [CodeQL](https://codeql.github.com/) wasn't enabled on the IBC-go repo --- 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. - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * update changelog (#1131) * update changelog * fix typo * build(deps): bump github.com/stretchr/testify from 1.7.0 to 1.7.1 (#1134) Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.7.0 to 1.7.1. - [Release notes](https://github.com/stretchr/testify/releases) - [Commits](https://github.com/stretchr/testify/compare/v1.7.0...v1.7.1) --- updated-dependencies: - dependency-name: github.com/stretchr/testify dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Damian Nolan * call packet.GetSequence() rather than passing the func as argument (#1130) * call packet.GetSequence() rather than passing the func as argument * add changelog entry * fix: prefix ResponseResultType enum for proto linting (#1143) * build(deps): bump actions/cache from 2.1.7 to 3 (#1150) Bumps [actions/cache](https://github.com/actions/cache) from 2.1.7 to 3. - [Release notes](https://github.com/actions/cache/releases) - [Commits](https://github.com/actions/cache/compare/v2.1.7...v3) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fixes for go-releaser configuration (#1148) * set the pre-release status if the tag contains alpha, beta or rc * add separate filter for final releases * add banner image (#1158) Co-authored-by: Carlos Rodriguez * Add alpha, beta, and rc release definitions (#1151) ## Description The proposed definitions for each phase of our release cycle. Please feel free to adjust my wording closes: #881 --- 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. - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * build(deps): bump google.golang.org/protobuf from 1.27.1 to 1.28.0 (#1164) Bumps [google.golang.org/protobuf](https://github.com/protocolbuffers/protobuf-go) from 1.27.1 to 1.28.0.
Release notes

Sourced from google.golang.org/protobuf's releases.

v1.28.0

Overview

The release provides a new unmarshal option for limiting the recursion depth when unmarshalling nested messages to prevent stack overflows. (UnmarshalOptions.RecursionLimit).

Notable changes

New features:

  • CL/340489: testing/protocmp: add Message.Unwrap

Documentation improvements:

  • CL/339569: reflect/protoreflect: add more docs on Value aliasing

Updated supported versions:

UnmarshalOption RecursionLimit

  • CL/385854: all: implement depth limit for unmarshalling

The new UnmarshalOptions.RecursionLimit limits the maximum recursion depth when unmarshalling messages. The limit is applied for nested messages. When messages are nested deeper than the specified limit the unmarshalling will fail. If unspecified, a default limit of 10,000 is applied.

In addition to the configurable limit for message nesting a non-configurable recursion limit for group nesting of 10,000 was introduced.

Upcoming breakage changes

The default recursion limit of 10,000 introduced in the release is subject to change. We want to align this limit with implementations for other languages in the long term. C++ and Java use a limit of 100 which is also the target for the Go implementation.

Commits
  • 32051b4 all: release v1.28.0
  • 3992ea8 all: implement depth limit for unmarshaling
  • e5db296 all: update supported versions
  • 3a9e1dc all: gofmt all
  • 26e8bcb all: remove unnecessary string([]byte) conversion in fmt.Sprintf with %s
  • 5aec41b testing/protocmp: add Message.Unwrap
  • 05be61f reflect/protoreflect: add more docs on Value aliasing
  • b03064a all: start v1.27.1-devel
  • See full diff in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=google.golang.org/protobuf&package-manager=go_modules&previous-version=1.27.1&new-version=1.28.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
* fix typos in the controller params (#1172) ## Description closes: #XXXX --- 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)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] Added a relevant changelog entry to the `Unreleased` section in `CHANGELOG.md` - [x] Re-reviewed `Files changed` in the Github PR explorer - [ ] Review `Codecov Report` in the comment section below once CI passes * add versions for new releases (#1175) ## Description closes: #XXXX --- 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)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] Added a relevant changelog entry to the `Unreleased` section in `CHANGELOG.md` - [x] Re-reviewed `Files changed` in the Github PR explorer - [ ] Review `Codecov Report` in the comment section below once CI passes * fix: link checker reporting broken milestone link (#1200) * update roadmap for q2 2022 and deleted history roadmap (don't think we'll need it) * requirements document for ICA (#1173) * add requirements document for interchain accounts * fix branch * added number in tittle. * apply suggestions from review Co-authored-by: Aditya * review comment Co-authored-by: Carlos Rodriguez Co-authored-by: Aditya * imp: improve Logger performance (#1160) * fix: Logger marshal errors * changelog * update * ICS 29: Fee Middleware (#276) * scaffolding for 29-fee (#274) * scaffolding for 29-fee * fix build * update keeper test * remove module test * feat: adding proto files for fee payment middleware (#272) * feat: adding proto files for fee payment middleware * grammar * fix: remove generated .pb files * fix: comment * feat: adding PacketId type * refactor: fee / genesis * refactor: escrowed fees map * Apply suggestions from code review Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * Update proto/ibc/applications/middleware/fee/v1/tx.proto Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * Update proto/ibc/applications/middleware/fee/v1/tx.proto Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * Update proto/ibc/applications/middleware/fee/v1/tx.proto Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * refactor: use packetID + minor changes * feat: adding query for all incentivized packets + some fixes * feat: adding pagination to incentivized query * fix: removing generated ibc directory + adding import/yaml * fix: naming * increase max depth for proto file searching and make proto all * Update proto/ibc/applications/middleware/fee/v1/fee.proto Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * refactor: remove file imports/add yaml/add argument for requests * refactor: updating IdentifiedPacketFee * fix: remove hidden file * removing middleware dir & adding query * remove junk file and update query rpcs * Apply suggestions from code review * Apply suggestions from code review * remove query yaml, make proto-all Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> Co-authored-by: Aditya Sripal * fix: removing unncessary fields MsgEscrow & adding query params (#300) * fix: removing unncessary fields MsgEscrow & adding query params * fix: grammar * fix: add yaml * feat: #258 Register Counterparty Address (#376) * feat: adding MsgServer for RegisterCounterPartyAddress & EscrowPacketFree * test: adding test for ValidateBasic * fix: removing validate basic check * fix: removing empty file * Update modules/apps/29-fee/keeper/msg_server.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * Update modules/apps/29-fee/types/msgs.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * Update modules/apps/29-fee/types/keys.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * Update modules/apps/29-fee/keeper/keeper.go Co-authored-by: Aditya * fix: fixing typos, variable names, comments * fix: updating import comments * test: adding test for KeyRelayerAddress * update: comments & key_test * Update modules/apps/29-fee/keeper/msg_server.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * fix: error message * docs: updating RegisterCounterpartyAddress fn description Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> Co-authored-by: Aditya * fix: remove comments for imports (#385) * feat: Add handshake logic to ics29 (#307) * do handshake logic, create test file * do cap logic and fix build * open handshake implementation and tests * remove prints * Update modules/apps/29-fee/module.go Co-authored-by: Sean King * debugging progress * fee enabled flag * cleanup handshake logic * fix tests * much cleaner simapp * split module.go file * cleanup and docs * assert IBC interfaces are fulfilled in middleware * Update modules/apps/transfer/module.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * fix unnecessary crossing hello logic * fix version negotiation bugs and improve tests * cleanup tests * Apply suggestions from code review Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * address rest of colin comments Co-authored-by: Sean King Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * Fee Middleware: Escrow logic (#465) * fix: adding second endpoint for async pay fee + renaming types * feat: adding escrow logic * feat: updating proto types & escrow logic * fix: stub fn & proto comment * feat: adding PayFee & PayFeeTimeout & escrow_test * test: adding happy path for EscrowPacketFee * fix: comments, error handling * fix: comments & grammar * test: adding unhappy path for escrow * tests(escrow): adding hasBalance check for module acc * test(PayFee): adding happy path for PayFee tests * tests(PayFee, PayFeeTimeout): adding tests * fix: adding relayers back to IdentifiedPacket * fix: removing refund acc from key * fix: storing IdentifiedPacketFee in state instead of Fee * feat: adding msg_server test for registerCPAddr, wiring for codec + stubs for sdk.Msg interface * test: adding msg_server test for PayPacketFee * test: adding PayPacketFeeAsync msg_server test * chore: updating PayFee -> DistributeFee & minor nits * nit: removing unnecessary nil check * refactor: add portId to store key & use packetId as param * fix: add DeleteFeeInEscrow & remove fee on successful distribution * tests: adding validation & signer tests for PayFee/Async & updating proto to use Signer sdk standard * chore: adding NewIdentifiedPacketFee fn * fix: getter/setter for counterparty address + fix NewIdentifiedPacketFee * fix: updating EscrowPacketFee with correct usage of coins api * test: adding balance check for refund acc after escrow * fix: remove unncessary errors * test: updating escrow tests + miscellaneous fixes * nit: updating var names * docs: godoc * refactor: IdentifiedPacketFee & Fee no longer pointers * fixes: small fixes * Update modules/apps/29-fee/keeper/escrow.go Co-authored-by: Aditya * Update modules/apps/29-fee/keeper/escrow.go Co-authored-by: Aditya * Update modules/apps/29-fee/keeper/keeper.go Co-authored-by: Aditya * Update modules/apps/29-fee/keeper/msg_server.go Co-authored-by: Aditya * Update modules/apps/29-fee/keeper/msg_server.go Co-authored-by: Aditya * Update modules/apps/29-fee/types/msgs.go Co-authored-by: Aditya * nit: proto doc & error fix * fix: escrow test * test: updating distribute fee tests * test: adding validation check for fee and updating tests * test: allow counterparty address to be arbitrary string * fix: message validation should pass if one fee is valid * Update modules/apps/29-fee/keeper/escrow.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * Update modules/apps/29-fee/keeper/escrow.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * fix: nits * Update modules/apps/29-fee/keeper/escrow.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * test: adding isZero check for msgs Co-authored-by: Aditya Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * feat: update protos, grpc queries (#488) * store refund address in IdentifiedPacketFee (#546) * 29-Fee: Genesis (#557) * proto: adding genesis state * feat: add GetAllIdentifiedPacketFees * feat: adding genesis.go & updating proto + app.go * fix: removing PortId from genesis * feat: adding GetAll for relayer addr/fee enabled chan + update genesis * test: TestExportGenesis * feat: update type + hook up to module.go * fix: remove PortKey * fix: imports + remove scoped keeper * nit: using NewPacketId helper and updating helper def to have correct params * feat: adding genesis validation + tests (#561) * feat: adding genesis validation + tests * fix: imports * Update modules/apps/29-fee/types/genesis.go * fix: nit * Update modules/apps/29-fee/types/genesis_test.go Co-authored-by: Aditya * nit: imporve default gen val test * chore: move packetId + val to channeltypes and use validate fn Co-authored-by: Aditya * feat: add incentivised ack proto (#564) * proto file * incentivized ack proto * Fee Closing Handshake (#551) * add iterate logic * add closing logic with tests * add comments for panic * change invariant breaking recovery to disabling middleware rather than panicing * docs, tests, minor refactor * Fee Middleware: Add ICS4 wrapper (#562) * chore: add ICS4 wrapper * fix: remove channelKeeper sender packet * chore: add WriteAck * feat: ics 29 packet callbacks (#357) * update imports to v3 * regenerate proto files * fix build * fix: event caching for fee distribution (#661) * proto file * initial impl * apply self review suggestions Deduplicate fee distribution code. Rename DistributeFee to DistributePacketFees. Rename DistributeFeeTimeout to DistributePacketFeesOnTimeout * fixup tests rename validCoins. DistributePacketFeesOnTimeout no longer has a valid error case Add test case for invalid forward relayer address on DistributePacketFees. * partially fix tests timeout fee is still being distributed depsite WriteFn() not being called * fix tests * address code nit Co-authored-by: Colin Axnér <25233464+colin-axner@users.noreply.github.com> * ics4 callbacks fee middleware (#580) * feat: adding WriteAcknowledgement * updating genesis & relayer prefix * fix: comment * fix: comments * Update modules/apps/29-fee/keeper/relay.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * feat: add DeleteForwardRelayerAddr helper + use Set in ack * fix: SetForwardAddr * chore: add panic * fix: remove fmt * test: add WriteAcknowledgement test * Update modules/apps/29-fee/ibc_module.go Co-authored-by: Aditya * fix: remove print * fix: WriteAck * fix: use constructor * Update modules/apps/29-fee/keeper/keeper.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * fix: nits * fix: remove found var not used * test: adding check that forward relayer address is successfully deleted if set * fix: merge issues Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> Co-authored-by: Aditya * chore: making PacketId non nullable (#737) * nits: proto spacing + naming (#739) * nits: proto spacing + naming * nit: update comment * fix: go.mod * nit: option above import proto * fix: spacing * sean/fix-proto-identified-fee-not-null (#746) * nits: more ics29 nits (#741) * nits: remove capital from error + add godoc * nit: add Wrapf * nit: use strings.TrimSpace * nit: add err type for MsgPayPacketFee * refactor: app version + add comment (#750) * chore: remove error * test: add test for whitespaced empty string * nit: update err syntax (#747) * nit: update err syntax * nit: more * nit: err syntax * feat: adding Route, Type, GetSignBytes for all messages (#743) * feat: adding Route, Type, GetSignBytes for all messages * tests: adding tests for Route/Type/GetSignBytes * hygiene: add validate fn for Fee (#748) * hygiene: add validate fn for Fee * Update modules/apps/29-fee/types/msgs.go Co-authored-by: Damian Nolan * fix: error message * test: move Validate to fee.go & abstract out test * chore: remove test cases Co-authored-by: Damian Nolan * fix: app.go (#789) * refactor: ics29 json encoded version metadata (#883) * adding metadata type to ics29 protos * updating ics29 handshake handlers to support json encoded metadata * updating tests to support json encoded metadata * Update modules/apps/29-fee/ibc_module.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * Update modules/apps/29-fee/ibc_module.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * renaming metadata version to fee_version Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * fix: return nil on OnRecvPacket for async pay (#911) * nit: ics29 comments (#910) * fix: comments * Update modules/apps/29-fee/keeper/escrow.go Co-authored-by: Aditya * chore: Add transfer test for ics29 (#901) * begin writing transfer test for ics29 * finish writing transfer test * refactor: ics29 OnChanOpenInit callback tests now use mock module (#924) * refactor: OnChanOpenInit callback tests now use mock module * Update modules/apps/29-fee/fee_test.go * feat: allow multiple addrs to incentivize packets (#915) * [WIP] allow multiple addresses to incentivize a packet * distribute multiple fees, fix broken tests * use NewIdentifiedPacketFees in EscrowPacketFee * cleanup var naming * removing commented out code and adding test case * Update modules/apps/29-fee/ibc_module.go Co-authored-by: Aditya * fix: refund RecvFee if ForwardAddr is invalid * test: update tests to distribute multiple identified fees * refactor: clean up DistrPacketFees * refactor: using .Empty() helper func for code hygiene Co-authored-by: Aditya Co-authored-by: Sean King Co-authored-by: Sean King * chore: remove spec directory from ics29 (#934) * refactor: use mock module for ics29 closing handshakes (#926) * refactor: use mock module for closing handshakes in ics29 * self-review fix * refactor: use mock module for ics29 grpc_query_test.go (#933) * refactor: readjust keeper_test.go to use mock module (#930) * fix: fields for genesis should be non nullable (#938) * refactor: use mock module for ics29 escrow_test.go (#932) * refactor: use mock module for ics29 genesis_test.go (#931) * ics29:feat: emit event escrow (#914) * feat: emit EventTypeSendIncentivizedPacket event on EscrowPacket * fix: string conversion * refactor: add helper fn for emit event * chore: godoc * nit: use .String()) * refactor: OnRecvPacket to use mock module (#927) Co-authored-by: Sean King * refactor: ics29 OnChanOpenTry/Ack use mock module for testing instead of ics20 (#925) Co-authored-by: Sean King * refactor: use mock module for OnAcknowledgePacket callback testing (#929) Co-authored-by: Sean King * refactor: OnTimeoutPacket to use mock module (#928) Co-authored-by: Sean King * chore: add packet id arg to EscrowPacketFee (#951) * adding packet id arg to EscrowPacketFee * updating tests * review adaptations * chore: remove legacy testing functions (#954) * fix:ics29: WriteAck update + adding success bool to IncentivizedAck (#952) * fix: updating WriteAck & adding Success boolean to IncentivizedAcknowledgement * feat: adding check of is fee enabled * nit: change successful to underlying_application_success * test: adding seperate test for fee disabled write async * Update modules/apps/29-fee/ibc_module_test.go Co-authored-by: Aditya * test: adding check to compare hash of acks * fix: var name Co-authored-by: Aditya * chore: add cli cmd to incentivize existing packet (async) (#965) * chore: add cli to incentivize existing packets * Update modules/apps/29-fee/client/cli/cli.go * Update modules/apps/29-fee/client/cli/cli.go Co-authored-by: Aditya * chore: update cli example Co-authored-by: Aditya * ics29:fix: counterparty addr must contain channelID (#937) * fix: counterparty address must chain channelID * nit: updating var name * test: adding validation check for channelID * nit: fn names * chore: fix err msg (#971) * ics29:fix: store source address for query later on WriteAck (#912) * fix: for async WriteAck store source address for query later * ics29:fix: update genesis type (#913) * fix: adding ForwardRelayerAddresses to genesis * fix: trimspace on string check * nit: err + trimspace error case * refactor: updating WriteAck + keeper fn name * Update modules/apps/29-fee/keeper/relay.go Co-authored-by: Damian Nolan * chore: remove legacy testing functions (#954) * fix:ics29: WriteAck update + adding success bool to IncentivizedAck (#952) * fix: updating WriteAck & adding Success boolean to IncentivizedAcknowledgement * feat: adding check of is fee enabled * nit: change successful to underlying_application_success * test: adding seperate test for fee disabled write async * Update modules/apps/29-fee/ibc_module_test.go Co-authored-by: Aditya * test: adding check to compare hash of acks * fix: var name Co-authored-by: Aditya Co-authored-by: Damian Nolan Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> Co-authored-by: Aditya * refactor: make fee storage more efficient (#956) * adding new proto types and codegen * refactoring ics29 fees for more efficient storage * updating tests * fixing typo in protodoc comments * chore: update ics29 genesis state to support multiple packet fees (#957) * adding new proto types and codegen * refactoring ics29 fees for more efficient storage * updating tests * updating genesis protos to use IdentifiedPacketFees * updating init/export genesis state functionality and tests * chore: update MsgPayPacketFeeAsync fields (#979) * adding new proto types and codegen * refactoring ics29 fees for more efficient storage * updating tests * fixing typo in protodoc comments * updating protos and codegen * updating MsgPayPacketFeeAsync handler and tests * chore: add ParseKeyFeesInEscrow helper function (#998) * chore: update grpc queries to handle multiple fees (#967) * adding new proto types and codegen * refactoring ics29 fees for more efficient storage * updating tests * updating protos and existing queries * updating grpc queries and refactoring tests * format error correct in favour of proto string() method * leveraging ParseKeyFeesInEscrow to obtain packet id in query * feat: CLI cmd for MsgRegisterCounterpartyAddress (#987) * feat: CLI cmd for MsgRegisterCounterpartyAddress * fix: examples * Update modules/apps/29-fee/client/cli/tx.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * Update modules/apps/29-fee/client/cli/tx.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * chore: remove print * nit: update address for counterparty Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * fix: ics29: switch source with destintion for chan/port IDs (#961) * fix: switch source with destintion for chan/port IDs * fix: blunder * test: adding tests in case of incorrect channel/port id * test: moving check to WriteAcknowledgement * add test case for Get/Set counterparty address * nit: path name * Update modules/apps/29-fee/keeper/msg_server_test.go * test: cleanup 29-fee/types tests (#1006) * feat: grpc query total recv packet fees (#1015) * adding query for total packet recv fees to proto query server * adding total packet recv fee query impl and tests * updating doc comments * chore: switch code ordering (#1025) * feat: Add ParseKeyFeeEnabled and rename FeeEnabledKey -> KeyFeeEnabled (#1023) * chore: add ParseKeyFeesInEscrow helper function * feat: add ParseKeyFeeEnabled function and rename FeeEnabledKey to KeyFeeEnabled * feat: ics29 cli for query total recv fees (#1035) * feat: grpc query total ack fees (#1032) * adding query for total packet recv fees to proto query server * adding total packet recv fee query impl and tests * updating doc comments * adding protos and codegen * adding total ack fees query and tests * fixing protodoc comment * feat: grpc query total timeout fees (#1033) * adding query for total packet recv fees to proto query server * adding total packet recv fee query impl and tests * updating doc comments * adding protos and codegen * adding total ack fees query and tests * adding protos and codegen * adding query total timeout fees and tests * fixing protodoc comment * fixing protodoc comment * feat: adding clis for total ack and timeout queries (#1043) * add ParseKeyForwardRelayerAddress function + test (#1046) * chore: remove unused ics29 keeper funcs (#1044) * removing keys, adding additional test, moving event attribute keys * removing unused code and updating tests * removing unused IdentifiedPacketFee type * chore: add gRPC for querying incentivized packets for a specific channel (#983) * generate proto files * feat: add gRPC for querying incentivized packets for a specific channel * test: add gRPC test for incentivized packets for channel query * fix build * partially fix tests * chore: fix tests * deduplicate code * chore: code cleanup * fix build * remove changes from merge conflict * nit: rename c to goCtx * add function EscrowAccountHasBalance (#1042) * add function EscrowAccountHasBalance * change API to use sdk.Coins * feat: ParseKeyCounterpartyRelayer function (#1047) * chore: adding queries to cmd builder (#1057) * chore: update ics29 protodocs (#1055) * updating protodocs comments and regen code/docs * Update proto/ibc/applications/fee/v1/tx.proto Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * updating incentivized ack doc Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * add counter party channel ID to argument list of on channel open ack (#1159) Co-authored-by: Carlos Rodriguez * ADR 004: Fee module locking in the presence of severe bugs (#1060) * add adr 004 * add to README * Update docs/architecture/adr-004-ics29-lock-fee-module.md Co-authored-by: Aditya * Update docs/architecture/adr-004-ics29-lock-fee-module.md Co-authored-by: Aditya Co-authored-by: Carlos Rodriguez Co-authored-by: Aditya * nit: packetID var name (#1214) * ics29: update with changes from main (#1221) * add banner image (#1158) Co-authored-by: Carlos Rodriguez * Add alpha, beta, and rc release definitions (#1151) ## Description The proposed definitions for each phase of our release cycle. Please feel free to adjust my wording closes: #881 --- 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. - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * build(deps): bump google.golang.org/protobuf from 1.27.1 to 1.28.0 (#1164) Bumps [google.golang.org/protobuf](https://github.com/protocolbuffers/protobuf-go) from 1.27.1 to 1.28.0.
Release notes

Sourced from google.golang.org/protobuf's releases.

v1.28.0

Overview

The release provides a new unmarshal option for limiting the recursion depth when unmarshalling nested messages to prevent stack overflows. (UnmarshalOptions.RecursionLimit).

Notable changes

New features:

  • CL/340489: testing/protocmp: add Message.Unwrap

Documentation improvements:

  • CL/339569: reflect/protoreflect: add more docs on Value aliasing

Updated supported versions:

UnmarshalOption RecursionLimit

  • CL/385854: all: implement depth limit for unmarshalling

The new UnmarshalOptions.RecursionLimit limits the maximum recursion depth when unmarshalling messages. The limit is applied for nested messages. When messages are nested deeper than the specified limit the unmarshalling will fail. If unspecified, a default limit of 10,000 is applied.

In addition to the configurable limit for message nesting a non-configurable recursion limit for group nesting of 10,000 was introduced.

Upcoming breakage changes

The default recursion limit of 10,000 introduced in the release is subject to change. We want to align this limit with implementations for other languages in the long term. C++ and Java use a limit of 100 which is also the target for the Go implementation.

Commits
  • 32051b4 all: release v1.28.0
  • 3992ea8 all: implement depth limit for unmarshaling
  • e5db296 all: update supported versions
  • 3a9e1dc all: gofmt all
  • 26e8bcb all: remove unnecessary string([]byte) conversion in fmt.Sprintf with %s
  • 5aec41b testing/protocmp: add Message.Unwrap
  • 05be61f reflect/protoreflect: add more docs on Value aliasing
  • b03064a all: start v1.27.1-devel
  • See full diff in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=google.golang.org/protobuf&package-manager=go_modules&previous-version=1.27.1&new-version=1.28.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
* fix typos in the controller params (#1172) ## Description closes: #XXXX --- 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)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] Added a relevant changelog entry to the `Unreleased` section in `CHANGELOG.md` - [x] Re-reviewed `Files changed` in the Github PR explorer - [ ] Review `Codecov Report` in the comment section below once CI passes * add versions for new releases (#1175) ## Description closes: #XXXX --- 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)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] Added a relevant changelog entry to the `Unreleased` section in `CHANGELOG.md` - [x] Re-reviewed `Files changed` in the Github PR explorer - [ ] Review `Codecov Report` in the comment section below once CI passes * fix: link checker reporting broken milestone link (#1200) * update roadmap for q2 2022 and deleted history roadmap (don't think we'll need it) * requirements document for ICA (#1173) * add requirements document for interchain accounts * fix branch * added number in tittle. * apply suggestions from review Co-authored-by: Aditya * review comment Co-authored-by: Carlos Rodriguez Co-authored-by: Aditya * imp: improve Logger performance (#1160) * fix: Logger marshal errors * changelog * update Co-authored-by: Carlos Rodriguez Co-authored-by: Carlos Rodriguez Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Damian Nolan Co-authored-by: Aditya Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> Co-authored-by: Sean King Co-authored-by: Charly Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Carlos Rodriguez Co-authored-by: Carlos Rodriguez Co-authored-by: Luke Rhoads <51463884+lukerhoads@users.noreply.github.com> Co-authored-by: Damian Nolan Co-authored-by: Dev Ojha Co-authored-by: Jack Zampolin Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> Co-authored-by: Leo Pang <34628052+allthatjazzleo@users.noreply.github.com> Co-authored-by: Barrie Byron Co-authored-by: Tyler <48813565+technicallyty@users.noreply.github.com> Co-authored-by: technicallyty <48813565+tytech3@users.noreply.github.com> Co-authored-by: Barrie Byron Co-authored-by: Marko Co-authored-by: Marko Baricevic Co-authored-by: Aleksandr Bezobchuk Co-authored-by: nir1218 Co-authored-by: Carlos Rodriguez Co-authored-by: Assaf Morami Co-authored-by: Dan McCandless Co-authored-by: Ramiro Carlucho Co-authored-by: frog power 4000 Co-authored-by: Sean King * build(deps): bump codecov/codecov-action from 2.1.0 to 3 (#1222) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 2.1.0 to 3. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/master/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v2.1.0...v3) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * ics29: feat: CLI query commands for packets (#1229) * feat: adding query for getting incentivized packet by packet-id * feat: add cli for getting all incentivized packets across all channels * nits: suggestions * Update modules/apps/29-fee/client/cli/query.go Co-authored-by: Damian Nolan * chore: changelog * nits: comments Co-authored-by: Damian Nolan * add check for blocked addr for forward relayer distribution (#1231) * feat: ics29 counterparty address grpc query and CLI (#1224) * generating codegen protobufs for query counterparty address * adding grpc query, tests and cli * Update modules/apps/29-fee/keeper/grpc_query_test.go Co-authored-by: Sean King * Update modules/apps/29-fee/keeper/grpc_query_test.go Co-authored-by: Sean King * updating godoc * adding changelog * updating cli descriptions for ics29 Co-authored-by: Sean King * chore: fix go ctx arg naming in ics29 grpc queries (#1226) * chore: ics29 module housekeeping cleanup (#1227) * feat: ics29 fee enabled channel queries (#1225) * adding protobuf codegen * adding grpc queries and tests * adding clis for queries * adding changelog * resolving nits from pr review * updating grpc gateway options * chore: cleanup OnAcknowledgement and OnTimeout code ics29 (#1228) * add changelog entry for #276 (#1233) * feat: adding CLI for getting incentivized packets on a specific channel (#1230) * feat: adding CLI for getting incentivized packets on a specific channel * chore: changelog * refactor: usage * chore: ics29 cleanup, addressing nits (#1236) * refactor: moving fn definition to bottom of file and switching params (#1232) ## Description closes: #868 #997 --- 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. - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * feat: allow ability to lock fee module in presence of severe bug (#1239) * apply code changes from pr #1031 * update comments * chore: update fee module is locked error string * update godoc to reference ADR 004 * add more references to ADR 004 * build(deps): bump actions/download-artifact from 2 to 3 (#1241) Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 2 to 3.
Release notes

Sourced from actions/download-artifact's releases.

v3.0.0

What's Changed

Breaking Changes

With the update to Node 16, all scripts will now be run with Node 16 rather than Node 12.

v2.1.0 Download Artifact

  • Improved output & logging
  • Fixed issue where downloading all artifacts could cause display percentages to be over 100%
  • Various small bug fixes & improvements

v2.0.10

  • Retry on HTTP 500 responses from the service

v2.0.9

  • Fixes to proxy related issues

v2.0.8

  • Improvements to retryability if an error is encountered during artifact download

v2.0.7 download-artifact

  • Improved download retry-ability if a partial download is encountered

v2.0.6

Update actions/core NPM package that is used internally

v2.0.5

  • Add Third Party License Information

v2.0.4

  • Use the latest version of the @actions/artifact NPM package

v2.0.3

  • Misc improvements

v2.0.2

  • Support for tilde expansion

v2.0.1

  • Download path output
  • Improved logging
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/download-artifact&package-manager=github_actions&previous-version=2&new-version=3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
* build(deps): bump actions/upload-artifact from 2 to 3 (#1240) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2 to 3.
Release notes

Sourced from actions/upload-artifact's releases.

v3.0.0

What's Changed

  • Update default runtime to node16 (#293)
  • Update package-lock.json file version to 2 (#302)

Breaking Changes

With the update to Node 16, all scripts will now be run with Node 16 rather than Node 12.

v2.3.1

Fix for empty fails on Windows failing on upload #281

v2.3.0 Upload Artifact

  • Optimizations for faster uploads of larger files that are already compressed
  • Significantly improved logging when there are chunked uploads
  • Clarifications in logs around the upload size and prohibited characters that aren't allowed in the artifact name or any uploaded files
  • Various other small bugfixes & optimizations

v2.2.4

  • Retry on HTTP 500 responses from the service

v2.2.3

  • Fixes for proxy related issues

v2.2.2

  • Improved retryability and error handling

v2.2.1

  • Update used actions/core package to the latest version

v2.2.0

  • Support for artifact retention

v2.1.4

  • Add Third Party License Information

v2.1.3

  • Use updated version of the @action/artifact NPM package

v2.1.2

  • Increase upload chunk size from 4MB to 8MB
  • Detect case insensitive file uploads

v2.1.1

  • Fix for certain symlinks not correctly being identified as directories before starting uploads

v2.1.0

  • Support for uploading artifacts with multiple paths
  • Support for using exclude paths
  • Updates to dependencies

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/upload-artifact&package-manager=github_actions&previous-version=2&new-version=3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
* build(deps): bump actions/setup-go from 2 to 3 (#1242) Bumps [actions/setup-go](https://github.com/actions/setup-go) from 2 to 3.
Release notes

Sourced from actions/setup-go's releases.

v3.0.0

What's Changed

Breaking Changes

With the update to Node 16, all scripts will now be run with Node 16 rather than Node 12.

This new major release removes the stable input, so there is no need to specify additional input to use pre-release versions. This release also corrects the pre-release versions syntax to satisfy the SemVer notation (1.18.0-beta1 -> 1.18.0-beta.1, 1.18.0-rc1 -> 1.18.0-rc.1).

steps:
  - uses: actions/checkout@v2
  - uses: actions/setup-go@v3
    with:
      go-version: '1.18.0-rc.1'
  - run: go version

Add check-latest input

In scope of this release we add the check-latest input. If check-latest is set to true, the action first checks if the cached version is the latest one. If the locally cached version is not the most up-to-date, a Go version will then be downloaded from go-versions repository. By default check-latest is set to false. Example of usage:

steps:
  - uses: actions/checkout@v2
  - uses: actions/setup-go@v2
    with:
      go-version: '1.16'
      check-latest: true
  - run: go version

Moreover, we updated @actions/core from 1.2.6 to 1.6.0

v2.1.5

In scope of this release we updated matchers.json to improve the problem matcher pattern. For more information please refer to this pull request

v2.1.4

What's Changed

New Contributors

Full Changelog: https://github.com/actions/setup-go/compare/v2.1.3...v2.1.4

v2.1.3

  • Updated communication with runner to use environment files rather then workflow commands

v2.1.2

This release includes vendored licenses for this action's npm dependencies.

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/setup-go&package-manager=github_actions&previous-version=2&new-version=3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
* build(deps): bump github.com/cosmos/cosmos-sdk from 0.45.1 to 0.45.3 (#1247) Bumps [github.com/cosmos/cosmos-sdk](https://github.com/cosmos/cosmos-sdk) from 0.45.1 to 0.45.3. - [Release notes](https://github.com/cosmos/cosmos-sdk/releases) - [Changelog](https://github.com/cosmos/cosmos-sdk/blob/v0.45.3/CHANGELOG.md) - [Commits](https://github.com/cosmos/cosmos-sdk/compare/v0.45.1...v0.45.3) --- updated-dependencies: - dependency-name: github.com/cosmos/cosmos-sdk dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(ics29): refund fees on distribution failure (#1245) * attempt to refund fees if distribution fails * adapting logic and updating testcases * updating inline comments * updating refund address testcase * refactor: Fix RefundFeesOnChannel (#1244) ## Description read #1060 closes: #860 closes: #780 --- 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//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` - [x] Re-reviewed `Files changed` in the Github PR explorer - [x] Review `Codecov Report` in the comment section below once CI passes * deprecate GetTransferAccount (#1250) * deprecate GetTransferAccount * add changelog entry * remove unused imports * docs: replace links in apps with correct ones (#1255) ## Description I noticed that 2 links at the bottom of the applications docs page were not correct. closes: #XXXX --- 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)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] Added a relevant changelog entry to the `Unreleased` section in `CHANGELOG.md` - [x] Re-reviewed `Files changed` in the Github PR explorer - [ ] Review `Codecov Report` in the comment section below once CI passes * update godoc of `RegisterInterchainAccount` (#1256) ## Description The godoc of `RegisterInterchainAccount` was still saying that the port identifier was constructed using as well the connection identifiers. I corrected that. closes: #XXXX --- 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)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] Added a relevant changelog entry to the `Unreleased` section in `CHANGELOG.md` - [x] Re-reviewed `Files changed` in the Github PR explorer - [ ] Review `Codecov Report` in the comment section below once CI passes * isolate packet fee distribution for on acknowledgement and on timeout into separate functions (#1253) ## Description Reduces the complexity contained in `DistributePacketFees` and `DistributePacketFeesOnAcknowledgement` in anticipation of #1251 closes: #XXXX --- 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. - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 * Update supported release lines (#1263) ## Description closes: #1152 --- 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)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] Added a relevant changelog entry to the `Unreleased` section in `CHANGELOG.md` - [x] Re-reviewed `Files changed` in the Github PR explorer - [ ] Review `Codecov Report` in the comment section below once CI passes * ics29: gracefully handle escrow out of balance edge case during fee distribution (#1251) ## Description closes: #821 --- 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//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` - [x] Re-reviewed `Files changed` in the Github PR explorer - [x] Review `Codecov Report` in the comment section below once CI passes * build(deps): bump github.com/spf13/viper from 1.10.1 to 1.11.0 (#1264) Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.10.1 to 1.11.0. - [Release notes](https://github.com/spf13/viper/releases) - [Commits](https://github.com/spf13/viper/compare/v1.10.1...v1.11.0) --- updated-dependencies: - dependency-name: github.com/spf13/viper dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(ics29): make EscrowPacketFee private (#1252) * making EsrowPacketFee private, adapting test cases to conform * refactor msg server tests to accomoodate escrowPacketFee * adding assertions on escrow account balance * adding error checks on bank sends in tests, omitting loop as per review * adding escrow balance checks to TestPayPacketFee * adding assertions on expected fees in state * add missing changelog entry for bump of sdk 0.45.3 (#1272) ## Description closes: #XXXX --- 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)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] 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` - [x] Re-reviewed `Files changed` in the Github PR explorer - [ ] Review `Codecov Report` in the comment section below once CI passes * feat: Add GetAppVersion to ICS4Wrapper (#1022) * feat: Add ICS4Wrapper function GetChannelVersion Add a function to 04-channel keeper which can be used in the ICS4Wrapper interface for obtaining the unwrapped channel verison * add docs * chore: rename GetChannelVersion to GetUnwrappedChannelVersion * add changelog entry * Update CHANGELOG.md * Update docs/ibc/middleware/develop.md * Update docs/ibc/middleware/develop.md * chore: GetUnwrappedChannelVersion -> GetAppVersion * add GetAppVersion for ics29 * add GetAppVersion to ics27 * add extra test for 29-fee * update docs * Apply suggestions from code review Co-authored-by: Sean King Co-authored-by: Sean King Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> * make URI path for getting all incentivized packets for channel consistent (#1278) * make URI path for getting all incentivized packets for a specific channel consistent * regenerate proto files and add changelog entry * something going with go.mod file... * update changelog entry Co-authored-by: Carlos Rodriguez * chore : add selected channel version to MsgChanOpenInitResponse and MsgChanOpenTryResponse (#1279) ## Description - Add a version field to MsgChannelOpenInitResponse and MsgChannelOpenTryResponse in proto and gen proto - Set the selected channel version in the [MsgChannelOpenInitResponse](https://github.com/notional-labs/ibc-go/blob/ed7a082565fadb9ce27067fa1efb56c23fafc8ef/modules/core/keeper/msg_server.go#L197) and [MsgChannelOpenTryResponse](https://github.com/notional-labs/ibc-go/blob/ed7a082565fadb9ce27067fa1efb56c23fafc8ef/modules/core/keeper/msg_server.go#L237) --- 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. - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] 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 closes: #1204 * fixing workflow/test.yml merge Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> Co-authored-by: Sean King Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> Co-authored-by: Carlos Rodriguez Co-authored-by: Carlos Rodriguez Co-authored-by: Aditya Co-authored-by: Sean King Co-authored-by: Tim Lind Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: daeMOn Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Joe Bowman Co-authored-by: khanh <50263489+catShaark@users.noreply.github.com> Co-authored-by: Carlos Rodriguez Co-authored-by: Charly Co-authored-by: Luke Rhoads <51463884+lukerhoads@users.noreply.github.com> Co-authored-by: Dev Ojha Co-authored-by: Jack Zampolin Co-authored-by: Leo Pang <34628052+allthatjazzleo@users.noreply.github.com> Co-authored-by: Barrie Byron Co-authored-by: Tyler <48813565+technicallyty@users.noreply.github.com> Co-authored-by: technicallyty <48813565+tytech3@users.noreply.github.com> Co-authored-by: Barrie Byron Co-authored-by: Marko Co-authored-by: Marko Baricevic Co-authored-by: Aleksandr Bezobchuk Co-authored-by: nir1218 Co-authored-by: Assaf Morami Co-authored-by: Dan McCandless Co-authored-by: Ramiro Carlucho Co-authored-by: frog power 4000 Co-authored-by: vuong <56973102+nguyenvuong1122000@users.noreply.github.com> --- .github/workflows/release.yml | 5 +- .github/workflows/test.yml | 30 +- .goreleaser.yml | 3 +- CHANGELOG.md | 16 + README.md | 7 +- RELEASES.md | 40 +- docs/.vuepress/config.js | 22 +- docs/apps/interchain-accounts/requirements.md | 119 + docs/architecture/README.md | 1 + .../adr-004-ics29-lock-fee-module.md | 55 + docs/ibc-go-image.png | Bin 0 -> 1460199 bytes docs/ibc/apps.md | 4 +- docs/ibc/middleware/develop.md | 21 + docs/ibc/proto-docs.md | 1747 +++++-- docs/roadmap/history.md | 44 - docs/roadmap/roadmap.md | 107 +- docs/versions | 5 + go.mod | 54 +- go.sum | 262 +- .../controller/ibc_module.go | 5 + .../controller/ibc_module_test.go | 21 + .../controller/keeper/account.go | 9 +- .../controller/keeper/keeper.go | 5 + .../controller/keeper/params.go | 6 +- .../types/expected_keepers.go | 1 + modules/apps/29-fee/client/cli/cli.go | 48 + modules/apps/29-fee/client/cli/query.go | 395 ++ modules/apps/29-fee/client/cli/tx.go | 124 + modules/apps/29-fee/fee_test.go | 73 + modules/apps/29-fee/ibc_module.go | 274 ++ modules/apps/29-fee/ibc_module_test.go | 912 ++++ modules/apps/29-fee/keeper/escrow.go | 232 + modules/apps/29-fee/keeper/escrow_test.go | 464 ++ modules/apps/29-fee/keeper/events.go | 25 + modules/apps/29-fee/keeper/genesis.go | 36 + modules/apps/29-fee/keeper/genesis_test.go | 113 + modules/apps/29-fee/keeper/grpc_query.go | 244 + modules/apps/29-fee/keeper/grpc_query_test.go | 641 +++ modules/apps/29-fee/keeper/keeper.go | 335 ++ modules/apps/29-fee/keeper/keeper_test.go | 259 + modules/apps/29-fee/keeper/msg_server.go | 79 + modules/apps/29-fee/keeper/msg_server_test.go | 310 ++ modules/apps/29-fee/keeper/relay.go | 65 + modules/apps/29-fee/keeper/relay_test.go | 172 + modules/apps/29-fee/module.go | 172 + modules/apps/29-fee/transfer_test.go | 72 + modules/apps/29-fee/types/ack.go | 27 + modules/apps/29-fee/types/ack.pb.go | 423 ++ modules/apps/29-fee/types/codec.go | 48 + modules/apps/29-fee/types/errors.go | 19 + modules/apps/29-fee/types/events.go | 10 + modules/apps/29-fee/types/expected_keepers.go | 42 + modules/apps/29-fee/types/fee.go | 92 + modules/apps/29-fee/types/fee.pb.go | 1182 +++++ modules/apps/29-fee/types/fee_test.go | 123 + modules/apps/29-fee/types/genesis.go | 81 + modules/apps/29-fee/types/genesis.pb.go | 1273 +++++ modules/apps/29-fee/types/genesis_test.go | 215 + modules/apps/29-fee/types/keys.go | 139 + modules/apps/29-fee/types/keys_test.go | 176 + modules/apps/29-fee/types/metadata.pb.go | 378 ++ modules/apps/29-fee/types/msgs.go | 164 + modules/apps/29-fee/types/msgs_test.go | 319 ++ modules/apps/29-fee/types/query.pb.go | 4162 +++++++++++++++++ modules/apps/29-fee/types/query.pb.gw.go | 1264 +++++ modules/apps/29-fee/types/tx.pb.go | 1544 ++++++ modules/apps/transfer/ibc_module_test.go | 14 +- modules/apps/transfer/keeper/genesis.go | 6 - modules/apps/transfer/keeper/keeper.go | 6 - modules/apps/transfer/keeper/keeper_test.go | 12 - modules/core/02-client/keeper/keeper_test.go | 3 +- modules/core/04-channel/keeper/events.go | 2 + modules/core/04-channel/keeper/keeper.go | 10 + modules/core/04-channel/keeper/keeper_test.go | 19 + modules/core/04-channel/keeper/packet.go | 7 +- modules/core/04-channel/keeper/timeout.go | 3 +- modules/core/04-channel/types/channel.pb.go | 360 +- modules/core/04-channel/types/events.go | 1 + modules/core/04-channel/types/packet.go | 22 + modules/core/04-channel/types/tx.pb.go | 265 +- modules/core/04-channel/types/version.go | 2 +- modules/core/05-port/types/module.go | 6 + modules/core/keeper/msg_server.go | 5 +- proto/ibc/applications/fee/v1/ack.proto | 17 + proto/ibc/applications/fee/v1/fee.proto | 56 + proto/ibc/applications/fee/v1/genesis.proto | 52 + proto/ibc/applications/fee/v1/metadata.proto | 16 + proto/ibc/applications/fee/v1/query.proto | 203 + proto/ibc/applications/fee/v1/tx.proto | 85 + proto/ibc/core/channel/v1/channel.proto | 14 + proto/ibc/core/channel/v1/tx.proto | 5 +- scripts/protocgen.sh | 3 +- testing/endpoint.go | 9 +- testing/simapp/app.go | 54 +- testing/values.go | 2 + 95 files changed, 19745 insertions(+), 829 deletions(-) create mode 100644 docs/apps/interchain-accounts/requirements.md create mode 100644 docs/architecture/adr-004-ics29-lock-fee-module.md create mode 100644 docs/ibc-go-image.png delete mode 100644 docs/roadmap/history.md create mode 100644 modules/apps/29-fee/client/cli/cli.go create mode 100644 modules/apps/29-fee/client/cli/query.go create mode 100644 modules/apps/29-fee/client/cli/tx.go create mode 100644 modules/apps/29-fee/fee_test.go create mode 100644 modules/apps/29-fee/ibc_module.go create mode 100644 modules/apps/29-fee/ibc_module_test.go create mode 100644 modules/apps/29-fee/keeper/escrow.go create mode 100644 modules/apps/29-fee/keeper/escrow_test.go create mode 100644 modules/apps/29-fee/keeper/events.go create mode 100644 modules/apps/29-fee/keeper/genesis.go create mode 100644 modules/apps/29-fee/keeper/genesis_test.go create mode 100644 modules/apps/29-fee/keeper/grpc_query.go create mode 100644 modules/apps/29-fee/keeper/grpc_query_test.go create mode 100644 modules/apps/29-fee/keeper/keeper.go create mode 100644 modules/apps/29-fee/keeper/keeper_test.go create mode 100644 modules/apps/29-fee/keeper/msg_server.go create mode 100644 modules/apps/29-fee/keeper/msg_server_test.go create mode 100644 modules/apps/29-fee/keeper/relay.go create mode 100644 modules/apps/29-fee/keeper/relay_test.go create mode 100644 modules/apps/29-fee/module.go create mode 100644 modules/apps/29-fee/transfer_test.go create mode 100644 modules/apps/29-fee/types/ack.go create mode 100644 modules/apps/29-fee/types/ack.pb.go create mode 100644 modules/apps/29-fee/types/codec.go create mode 100644 modules/apps/29-fee/types/errors.go create mode 100644 modules/apps/29-fee/types/events.go create mode 100644 modules/apps/29-fee/types/expected_keepers.go create mode 100644 modules/apps/29-fee/types/fee.go create mode 100644 modules/apps/29-fee/types/fee.pb.go create mode 100644 modules/apps/29-fee/types/fee_test.go create mode 100644 modules/apps/29-fee/types/genesis.go create mode 100644 modules/apps/29-fee/types/genesis.pb.go create mode 100644 modules/apps/29-fee/types/genesis_test.go create mode 100644 modules/apps/29-fee/types/keys.go create mode 100644 modules/apps/29-fee/types/keys_test.go create mode 100644 modules/apps/29-fee/types/metadata.pb.go create mode 100644 modules/apps/29-fee/types/msgs.go create mode 100644 modules/apps/29-fee/types/msgs_test.go create mode 100644 modules/apps/29-fee/types/query.pb.go create mode 100644 modules/apps/29-fee/types/query.pb.gw.go create mode 100644 modules/apps/29-fee/types/tx.pb.go create mode 100644 proto/ibc/applications/fee/v1/ack.proto create mode 100644 proto/ibc/applications/fee/v1/fee.proto create mode 100644 proto/ibc/applications/fee/v1/genesis.proto create mode 100644 proto/ibc/applications/fee/v1/metadata.proto create mode 100644 proto/ibc/applications/fee/v1/query.proto create mode 100644 proto/ibc/applications/fee/v1/tx.proto diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ffffe3a4dae..06b91b20c75 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,7 +3,8 @@ name: Release on: push: tags: - - 'v[0-9]+.[0-9]+.[0-9]+-?[a-z0-9]*' # Push events to matching v*, i.e. v1.0.0, v20.15.10, v3.0.0-alpha1 + - 'v[0-9]+.[0-9]+.[0-9]+' # Push events to matching v*, i.e. v1.0.0, v20.15.10 + - 'v[0-9]+.[0-9]+.[0-9]+-?[a-z0-9]+' # Push events to matching v*-[alpha/beta/rc], i.e. v3.0.0-alpha1, v3.0.0-beta1, v3.0.0-rc0 jobs: goreleaser: @@ -14,7 +15,7 @@ jobs: with: fetch-depth: 0 - - uses: actions/setup-go@v2 + - uses: actions/setup-go@v3 with: go-version: '1.17' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fdcc086ae4f..31ef80c80f2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,7 +18,7 @@ jobs: install-tparse: runs-on: ubuntu-latest steps: - - uses: actions/setup-go@v2.1.5 + - uses: actions/setup-go@v3 with: go-version: 1.17 - name: Display go version @@ -26,7 +26,7 @@ jobs: - name: install tparse run: | export GO111MODULE="on" && go get github.com/mfridman/tparse@v0.8.3 - - uses: actions/cache@v2.1.7 + - uses: actions/cache@v3 with: path: ~/go/bin key: ${{ runner.os }}-go-tparse-binary @@ -38,7 +38,7 @@ jobs: go-arch: ["amd64", "arm", "arm64"] steps: - uses: actions/checkout@v3 - - uses: actions/setup-go@v2.1.5 + - uses: actions/setup-go@v3 with: go-version: 1.17 - uses: technote-space/get-diff-action@v6.0.1 @@ -60,19 +60,19 @@ jobs: - name: Split pkgs into 4 files run: split -d -n l/4 pkgs.txt pkgs.txt.part. # cache multiple - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: name: "${{ github.sha }}-00" path: ./pkgs.txt.part.00 - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: name: "${{ github.sha }}-01" path: ./pkgs.txt.part.01 - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: name: "${{ github.sha }}-02" path: ./pkgs.txt.part.02 - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: name: "${{ github.sha }}-03" path: ./pkgs.txt.part.03 @@ -86,7 +86,7 @@ jobs: part: ["00", "01", "02", "03"] steps: - uses: actions/checkout@v3 - - uses: actions/setup-go@v2.1.5 + - uses: actions/setup-go@v3 with: go-version: 1.17 - uses: technote-space/get-diff-action@v6.0.1 @@ -95,7 +95,7 @@ jobs: **/**.go go.mod go.sum - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 with: name: "${{ github.sha }}-${{ matrix.part }}" if: env.GIT_DIFF @@ -103,7 +103,7 @@ jobs: run: | cat pkgs.txt.part.${{ matrix.part }} | xargs go test -race -mod=readonly -timeout 30m -coverprofile=${{ matrix.part }}profile.out -covermode=atomic -tags='ledger test_ledger_mock' if: env.GIT_DIFF - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: name: "${{ github.sha }}-${{ matrix.part }}-coverage" path: ./${{ matrix.part }}profile.out @@ -119,19 +119,19 @@ jobs: **/**.go go.mod go.sum - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 with: name: "${{ github.sha }}-00-coverage" if: env.GIT_DIFF - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 with: name: "${{ github.sha }}-01-coverage" if: env.GIT_DIFF - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 with: name: "${{ github.sha }}-02-coverage" if: env.GIT_DIFF - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 with: name: "${{ github.sha }}-03-coverage" if: env.GIT_DIFF @@ -150,7 +150,7 @@ jobs: sed -i.bak "/$(echo $filename | sed 's/\//\\\//g')/d" coverage.txt done if: env.GIT_DIFF - - uses: codecov/codecov-action@v2.1.0 + - uses: codecov/codecov-action@v3 with: file: ./coverage.txt if: env.GIT_DIFF diff --git a/.goreleaser.yml b/.goreleaser.yml index a1df128c661..63ecd8822e7 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -24,6 +24,7 @@ checksum: release: mode: keep-existing + prerelease: auto archives: - name_template: "{{ .ProjectName }}_simd_v{{ .Version }}_{{ .Os }}_{{ .Arch }}" @@ -32,4 +33,4 @@ archives: - README.md - RELEASES.md - SECURITY.md - - CHANGELOG.md \ No newline at end of file + - CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md index c2190232888..d243bcb0286 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,11 +38,20 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Dependencies +* [\#1247](https://github.com/cosmos/ibc-go/pull/1247) Bump SDK version to v0.45.3 and Tendermint to version 0.34.19 + ### API Breaking +* (transfer) [\#1250](https://github.com/cosmos/ibc-go/pull/1250) Deprecate `GetTransferAccount` since the `transfer` module account is never used. + ### State Machine Breaking ### Improvements + +* (middleware) [\#1022](https://github.com/cosmos/ibc-go/pull/1022) Add `GetAppVersion` to the ICS4Wrapper interface. This function should be used by IBC applications to obtain their own version since the version set in the channel structure may be wrapped many times by middleware. +* (modules/core/04-channel) [\#1160](https://github.com/cosmos/ibc-go/pull/1160) Improve `uint64 -> string` performance in `Logger`. +* (modules/core/04-channel) [\#1232](https://github.com/cosmos/ibc-go/pull/1232) Updating params on `NewPacketId` and moving to bottom of file. +* (modules/core/04-channel) [\#1279](https://github.com/cosmos/ibc-go/pull/1279) Add selected channel version to MsgChanOpenInitResponse and MsgChanOpenTryResponse. Emit channel version during OpenInit/OpenTry * (modules/light-clients/07-tendermint) [\#1118](https://github.com/cosmos/ibc-go/pull/1118) Deprecating `AllowUpdateAfterExpiry and AllowUpdateAfterMisbehaviour`. See ADR-026 for context. * (modules/core/02-client) [\#1188](https://github.com/cosmos/ibc-go/pull/1188/files) Routing `MsgSubmitMisbehaviour` to `UpdateClient` keeper function. Deprecating `SubmitMisbehaviour` endpoint. * (modules/core/02-client) [\#1208](https://github.com/cosmos/ibc-go/pull/1208) Replace `CheckHeaderAndUpdateState` usage in 02-client with calls to `VerifyClientMessage`, `CheckForMisbehaviour`, `UpdateStateOnMisbehaviour` and `UpdateState`. @@ -58,9 +67,16 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Features +* [\#276](https://github.com/cosmos/ibc-go/pull/276) Adding the Fee Middleware module v1 +* (apps/29-fee) [\#1229](https://github.com/cosmos/ibc-go/pull/1229) Adding CLI commands for getting all unrelayed incentivized packets and packet by packet-id. +* (apps/29-fee) [\#1224](https://github.com/cosmos/ibc-go/pull/1224) Adding Query/CounterpartyAddress and CLI to ICS29 fee middleware +* (apps/29-fee) [\#1225](https://github.com/cosmos/ibc-go/pull/1225) Adding Query/FeeEnabledChannel and Query/FeeEnabledChannels with CLIs to ICS29 fee middleware. +* (modules/apps/29-fee) [\#1230](https://github.com/cosmos/ibc-go/pull/1230) Adding CLI command for getting incentivized packets for a specific channel-id. + ### Bug Fixes * (modules/core/04-channel) [\#1130](https://github.com/cosmos/ibc-go/pull/1130) Call `packet.GetSequence()` rather than passing func in `WriteAcknowledgement` log output +* (apps/29-fee) [\#1278](https://github.com/cosmos/ibc-go/pull/1278) The URI path for the query to get all incentivized packets for a specifc channel did not follow the same format as the rest of queries. ## [v3.0.0](https://github.com/cosmos/ibc-go/releases/tag/v3.0.0) - 2022-03-15 diff --git a/README.md b/README.md index 61e1ab0c139..ff3e074b04f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,9 @@ -# ibc-go +
+

ibc-go

+
+ +![banner](docs/ibc-go-image.png) +
Version diff --git a/RELEASES.md b/RELEASES.md index 27f0874c650..c92cc13aed0 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -21,6 +21,38 @@ To summarize: **All our ibc-go releases allow chains to communicate successfully We ensure all major releases are supported by relayers ([hermes](https://github.com/informalsystems/ibc-rs), [rly](https://github.com/strangelove-ventures/relayer) and [ts-relayer](https://github.com/confio/ts-relayer) at the moment) which can relay between the new major release and older releases. We have no plans of upgrading to an IBC protocol specification v2.0, as this would be very disruptive to the ecosystem. +## Release cycle + +IBC-Go follows a traditional release cycle involving an alpha, beta, and rc (release candidate) releases before finalizing a new version. As ibc-go works in a non-traditional area, we apply our own interpretation to each release type. We reserve the right to make both go API breaking changes and state machine breaking changes throughout the entire release cycle. The stable release guarentees do not go into affect until a final release is performed. + +It is never advisable to use a non-final release in production. + +### Alpha + +Alpha releases are intended to make available new features as soon as they are functional. No correctness guarentees are made and alpha releases **may** contain serious security vulnerabilities, bugs, and lack of user tooling, so long as they don't affect the core functionality. + +Initial users of alpha releases are expected to be advanced, patient, and capable of handling unusual errors. Very basic integration testing will be performed by the ibc-go development team before alpha releases. + +An internal audit is typically performed before the alpha release allowing the development team to guage the maturity and stability of changes included in the next release. + +### Beta + +Beta releases are intended to signal design stability. While the go API is still subject to change, the core design of the new features should not be. Developers integrating the new features should expect to handle breaking changes when upgrading to RC's. + +Beta releases should not be made with known bugs or security vulnerabilities. Beta releases should focus on ironing out remaining bugs and filling out the UX functionality required by a final release. Beta releases should have a clearly defined scope of the features that will be included in the release. Only highly requested feature additions should be acted upon in this phase. + +When the development team has determined a release is ready to enter the RC phase, a final security audit should be performed. The security audit should be limited to looking for bugs and security vulnerabilities. Code improvements may be noted, but they should not be acted upon unless highly desirable. + +### RC + +RC's are release candidates. Final releases should contain little to no changes in comparison to the latest RC. Changes included in between RC releases should be limited to: +- Improved testing +- UX additions +- Bug fixes +- Highly requested changes by the community + +A release should not be finalized until the development team and the external community have done sufficient integration tests on the targeted release. + ## Stable Release Policy The beginning of a new major release series is marked by the release of a new major version. A major release series is comprised of all minor and patch releases made under the same major version number. The series continues to receive bug fixes (released as minor or patch releases) until it reaches end of life. The date when a major release series reaches end of life is determined by one of the two following methods: @@ -33,9 +65,11 @@ Only the following major release series have a stable release status: |Release|End of Life Date| |-------|-------| -|`v1.1.x`|July 01, 2022| -|`v1.2.x`|July 01, 2022| -|`v2.0.x`|February 01, 2023| +|`v1.3.x`|July 01, 2022| +|`v1.4.x`|July 01, 2022| +|`v2.1.x`|February 01, 2023| +|`v2.2.x`|February 01, 2023| +|`v3.0.x`|March 15, 2023| **Note**: The v1 major release series will reach end of life 6 months after merging this policy. v2 will reach end of life one year after merging this policy. diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 8e83c7c7cd3..bdd58cd8d72 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -42,10 +42,30 @@ module.exports = { "label": "v1.2.0", "key": "v1.2.0" }, + { + "label": "v1.3.0", + "key": "v1.3.0" + }, + { + "label": "v1.4.0", + "key": "v1.4.0" + }, { "label": "v2.0.0", "key": "v2.0.0" - } + } , + { + "label": "v2.1.0", + "key": "v2.1.0" + }, + { + "label": "v2.2.0", + "key": "v2.2.0" + }, + { + "label": "v3.0.0", + "key": "v3.0.0" + } ], topbar: { banner: true diff --git a/docs/apps/interchain-accounts/requirements.md b/docs/apps/interchain-accounts/requirements.md new file mode 100644 index 00000000000..fff919f8088 --- /dev/null +++ b/docs/apps/interchain-accounts/requirements.md @@ -0,0 +1,119 @@ +# Business requirements + +> **TL;DR**: Rather than creating an IBC application to expose cross-chain access to every module's features, the Interchain Accounts feature would allow to leverage the capabilities of an account to access a blockchain's application-specific features. + +## Problem + +Without Interchain Accounts, cross-chain access to chain-specific features (such as staking, sending, voting, etc) has to be built as separate applications on top of the IBC TAO (Transport, Authentication, Ordering) layer. Creating new IBC application standards and implementations for each application-specific feature requires considerable time and resources. Interchain Accounts will allow new chain-specific features to be immediately available over IBC. + +## Objectives + +Provide a way to programmatically create accounts on a destination blockchain (called the host) and control them via transactions over IBC. An IBC packet will take a message from the controller blockchain to the host blockchain where it will be executed. This will allow new features on a blockchain to be immediately supported as IBC transactions, since the (destination blockchain) native messages are encapsulated in an IBC packet in an agnostic way. This will allow all of the modules on a chain to take advantage of the network effects created by the IBC ecosystem. + +## Scope + +| Features | Release | +| --------- | ------- | +| Deterministically create a new interchain account over IBC on the host chain. | v1 | +| Send over IBC a packet that contains the message to be executed by the interchain account on the host. | v1 | + +# User requirements + +## Use cases + +### Injective <> Band Chain + +Currently, Injective sends an IBC transaction to Band Chain via their custom IBC oracle module, which is a data request. When this IBC packet is executed on Band Chain, validators on Band Chain fetch prices for 10 different markets. A random selection of validators will post this selection on-chain. Once a minimum quorum has been reached, an IBC packet is sent to Injective with the prices of markets. The roundtrip latency of this flow is around 30 seconds when things go well (no packet timeouts or delays in validation). + +However, Injective wants to minimise as much as possible the latency between real world price updates and price updates on Injective. They can simplify this two-transaction flow to a single transaction using Interchain Accounts: Injective opens an interchain account on Band Chain, which would be able to pay for a continuous set of update transactions and maintain a standing request for the prices of marke. This would simplify the transaction flow to a single transaction, and introduce a simple flow to update the standing request if necessary. + +### Umee <> Cosmos Hub + +Users on the Hub would send their ATOM to Umee. In return, the user gets equivalent amount of meTokens (this token would be a form of a liquid staking token), which could then be staked on the Hub, in some other liquidity pool, etc, in addition to other business logic which Umee could perform on behalf of the users in return for the ATOM. + +Umee then stakes these ATOM tokens on the Hub on behalf of Umee (ATOMs get inflation rewards, etc). Without Interchain Accounts, Umee would have to use validator controlled multisig, because for this flow Umee needs an account on the Hub which can be controlled externally in a decentralised way. With Interchain Accounts, Umee can register an interchain account on the Hub and then receive the staking rewards for the ATOM, figure out distribution back to Umee chain, and send back to the corresponding existing account on Umee. + +### Hub custodial services + +The problem the Cosmos ecosystem faces is fragmentation of services. When a new chain goes live, they need to talk to custodial solutions and exchanges to integrate. Many exchanges and custodial solutions don't want to integrate tens of chains unless paid in advance. + +An alternative is offering the custodial service through the Hub. When a new chain goes live, the tokens of the chain are transferred through IBC to the Hub. This means that the custodial service would just have to integrate with one chain (the Hub), rather with an arbitrary number of them. + +Using Interchain Accounts, a service could be built in which a user sends tokens to an interchain account address on chain `X`, which corresponds to the registered interchain account of chain `X` on the Hub. This account would handle the token transfer to the Hub and then further on to the custodial wallet. + +# Functional requirements + +## Assumptions + +1. Interchain account packets will rarely timeout with application-set values. +2. Cosmos-SDK modules deployed on a chain are not malicious. +3. Authentication modules may implement their own permissioning scheme. + +## Features + +### 1 - Configuration + +| ID | Description | Verification | Status | +| --- | ----------- | ------------ | ------ | +| 1.01 | A chain shall have the ability to enable or disable Interchain Accounts controller functionality in the genesis state. | The controller parameters have a [flag](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/types/host.pb.go#L30) to enable/disable the controller submodule, and this flag [is stored during genesis initialization](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/keeper/params.go#L24). | `Implemented` | +| 1.02 | A chain shall have the ability to export the Interchain Accounts controller genesis state. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/keeper/genesis_test.go#L47) | `Implemented` | +| 1.03 | A chain shall have the ability to initialize the Interchain Accounts controller genesis state. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/keeper/genesis_test.go#L10) | `Implemented` | +| 1.04 | A chain shall have the ability to set the Interchain Accounts controller parameters when upgrading or via proposal. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/module_test.go#L33) | `Implemented` | +| 1.05 | A chain shall have the ability to enable or disable Interchain Accounts host functionality in the genesis state. | The host parameters have a [flag](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/types/host.pb.go#L30) to enable/disable the host submodule, and this flag [is stored during genesis initialization](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/params.go#L31) | `Implemented` | +| 1.06 | A chain shall have the ability to export the Interchain Accounts host genesis state. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/genesis_test.go#L46) | `Implemented` | +| 1.07 | A chain shall have the ability to initialize the Interchain Accounts host genesis state. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/genesis_test.go#L10) | `Implemented` | +| 1.08 | A chain shall have the ability to set the Interchain Accounts host parameters when upgrading or via proposal. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/module_test.go#L33) | `Implemented` | +| 1.09 | The host chain shall have the ability to whitelist what types of messages or transactions that it chooses to facilitate (e.g. it can decide that registered interchain accounts cannot execute staking messages). | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/params_test.go#L5) | `Implemented` | + +### 2 - Registration + +| ID | Description | Verification | Status | +| --- | ----------- | ------------ | ------ | +| 2.01 | The controller chain can programmatically create interchain accounts on the host chain that shall be controlled only by the owner account on the controller chain. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/account_test.go#L10) | `Implemented` | +| 2.02 | An interchain account shall be created by any actor without the approval of a third party (e.g. chain governance). | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/account_test.go#L10) | `Implemented` | + +### 3 - Control + +| ID | Description | Verification | Status | +| --- | ----------- | ------------ | ------ | +| 3.01 | The controller chain can programmatically control the interchain account by submitting transactions to be executed on the host chain on the behalf of the interchain account. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/keeper/relay_test.go#L29) | `Implemented` | +| 3.02 | Under no circumstances shall the owner account on the controller chain irretrievably lose control over the registered interchain account on the host chain. | If the channel between controller and host closes, then [a relayer can open a new channel on the existing controller port](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/keeper/account.go#L16-L17). | `Implemented` | + +### 4 - Host execution + +| ID | Description | Verification | Status | +| --- | ----------- | ------------ | ------ | +| 4.01 | Transactions shall be executed by an interchain account on the host chain in exactly the same order in which they are submitted by the controller chain. | IBC packets with SDK messages will be sent from the controller to the host over an [ordered channel](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/keeper/account.go#L60). | `Implemented` | +| 4.02 | The host shall execute only messages in the allow list. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/relay_test.go#L340) | `Implemented` | +| 4.03 | The controller chain shall know how the host chain will handle the transaction bytes in advance. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/keeper/handshake_test.go#L109-L133) | `Implemented` | +| 4.04 | Each transaction submitted by the controller chain shall be executed only once by the interchain account on the host chain. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/relay_test.go#L248) | `Implemented` | + +# Non-functional requirements + +## 5 - Security + +| ID | Description | Verification | Status | +| -- | ----------- | ------------ | ------ | +| 5.01 | There shall be no means for the interchain account to execute transactions that have not been submitted first by the respective owner account on the controller chain. |[Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/relay_test.go#L361) | `Implemented` | +| 5.02 | Every interchain account on the host chain shall have one and only one respective owner account on the controller chain. | The interchain account on the host [is generated using the host connection ID and the address of the owner on the controller](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/handshake.go#L73-L76). | `Implemented` | +| 5.03 | The owner account on a controller chain shall not be able to control interchain accounts registered by other owner accounts on the same controller chain. | Before the host logic executes the received messages, it [retrieves the interchain account associated with the port ID](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/keeper/relay.go#L94) over which it received the message. For owner address B to be able to execute a message with the interchain account registered with owner address A, it would need to send the messages over a channel that binds to a port ID that contains the owner address A, and since we have assumption number 3, this should not be allowed by applications. | `Implemented` | +| 5.04 | A controller chain shall not be able to control interchain accounts registered by owner accounts on different controller chains. | Same as 5.03. | `Implemented` | | +| 5.05 | Each interchain account on the host chain is owned by a single owner account on the controller chain. It shall not be possible to register a second interchain account with the same owner account on the controller chain. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/keeper/account_test.go#L42) | `Implemented` | + +# External interface requirements + +## 6 - CLI + +| ID | Description | Verification | Status | +| -- | ----------- | ------------ | ------ | +| 6.01 | There shall be a CLI command available to query the host parameters. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/client/cli/query.go#L22) | `Implemented` | +| 6.02 | There shall be a CLI command available to query the receive packet events on the host chain to check the result of the execution of the message on the host. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/host/client/cli/query.go#L51) | `Implemented` | +| 6.03 | There shall be a CLI command available to query the controller parameters. | [Acceptance tests](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/client/cli/query.go#L15) | `Implemented` | + + +## 7 - Application developers + +| ID | Description | Verification | Status | +| -- | ----------- | ------------ | ------ | +| 7.01 | An IBC application developer shall be able to develop an Interchain Accounts authentication module that can register interchain accounts. | The [`RegisterInterchainAccount` function](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/keeper/account.go#L18) is the entry point to registering an interchain account. | `Implemented` | +| 7.02 | An IBC application developer shall be able to develop an Interchain Accounts authentication module that can send messages from the controller to the host. | The [`SendTx` function](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/apps/27-interchain-accounts/controller/keeper/relay.go#L18) takes pre-built packet data containing messages to be executed on the host chain from an authentication module and attempts to send the packet. | `Implemented` | \ No newline at end of file diff --git a/docs/architecture/README.md b/docs/architecture/README.md index 091ba899dc6..edfbea4f0c8 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -30,6 +30,7 @@ To suggest an ADR, please make use of the [ADR template](./adr-template.md) prov | [001](./adr-001-coin-source-tracing.md) | ICS-20 coin denomination format | Accepted, Implemented | | [002](./adr-002-go-module-versioning.md) | Go module versioning | Accepted | | [003](./adr-003-ics27-acknowledgement.md) | ICS27 acknowledgement format | Accepted | +| [004](./adr-004-ics29-lock-fee-module.md) | ICS29 module locking upon escrow out of balance | Accepted | | [015](./adr-015-ibc-packet-receiver.md) | IBC Packet Routing | Accepted | | [025](./adr-025-ibc-passive-channels.md) | IBC passive channels | Deprecated | | [026](./adr-026-ibc-client-recovery-mechanisms.md) | IBC client recovery mechansisms | Accepted | diff --git a/docs/architecture/adr-004-ics29-lock-fee-module.md b/docs/architecture/adr-004-ics29-lock-fee-module.md new file mode 100644 index 00000000000..5b17717e669 --- /dev/null +++ b/docs/architecture/adr-004-ics29-lock-fee-module.md @@ -0,0 +1,55 @@ +# ADR 004: Lock fee module upon escrow out of balance + +## Changelog +* 03/03/2022: initial draft + +## Status + +Accepted + +## Context + +The fee module maintains an escrow account for all fees escrowed to incentivize packet relays. +It also tracks each packet fee escrowed separately from the escrow account. This is because the escrow account only maintains a total balance. It has no reference for which coins belonged to which packet fee. +In the presence of a severe bug, it is possible the escrow balance will become out of sync with the packet fees marked as escrowed. +The ICS29 module should be capable of elegantly handling such a scenario. + +## Decision + +We will allow for the ICS29 module to become "locked" if the escrow balance is determined to be out of sync with the packet fees marked as escrowed. +A "locked" fee module will not allow for packet escrows to occur nor will it distribute fees. All IBC callbacks will skip performing fee logic, similar to fee disabled channels. + +Manual intervention will be needed to unlock the fee module. + +### Sending side + +Special behaviour will have to be accounted for in `OnAcknowledgementPacket`. Since the counterparty will continue to send incentivized acknowledgements for fee enabled channels, the acknowledgement will still need to be unmarshalled into an incentivized acknowledgement before calling the underlying application `OnAcknowledgePacket` callback. + +When distributing fees, a cached context should be used. If the escrow account balance would become negative, the current state changes should be discarded and the fee module should be locked using the uncached context. This prevents fees from being partially distributed for a given packetID. + +### Receiving side + +`OnRecvPacket` should remain unaffected by the fee module becoming locked since escrow accounts only affect the sending side. + +## Consequences + +### Positive + +The fee module can be elegantly disabled in the presence of severe bugs. + +### Negative + +Extra logic is added to account for edge cases which are only possible in the presence of bugs. + +### Neutral + +## References + +Issues: +- [#821](https://github.com/cosmos/ibc-go/issues/821) +- [#860](https://github.com/cosmos/ibc-go/issues/860) + +PR's: +- [#1031](https://github.com/cosmos/ibc-go/pull/1031) +- [#1029](https://github.com/cosmos/ibc-go/pull/1029) +- [#1056](https://github.com/cosmos/ibc-go/pull/1056) diff --git a/docs/ibc-go-image.png b/docs/ibc-go-image.png new file mode 100644 index 0000000000000000000000000000000000000000..649c879bad963a747cc6042dad4b0fe14bc18dd4 GIT binary patch literal 1460199 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
  • iOZsD$GZeBN0@4>$%lrz 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%0Aq({=Vu3vHpjfV_Zl*0BwSnkU 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

    &96N)%duM4y@Mu&|%rR!TnPz$UXqQ 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

    0guc2B*pjrc8+#PxRh!<>Z*? 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 z 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�mH9P!QP)f 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�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

    )RDt(CkMhTt{hae9 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:?l==5p=cC|+mCOugrHK9 zz|{%j-mW&BvbZ|z!BXVHlYk9$dsoOod)2*@88nu>blBx}@!{K!dOPEgCEWPJ=8g$} zbA#}|apF!`5Lim0r|Kfl(s2cTrdO0w@22OdwuLq>w94-hMsF z`vHz)$80CMjARh=A|)-Vzs-$+mLFY8l z39ns6kj4$lOL=?Db25Uz&#T{3=^_$YtyleheY&DqZbwBWt=gvHE-`?eTpT?k^mjd>51AU;li^}sO zVz1sN`sqiWWA;#rPfh5;+|dTc{ga}a_LH*NF8Q0DAtcrgp7xYVdO?@jj#7e4zYL#s z7RE0xDxq@gO|6aW&alW`rV@SoQ?;<<(d9-MH*X(hX@mKrCrw8`Pf`3PZw8yDB=WP< z&_9(|l_BzYGdYp?_a4a<7^J0slQ=_qdI=6$LlE%WO5jb)4o=yg_TG?V=606Lp2SbE zdF zN*F1C#!8)S+xr;s49qmL%i+sXg#|G})3$nIRkBVfW9H*{!@}#i0&<=|N0{Hy=*_Tq zqos#{Gby=j&B!-;DRg&Q3b$1m-cL5Sdh&UT6D~sPc(aCm^#1$+!22RO#NGq#TP|z6>$HxMRP7ntuDkvL$;kA$#c{h0J;38{FvpaxR<7GLj&i7cj z1yu8>Y@zKVe>ic+vz<9)6l6(4j#n7}dBytJ-RiL4hUA+wJTNuEZ@5YP&@U%L#aAp+D<$%-Rc+!j5sivN zQ3`-`|1;bI5OrEo6n_x9YEIDsdk%}ELwT(L=>IZVK%V-2ZJs-x=USsRv9N$lF4__o z8Zoae#r)grF;}F$^AhOw=j0-Uvt6{Mv*`^mbT>;A5pvG*mt8OnRMH(cHEU5I1GQ>I z!(Aj|O6_`I$E8d~y@Ax!StY<-&awcxkmpvlqFf=u?f-y_VuD>QF)h4yt_jOUIMOz8PA)jz!K^1&(2Nj&Cj_D>z4y;Z%2;K3Deq_-)C}7M&7jSP;d34 zbc&AYClnFx<|8`VFQUj4iWgfsTzDYe38fLolr&hX7uC>IDA`>jKs>ava?$l~>G@pO zkH2kWv9uROVR1{Fe_ZLFHio3hSS?xq#5-EMOqnNSQxoikLuK_JuYX!hrW|J?g)Z=& zKmPG^8p6=p3Q^x1-|C5%css;w;tK{Ie6t%}uOuyo(G#IF>ygpy(U`N{k-;$J(%%zl zT=imHb4s0AxejWeHaQ+7VPT4$St9_3#g_ZMunoUU_;y1ubMyzdKp zscVlGJopL8iB+}aGD&)q2p8tsVm@DJh^P#d?zhyWI85QkhVrkJstvj1b^0TtECAMr zyUgdRKu{q-*TK){5N>1uavS?(g}MIrf9Bx8&y17=0c~fB0%h&ra?62UjT4VO$EEsK z0vTy*SNV;dnwX@||Y%aop6brM6RsQk+u-pUVfJgNnoDppDyVpDWYK0w~`nekhQEnOz^K7C^4L|qKWGFcLfUs{90 zE5d#z<={S9VEX)k@Np==i-|KkS(?`} z+S6{}U2{P63gAiY;@n*Ap*~=`J&(8&8f^KvFd}B{D*VAg@7sFj8ppnbz@FFGE)ty5 zxim1yU#&=nX9RWHx&5qlo(JjTGqguv(kn_8+-P&{1}~4;R~bk)Y1wlzS3K_Zl@+Se0?^4A~w7@BK*bJ_!8F z^Cj;L7XFUM_;1`Q<)cVzrAqjmL)Zq6I5mo9|G$jXrghHl%M?esCRzC6$E?nrN5FGA zsD{opXF0Bj(*r)K*Q|#=8!1oGWQ@hMUTR(W9ofPpk^BC-g(0i`9y zaAiolqdKC3j+)0YQ)=+bSD z*K1+s7muQl=L!0rab6fqAvU~CHxnX5_rp$ zKy4BDF%+^K=exSt6N)>#7tZkwRqLSbs{*f8o

    bt->3wkC2LAjKd8Y)n$3&Y}-UT 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 z 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

    VcD?Q%^jKvdP9hR{4;15 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

    iI1u#`%}CGKBk+{81B4B6*rtW;@B-Qm^^_ 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%0D80``zrp2J7JG|Zg-4yAzhc-RVI(mb${N) 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
    F&-yr8acL14HR|6hb3ZAj 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

    lVU})PP{DbWXcmgy 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�qYGO11+Yt_Ob> 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

  • uLzUU6*j9N#{SjF+L98x%y)ByF&-x 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 z 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 z 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

    Top

    -## ibc/applications/interchain_accounts/v1/account.proto +## ibc/applications/fee/v1/ack.proto - + -### InterchainAccount -An InterchainAccount is defined as a BaseAccount & the address of the account owner on the controller chain +### IncentivizedAcknowledgement +IncentivizedAcknowledgement is the acknowledgement format to be used by applications wrapped in the fee middleware | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `base_account` | [cosmos.auth.v1beta1.BaseAccount](#cosmos.auth.v1beta1.BaseAccount) | | | -| `account_owner` | [string](#string) | | | +| `result` | [bytes](#bytes) | | the underlying app acknowledgement result bytes | +| `forward_relayer_address` | [string](#string) | | the relayer address which submits the recv packet message | +| `underlying_app_success` | [bool](#bool) | | success flag of the base application callback | @@ -300,130 +370,138 @@ An InterchainAccount is defined as a BaseAccount & the address of the account ow - +

    Top

    -## ibc/applications/interchain_accounts/v1/genesis.proto +## ibc/core/client/v1/client.proto - + -### ActiveChannel -ActiveChannel contains a connection ID, port ID and associated active channel ID +### ClientConsensusStates +ClientConsensusStates defines all the stored consensus states for a given +client. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `connection_id` | [string](#string) | | | -| `port_id` | [string](#string) | | | -| `channel_id` | [string](#string) | | | +| `client_id` | [string](#string) | | client identifier | +| `consensus_states` | [ConsensusStateWithHeight](#ibc.core.client.v1.ConsensusStateWithHeight) | repeated | consensus states and their heights associated with the client | - + -### ControllerGenesisState -ControllerGenesisState defines the interchain accounts controller genesis state +### ClientUpdateProposal +ClientUpdateProposal is a governance proposal. If it passes, the substitute +client's latest consensus state is copied over to the subject client. The proposal +handler may fail if the subject and the substitute do not match in client and +chain parameters (with exception to latest height, frozen height, and chain-id). | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `active_channels` | [ActiveChannel](#ibc.applications.interchain_accounts.v1.ActiveChannel) | repeated | | -| `interchain_accounts` | [RegisteredInterchainAccount](#ibc.applications.interchain_accounts.v1.RegisteredInterchainAccount) | repeated | | -| `ports` | [string](#string) | repeated | | -| `params` | [ibc.applications.interchain_accounts.controller.v1.Params](#ibc.applications.interchain_accounts.controller.v1.Params) | | | +| `title` | [string](#string) | | the title of the update proposal | +| `description` | [string](#string) | | the description of the proposal | +| `subject_client_id` | [string](#string) | | the client identifier for the client to be updated if the proposal passes | +| `substitute_client_id` | [string](#string) | | the substitute client identifier for the client standing in for the subject client | - + -### GenesisState -GenesisState defines the interchain accounts genesis state +### ConsensusStateWithHeight +ConsensusStateWithHeight defines a consensus state with an additional height +field. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `controller_genesis_state` | [ControllerGenesisState](#ibc.applications.interchain_accounts.v1.ControllerGenesisState) | | | -| `host_genesis_state` | [HostGenesisState](#ibc.applications.interchain_accounts.v1.HostGenesisState) | | | +| `height` | [Height](#ibc.core.client.v1.Height) | | consensus state height | +| `consensus_state` | [google.protobuf.Any](#google.protobuf.Any) | | consensus state | - + -### HostGenesisState -HostGenesisState defines the interchain accounts host genesis state +### Height +Height is a monotonically increasing data type +that can be compared against another Height for the purposes of updating and +freezing clients + +Normally the RevisionHeight is incremented at each height while keeping +RevisionNumber the same. However some consensus algorithms may choose to +reset the height in certain conditions e.g. hard forks, state-machine +breaking changes In these cases, the RevisionNumber is incremented so that +height continues to be monitonically increasing even as the RevisionHeight +gets reset | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `active_channels` | [ActiveChannel](#ibc.applications.interchain_accounts.v1.ActiveChannel) | repeated | | -| `interchain_accounts` | [RegisteredInterchainAccount](#ibc.applications.interchain_accounts.v1.RegisteredInterchainAccount) | repeated | | -| `port` | [string](#string) | | | -| `params` | [ibc.applications.interchain_accounts.host.v1.Params](#ibc.applications.interchain_accounts.host.v1.Params) | | | +| `revision_number` | [uint64](#uint64) | | the revision that the client is currently on | +| `revision_height` | [uint64](#uint64) | | the height within the given revision | - + -### RegisteredInterchainAccount -RegisteredInterchainAccount contains a connection ID, port ID and associated interchain account address +### IdentifiedClientState +IdentifiedClientState defines a client state with an additional client +identifier field. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `connection_id` | [string](#string) | | | -| `port_id` | [string](#string) | | | -| `account_address` | [string](#string) | | | +| `client_id` | [string](#string) | | client identifier | +| `client_state` | [google.protobuf.Any](#google.protobuf.Any) | | client state | - - + - +### Params +Params defines the set of IBC light client parameters. - +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `allowed_clients` | [string](#string) | repeated | allowed_clients defines the list of allowed client state types. | - -

    Top

    -## ibc/applications/interchain_accounts/v1/metadata.proto - + -### Metadata -Metadata defines a set of protocol specific data encoded into the ICS27 channel version bytestring -See ICS004: https://github.com/cosmos/ibc/tree/master/spec/core/ics-004-channel-and-packet-semantics#Versioning +### UpgradeProposal +UpgradeProposal is a gov Content type for initiating an IBC breaking +upgrade. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `version` | [string](#string) | | version defines the ICS27 protocol version | -| `controller_connection_id` | [string](#string) | | controller_connection_id is the connection identifier associated with the controller chain | -| `host_connection_id` | [string](#string) | | host_connection_id is the connection identifier associated with the host chain | -| `address` | [string](#string) | | address defines the interchain account address to be fulfilled upon the OnChanOpenTry handshake step NOTE: the address field is empty on the OnChanOpenInit handshake step | -| `encoding` | [string](#string) | | encoding defines the supported codec format | -| `tx_type` | [string](#string) | | tx_type defines the type of transactions the interchain account can execute | +| `title` | [string](#string) | | | +| `description` | [string](#string) | | | +| `plan` | [cosmos.upgrade.v1beta1.Plan](#cosmos.upgrade.v1beta1.Plan) | | | +| `upgraded_client_state` | [google.protobuf.Any](#google.protobuf.Any) | | 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 | @@ -439,137 +517,150 @@ See ICS004: https://github.com/cosmos/ibc/tree/master/spec/core/ics-004-channel- - +

    Top

    -## ibc/applications/interchain_accounts/v1/packet.proto +## ibc/core/channel/v1/channel.proto - + -### CosmosTx -CosmosTx contains a list of sdk.Msg's. It should be used when sending transactions to an SDK host chain. +### Acknowledgement +Acknowledgement is the recommended acknowledgement format to be used by +app-specific protocols. +NOTE: The field numbers 21 and 22 were explicitly chosen to avoid accidental +conflicts with other protobuf message formats used for acknowledgements. +The first byte of any message with this format will be the non-ASCII values +`0xaa` (result) or `0xb2` (error). Implemented as defined by ICS: +https://github.com/cosmos/ibc/tree/master/spec/core/ics-004-channel-and-packet-semantics#acknowledgement-envelope | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `messages` | [google.protobuf.Any](#google.protobuf.Any) | repeated | | +| `result` | [bytes](#bytes) | | | +| `error` | [string](#string) | | | - + -### InterchainAccountPacketData -InterchainAccountPacketData is comprised of a raw transaction, type of transaction and optional memo field. +### Channel +Channel defines pipeline for exactly-once packet delivery between specific +modules on separate blockchains, which has at least one end capable of +sending packets and one end capable of receiving packets. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `type` | [Type](#ibc.applications.interchain_accounts.v1.Type) | | | -| `data` | [bytes](#bytes) | | | -| `memo` | [string](#string) | | | - - - - - - +| `state` | [State](#ibc.core.channel.v1.State) | | current state of the channel end | +| `ordering` | [Order](#ibc.core.channel.v1.Order) | | whether the channel is ordered or unordered | +| `counterparty` | [Counterparty](#ibc.core.channel.v1.Counterparty) | | counterparty channel end | +| `connection_hops` | [string](#string) | repeated | list of connection identifiers, in order, along which packets sent on this channel will travel | +| `version` | [string](#string) | | opaque channel version, which is agreed upon during the handshake | - -### Type -Type defines a classification of message issued from a controller chain to its associated interchain accounts -host -| Name | Number | Description | -| ---- | ------ | ----------- | -| TYPE_UNSPECIFIED | 0 | Default zero value enumeration | -| TYPE_EXECUTE_TX | 1 | Execute a transaction on an interchain accounts host chain | - + - +### Counterparty +Counterparty defines a channel end counterparty - +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `port_id` | [string](#string) | | port on the counterparty chain which owns the other end of the channel. | +| `channel_id` | [string](#string) | | channel end on the counterparty chain | - -

    Top

    -## ibc/applications/transfer/v1/transfer.proto - + -### DenomTrace -DenomTrace contains the base denomination for ICS20 fungible tokens and the -source tracing information path. +### IdentifiedChannel +IdentifiedChannel defines a channel with additional port and channel +identifier fields. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `path` | [string](#string) | | path defines the chain of port/channel identifiers used for tracing the source of the fungible token. | -| `base_denom` | [string](#string) | | base denomination of the relayed fungible token. | - +| `state` | [State](#ibc.core.channel.v1.State) | | current state of the channel end | +| `ordering` | [Order](#ibc.core.channel.v1.Order) | | whether the channel is ordered or unordered | +| `counterparty` | [Counterparty](#ibc.core.channel.v1.Counterparty) | | counterparty channel end | +| `connection_hops` | [string](#string) | repeated | list of connection identifiers, in order, along which packets sent on this channel will travel | +| `version` | [string](#string) | | opaque channel version, which is agreed upon during the handshake | +| `port_id` | [string](#string) | | port identifier | +| `channel_id` | [string](#string) | | channel identifier | - -### Params -Params defines the set of IBC transfer parameters. -NOTE: To prevent a single token from being transferred, set the -TransfersEnabled parameter to true and then set the bank module's SendEnabled -parameter for the denomination to false. + + +### Packet +Packet defines a type that carries data across different chains through IBC | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `send_enabled` | [bool](#bool) | | send_enabled enables or disables all cross-chain token transfers from this chain. | -| `receive_enabled` | [bool](#bool) | | receive_enabled enables or disables all cross-chain token transfers to this chain. | +| `sequence` | [uint64](#uint64) | | number corresponds to the order of sends and receives, where a Packet with an earlier sequence number must be sent and received before a Packet with a later sequence number. | +| `source_port` | [string](#string) | | identifies the port on the sending chain. | +| `source_channel` | [string](#string) | | identifies the channel end on the sending chain. | +| `destination_port` | [string](#string) | | identifies the port on the receiving chain. | +| `destination_channel` | [string](#string) | | identifies the channel end on the receiving chain. | +| `data` | [bytes](#bytes) | | actual opaque bytes transferred directly to the application module | +| `timeout_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | block height after which the packet times out | +| `timeout_timestamp` | [uint64](#uint64) | | block timestamp (in nanoseconds) after which the packet times out | - - + - +### PacketId +PacketId is an identifer for a unique Packet +Source chains refer to packets by source port/channel +Destination chains refer to packets by destination port/channel - +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `port_id` | [string](#string) | | channel port identifier | +| `channel_id` | [string](#string) | | channel unique identifier | +| `sequence` | [uint64](#uint64) | | packet sequence | - -

    Top

    -## ibc/applications/transfer/v1/genesis.proto - + -### GenesisState -GenesisState defines the ibc-transfer genesis state +### PacketState +PacketState defines the generic type necessary to retrieve and store +packet commitments, acknowledgements, and receipts. +Caller is responsible for knowing the context necessary to interpret this +state as a commitment, acknowledgement, or a receipt. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | | -| `denom_traces` | [DenomTrace](#ibc.applications.transfer.v1.DenomTrace) | repeated | | -| `params` | [Params](#ibc.applications.transfer.v1.Params) | | | +| `port_id` | [string](#string) | | channel port identifier. | +| `channel_id` | [string](#string) | | channel unique identifier. | +| `sequence` | [uint64](#uint64) | | packet sequence. | +| `data` | [bytes](#bytes) | | embedded data that represents packet state. | @@ -577,6 +668,35 @@ GenesisState defines the ibc-transfer genesis state + + + +### Order +Order defines if a channel is ORDERED or UNORDERED + +| Name | Number | Description | +| ---- | ------ | ----------- | +| ORDER_NONE_UNSPECIFIED | 0 | zero-value for channel ordering | +| ORDER_UNORDERED | 1 | packets can be delivered in any order, which may differ from the order in which they were sent. | +| ORDER_ORDERED | 2 | packets are delivered exactly in the order which they were sent | + + + + + +### State +State defines if a channel is in one of the following states: +CLOSED, INIT, TRYOPEN, OPEN or UNINITIALIZED. + +| Name | Number | Description | +| ---- | ------ | ----------- | +| STATE_UNINITIALIZED_UNSPECIFIED | 0 | Default State | +| STATE_INIT | 1 | A channel has just started the opening handshake. | +| STATE_TRYOPEN | 2 | A channel has acknowledged the handshake step on the counterparty chain. | +| STATE_OPEN | 3 | A channel has completed the handshake. Open channels are ready to send and receive packets. | +| STATE_CLOSED | 4 | A channel has been closed and can no longer be used to send or receive packets. | + + @@ -585,129 +705,155 @@ GenesisState defines the ibc-transfer genesis state - +

    Top

    -## ibc/applications/transfer/v1/query.proto +## ibc/applications/fee/v1/fee.proto - + -### QueryDenomHashRequest -QueryDenomHashRequest is the request type for the Query/DenomHash RPC -method +### Fee +Fee defines the ICS29 receive, acknowledgement and timeout fees | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `trace` | [string](#string) | | The denomination trace ([port_id]/[channel_id])+/[denom] | +| `recv_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | the packet receive fee | +| `ack_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | the packet acknowledgement fee | +| `timeout_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | the packet timeout fee | - + -### QueryDenomHashResponse -QueryDenomHashResponse is the response type for the Query/DenomHash RPC -method. +### IdentifiedPacketFees +IdentifiedPacketFees contains a list of type PacketFee and associated PacketId | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `hash` | [string](#string) | | hash (in hex format) of the denomination trace information. | +| `packet_id` | [ibc.core.channel.v1.PacketId](#ibc.core.channel.v1.PacketId) | | unique packet identifier comprised of the channel ID, port ID and sequence | +| `packet_fees` | [PacketFee](#ibc.applications.fee.v1.PacketFee) | repeated | list of packet fees | - + -### QueryDenomTraceRequest -QueryDenomTraceRequest is the request type for the Query/DenomTrace RPC -method +### PacketFee +PacketFee contains ICS29 relayer fees, refund address and optional list of permitted relayers | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `hash` | [string](#string) | | hash (in hex format) of the denomination trace information. | +| `fee` | [Fee](#ibc.applications.fee.v1.Fee) | | fee encapsulates the recv, ack and timeout fees associated with an IBC packet | +| `refund_address` | [string](#string) | | the refund address for unspent fees | +| `relayers` | [string](#string) | repeated | optional list of relayers permitted to receive fees | - + -### QueryDenomTraceResponse -QueryDenomTraceResponse is the response type for the Query/DenomTrace RPC -method. +### PacketFees +PacketFees contains a list of type PacketFee | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `denom_trace` | [DenomTrace](#ibc.applications.transfer.v1.DenomTrace) | | denom_trace returns the requested denomination trace information. | +| `packet_fees` | [PacketFee](#ibc.applications.fee.v1.PacketFee) | repeated | list of packet fees | + - + -### QueryDenomTracesRequest -QueryConnectionsRequest is the request type for the Query/DenomTraces RPC -method + + + + + + + +

    Top

    + +## ibc/applications/fee/v1/genesis.proto + + + + + +### FeeEnabledChannel +FeeEnabledChannel contains the PortID & ChannelID for a fee enabled channel | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | +| `port_id` | [string](#string) | | unique port identifier | +| `channel_id` | [string](#string) | | unique channel identifier | - + -### QueryDenomTracesResponse -QueryConnectionsResponse is the response type for the Query/DenomTraces RPC -method. +### ForwardRelayerAddress +ForwardRelayerAddress contains the forward relayer address and PacketId used for async acknowledgements | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `denom_traces` | [DenomTrace](#ibc.applications.transfer.v1.DenomTrace) | repeated | denom_traces returns all denominations trace information. | -| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines the pagination in the response. | +| `address` | [string](#string) | | the forward relayer address | +| `packet_id` | [ibc.core.channel.v1.PacketId](#ibc.core.channel.v1.PacketId) | | unique packet identifer comprised of the channel ID, port ID and sequence | - + -### QueryParamsRequest -QueryParamsRequest is the request type for the Query/Params RPC method. +### GenesisState +GenesisState defines the ICS29 fee middleware genesis state +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `identified_fees` | [IdentifiedPacketFees](#ibc.applications.fee.v1.IdentifiedPacketFees) | repeated | list of identified packet fees | +| `fee_enabled_channels` | [FeeEnabledChannel](#ibc.applications.fee.v1.FeeEnabledChannel) | repeated | list of fee enabled channels | +| `registered_relayers` | [RegisteredRelayerAddress](#ibc.applications.fee.v1.RegisteredRelayerAddress) | repeated | list of registered relayer addresses | +| `forward_relayers` | [ForwardRelayerAddress](#ibc.applications.fee.v1.ForwardRelayerAddress) | repeated | list of forward relayer addresses | - -### QueryParamsResponse -QueryParamsResponse is the response type for the Query/Params RPC method. + + + +### RegisteredRelayerAddress +RegisteredRelayerAddress contains the address and counterparty address for a specific relayer (for distributing fees) | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `params` | [Params](#ibc.applications.transfer.v1.Params) | | params defines the parameters of the module. | +| `address` | [string](#string) | | the relayer address | +| `counterparty_address` | [string](#string) | | the counterparty relayer address | +| `channel_id` | [string](#string) | | unique channel identifier | @@ -719,155 +865,1076 @@ QueryParamsResponse is the response type for the Query/Params RPC method. + - -### Query -Query provides defines the gRPC querier service. -| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | -| ----------- | ------------ | ------------- | ------------| ------- | -------- | -| `DenomTrace` | [QueryDenomTraceRequest](#ibc.applications.transfer.v1.QueryDenomTraceRequest) | [QueryDenomTraceResponse](#ibc.applications.transfer.v1.QueryDenomTraceResponse) | DenomTrace queries a denomination trace information. | GET|/ibc/apps/transfer/v1/denom_traces/{hash}| -| `DenomTraces` | [QueryDenomTracesRequest](#ibc.applications.transfer.v1.QueryDenomTracesRequest) | [QueryDenomTracesResponse](#ibc.applications.transfer.v1.QueryDenomTracesResponse) | DenomTraces queries all denomination traces. | GET|/ibc/apps/transfer/v1/denom_traces| -| `Params` | [QueryParamsRequest](#ibc.applications.transfer.v1.QueryParamsRequest) | [QueryParamsResponse](#ibc.applications.transfer.v1.QueryParamsResponse) | Params queries all parameters of the ibc-transfer module. | GET|/ibc/apps/transfer/v1/params| -| `DenomHash` | [QueryDenomHashRequest](#ibc.applications.transfer.v1.QueryDenomHashRequest) | [QueryDenomHashResponse](#ibc.applications.transfer.v1.QueryDenomHashResponse) | DenomHash queries a denomination hash information. | GET|/ibc/apps/transfer/v1/denom_hashes/{trace}| + +

    Top

    + +## ibc/applications/fee/v1/metadata.proto + + + + + +### Metadata +Metadata defines the ICS29 channel specific metadata encoded into the channel version bytestring +See ICS004: https://github.com/cosmos/ibc/tree/master/spec/core/ics-004-channel-and-packet-semantics#Versioning + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `fee_version` | [string](#string) | | fee_version defines the ICS29 fee version | +| `app_version` | [string](#string) | | app_version defines the underlying application version, which may or may not be a JSON encoded bytestring | + + + + + + + + + + - +

    Top

    -## ibc/core/client/v1/client.proto +## ibc/applications/fee/v1/query.proto - + -### ClientConsensusStates -ClientConsensusStates defines all the stored consensus states for a given -client. +### QueryCounterpartyAddressRequest +QueryCounterpartyAddressRequest defines the request type for the CounterpartyAddress rpc | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `client_id` | [string](#string) | | client identifier | -| `consensus_states` | [ConsensusStateWithHeight](#ibc.core.client.v1.ConsensusStateWithHeight) | repeated | consensus states and their heights associated with the client | +| `channel_id` | [string](#string) | | unique channel identifier | +| `relayer_address` | [string](#string) | | the relayer address to which the counterparty is registered | - + -### ClientUpdateProposal -ClientUpdateProposal is a governance proposal. If it passes, the substitute -client's latest consensus state is copied over to the subject client. The proposal -handler may fail if the subject and the substitute do not match in client and -chain parameters (with exception to latest height, frozen height, and chain-id). +### QueryCounterpartyAddressResponse +QueryCounterpartyAddressResponse defines the response type for the CounterpartyAddress rpc + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `counterparty_address` | [string](#string) | | the counterparty address used to compensate forward relaying | + + + + + + + + +### QueryFeeEnabledChannelRequest +QueryFeeEnabledChannelRequest defines the request type for the FeeEnabledChannel rpc + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `port_id` | [string](#string) | | unique port identifier | +| `channel_id` | [string](#string) | | unique channel identifier | + + + + + + + + +### QueryFeeEnabledChannelResponse +QueryFeeEnabledChannelResponse defines the response type for the FeeEnabledChannel rpc + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `fee_enabled` | [bool](#bool) | | boolean flag representing the fee enabled channel status | + + + + + + + + +### QueryFeeEnabledChannelsRequest +QueryFeeEnabledChannelsRequest defines the request type for the FeeEnabledChannels rpc + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | +| `query_height` | [uint64](#uint64) | | block height at which to query | + + + + + + + + +### QueryFeeEnabledChannelsResponse +QueryFeeEnabledChannelsResponse defines the response type for the FeeEnabledChannels rpc + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `fee_enabled_channels` | [FeeEnabledChannel](#ibc.applications.fee.v1.FeeEnabledChannel) | repeated | list of fee enabled channels | + + + + + + + + +### QueryIncentivizedPacketRequest +QueryIncentivizedPacketRequest defines the request type for the IncentivizedPacket rpc + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `packet_id` | [ibc.core.channel.v1.PacketId](#ibc.core.channel.v1.PacketId) | | unique packet identifier comprised of channel ID, port ID and sequence | +| `query_height` | [uint64](#uint64) | | block height at which to query | + + + + + + + + +### QueryIncentivizedPacketResponse +QueryIncentivizedPacketsResponse defines the response type for the IncentivizedPacket rpc + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `incentivized_packet` | [IdentifiedPacketFees](#ibc.applications.fee.v1.IdentifiedPacketFees) | | the identified fees for the incentivized packet | + + + + + + + + +### QueryIncentivizedPacketsForChannelRequest +QueryIncentivizedPacketsForChannelRequest defines the request type for querying for all incentivized packets +for a specific channel + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | +| `port_id` | [string](#string) | | | +| `channel_id` | [string](#string) | | | +| `query_height` | [uint64](#uint64) | | Height to query at | + + + + + + + + +### QueryIncentivizedPacketsForChannelResponse +QueryIncentivizedPacketsResponse defines the response type for the incentivized packets RPC + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `incentivized_packets` | [IdentifiedPacketFees](#ibc.applications.fee.v1.IdentifiedPacketFees) | repeated | Map of all incentivized_packets | + + + + + + + + +### QueryIncentivizedPacketsRequest +QueryIncentivizedPacketsRequest defines the request type for the IncentivizedPackets rpc + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | +| `query_height` | [uint64](#uint64) | | block height at which to query | + + + + + + + + +### QueryIncentivizedPacketsResponse +QueryIncentivizedPacketsResponse defines the response type for the IncentivizedPackets rpc + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `incentivized_packets` | [IdentifiedPacketFees](#ibc.applications.fee.v1.IdentifiedPacketFees) | repeated | list of identified fees for incentivized packets | + + + + + + + + +### QueryTotalAckFeesRequest +QueryTotalAckFeesRequest defines the request type for the TotalAckFees rpc + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `packet_id` | [ibc.core.channel.v1.PacketId](#ibc.core.channel.v1.PacketId) | | the packet identifier for the associated fees | + + + + + + + + +### QueryTotalAckFeesResponse +QueryTotalAckFeesResponse defines the response type for the TotalAckFees rpc + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `ack_fees` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | the total packet acknowledgement fees | + + + + + + + + +### QueryTotalRecvFeesRequest +QueryTotalRecvFeesRequest defines the request type for the TotalRecvFees rpc + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `packet_id` | [ibc.core.channel.v1.PacketId](#ibc.core.channel.v1.PacketId) | | the packet identifier for the associated fees | + + + + + + + + +### QueryTotalRecvFeesResponse +QueryTotalRecvFeesResponse defines the response type for the TotalRecvFees rpc + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `recv_fees` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | the total packet receive fees | + + + + + + + + +### QueryTotalTimeoutFeesRequest +QueryTotalTimeoutFeesRequest defines the request type for the TotalTimeoutFees rpc + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `packet_id` | [ibc.core.channel.v1.PacketId](#ibc.core.channel.v1.PacketId) | | the packet identifier for the associated fees | + + + + + + + + +### QueryTotalTimeoutFeesResponse +QueryTotalTimeoutFeesResponse defines the response type for the TotalTimeoutFees rpc + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `timeout_fees` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | the total packet timeout fees | + + + + + + + + + + + + + + +### Query +Query defines the ICS29 gRPC querier service. + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `IncentivizedPackets` | [QueryIncentivizedPacketsRequest](#ibc.applications.fee.v1.QueryIncentivizedPacketsRequest) | [QueryIncentivizedPacketsResponse](#ibc.applications.fee.v1.QueryIncentivizedPacketsResponse) | IncentivizedPackets returns all incentivized packets and their associated fees | GET|/ibc/apps/fee/v1/incentivized_packets| +| `IncentivizedPacket` | [QueryIncentivizedPacketRequest](#ibc.applications.fee.v1.QueryIncentivizedPacketRequest) | [QueryIncentivizedPacketResponse](#ibc.applications.fee.v1.QueryIncentivizedPacketResponse) | IncentivizedPacket returns all packet fees for a packet given its identifier | GET|/ibc/apps/fee/v1/incentivized_packet/port/{packet_id.port_id}/channel/{packet_id.channel_id}/sequence/{packet_id.sequence}| +| `IncentivizedPacketsForChannel` | [QueryIncentivizedPacketsForChannelRequest](#ibc.applications.fee.v1.QueryIncentivizedPacketsForChannelRequest) | [QueryIncentivizedPacketsForChannelResponse](#ibc.applications.fee.v1.QueryIncentivizedPacketsForChannelResponse) | Gets all incentivized packets for a specific channel | GET|/ibc/apps/fee/v1/incentivized_packets/port/{port_id}/channel/{channel_id}| +| `TotalRecvFees` | [QueryTotalRecvFeesRequest](#ibc.applications.fee.v1.QueryTotalRecvFeesRequest) | [QueryTotalRecvFeesResponse](#ibc.applications.fee.v1.QueryTotalRecvFeesResponse) | TotalRecvFees returns the total receive fees for a packet given its identifier | GET|/ibc/apps/fee/v1/total_recv_fees/port/{packet_id.port_id}/channel/{packet_id.channel_id}/sequence/{packet_id.sequence}| +| `TotalAckFees` | [QueryTotalAckFeesRequest](#ibc.applications.fee.v1.QueryTotalAckFeesRequest) | [QueryTotalAckFeesResponse](#ibc.applications.fee.v1.QueryTotalAckFeesResponse) | TotalAckFees returns the total acknowledgement fees for a packet given its identifier | GET|/ibc/apps/fee/v1/total_ack_fees/port/{packet_id.port_id}/channel/{packet_id.channel_id}/sequence/{packet_id.sequence}| +| `TotalTimeoutFees` | [QueryTotalTimeoutFeesRequest](#ibc.applications.fee.v1.QueryTotalTimeoutFeesRequest) | [QueryTotalTimeoutFeesResponse](#ibc.applications.fee.v1.QueryTotalTimeoutFeesResponse) | TotalTimeoutFees returns the total timeout fees for a packet given its identifier | GET|/ibc/apps/fee/v1/total_timeout_fees/port/{packet_id.port_id}/channel/{packet_id.channel_id}/sequence/{packet_id.sequence}| +| `CounterpartyAddress` | [QueryCounterpartyAddressRequest](#ibc.applications.fee.v1.QueryCounterpartyAddressRequest) | [QueryCounterpartyAddressResponse](#ibc.applications.fee.v1.QueryCounterpartyAddressResponse) | CounterpartyAddress returns the registered counterparty address for forward relaying | GET|/ibc/apps/fee/v1/counterparty_address/{relayer_address}/channel/{channel_id}| +| `FeeEnabledChannels` | [QueryFeeEnabledChannelsRequest](#ibc.applications.fee.v1.QueryFeeEnabledChannelsRequest) | [QueryFeeEnabledChannelsResponse](#ibc.applications.fee.v1.QueryFeeEnabledChannelsResponse) | FeeEnabledChannels returns a list of all fee enabled channels | GET|/ibc/apps/fee/v1/fee_enabled| +| `FeeEnabledChannel` | [QueryFeeEnabledChannelRequest](#ibc.applications.fee.v1.QueryFeeEnabledChannelRequest) | [QueryFeeEnabledChannelResponse](#ibc.applications.fee.v1.QueryFeeEnabledChannelResponse) | FeeEnabledChannel returns true if the provided port and channel identifiers belong to a fee enabled channel | GET|/ibc/apps/fee/v1/fee_enabled/port/{port_id}/channel/{channel_id}| + + + + + + +

    Top

    + +## ibc/applications/fee/v1/tx.proto + + + + + +### MsgPayPacketFee +MsgPayPacketFee defines the request type for the PayPacketFee rpc +This Msg can be used to pay for a packet at the next sequence send & should be combined with the Msg that will be +paid for + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `fee` | [Fee](#ibc.applications.fee.v1.Fee) | | fee encapsulates the recv, ack and timeout fees associated with an IBC packet | +| `source_port_id` | [string](#string) | | the source port unique identifier | +| `source_channel_id` | [string](#string) | | the source channel unique identifer | +| `signer` | [string](#string) | | account address to refund fee if necessary | +| `relayers` | [string](#string) | repeated | optional list of relayers permitted to the receive packet fees | + + + + + + + + +### MsgPayPacketFeeAsync +MsgPayPacketFeeAsync defines the request type for the PayPacketFeeAsync rpc +This Msg can be used to pay for a packet at a specified sequence (instead of the next sequence send) + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `packet_id` | [ibc.core.channel.v1.PacketId](#ibc.core.channel.v1.PacketId) | | unique packet identifier comprised of the channel ID, port ID and sequence | +| `packet_fee` | [PacketFee](#ibc.applications.fee.v1.PacketFee) | | the packet fee associated with a particular IBC packet | + + + + + + + + +### MsgPayPacketFeeAsyncResponse +MsgPayPacketFeeAsyncResponse defines the response type for the PayPacketFeeAsync rpc + + + + + + + + +### MsgPayPacketFeeResponse +MsgPayPacketFeeResponse defines the response type for the PayPacketFee rpc + + + + + + + + +### MsgRegisterCounterpartyAddress +MsgRegisterCounterpartyAddress defines the request type for the RegisterCounterpartyAddress rpc + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `address` | [string](#string) | | the relayer address | +| `counterparty_address` | [string](#string) | | the counterparty relayer address | +| `channel_id` | [string](#string) | | unique channel identifier | + + + + + + + + +### MsgRegisterCounterpartyAddressResponse +MsgRegisterCounterpartyAddressResponse defines the response type for the RegisterCounterpartyAddress rpc + + + + + + + + + + + + + + +### Msg +Msg defines the ICS29 Msg service. + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `RegisterCounterpartyAddress` | [MsgRegisterCounterpartyAddress](#ibc.applications.fee.v1.MsgRegisterCounterpartyAddress) | [MsgRegisterCounterpartyAddressResponse](#ibc.applications.fee.v1.MsgRegisterCounterpartyAddressResponse) | RegisterCounterpartyAddress defines a rpc handler method for MsgRegisterCounterpartyAddress RegisterCounterpartyAddress is called by the relayer on each channelEnd and allows them to specify their counterparty address before relaying. This ensures they will be properly compensated for forward relaying since destination chain must send back relayer's source address (counterparty address) in acknowledgement. This function may be called more than once by a relayer, in which case, latest counterparty address is always used. | | +| `PayPacketFee` | [MsgPayPacketFee](#ibc.applications.fee.v1.MsgPayPacketFee) | [MsgPayPacketFeeResponse](#ibc.applications.fee.v1.MsgPayPacketFeeResponse) | PayPacketFee defines a rpc handler method for MsgPayPacketFee PayPacketFee is an open callback that may be called by any module/user that wishes to escrow funds in order to incentivize the relaying of the packet at the next sequence NOTE: This method is intended to be used within a multi msg transaction, where the subsequent msg that follows initiates the lifecycle of the incentivized packet | | +| `PayPacketFeeAsync` | [MsgPayPacketFeeAsync](#ibc.applications.fee.v1.MsgPayPacketFeeAsync) | [MsgPayPacketFeeAsyncResponse](#ibc.applications.fee.v1.MsgPayPacketFeeAsyncResponse) | PayPacketFeeAsync defines a rpc handler method for MsgPayPacketFeeAsync PayPacketFeeAsync is an open callback that may be called by any module/user that wishes to escrow funds in order to incentivize the relaying of a known packet (i.e. at a particular sequence) | | + + + + + + +

    Top

    + +## ibc/applications/interchain_accounts/controller/v1/controller.proto + + + + + +### Params +Params defines the set of on-chain interchain accounts parameters. +The following parameters may be used to disable the controller submodule. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `controller_enabled` | [bool](#bool) | | controller_enabled enables or disables the controller submodule. | + + + + + + + + + + + + + + + + +

    Top

    + +## ibc/applications/interchain_accounts/controller/v1/query.proto + + + + + +### QueryParamsRequest +QueryParamsRequest is the request type for the Query/Params RPC method. + + + + + + + + +### QueryParamsResponse +QueryParamsResponse is the response type for the Query/Params RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `params` | [Params](#ibc.applications.interchain_accounts.controller.v1.Params) | | params defines the parameters of the module. | + + + + + + + + + + + + + + +### Query +Query provides defines the gRPC querier service. + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `Params` | [QueryParamsRequest](#ibc.applications.interchain_accounts.controller.v1.QueryParamsRequest) | [QueryParamsResponse](#ibc.applications.interchain_accounts.controller.v1.QueryParamsResponse) | Params queries all parameters of the ICA controller submodule. | GET|/ibc/apps/interchain_accounts/controller/v1/params| + + + + + + +

    Top

    + +## ibc/applications/interchain_accounts/host/v1/host.proto + + + + + +### Params +Params defines the set of on-chain interchain accounts parameters. +The following parameters may be used to disable the host submodule. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `host_enabled` | [bool](#bool) | | host_enabled enables or disables the host submodule. | +| `allow_messages` | [string](#string) | repeated | allow_messages defines a list of sdk message typeURLs allowed to be executed on a host chain. | + + + + + + + + + + + + + + + + +

    Top

    + +## ibc/applications/interchain_accounts/host/v1/query.proto + + + + + +### QueryParamsRequest +QueryParamsRequest is the request type for the Query/Params RPC method. + + + + + + + + +### QueryParamsResponse +QueryParamsResponse is the response type for the Query/Params RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `params` | [Params](#ibc.applications.interchain_accounts.host.v1.Params) | | params defines the parameters of the module. | + + + + + + + + + + + + + + +### Query +Query provides defines the gRPC querier service. + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `Params` | [QueryParamsRequest](#ibc.applications.interchain_accounts.host.v1.QueryParamsRequest) | [QueryParamsResponse](#ibc.applications.interchain_accounts.host.v1.QueryParamsResponse) | Params queries all parameters of the ICA host submodule. | GET|/ibc/apps/interchain_accounts/host/v1/params| + + + + + + +

    Top

    + +## ibc/applications/interchain_accounts/v1/account.proto + + + + + +### InterchainAccount +An InterchainAccount is defined as a BaseAccount & the address of the account owner on the controller chain + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `base_account` | [cosmos.auth.v1beta1.BaseAccount](#cosmos.auth.v1beta1.BaseAccount) | | | +| `account_owner` | [string](#string) | | | + + + + + + + + + + + + + + + + +

    Top

    + +## ibc/applications/interchain_accounts/v1/genesis.proto + + + + + +### ActiveChannel +ActiveChannel contains a connection ID, port ID and associated active channel ID + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `connection_id` | [string](#string) | | | +| `port_id` | [string](#string) | | | +| `channel_id` | [string](#string) | | | + + + + + + + + +### ControllerGenesisState +ControllerGenesisState defines the interchain accounts controller genesis state + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `active_channels` | [ActiveChannel](#ibc.applications.interchain_accounts.v1.ActiveChannel) | repeated | | +| `interchain_accounts` | [RegisteredInterchainAccount](#ibc.applications.interchain_accounts.v1.RegisteredInterchainAccount) | repeated | | +| `ports` | [string](#string) | repeated | | +| `params` | [ibc.applications.interchain_accounts.controller.v1.Params](#ibc.applications.interchain_accounts.controller.v1.Params) | | | + + + + + + + + +### GenesisState +GenesisState defines the interchain accounts genesis state + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `controller_genesis_state` | [ControllerGenesisState](#ibc.applications.interchain_accounts.v1.ControllerGenesisState) | | | +| `host_genesis_state` | [HostGenesisState](#ibc.applications.interchain_accounts.v1.HostGenesisState) | | | + + + + + + + + +### HostGenesisState +HostGenesisState defines the interchain accounts host genesis state + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `active_channels` | [ActiveChannel](#ibc.applications.interchain_accounts.v1.ActiveChannel) | repeated | | +| `interchain_accounts` | [RegisteredInterchainAccount](#ibc.applications.interchain_accounts.v1.RegisteredInterchainAccount) | repeated | | +| `port` | [string](#string) | | | +| `params` | [ibc.applications.interchain_accounts.host.v1.Params](#ibc.applications.interchain_accounts.host.v1.Params) | | | + + + + + + + + +### RegisteredInterchainAccount +RegisteredInterchainAccount contains a connection ID, port ID and associated interchain account address + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `connection_id` | [string](#string) | | | +| `port_id` | [string](#string) | | | +| `account_address` | [string](#string) | | | + + + + + + + + + + + + + + + + +

    Top

    + +## ibc/applications/interchain_accounts/v1/metadata.proto + + + + + +### Metadata +Metadata defines a set of protocol specific data encoded into the ICS27 channel version bytestring +See ICS004: https://github.com/cosmos/ibc/tree/master/spec/core/ics-004-channel-and-packet-semantics#Versioning + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `version` | [string](#string) | | version defines the ICS27 protocol version | +| `controller_connection_id` | [string](#string) | | controller_connection_id is the connection identifier associated with the controller chain | +| `host_connection_id` | [string](#string) | | host_connection_id is the connection identifier associated with the host chain | +| `address` | [string](#string) | | address defines the interchain account address to be fulfilled upon the OnChanOpenTry handshake step NOTE: the address field is empty on the OnChanOpenInit handshake step | +| `encoding` | [string](#string) | | encoding defines the supported codec format | +| `tx_type` | [string](#string) | | tx_type defines the type of transactions the interchain account can execute | + + + + + + + + + + + + + + + + +

    Top

    + +## ibc/applications/interchain_accounts/v1/packet.proto + + + + + +### CosmosTx +CosmosTx contains a list of sdk.Msg's. It should be used when sending transactions to an SDK host chain. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `messages` | [google.protobuf.Any](#google.protobuf.Any) | repeated | | + + + + + + + + +### InterchainAccountPacketData +InterchainAccountPacketData is comprised of a raw transaction, type of transaction and optional memo field. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `type` | [Type](#ibc.applications.interchain_accounts.v1.Type) | | | +| `data` | [bytes](#bytes) | | | +| `memo` | [string](#string) | | | + + + + + + + + + + +### Type +Type defines a classification of message issued from a controller chain to its associated interchain accounts +host + +| Name | Number | Description | +| ---- | ------ | ----------- | +| TYPE_UNSPECIFIED | 0 | Default zero value enumeration | +| TYPE_EXECUTE_TX | 1 | Execute a transaction on an interchain accounts host chain | + + + + + + + + + + + +

    Top

    + +## ibc/applications/transfer/v1/transfer.proto + + + + + +### DenomTrace +DenomTrace contains the base denomination for ICS20 fungible tokens and the +source tracing information path. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `path` | [string](#string) | | path defines the chain of port/channel identifiers used for tracing the source of the fungible token. | +| `base_denom` | [string](#string) | | base denomination of the relayed fungible token. | + + + + + + + + +### Params +Params defines the set of IBC transfer parameters. +NOTE: To prevent a single token from being transferred, set the +TransfersEnabled parameter to true and then set the bank module's SendEnabled +parameter for the denomination to false. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `send_enabled` | [bool](#bool) | | send_enabled enables or disables all cross-chain token transfers from this chain. | +| `receive_enabled` | [bool](#bool) | | receive_enabled enables or disables all cross-chain token transfers to this chain. | + + + + + + + + + + + + + + + + +

    Top

    + +## ibc/applications/transfer/v1/genesis.proto + + + + + +### GenesisState +GenesisState defines the ibc-transfer genesis state + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `port_id` | [string](#string) | | | +| `denom_traces` | [DenomTrace](#ibc.applications.transfer.v1.DenomTrace) | repeated | | +| `params` | [Params](#ibc.applications.transfer.v1.Params) | | | + + + + + + + + + + + + + + + + +

    Top

    + +## ibc/applications/transfer/v1/query.proto + + + + + +### QueryDenomHashRequest +QueryDenomHashRequest is the request type for the Query/DenomHash RPC +method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `trace` | [string](#string) | | The denomination trace ([port_id]/[channel_id])+/[denom] | + + + + + + + + +### QueryDenomHashResponse +QueryDenomHashResponse is the response type for the Query/DenomHash RPC +method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `hash` | [string](#string) | | hash (in hex format) of the denomination trace information. | + + + + + + + + +### QueryDenomTraceRequest +QueryDenomTraceRequest is the request type for the Query/DenomTrace RPC +method | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `title` | [string](#string) | | the title of the update proposal | -| `description` | [string](#string) | | the description of the proposal | -| `subject_client_id` | [string](#string) | | the client identifier for the client to be updated if the proposal passes | -| `substitute_client_id` | [string](#string) | | the substitute client identifier for the client standing in for the subject client | +| `hash` | [string](#string) | | hash (in hex format) of the denomination trace information. | - + -### ConsensusStateWithHeight -ConsensusStateWithHeight defines a consensus state with an additional height -field. +### QueryDenomTraceResponse +QueryDenomTraceResponse is the response type for the Query/DenomTrace RPC +method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `height` | [Height](#ibc.core.client.v1.Height) | | consensus state height | -| `consensus_state` | [google.protobuf.Any](#google.protobuf.Any) | | consensus state | - +| `denom_trace` | [DenomTrace](#ibc.applications.transfer.v1.DenomTrace) | | denom_trace returns the requested denomination trace information. | - -### Height -Height is a monotonically increasing data type -that can be compared against another Height for the purposes of updating and -freezing clients + -Normally the RevisionHeight is incremented at each height while keeping -RevisionNumber the same. However some consensus algorithms may choose to -reset the height in certain conditions e.g. hard forks, state-machine -breaking changes In these cases, the RevisionNumber is incremented so that -height continues to be monitonically increasing even as the RevisionHeight -gets reset +### QueryDenomTracesRequest +QueryConnectionsRequest is the request type for the Query/DenomTraces RPC +method | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `revision_number` | [uint64](#uint64) | | the revision that the client is currently on | -| `revision_height` | [uint64](#uint64) | | the height within the given revision | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | - + -### IdentifiedClientState -IdentifiedClientState defines a client state with an additional client -identifier field. +### QueryDenomTracesResponse +QueryConnectionsResponse is the response type for the Query/DenomTraces RPC +method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `client_id` | [string](#string) | | client identifier | -| `client_state` | [google.protobuf.Any](#google.protobuf.Any) | | client state | - - +| `denom_traces` | [DenomTrace](#ibc.applications.transfer.v1.DenomTrace) | repeated | denom_traces returns all denominations trace information. | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines the pagination in the response. | - -### Params -Params defines the set of IBC light client parameters. + -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `allowed_clients` | [string](#string) | repeated | allowed_clients defines the list of allowed client state types. | +### QueryParamsRequest +QueryParamsRequest is the request type for the Query/Params RPC method. - + -### UpgradeProposal -UpgradeProposal is a gov Content type for initiating an IBC breaking -upgrade. +### QueryParamsResponse +QueryParamsResponse is the response type for the Query/Params RPC method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `title` | [string](#string) | | | -| `description` | [string](#string) | | | -| `plan` | [cosmos.upgrade.v1beta1.Plan](#cosmos.upgrade.v1beta1.Plan) | | | -| `upgraded_client_state` | [google.protobuf.Any](#google.protobuf.Any) | | 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 | +| `params` | [Params](#ibc.applications.transfer.v1.Params) | | params defines the parameters of the module. | @@ -879,6 +1946,19 @@ upgrade. + + + +### Query +Query provides defines the gRPC querier service. + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `DenomTrace` | [QueryDenomTraceRequest](#ibc.applications.transfer.v1.QueryDenomTraceRequest) | [QueryDenomTraceResponse](#ibc.applications.transfer.v1.QueryDenomTraceResponse) | DenomTrace queries a denomination trace information. | GET|/ibc/apps/transfer/v1/denom_traces/{hash}| +| `DenomTraces` | [QueryDenomTracesRequest](#ibc.applications.transfer.v1.QueryDenomTracesRequest) | [QueryDenomTracesResponse](#ibc.applications.transfer.v1.QueryDenomTracesResponse) | DenomTraces queries all denomination traces. | GET|/ibc/apps/transfer/v1/denom_traces| +| `Params` | [QueryParamsRequest](#ibc.applications.transfer.v1.QueryParamsRequest) | [QueryParamsResponse](#ibc.applications.transfer.v1.QueryParamsResponse) | Params queries all parameters of the ibc-transfer module. | GET|/ibc/apps/transfer/v1/params| +| `DenomHash` | [QueryDenomHashRequest](#ibc.applications.transfer.v1.QueryDenomHashRequest) | [QueryDenomHashResponse](#ibc.applications.transfer.v1.QueryDenomHashResponse) | DenomHash queries a denomination hash information. | GET|/ibc/apps/transfer/v1/denom_hashes/{trace}| + @@ -978,175 +2058,6 @@ https://github.com/cosmos/ibc/tree/master/spec/app/ics-020-fungible-token-transf - -

    Top

    - -## ibc/core/channel/v1/channel.proto - - - - - -### Acknowledgement -Acknowledgement is the recommended acknowledgement format to be used by -app-specific protocols. -NOTE: The field numbers 21 and 22 were explicitly chosen to avoid accidental -conflicts with other protobuf message formats used for acknowledgements. -The first byte of any message with this format will be the non-ASCII values -`0xaa` (result) or `0xb2` (error). Implemented as defined by ICS: -https://github.com/cosmos/ibc/tree/master/spec/core/ics-004-channel-and-packet-semantics#acknowledgement-envelope - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `result` | [bytes](#bytes) | | | -| `error` | [string](#string) | | | - - - - - - - - -### Channel -Channel defines pipeline for exactly-once packet delivery between specific -modules on separate blockchains, which has at least one end capable of -sending packets and one end capable of receiving packets. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `state` | [State](#ibc.core.channel.v1.State) | | current state of the channel end | -| `ordering` | [Order](#ibc.core.channel.v1.Order) | | whether the channel is ordered or unordered | -| `counterparty` | [Counterparty](#ibc.core.channel.v1.Counterparty) | | counterparty channel end | -| `connection_hops` | [string](#string) | repeated | list of connection identifiers, in order, along which packets sent on this channel will travel | -| `version` | [string](#string) | | opaque channel version, which is agreed upon during the handshake | - - - - - - - - -### Counterparty -Counterparty defines a channel end counterparty - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | port on the counterparty chain which owns the other end of the channel. | -| `channel_id` | [string](#string) | | channel end on the counterparty chain | - - - - - - - - -### IdentifiedChannel -IdentifiedChannel defines a channel with additional port and channel -identifier fields. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `state` | [State](#ibc.core.channel.v1.State) | | current state of the channel end | -| `ordering` | [Order](#ibc.core.channel.v1.Order) | | whether the channel is ordered or unordered | -| `counterparty` | [Counterparty](#ibc.core.channel.v1.Counterparty) | | counterparty channel end | -| `connection_hops` | [string](#string) | repeated | list of connection identifiers, in order, along which packets sent on this channel will travel | -| `version` | [string](#string) | | opaque channel version, which is agreed upon during the handshake | -| `port_id` | [string](#string) | | port identifier | -| `channel_id` | [string](#string) | | channel identifier | - - - - - - - - -### Packet -Packet defines a type that carries data across different chains through IBC - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `sequence` | [uint64](#uint64) | | number corresponds to the order of sends and receives, where a Packet with an earlier sequence number must be sent and received before a Packet with a later sequence number. | -| `source_port` | [string](#string) | | identifies the port on the sending chain. | -| `source_channel` | [string](#string) | | identifies the channel end on the sending chain. | -| `destination_port` | [string](#string) | | identifies the port on the receiving chain. | -| `destination_channel` | [string](#string) | | identifies the channel end on the receiving chain. | -| `data` | [bytes](#bytes) | | actual opaque bytes transferred directly to the application module | -| `timeout_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | block height after which the packet times out | -| `timeout_timestamp` | [uint64](#uint64) | | block timestamp (in nanoseconds) after which the packet times out | - - - - - - - - -### PacketState -PacketState defines the generic type necessary to retrieve and store -packet commitments, acknowledgements, and receipts. -Caller is responsible for knowing the context necessary to interpret this -state as a commitment, acknowledgement, or a receipt. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `port_id` | [string](#string) | | channel port identifier. | -| `channel_id` | [string](#string) | | channel unique identifier. | -| `sequence` | [uint64](#uint64) | | packet sequence. | -| `data` | [bytes](#bytes) | | embedded data that represents packet state. | - - - - - - - - - - -### Order -Order defines if a channel is ORDERED or UNORDERED - -| Name | Number | Description | -| ---- | ------ | ----------- | -| ORDER_NONE_UNSPECIFIED | 0 | zero-value for channel ordering | -| ORDER_UNORDERED | 1 | packets can be delivered in any order, which may differ from the order in which they were sent. | -| ORDER_ORDERED | 2 | packets are delivered exactly in the order which they were sent | - - - - - -### State -State defines if a channel is in one of the following states: -CLOSED, INIT, TRYOPEN, OPEN or UNINITIALIZED. - -| Name | Number | Description | -| ---- | ------ | ----------- | -| STATE_UNINITIALIZED_UNSPECIFIED | 0 | Default State | -| STATE_INIT | 1 | A channel has just started the opening handshake. | -| STATE_TRYOPEN | 2 | A channel has acknowledged the handshake step on the counterparty chain. | -| STATE_OPEN | 3 | A channel has completed the handshake. Open channels are ready to send and receive packets. | -| STATE_CLOSED | 4 | A channel has been closed and can no longer be used to send or receive packets. | - - - - - - - - - -

    Top

    @@ -1895,6 +2806,7 @@ MsgChannelOpenInitResponse defines the Msg/ChannelOpenInit response type. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `channel_id` | [string](#string) | | | +| `version` | [string](#string) | | | @@ -1930,6 +2842,11 @@ value will be ignored by core IBC. MsgChannelOpenTryResponse defines the Msg/ChannelOpenTry response type. +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `version` | [string](#string) | | | + + diff --git a/docs/roadmap/history.md b/docs/roadmap/history.md deleted file mode 100644 index 575ed5099e6..00000000000 --- a/docs/roadmap/history.md +++ /dev/null @@ -1,44 +0,0 @@ - -# Past roadmap ibc-go - -History of the roadmap for past quarters. - -## Q4 - 2021 - -### Interchain accounts - -- Finalize the issues raised during the internal audit. -- Prepare codebase & specification for two external audits. -- Write developer documentation. -- Integration with hermes relayer and end-2-end testing. -- Create alpha release. - -### Relayer incentivisation - -- Finalize implementation. -- Update specification and write documentation. -- Do internal audit and write issues that may arise. - -### Align implementation with ICS02 - -We will work to bring the ibc-go implementation in line with [ICS02](https://github.com/cosmos/ibc/tree/master/spec/core/ics-002-client-semantics): [#284](https://github.com/cosmos/ibc-go/issues/284), [#285](https://github.com/cosmos/ibc-go/issues/285), [#286](https://github.com/cosmos/ibc-go/issues/286), [#594](https://github.com/cosmos/ibc-go/issues/594) and [#599](https://github.com/cosmos/ibc-go/issues/599). The support for Wasm-based light clients relies on these issues as well. - -### Release schedule - -|Release|Milestone|Date| -|-------|---------|----| -|[`v1.1.0`](https://github.com/cosmos/ibc-go/releases/tag/v1.1.1)||Oct 04, 2021| -|[`v1.2.1`](https://github.com/cosmos/ibc-go/releases/tag/v1.2.1)||Oct 04, 2021| -|[`v2.0.0-rc0`](https://github.com/cosmos/ibc-go/releases/tag/v2.0.0-rc0)|[Link](https://github.com/cosmos/ibc-go/milestone/3)|Oct 05, 2021| -|[`v1.1.2`](https://github.com/cosmos/ibc-go/releases/tag/v1.1.2)||Oct 15, 2021| -|[`v1.2.2`](https://github.com/cosmos/ibc-go/releases/tag/v1.2.2)||Oct 15, 2021| -|[`v1.1.3`](https://github.com/cosmos/ibc-go/releases/tag/v1.1.3)||Nov 09, 2021| -|[`v1.2.3`](https://github.com/cosmos/ibc-go/releases/tag/v1.2.3)||Nov 09, 2021| -|[`v2.0.0`](https://github.com/cosmos/ibc-go/releases/tag/v2.0.0)|[Link](https://github.com/cosmos/ibc-go/milestone/3)|Nov 09, 2021| -|[`v1.1.4`](https://github.com/cosmos/ibc-go/releases/tag/v1.1.5)||Dec 06, 2021| -|[`v1.2.4`](https://github.com/cosmos/ibc-go/releases/tag/v1.2.4)||Dec 06, 2021| -|[`v2.0.1`](https://github.com/cosmos/ibc-go/releases/tag/v2.0.1)|[Link](https://github.com/cosmos/ibc-go/milestone/11)|Dec 06, 2021| -|[`v1.1.5`](https://github.com/cosmos/ibc-go/releases/tag/v1.1.5)||Dec 15, 2021| -|[`v1.2.5`](https://github.com/cosmos/ibc-go/releases/tag/v1.2.5)||Dec 15, 2021| -|[`v2.0.2`](https://github.com/cosmos/ibc-go/releases/tag/v2.0.2)|[Link](https://github.com/cosmos/ibc-go/milestone/20)|Dec 15, 2021| -|[`v3.0.0-alpha1`](https://github.com/cosmos/ibc-go/releases/tag/v3.0.0-alpha1)|[Link](https://github.com/cosmos/ibc-go/milestone/12)|Dec 21, 2021| \ No newline at end of file diff --git a/docs/roadmap/roadmap.md b/docs/roadmap/roadmap.md index 0a4819b4bb1..3ddc67399c7 100644 --- a/docs/roadmap/roadmap.md +++ b/docs/roadmap/roadmap.md @@ -4,102 +4,55 @@ order: 1 # Roadmap ibc-go -_Lastest update: Dec 22, 2021_ +_Lastest update: March 31, 2022_ -This document endeavours to inform the wider IBC community about plans and priorities for work on ibc-go byt the team at Interchain GmbH. It is intended to broadly inform all users of ibc-go, including developers and operators of IBC, relayer, chain and wallet applications. +This document endeavours to inform the wider IBC community about plans and priorities for work on ibc-go by the team at Interchain GmbH. It is intended to broadly inform all users of ibc-go, including developers and operators of IBC, relayer, chain and wallet applications. This roadmap should be read as a high-level guide, rather than a commitment to schedules and deliverables. The degree of specificity is inversely proportional to the timeline. We will update this document periodically to reflect the status and plans. -The release tags and timelines are educated guesses based on the information at hand at the moment of updating this document. Since predicting the final version number (specially for minor and patch numbers) can be challenging (since we might need to release unforeseen security vulnerability patches or urgent bug fixes), we are using alphabet letters as placeholders. Once we get closer to the release date, the placeholder will be replaced with the right number. An example for clarification... - -Let's assume that the planned release schedule looks like the following: -- At time `t0`: - - The first planned patch release for the `v2.0.x` release series with release tag `v2.0.a`. The placeholder is `a` since this is the first patch release in the planning. - - The first planned minor release for the `v2.x` release series with release tag `v2.a.0`. The placeholder is `a` since this is the first minor release in the planning. -- At time `t0 + delta`: - - The second planned patch release for the `v2.0.x` release series with release tag `v2.0.b`. The placehoder is `b` since this is the next patch release of this release series after `v2.0.a` in the planning. - - The first planned patch release for the new `v2.a.x` release series with release tag `v2.a.a`. The patch version placeholder is `a` because this is the first planned patch release of the `v2.a.x` release series. - -## Q1 - 2022 - -## Features - -### Interchain accounts - -- Work on any issues that may come out of the two external audits. -- Create beta, release candidate and final releases. - -### Relayer incentivisation - -- Work on issues that may arise from internal audit. -- External audit (issues may arise that we need to work on before release). -- Create alpha, beta, release candidate and final release. - -### Align implementation with ICS02 - -- Finalize work for: [#284](https://github.com/cosmos/ibc-go/issues/284), [#285](https://github.com/cosmos/ibc-go/issues/285), [#286](https://github.com/cosmos/ibc-go/issues/286), [#594](https://github.com/cosmos/ibc-go/issues/594) and [#599](https://github.com/cosmos/ibc-go/issues/599). - -### Interchain security - -- Testnet testing of [V1](https://github.com/cosmos/gaia/blob/main/docs/interchain-security.md#v1---full-validator-set). - -### Backlog issues - -- [#545](https://github.com/cosmos/ibc-go/issues/545): Remove the `GetTransferAccount` function, since we never use the ICS20 transfer module account (every escrow address is created as a regular account). -- [#559](https://github.com/cosmos/ibc-go/issues/559): Changes needed to support the migration to SMT storage. This is basically adding a new proof spec that will be used during connection handshake with a chain that has migrated to SMT to verify that the light client of the counterparty chain uses the correct proof specs to be able to verify proofs for that chain. -- And more to be added later! - -## Release schedule - -|Release|Milestone|Date| -|-------|---------|----| -|[`v3.0.0-alpha2`](https://github.com/cosmos/ibc-go/releases/tag/v3.0.0-alpha2)||Jan 07, 2021| - -During this quarter we will also probably release versions that bump the Cosmos SDK to `v0.45` and Tendermint to `v0.35`, but at the moment of writing it is difficult to estimate when. Check our roadmap regularly for updates. - -### H2 January - -- [`v2.0.a`](https://github.com/cosmos/ibc-go/milestone/11) -- [`v3.0.0-beta1`](https://github.com/cosmos/ibc-go/milestone/12): Beta 1 release of `v3.0.0` including Interchain Accounts, an update of Golang from `v1.15` to `v1.17`, and some core improvements. This is a Go-API breaking release because of [#472](https://github.com/cosmos/ibc-go/issues/472) and [#675](https://github.com/cosmos/ibc-go/pull/675). - -### H1 February - -- [`v3.0.0-rc0`](https://github.com/cosmos/ibc-go/milestone/12): Release candidate 0 of `v3.0.0` including Interchain Accounts, an update of Golang from `v1.15` to `v1.17`, and some core improvements. This is a Go-API breaking release because of [#472](https://github.com/cosmos/ibc-go/issues/472) and [#675](https://github.com/cosmos/ibc-go/pull/675). - -### H2 February +## Q2 - 2022 -- [`v3.a.0-alpha1`](https://github.com/cosmos/ibc-go/milestone/16): Alpha release of `v3.a.0` including Relayer Incentivisation. This release will include fixes to issues that surfaced during the internal audit. +At a high level we will focus on: -### H1 March +- Finishing the implementation of [relayer incentivisation](https://github.com/orgs/cosmos/projects/7/views/8). +- Finishing the [refactoring of 02-client](https://github.com/cosmos/ibc-go/milestone/16). +- Finishing the upgrade to Cosmos SDK v0.46 and Tendermint v0.35. +- Implementing and testing the changes needed to support the [transtion to SMT storage](https://github.com/cosmos/ibc-go/milestone/21) in the Cosmos SDK. +- Desiging the implementation and scoping the engineering work for [channel upgradability](https://github.com/cosmos/ibc/blob/master/spec/core/ics-004-channel-and-packet-semantics/UPGRADES.md). +- Improving the project's documentation and writing guides for [light client](https://github.com/cosmos/ibc-go/issues/59) and middleware implementation. +- Working on [core backlog issues](https://github.com/cosmos/ibc-go/milestone/8). +- Spending time on expanding and deepening our knowledge of IBC, but also other parts of the Cosmos stack. +- And last, but not least, onboarding new members to the team. -- [`v3.0.0`](https://github.com/cosmos/ibc-go/milestone/12): Final release of `v3.0.0` including Interchain Accounts, an update of Golang from `v1.15` to `v1.17`, and some core improvements. This is a Go-API breaking release because of [#472](https://github.com/cosmos/ibc-go/issues/472) and [#675](https://github.com/cosmos/ibc-go/pull/675). -- [`v3.a.0-beta1`](https://github.com/cosmos/ibc-go/milestone/16): Beta release of `v3.a.0` including Relayer Incentivisation. This release will include fixes to issues that surfaced during the external audit. +For a detail view of each iteration's planned work, please check out our [project board](https://github.com/orgs/cosmos/projects/7). -### H2 March +### Release schedule -- [`v3.a.0-rc0`](https://github.com/cosmos/ibc-go/milestone/16): Release candiate 0 `v3.1.0` including Relayer Incentivisation. +#### **April** -## Q2 - 2022 +In the first half of the month we will probably cut: -### Features +- Alpha/beta pre-releases with the upgrade to SDK 0.46 and Tendermint v0.35. +- [Alpha](https://github.com/cosmos/ibc-go/milestone/5) pre-release with the implementation of relayer incentivisation. -> Full scope still TBD. +In the second half, and depending on the date of the final release of Cosmos SDK 0.46, we will probably cut the final release with the upgrade to SDK 0.46 and Tendermint v0.35, and also a [beta](https://github.com/cosmos/ibc-go/milestone/23) pre-release with the implementation of relayer incentivisation. -### Support for Wasm-based light clients +In the second half of the month we also plan to do a second internal audit of the implementation of relayer incentivisation, and issues will most likely will be created from the audit. Depending on the nature and type of the issues we create, those would be released in a second beta pre-release or in a [release candidate](https://github.com/cosmos/ibc-go/milestone/24). -There is an open [PR](https://github.com/cosmos/ibc-go/pull/208) that implements support for Wasm-based light clients, but it needs to be updated after the finalization of the [ICS28](https://github.com/cosmos/ibc/tree/master/spec/client/ics-008-wasm-client) specification. The PR need thorough review, more tests and potentially implementation changes. +#### **May** -## Release schedule +In the first half we will probably start cutting release candidates with relayer incentivisation and the 02-client refactor. Final release would most likely come out at the end of the month or beginning of June. -During this quarter we will also probably release versions that bump the Cosmos SDK to `v0.46` and to `v1.0`, but at the moment of writing it is difficult to estimate when. Check our roadmap regularly for updates. +#### **June** -### H1 April +We will probably cut at the end of the month or beginning of Q3 patch or minor releases on all the supported release lines with the [small features and core improvements](https://github.com/cosmos/ibc-go/milestone/8) that we work on during the quarter. -- [`v3.a.0`](https://github.com/cosmos/ibc-go/milestone/16): Final release of `v3.a.0` including Relayer Incentivisation. -- [`v4.0.0-rc0`](https://github.com/cosmos/ibc-go/milestone/16): Release candidate 0 of `v4.0.0` including the work for the issues to bring ibc-go implementation in line with ICS02 (which are Go-API breaking changes). +## Q3 - 2022 -### H2 April +We will most likely start the implementation of [channel upgradability](https://github.com/cosmos/ibc/blob/master/spec/core/ics-004-channel-and-packet-semantics/UPGRADES.md). At the end of Q2 or maybe beginning of Q3 we might also work on designing the implementation and scoping the engineering work to add support for [ordered channels that can timeout](https://github.com/cosmos/ibc/pull/636), and we could potentially work on this feature also in Q3. -- [`v4.0.0`](https://github.com/cosmos/ibc-go/milestone/16): Release candidate 0 of `v4.0.0` including the work for the issues to bring ibc-go implementation in line with ICS02 (which are Go-API breaking changes). +We will also probably do an audit of the implementation of the [CCV application](https://github.com/cosmos/interchain-security/tree/main/x/ccv) for Interchain Security. +### Release schedule +In this quarter we will make the final release to support the migration to SMT storage. \ No newline at end of file diff --git a/docs/versions b/docs/versions index 45d6f133c48..e568f9a716f 100644 --- a/docs/versions +++ b/docs/versions @@ -1,4 +1,9 @@ +release/v3.0.x v3.0.0 +release/v2.2.x v2.2.0 +release/v2.1.x v2.1.0 release/v2.0.x v2.0.0 +release/v1.4.x v1.4.0 +release/v1.3.x v1.3.0 release/v1.2.x v1.2.0 release/v1.1.x v1.1.0 main main diff --git a/go.mod b/go.mod index 2bc717ea5f3..4f56c03dd9e 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alp require ( github.com/armon/go-metrics v0.3.10 github.com/confio/ics23/go v0.7.0 - github.com/cosmos/cosmos-sdk v0.45.1 + github.com/cosmos/cosmos-sdk v0.45.3 github.com/gogo/protobuf v1.3.3 github.com/golang/protobuf v1.5.2 github.com/gorilla/mux v1.8.0 @@ -16,13 +16,13 @@ require ( github.com/regen-network/cosmos-proto v0.3.1 github.com/spf13/cast v1.4.1 github.com/spf13/cobra v1.4.0 - github.com/spf13/viper v1.10.1 + github.com/spf13/viper v1.11.0 github.com/stretchr/testify v1.7.1 - github.com/tendermint/tendermint v0.34.14 - github.com/tendermint/tm-db v0.6.4 - google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa + github.com/tendermint/tendermint v0.34.19 + github.com/tendermint/tm-db v0.6.6 + google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac google.golang.org/grpc v1.45.0 - google.golang.org/protobuf v1.27.1 + google.golang.org/protobuf v1.28.0 gopkg.in/yaml.v2 v2.4.0 ) @@ -31,7 +31,7 @@ require ( github.com/99designs/keyring v1.1.6 // indirect github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d // indirect github.com/DataDog/zstd v1.4.5 // indirect - github.com/Workiva/go-datastructures v1.0.52 // indirect + github.com/Workiva/go-datastructures v1.0.53 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.0 // indirect github.com/btcsuite/btcd v0.22.0-beta // indirect @@ -54,15 +54,16 @@ require ( github.com/felixge/httpsnoop v1.0.1 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/gin-gonic/gin v1.7.0 // indirect - github.com/go-kit/kit v0.10.0 // indirect - github.com/go-logfmt/logfmt v0.5.0 // indirect + github.com/go-kit/kit v0.12.0 // indirect + github.com/go-kit/log v0.2.0 // indirect + github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gogo/gateway v1.1.0 // indirect github.com/golang/snappy v0.0.3 // indirect github.com/google/btree v1.0.0 // indirect github.com/google/orderedcode v0.0.1 // indirect github.com/gorilla/handlers v1.5.1 // indirect - github.com/gorilla/websocket v1.4.2 // indirect + github.com/gorilla/websocket v1.5.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect github.com/gtank/merlin v0.1.1 // indirect @@ -75,32 +76,31 @@ require ( github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d // indirect - github.com/klauspost/compress v1.11.7 // indirect - github.com/lib/pq v1.10.2 // indirect + github.com/klauspost/compress v1.13.6 // indirect + github.com/lib/pq v1.10.4 // indirect github.com/libp2p/go-buffer-pool v0.0.2 // indirect - github.com/magiconair/properties v1.8.5 // indirect + github.com/magiconair/properties v1.8.6 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 // indirect - github.com/minio/highwayhash v1.0.1 // indirect + github.com/minio/highwayhash v1.0.2 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.4.3 // indirect github.com/mtibben/percent v0.2.1 // indirect - github.com/opencontainers/image-spec v1.0.2 // indirect - github.com/opencontainers/runc v1.0.3 // indirect github.com/pelletier/go-toml v1.9.4 // indirect + github.com/pelletier/go-toml/v2 v2.0.0-beta.8 // indirect github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.11.0 // indirect + github.com/prometheus/client_golang v1.12.1 // indirect github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.29.0 // indirect - github.com/prometheus/procfs v0.6.0 // indirect + github.com/prometheus/common v0.32.1 // indirect + github.com/prometheus/procfs v0.7.3 // indirect github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect - github.com/rs/cors v1.7.0 // indirect + github.com/rs/cors v1.8.2 // indirect github.com/rs/zerolog v1.23.0 // indirect github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa // indirect - github.com/spf13/afero v1.6.0 // indirect + github.com/spf13/afero v1.8.2 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.2.0 // indirect @@ -110,13 +110,13 @@ require ( github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 // indirect github.com/tendermint/go-amino v0.16.0 // indirect github.com/zondax/hid v0.9.0 // indirect - go.etcd.io/bbolt v1.3.5 // indirect - golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect - golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f // indirect - golang.org/x/sys v0.0.0-20211210111614-af8b64212486 // indirect - golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect + go.etcd.io/bbolt v1.3.6 // indirect + golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect + golang.org/x/net v0.0.0-20220412020605-290c469a71a5 // indirect + golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect + golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/text v0.3.7 // indirect - gopkg.in/ini.v1 v1.66.2 // indirect + gopkg.in/ini.v1 v1.66.4 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect nhooyr.io/websocket v1.8.6 // indirect ) diff --git a/go.sum b/go.sum index cb1b3b22474..a8f6d150085 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,10 @@ +bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= @@ -15,6 +17,7 @@ cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOY cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= @@ -26,12 +29,16 @@ cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+Y cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= +cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= +cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= @@ -45,6 +52,7 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.0.0-beta.2 h1:/BZRNzm8N4K4eWfK28dL4yescorxtO7YG1yun8fy+pI= filippo.io/edwards25519 v1.0.0-beta.2/go.mod h1:X+pm78QAUPtFLi1z9PYIlS/bdDnvbCOGKtZ+ACWEf7o= @@ -52,6 +60,9 @@ github.com/99designs/keyring v1.1.6 h1:kVDC2uCgVwecxCk+9zoCt2uEL6dt+dfVzMvGgnVcI github.com/99designs/keyring v1.1.6/go.mod h1:16e0ds7LGQQcT59QqkTg72Hh5ShM51Byv5PEmW6uoRU= github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= +github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0= +github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8= github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= @@ -67,17 +78,22 @@ github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg= github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= +github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/HdrHistogram/hdrhistogram-go v1.1.0/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= +github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= +github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= @@ -88,13 +104,16 @@ github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrU github.com/VictoriaMetrics/fastcache v1.5.7/go.mod h1:ptDBkNMQI4RtmVo8VS/XwRY6RoTu1dAWCbrk+6WsEM8= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/Workiva/go-datastructures v1.0.52 h1:PLSK6pwn8mYdaoaCZEMsXBpBotr4HHn9abU0yMQt0NI= github.com/Workiva/go-datastructures v1.0.52/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= +github.com/Workiva/go-datastructures v1.0.53 h1:J6Y/52yX10Xc5JjXmGtWoSSxs3mZnGSaq37xZZh7Yig= +github.com/Workiva/go-datastructures v1.0.53/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= github.com/Zilliqa/gozilliqa-sdk v1.2.1-0.20201201074141-dd0ecada1be6/go.mod h1:eSYp2T6f0apnuW8TzhV3f6Aff2SE8Dwio++U4ha4yEM= -github.com/adlio/schema v1.1.13 h1:LeNMVg5Z1FX+Qgz8tJUijBLRdcpbFUElz+d1489On98= github.com/adlio/schema v1.1.13/go.mod h1:L5Z7tw+7lRK1Fnpi/LT/ooCP1elkXn0krMWBQHUhEDE= +github.com/adlio/schema v1.3.0 h1:eSVYLxYWbm/6ReZBCkLw4Fz7uqC+ZNoPvA39bOwi52A= +github.com/adlio/schema v1.3.0/go.mod h1:51QzxkpeFs6lRY11kPye26IaFPOV+HqEj01t5aXXKfs= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -117,7 +136,12 @@ github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6l github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= +github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o= +github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -126,7 +150,6 @@ github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ= github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0= github.com/btcsuite/btcd v0.0.0-20190315201642-aa6e0f35703c/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= @@ -149,8 +172,10 @@ github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/casbin/casbin/v2 v2.37.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= @@ -166,6 +191,7 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod h1:1MxXX1Ux4x6mqPmjkUgTP1CdXIBXKX7T+Jk9Gxrmx+U= @@ -187,8 +213,9 @@ github.com/confio/ics23/go v0.6.6/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4ur github.com/confio/ics23/go v0.7.0 h1:00d2kukk7sPoHWL4zZBZwzxnpA2pec1NPdwbSokJ5w8= github.com/confio/ics23/go v0.7.0/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg= github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= -github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6 h1:NmTXa/uVnDyp0TY5MKi197+3HWcnYWfnHGyaFthlnGw= github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.2.1 h1:/EeEo2EtN3umhbbgCveyjifoMYg0pS+nMMEemaYw634= +github.com/containerd/continuity v0.2.1/go.mod h1:wCYX+dRqZdImhGucXOqTQn05AhX6EUDaGEMUzTFFpLg= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -202,8 +229,8 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cosmos/btcutil v1.0.4 h1:n7C2ngKXo7UC9gNyMNLbzqz7Asuf+7Qv4gnX/rOdQ44= github.com/cosmos/btcutil v1.0.4/go.mod h1:Ffqc8Hn6TJUdDgHBwIZLtrLQC1KdJ9jGJl/TvgUaxbU= -github.com/cosmos/cosmos-sdk v0.45.1 h1:PY79YxPea5qlRLExRnzg8/rT1Scc8GGgRs22p7DX99Q= -github.com/cosmos/cosmos-sdk v0.45.1/go.mod h1:XXS/asyCqWNWkx2rW6pSuen+EVcpAFxq6khrhnZgHaQ= +github.com/cosmos/cosmos-sdk v0.45.3 h1:PiVSU3IkNEDPhoxOZHk2lPnhwBBJgEYAtAR0jGXRN4g= +github.com/cosmos/cosmos-sdk v0.45.3/go.mod h1:qYm5JEr0ZlbnmoP/Q3b+dYMOliHf4ddHirpILiwZzqg= github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= @@ -228,6 +255,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= +github.com/denisenkom/go-mssqldb v0.12.0/go.mod h1:iiK0YP1ZeepvmBQk/QpLEhhTNJgfzrpArPY/aFvc9yU= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= github.com/dgraph-io/badger/v2 v2.2007.2 h1:EjjK0KqwaFMlPin1ajhP943VPENHJdEz1KLIegjaI3k= @@ -241,6 +269,7 @@ github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WA github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= @@ -281,13 +310,16 @@ github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= +github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goblin v0.0.0-20210519012713-85d372ac71e2/go.mod h1:VzmDKDJVZI3aJmnRI9VjAn9nJ8qPPsN1fqzr9dqInIo= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -306,13 +338,17 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= +github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.0 h1:7i2K3eKTos3Vc0enKCfnVcgHh2olr/MyfboYq7cAcFw= +github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= @@ -324,9 +360,10 @@ github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7a github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= @@ -339,6 +376,10 @@ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x github.com/gogo/gateway v1.1.0 h1:u0SuhL9+Il+UbjM9VIE3ntfRujKbvVpFvNB4HbjeVQ0= github.com/gogo/gateway v1.1.0/go.mod h1:S7rR8FRQyG3QFESeSv4l2WnsyzlCLG0CzBbUUo/mbic= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188/go.mod h1:vXjM/+wXQnTPR4KqTKDgJukSZ6amVRtWMPEjE6sQoK8= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -394,8 +435,9 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa h1:Q75Upo5UN4JbPFURXZ8nLKYUvF85dyFRop/vQ0Rv+64= @@ -415,6 +457,7 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= @@ -428,6 +471,9 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= +github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= @@ -438,10 +484,12 @@ github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2z github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= @@ -464,6 +512,7 @@ github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uM github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= @@ -473,7 +522,9 @@ github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= @@ -498,16 +549,21 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= +github.com/hashicorp/serf v0.9.7/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= github.com/hdevalence/ed25519consensus v0.0.0-20210204194344-59a8610d2b87 h1:uUjLpLt6bVvZ72SQc/B4dXcPBw4Vgd7soowdRl52qEM= github.com/hdevalence/ed25519consensus v0.0.0-20210204194344-59a8610d2b87/go.mod h1:XGsKKeXxeRr95aEOgipvluMPlgjr7dGlk9ZTWOjcUcg= github.com/holiman/uint256 v1.1.1/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo= github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= @@ -519,6 +575,7 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -526,6 +583,8 @@ github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS github.com/jhump/protoreflect v1.9.0 h1:npqHz788dryJiR/l6K/RUQAyh2SwV91+d1dnh4RjO9w= github.com/jhump/protoreflect v1.9.0/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= @@ -545,6 +604,7 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV github.com/julienschmidt/httprouter v1.1.1-0.20170430222011-975b5c4c7c21/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d h1:Z+RDyXzjKE0i2sTjZ/b1uxiGtPhFy34Ou/Tk0qwN0kM= github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d/go.mod h1:JJNrCn9otv/2QP4D7SMJBgaleKpOf66PnW6F5WGNRIc= @@ -552,8 +612,10 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.11.7 h1:0hzRabrMN4tSTvMfnL3SCv1ZGeAP23ynzodBgaHeMeg= github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= @@ -569,8 +631,8 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+ github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= -github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= +github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs= github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= @@ -580,8 +642,9 @@ github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc8 github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= +github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -603,15 +666,18 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 h1:hLDRPB66XQT/8+wG9WsDpiCvZf1yKO7sz7scAjSlBa0= github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= -github.com/minio/highwayhash v1.0.1 h1:dZ6IIu8Z14VlC0VpfKofAhCy74wu/Qb5gcn52yWoz/0= github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= +github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= +github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -623,7 +689,7 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= @@ -634,6 +700,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= @@ -645,10 +712,16 @@ github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/jwt v1.2.2/go.mod h1:/xX356yQA6LuXI9xWW7mZNpxgF2mBmGecH+Fj34sP5Q= +github.com/nats-io/jwt/v2 v2.0.3/go.mod h1:VRP+deawSXyhNjXmxPCHskrR6Mq50BqpEI5SEcNiGlY= github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats-server/v2 v2.5.0/go.mod h1:Kj86UtrXAL6LwYRA6H4RqzkHhK0Vcv2ZnKD5WbQ1t3g= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nats.go v1.12.1/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.2.0/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s= +github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/neilotoole/errgroup v0.1.5/go.mod h1:Q2nLGf+594h0CLBs/Mbg6qOr7GtqDK7C2S41udRnToE= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= @@ -677,8 +750,9 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak= github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= -github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= @@ -692,10 +766,12 @@ github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE= github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= github.com/otiai10/copy v1.6.0 h1:IinKAryFFuPONZ7cm6T6E2QX/vcJwSnlaA5lfoaXIiQ= @@ -711,21 +787,26 @@ github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144T github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml/v2 v2.0.0-beta.8 h1:dy81yyLYJDwMTifq24Oi/IslOslRrDSb3jwDggjz3Z0= +github.com/pelletier/go-toml/v2 v2.0.0-beta.8/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/performancecopilot/speed/v4 v4.0.0/go.mod h1:qxrSyuDGrTOWfV+uKRFhfxw6h/4HXRGUiZiufxo49BM= github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= @@ -738,8 +819,9 @@ github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeD github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.8.0/go.mod h1:O9VU6huf47PktckDQfMTX0Y8tY0/7TSWwj+ITvv0TnM= -github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= +github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -757,8 +839,9 @@ github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB8 github.com/prometheus/common v0.14.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.29.0 h1:3jqPBvKT4OHAbje2Ql7KeaaSicDBCxMYwEJU1zRJceE= -github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -767,8 +850,9 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= @@ -785,8 +869,9 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U= +github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.23.0 h1:UskrK+saS9P9Y789yNNulYKdARjPZuS35B8gJF2x60g= @@ -796,6 +881,7 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sagikazarmark/crypt v0.4.0/go.mod h1:ALv2SRj7GxYV4HO9elxH9nS6M9gW+xDNxqmyJ6RfDFM= +github.com/sagikazarmark/crypt v0.5.0/go.mod h1:l+nzl7KWh51rpzp2h7t4MZWyiEWdhNpOAnclKvg+mdA= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa h1:0U2s5loxrTy6/VgfVoLuVLFJcURKLH49ie0zSch7gh4= github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= @@ -821,16 +907,16 @@ github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0b github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= +github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= -github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= @@ -841,17 +927,20 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= -github.com/spf13/viper v1.10.1 h1:nuJZuYpG7gTj/XqiUwg8bA0cp1+M2mC3J4g5luUYBKk= github.com/spf13/viper v1.10.1/go.mod h1:IGlFPqhNAPKRxohIzWpI5QEy4kuI7tcl5WvR+8qy1rU= +github.com/spf13/viper v1.11.0 h1:7OX/1FS6n7jHD1zGrZTM7WtY13ZELRyosK4k93oPr44= +github.com/spf13/viper v1.11.0/go.mod h1:djo0X/bA5+tYVoCn+C7cAYJGcVn/qYLFTG8gdUsX7Zk= github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570/go.mod h1:8OR4w3TdeIHIh1g6EMY5p0gVNOovcWC+1vpc7naMuAw= github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= +github.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= @@ -877,19 +966,25 @@ github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 h1:hqAk8riJvK4RM github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15/go.mod h1:z4YtwM70uOnk8h0pjJYlj3zdYwi9l03By6iAIF5j/Pk= github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E= github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= -github.com/tendermint/tendermint v0.34.14 h1:GCXmlS8Bqd2Ix3TQCpwYLUNHe+Y+QyJsm5YE+S/FkPo= github.com/tendermint/tendermint v0.34.14/go.mod h1:FrwVm3TvsVicI9Z7FlucHV6Znfd5KBc/Lpp69cCwtk0= -github.com/tendermint/tm-db v0.6.4 h1:3N2jlnYQkXNQclQwd/eKV/NzlqPlfK21cpRRIx80XXQ= +github.com/tendermint/tendermint v0.34.19 h1:y0P1qI5wSa9IRuhKnTDA6IUcOrLi1hXJuALR+R7HFEk= +github.com/tendermint/tendermint v0.34.19/go.mod h1:R5+wgIwSxMdKQcmOaeudL0Cjkr3HDkhpcdum6VeU3R4= github.com/tendermint/tm-db v0.6.4/go.mod h1:dptYhIpJ2M5kUuenLr+Yyf3zQOv1SgBZcl8/BmWlMBw= +github.com/tendermint/tm-db v0.6.6 h1:EzhaOfR0bdKyATqcd5PNeyeq8r+V4bRPHBfyFdD9kGM= +github.com/tendermint/tm-db v0.6.6/go.mod h1:wP8d49A85B7/erz/r4YbKssKw6ylsO/hKtFk7E1aWZI= github.com/tidwall/gjson v1.6.7/go.mod h1:zeFuBCIqD4sN/gmqBzZ4j7Jd6UcA2Fc56x7QFsv+8fI= github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/sjson v1.1.4/go.mod h1:wXpKXu8CtDjKAZ+3DrKY5ROCorDFahq8l0tey/Lx1fg= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= github.com/tyler-smith/go-bip39 v1.0.2/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= @@ -914,15 +1009,20 @@ github.com/zondax/hid v0.9.0 h1:eiT3P6vNxAEVxXMw66eZUAAnU2zD33JBkfG/EnfAKl8= github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= +go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/api/v3 v3.5.2/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/pkg/v3 v3.5.2/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs= +go.etcd.io/etcd/client/v2 v2.305.2/go.mod h1:2D7ZejHVMIfog1221iLSYlQRzrtECw3kz4I4VAQm3qI= +go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -937,13 +1037,17 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -959,14 +1063,25 @@ golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= +golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210915214749-c084706c2272/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA= +golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= @@ -978,6 +1093,7 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1022,6 +1138,7 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1060,9 +1177,18 @@ golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f h1:w6wWR0H+nyVpbSAQbzVEIACVyr/h8l/BEkY6Sokc7Eg= golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211208012354-db4efeb81f4b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5 h1:bRb386wvrE+oBNdF1d/Xh9mQrfQ4ecYhW5qJ5GvTGT4= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1074,13 +1200,15 @@ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1126,6 +1254,7 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1152,6 +1281,7 @@ golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200824131525-c12d262b63d8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1161,6 +1291,7 @@ golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1168,6 +1299,7 @@ golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1181,14 +1313,24 @@ golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486 h1:5hpz5aRr+W1erYCL5JRhSUBJRph7l9XkNveoExlrKYk= +golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1203,9 +1345,13 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -1255,12 +1401,14 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= @@ -1270,8 +1418,13 @@ golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -1294,7 +1447,6 @@ google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34q google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= @@ -1306,6 +1458,10 @@ google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdr google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU= google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= +google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= +google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= +google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1354,8 +1510,10 @@ google.golang.org/genproto v0.0.0-20201119123407-9b1e624d6bc4/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -1375,13 +1533,24 @@ google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEc google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa h1:I0YcKz0I7OAhddo7ya8kMnvprhcWM045PmkBdMO9zN0= google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac h1:qSNTkEN+L2mvWcLgJOR+8bdHX9rN/IdU3A1Ghpfb1Rg= +google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -1417,6 +1586,7 @@ google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9K google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= @@ -1433,12 +1603,14 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.25.1-0.20200805231151-a709e31e5d12/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= @@ -1446,9 +1618,9 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4= +gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= @@ -1481,7 +1653,9 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k= nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/modules/apps/27-interchain-accounts/controller/ibc_module.go b/modules/apps/27-interchain-accounts/controller/ibc_module.go index c00c9f5d1c2..325ec70959b 100644 --- a/modules/apps/27-interchain-accounts/controller/ibc_module.go +++ b/modules/apps/27-interchain-accounts/controller/ibc_module.go @@ -163,3 +163,8 @@ func (im IBCModule) OnTimeoutPacket( return im.app.OnTimeoutPacket(ctx, packet, relayer) } + +// GetAppVersion returns the interchain accounts metadata. +func (im IBCModule) GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) { + return im.keeper.GetAppVersion(ctx, portID, channelID) +} diff --git a/modules/apps/27-interchain-accounts/controller/ibc_module_test.go b/modules/apps/27-interchain-accounts/controller/ibc_module_test.go index c0163e954e1..608996515c1 100644 --- a/modules/apps/27-interchain-accounts/controller/ibc_module_test.go +++ b/modules/apps/27-interchain-accounts/controller/ibc_module_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/suite" "github.com/tendermint/tendermint/crypto" + icacontroller "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller" "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller/types" icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" @@ -702,3 +703,23 @@ func (suite *InterchainAccountsTestSuite) TestSingleHostMultipleControllers() { }) } } + +func (suite *InterchainAccountsTestSuite) TestGetAppVersion() { + path := NewICAPath(suite.chainA, suite.chainB) + suite.coordinator.SetupConnections(path) + + err := SetupICAPath(path, TestOwnerAddress) + suite.Require().NoError(err) + + module, _, err := suite.chainA.App.GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID) + suite.Require().NoError(err) + + cbs, ok := suite.chainA.App.GetIBCKeeper().Router.GetRoute(module) + suite.Require().True(ok) + + controllerModule := cbs.(icacontroller.IBCModule) + + appVersion, found := controllerModule.GetAppVersion(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) + suite.Require().True(found) + suite.Require().Equal(path.EndpointA.ChannelConfig.Version, appVersion) +} diff --git a/modules/apps/27-interchain-accounts/controller/keeper/account.go b/modules/apps/27-interchain-accounts/controller/keeper/account.go index dd87348876e..03eeef69f1f 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/account.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/account.go @@ -10,11 +10,10 @@ import ( ) // RegisterInterchainAccount is the entry point to registering an interchain account. -// It generates a new port identifier using the owner address, connection identifier, -// and counterparty connection identifier. It will bind to the port identifier and -// call 04-channel 'ChanOpenInit'. An error is returned if the port identifier is -// already in use. Gaining access to interchain accounts whose channels have closed -// cannot be done with this function. A regular MsgChanOpenInit must be used. +// It generates a new port identifier using the owner address. It will bind to the +// port identifier and call 04-channel 'ChanOpenInit'. An error is returned if the port +// identifier is already in use. Gaining access to interchain accounts whose channels +// have closed cannot be done with this function. A regular MsgChanOpenInit must be used. func (k Keeper) RegisterInterchainAccount(ctx sdk.Context, connectionID, owner string) error { portID, err := icatypes.NewControllerPortID(owner) if err != nil { diff --git a/modules/apps/27-interchain-accounts/controller/keeper/keeper.go b/modules/apps/27-interchain-accounts/controller/keeper/keeper.go index 87af9ae9c6f..86099b954bd 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/keeper.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/keeper.go @@ -102,6 +102,11 @@ func (k Keeper) ClaimCapability(ctx sdk.Context, cap *capabilitytypes.Capability return k.scopedKeeper.ClaimCapability(ctx, cap, name) } +// GetAppVersion calls the ICS4Wrapper GetAppVersion function. +func (k Keeper) GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) { + return k.ics4Wrapper.GetAppVersion(ctx, portID, channelID) +} + // GetActiveChannelID retrieves the active channelID from the store, keyed by the provided connectionID and portID func (k Keeper) GetActiveChannelID(ctx sdk.Context, connectionID, portID string) (string, bool) { store := ctx.KVStore(k.storeKey) diff --git a/modules/apps/27-interchain-accounts/controller/keeper/params.go b/modules/apps/27-interchain-accounts/controller/keeper/params.go index d199b8b554d..dce72c0c491 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/params.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/params.go @@ -6,7 +6,7 @@ import ( "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller/types" ) -// IsControllerEnabled retrieves the host enabled boolean from the paramstore. +// IsControllerEnabled retrieves the controller enabled boolean from the paramstore. // True is returned if the controller submodule is enabled. func (k Keeper) IsControllerEnabled(ctx sdk.Context) bool { var res bool @@ -14,12 +14,12 @@ func (k Keeper) IsControllerEnabled(ctx sdk.Context) bool { return res } -// GetParams returns the total set of the host submodule parameters. +// GetParams returns the total set of the controller submodule parameters. func (k Keeper) GetParams(ctx sdk.Context) types.Params { return types.NewParams(k.IsControllerEnabled(ctx)) } -// SetParams sets the total set of the host submodule parameters. +// SetParams sets the total set of the controller submodule parameters. func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { k.paramSpace.SetParamSet(ctx, ¶ms) } diff --git a/modules/apps/27-interchain-accounts/types/expected_keepers.go b/modules/apps/27-interchain-accounts/types/expected_keepers.go index 4c6a1708e43..8fb0b743cda 100644 --- a/modules/apps/27-interchain-accounts/types/expected_keepers.go +++ b/modules/apps/27-interchain-accounts/types/expected_keepers.go @@ -21,6 +21,7 @@ type AccountKeeper interface { // ICS4Wrapper defines the expected ICS4Wrapper for middleware type ICS4Wrapper interface { SendPacket(ctx sdk.Context, channelCap *capabilitytypes.Capability, packet ibcexported.PacketI) error + GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) } // ChannelKeeper defines the expected IBC channel keeper diff --git a/modules/apps/29-fee/client/cli/cli.go b/modules/apps/29-fee/client/cli/cli.go new file mode 100644 index 00000000000..a0141c20f9a --- /dev/null +++ b/modules/apps/29-fee/client/cli/cli.go @@ -0,0 +1,48 @@ +package cli + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/spf13/cobra" +) + +// GetQueryCmd returns the query commands for 29-fee +func GetQueryCmd() *cobra.Command { + queryCmd := &cobra.Command{ + Use: "ibc-fee", + Short: "IBC relayer incentivization query subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + } + + queryCmd.AddCommand( + GetCmdIncentivizedPacket(), + GetCmdIncentivizedPackets(), + GetCmdTotalRecvFees(), + GetCmdTotalAckFees(), + GetCmdTotalTimeoutFees(), + GetCmdIncentivizedPacketsForChannel(), + GetCmdCounterpartyAddress(), + GetCmdFeeEnabledChannel(), + GetCmdFeeEnabledChannels(), + ) + + return queryCmd +} + +// NewTxCmd returns the transaction commands for 29-fee +func NewTxCmd() *cobra.Command { + txCmd := &cobra.Command{ + Use: "ibc-fee", + Short: "IBC relayer incentivization transaction subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + txCmd.AddCommand( + NewPayPacketFeeAsyncTxCmd(), + NewRegisterCounterpartyAddress(), + ) + + return txCmd +} diff --git a/modules/apps/29-fee/client/cli/query.go b/modules/apps/29-fee/client/cli/query.go new file mode 100644 index 00000000000..a183c8f89d3 --- /dev/null +++ b/modules/apps/29-fee/client/cli/query.go @@ -0,0 +1,395 @@ +package cli + +import ( + "fmt" + "strconv" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + "github.com/spf13/cobra" +) + +// GetCmdIncentivizedPacket returns the unrelayed incentivized packet for a given packetID +func GetCmdIncentivizedPacket() *cobra.Command { + cmd := &cobra.Command{ + Use: "packet [port-id] [channel-id] [sequence]", + Short: "Query for an unrelayed incentivized packet by port-id, channel-id and packet sequence.", + Long: "Query for an unrelayed incentivized packet by port-id, channel-id and packet sequence.", + Args: cobra.ExactArgs(3), + Example: fmt.Sprintf("%s query ibc-fee packet-by-id", version.AppName), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + seq, err := strconv.ParseUint(args[2], 10, 64) + if err != nil { + return err + } + + packetID := channeltypes.PacketId{ + PortId: args[0], + ChannelId: args[1], + Sequence: seq, + } + + req := &types.QueryIncentivizedPacketRequest{ + PacketId: packetID, + QueryHeight: uint64(clientCtx.Height), + } + + queryClient := types.NewQueryClient(clientCtx) + + res, err := queryClient.IncentivizedPacket(cmd.Context(), req) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetCmdIncentivizedPackets returns all of the unrelayed incentivized packets +func GetCmdIncentivizedPackets() *cobra.Command { + cmd := &cobra.Command{ + Use: "packets", + Short: "Query for all of the unrelayed incentivized packets and associated fees across all channels.", + Long: "Query for all of the unrelayed incentivized packets and associated fees across all channels.", + Args: cobra.NoArgs, + Example: fmt.Sprintf("%s query ibc-fee packets", version.AppName), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + req := &types.QueryIncentivizedPacketsRequest{ + Pagination: pageReq, + QueryHeight: uint64(clientCtx.Height), + } + + queryClient := types.NewQueryClient(clientCtx) + + res, err := queryClient.IncentivizedPackets(cmd.Context(), req) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "packets") + + return cmd +} + +// GetCmdTotalRecvFees returns the command handler for the Query/TotalRecvFees rpc. +func GetCmdTotalRecvFees() *cobra.Command { + cmd := &cobra.Command{ + Use: "total-recv-fees [port-id] [channel-id] [sequence]", + Short: "Query the total receive fees for a packet", + Long: "Query the total receive fees for a packet", + Args: cobra.ExactArgs(3), + Example: fmt.Sprintf("%s query ibc-fee total-recv-fees transfer channel-5 100", version.AppName), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + portID, channelID := args[0], args[1] + seq, err := strconv.ParseUint(args[2], 10, 64) + if err != nil { + return err + } + + packetID := channeltypes.NewPacketId(portID, channelID, seq) + + if err := packetID.Validate(); err != nil { + return err + } + + queryClient := types.NewQueryClient(clientCtx) + + req := &types.QueryTotalRecvFeesRequest{ + PacketId: packetID, + } + + res, err := queryClient.TotalRecvFees(cmd.Context(), req) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetCmdTotalAckFees returns the command handler for the Query/TotalAckFees rpc. +func GetCmdTotalAckFees() *cobra.Command { + cmd := &cobra.Command{ + Use: "total-ack-fees [port-id] [channel-id] [sequence]", + Short: "Query the total acknowledgement fees for a packet", + Long: "Query the total acknowledgement fees for a packet", + Args: cobra.ExactArgs(3), + Example: fmt.Sprintf("%s query ibc-fee total-ack-fees transfer channel-5 100", version.AppName), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + portID, channelID := args[0], args[1] + seq, err := strconv.ParseUint(args[2], 10, 64) + if err != nil { + return err + } + + packetID := channeltypes.NewPacketId(portID, channelID, seq) + + if err := packetID.Validate(); err != nil { + return err + } + + queryClient := types.NewQueryClient(clientCtx) + + req := &types.QueryTotalAckFeesRequest{ + PacketId: packetID, + } + + res, err := queryClient.TotalAckFees(cmd.Context(), req) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetCmdTotalTimeoutFees returns the command handler for the Query/TotalTimeoutFees rpc. +func GetCmdTotalTimeoutFees() *cobra.Command { + cmd := &cobra.Command{ + Use: "total-timeout-fees [port-id] [channel-id] [sequence]", + Short: "Query the total timeout fees for a packet", + Long: "Query the total timeout fees for a packet", + Args: cobra.ExactArgs(3), + Example: fmt.Sprintf("%s query ibc-fee total-timeout-fees transfer channel-5 100", version.AppName), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + portID, channelID := args[0], args[1] + seq, err := strconv.ParseUint(args[2], 10, 64) + if err != nil { + return err + } + + packetID := channeltypes.NewPacketId(portID, channelID, seq) + + if err := packetID.Validate(); err != nil { + return err + } + + queryClient := types.NewQueryClient(clientCtx) + + req := &types.QueryTotalTimeoutFeesRequest{ + PacketId: packetID, + } + + res, err := queryClient.TotalTimeoutFees(cmd.Context(), req) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetCmdCounterpartyAddress returns the command handler for the Query/CounterpartyAddress rpc. +func GetCmdCounterpartyAddress() *cobra.Command { + cmd := &cobra.Command{ + Use: "counterparty-address [channel-id] [address]", + Short: "Query the relayer counterparty address on a given channel", + Long: "Query the relayer counterparty address on a given channel", + Args: cobra.ExactArgs(2), + Example: fmt.Sprintf("%s query ibc-fee counterparty-address channel-5 cosmos1layxcsmyye0dc0har9sdfzwckaz8sjwlfsj8zs", version.AppName), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + if _, err := sdk.AccAddressFromBech32(args[1]); err != nil { + return err + } + + queryClient := types.NewQueryClient(clientCtx) + + req := &types.QueryCounterpartyAddressRequest{ + ChannelId: args[0], + RelayerAddress: args[1], + } + + res, err := queryClient.CounterpartyAddress(cmd.Context(), req) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + return cmd +} + +// GetCmdFeeEnabledChannels returns the command handler for the Query/FeeEnabledChannels rpc. +func GetCmdFeeEnabledChannels() *cobra.Command { + cmd := &cobra.Command{ + Use: "channels", + Short: "Query the ibc-fee enabled channels", + Long: "Query the ibc-fee enabled channels", + Args: cobra.NoArgs, + Example: fmt.Sprintf("%s query ibc-fee channels", version.AppName), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + req := &types.QueryFeeEnabledChannelsRequest{ + Pagination: pageReq, + QueryHeight: uint64(clientCtx.Height), + } + + queryClient := types.NewQueryClient(clientCtx) + + res, err := queryClient.FeeEnabledChannels(cmd.Context(), req) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "channels") + + return cmd +} + +// GetCmdFeeEnabledChannel returns the command handler for the Query/FeeEnabledChannel rpc. +func GetCmdFeeEnabledChannel() *cobra.Command { + cmd := &cobra.Command{ + Use: "channel [port-id] [channel-id]", + Short: "Query the ibc-fee enabled status of a channel", + Long: "Query the ibc-fee enabled status of a channel", + Args: cobra.ExactArgs(2), + Example: fmt.Sprintf("%s query ibc-fee channel transfer channel-6", version.AppName), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + req := &types.QueryFeeEnabledChannelRequest{ + PortId: args[0], + ChannelId: args[1], + } + + queryClient := types.NewQueryClient(clientCtx) + + res, err := queryClient.FeeEnabledChannel(cmd.Context(), req) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetCmdIncentivizedPacketsForChannel returns all of the unrelayed incentivized packets on a given channel +func GetCmdIncentivizedPacketsForChannel() *cobra.Command { + cmd := &cobra.Command{ + Use: "packets-for-channel [port-id] [channel-id]", + Short: "Query for all of the unrelayed incentivized packets on a given channel", + Long: "Query for all of the unrelayed incentivized packets on a given channel. These are packets that have not yet been relayed.", + Args: cobra.ExactArgs(2), + Example: fmt.Sprintf("%s query ibc-fee packets-for-channel", version.AppName), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + req := &types.QueryIncentivizedPacketsForChannelRequest{ + Pagination: pageReq, + PortId: args[0], + ChannelId: args[1], + QueryHeight: uint64(clientCtx.Height), + } + + queryClient := types.NewQueryClient(clientCtx) + + res, err := queryClient.IncentivizedPacketsForChannel(cmd.Context(), req) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "packets-for-channel") + + return cmd +} diff --git a/modules/apps/29-fee/client/cli/tx.go b/modules/apps/29-fee/client/cli/tx.go new file mode 100644 index 00000000000..f1f858b60cd --- /dev/null +++ b/modules/apps/29-fee/client/cli/tx.go @@ -0,0 +1,124 @@ +package cli + +import ( + "fmt" + "strconv" + "strings" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + "github.com/spf13/cobra" + + "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" +) + +const ( + flagRecvFee = "recv-fee" + flagAckFee = "ack-fee" + flagTimeoutFee = "timeout-fee" +) + +// NewPayPacketFeeAsyncTxCmd returns the command to create a MsgPayPacketFeeAsync +func NewPayPacketFeeAsyncTxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "pay-packet-fee [src-port] [src-channel] [sequence]", + Short: "Pay a fee to incentivize an existing IBC packet", + Long: strings.TrimSpace(`Pay a fee to incentivize an existing IBC packet.`), + Example: fmt.Sprintf("%s tx ibc-fee pay-packet-fee transfer channel-0 1 --recv-fee 10stake --ack-fee 10stake --timeout-fee 10stake", version.AppName), + Args: cobra.ExactArgs(3), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + // NOTE: specifying non-nil relayers is currently unsupported + var relayers []string + + sender := clientCtx.GetFromAddress().String() + seq, err := strconv.ParseUint(args[2], 10, 64) + if err != nil { + return err + } + + packetID := channeltypes.NewPacketId(args[0], args[1], seq) + + recvFeeStr, err := cmd.Flags().GetString(flagRecvFee) + if err != nil { + return err + } + + recvFee, err := sdk.ParseCoinsNormalized(recvFeeStr) + if err != nil { + return err + } + + ackFeeStr, err := cmd.Flags().GetString(flagAckFee) + if err != nil { + return err + } + + ackFee, err := sdk.ParseCoinsNormalized(ackFeeStr) + if err != nil { + return err + } + + timeoutFeeStr, err := cmd.Flags().GetString(flagTimeoutFee) + if err != nil { + return err + } + + timeoutFee, err := sdk.ParseCoinsNormalized(timeoutFeeStr) + if err != nil { + return err + } + + fee := types.Fee{ + RecvFee: recvFee, + AckFee: ackFee, + TimeoutFee: timeoutFee, + } + + packetFee := types.NewPacketFee(fee, sender, relayers) + msg := types.NewMsgPayPacketFeeAsync(packetID, packetFee) + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + cmd.Flags().String(flagRecvFee, "", "Fee paid to a relayer for relaying a packet receive.") + cmd.Flags().String(flagAckFee, "", "Fee paid to a relayer for relaying a packet acknowledgement.") + cmd.Flags().String(flagTimeoutFee, "", "Fee paid to a relayer for relaying a packet timeout.") + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// NewRegisterCounterpartyAddress returns the command to create a MsgRegisterCounterpartyAddress +func NewRegisterCounterpartyAddress() *cobra.Command { + cmd := &cobra.Command{ + Use: "register-counterparty [address] [counterparty-address] [channel-id]", + Short: "Register a counterparty relayer address on a given channel.", + Long: strings.TrimSpace(`Register a counterparty relayer address on a given channel.`), + Example: fmt.Sprintf("%s tx ibc-fee register-counterparty cosmos1rsp837a4kvtgp2m4uqzdge0zzu6efqgucm0qdh osmo1v5y0tz01llxzf4c2afml8s3awue0ymju22wxx2 channel-0", version.AppName), + Args: cobra.ExactArgs(3), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + msg := types.NewMsgRegisterCounterpartyAddress(args[0], args[1], args[2]) + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/modules/apps/29-fee/fee_test.go b/modules/apps/29-fee/fee_test.go new file mode 100644 index 00000000000..d2445adef22 --- /dev/null +++ b/modules/apps/29-fee/fee_test.go @@ -0,0 +1,73 @@ +package fee_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" + clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v3/testing" + ibcmock "github.com/cosmos/ibc-go/v3/testing/mock" +) + +type FeeTestSuite struct { + suite.Suite + + coordinator *ibctesting.Coordinator + + chainA *ibctesting.TestChain + chainB *ibctesting.TestChain + chainC *ibctesting.TestChain + + path *ibctesting.Path + pathAToC *ibctesting.Path +} + +func (suite *FeeTestSuite) SetupTest() { + suite.coordinator = ibctesting.NewCoordinator(suite.T(), 3) + suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1)) + suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2)) + suite.chainC = suite.coordinator.GetChain(ibctesting.GetChainID(3)) + + path := ibctesting.NewPath(suite.chainA, suite.chainB) + mockFeeVersion := string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: ibcmock.Version})) + path.EndpointA.ChannelConfig.Version = mockFeeVersion + path.EndpointB.ChannelConfig.Version = mockFeeVersion + path.EndpointA.ChannelConfig.PortID = ibctesting.MockFeePort + path.EndpointB.ChannelConfig.PortID = ibctesting.MockFeePort + suite.path = path + + path = ibctesting.NewPath(suite.chainA, suite.chainC) + path.EndpointA.ChannelConfig.Version = mockFeeVersion + path.EndpointB.ChannelConfig.Version = mockFeeVersion + path.EndpointA.ChannelConfig.PortID = ibctesting.MockFeePort + path.EndpointB.ChannelConfig.PortID = ibctesting.MockFeePort + suite.pathAToC = path +} + +func TestIBCFeeTestSuite(t *testing.T) { + suite.Run(t, new(FeeTestSuite)) +} + +func (suite *FeeTestSuite) CreateMockPacket() channeltypes.Packet { + return channeltypes.NewPacket( + ibcmock.MockPacketData, + suite.chainA.SenderAccount.GetSequence(), + suite.path.EndpointA.ChannelConfig.PortID, + suite.path.EndpointA.ChannelID, + suite.path.EndpointB.ChannelConfig.PortID, + suite.path.EndpointB.ChannelID, + clienttypes.NewHeight(0, 100), + 0, + ) +} + +// helper function +func lockFeeModule(chain *ibctesting.TestChain) { + ctx := chain.GetContext() + storeKey := chain.GetSimApp().GetKey(types.ModuleName) + store := ctx.KVStore(storeKey) + store.Set(types.KeyLocked(), []byte{1}) +} diff --git a/modules/apps/29-fee/ibc_module.go b/modules/apps/29-fee/ibc_module.go new file mode 100644 index 00000000000..6ed9faa1f34 --- /dev/null +++ b/modules/apps/29-fee/ibc_module.go @@ -0,0 +1,274 @@ +package fee + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + + "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/keeper" + "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + porttypes "github.com/cosmos/ibc-go/v3/modules/core/05-port/types" + "github.com/cosmos/ibc-go/v3/modules/core/exported" +) + +// IBCModule implements the ICS26 callbacks for the fee middleware given the fee keeper and the underlying application. +type IBCModule struct { + keeper keeper.Keeper + app porttypes.IBCModule +} + +// NewIBCModule creates a new IBCModule given the keeper and underlying application +func NewIBCModule(k keeper.Keeper, app porttypes.IBCModule) IBCModule { + return IBCModule{ + keeper: k, + app: app, + } +} + +// OnChanOpenInit implements the IBCModule interface +func (im IBCModule) OnChanOpenInit( + ctx sdk.Context, + order channeltypes.Order, + connectionHops []string, + portID string, + channelID string, + chanCap *capabilitytypes.Capability, + counterparty channeltypes.Counterparty, + version string, +) error { + 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 middleware version may be for a middleware + // lower 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.OnChanOpenInit(ctx, order, connectionHops, portID, channelID, + chanCap, counterparty, version) + } + + if versionMetadata.FeeVersion != types.Version { + return sdkerrors.Wrapf(types.ErrInvalidVersion, "expected %s, got %s", types.Version, versionMetadata.FeeVersion) + } + + im.keeper.SetFeeEnabled(ctx, portID, channelID) + + // call underlying app's OnChanOpenInit callback with the appVersion + return im.app.OnChanOpenInit(ctx, order, connectionHops, portID, channelID, + chanCap, counterparty, versionMetadata.AppVersion) +} + +// OnChanOpenTry implements the IBCModule interface +// If the channel is not fee enabled the underlying application version will be returned +// If the channel is fee enabled we merge the underlying application version with the ics29 version +func (im IBCModule) OnChanOpenTry( + ctx sdk.Context, + order channeltypes.Order, + connectionHops []string, + portID, + channelID string, + chanCap *capabilitytypes.Capability, + counterparty channeltypes.Counterparty, + counterpartyVersion string, +) (string, error) { + 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 middleware version may be for a middleware + // lower 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.OnChanOpenTry(ctx, order, connectionHops, portID, channelID, chanCap, counterparty, counterpartyVersion) + } + + if versionMetadata.FeeVersion != types.Version { + return "", sdkerrors.Wrapf(types.ErrInvalidVersion, "expected %s, got %s", types.Version, versionMetadata.FeeVersion) + } + + im.keeper.SetFeeEnabled(ctx, portID, channelID) + + // call underlying app's OnChanOpenTry callback with the app versions + appVersion, err := im.app.OnChanOpenTry(ctx, order, connectionHops, portID, channelID, chanCap, counterparty, versionMetadata.AppVersion) + if err != nil { + return "", err + } + + versionMetadata.AppVersion = appVersion + + versionBytes, err := types.ModuleCdc.MarshalJSON(&versionMetadata) + if err != nil { + return "", err + } + + return string(versionBytes), nil +} + +// OnChanOpenAck implements the IBCModule interface +func (im IBCModule) OnChanOpenAck( + ctx sdk.Context, + portID, + channelID string, + counterpartyChannelID string, + counterpartyVersion string, +) error { + // If handshake was initialized with fee enabled it must complete with fee enabled. + // If 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 sdkerrors.Wrap(types.ErrInvalidVersion, "failed to unmarshal ICS29 counterparty version metadata") + } + + if versionMetadata.FeeVersion != types.Version { + return sdkerrors.Wrapf(types.ErrInvalidVersion, "expected counterparty fee version: %s, got: %s", types.Version, versionMetadata.FeeVersion) + } + + // call underlying app's OnChanOpenAck callback with the counterparty app version. + return im.app.OnChanOpenAck(ctx, portID, channelID, counterpartyChannelID, versionMetadata.AppVersion) + } + + // call underlying app's OnChanOpenAck callback with the counterparty app version. + return im.app.OnChanOpenAck(ctx, portID, channelID, counterpartyChannelID, counterpartyVersion) +} + +// OnChanOpenConfirm implements the IBCModule interface +func (im IBCModule) OnChanOpenConfirm( + ctx sdk.Context, + portID, + channelID string, +) error { + // call underlying app's OnChanOpenConfirm callback. + return im.app.OnChanOpenConfirm(ctx, portID, channelID) +} + +// OnChanCloseInit implements the IBCModule interface +func (im IBCModule) OnChanCloseInit( + ctx sdk.Context, + portID, + channelID string, +) error { + if err := im.app.OnChanCloseInit(ctx, portID, channelID); err != nil { + return err + } + + if err := im.keeper.RefundFeesOnChannelClosure(ctx, portID, channelID); err != nil { + return err + } + + return nil +} + +// OnChanCloseConfirm implements the IBCModule interface +func (im IBCModule) OnChanCloseConfirm( + ctx sdk.Context, + portID, + channelID string, +) error { + if err := im.app.OnChanCloseConfirm(ctx, portID, channelID); err != nil { + return err + } + + if err := im.keeper.RefundFeesOnChannelClosure(ctx, portID, channelID); err != nil { + return err + } + + return nil +} + +// OnRecvPacket implements the IBCModule interface. +// If fees are not enabled, this callback will default to the ibc-core packet callback +func (im IBCModule) OnRecvPacket( + ctx sdk.Context, + packet channeltypes.Packet, + relayer sdk.AccAddress, +) exported.Acknowledgement { + if !im.keeper.IsFeeEnabled(ctx, packet.DestinationPort, packet.DestinationChannel) { + return im.app.OnRecvPacket(ctx, packet, relayer) + } + + ack := im.app.OnRecvPacket(ctx, packet, relayer) + + // incase of async aknowledgement (ack == nil) store the relayer address for use later during async WriteAcknowledgement + if ack == nil { + im.keeper.SetRelayerAddressForAsyncAck(ctx, channeltypes.NewPacketId(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()), relayer.String()) + return nil + } + + // if forwardRelayer is not found we refund recv_fee + forwardRelayer, _ := im.keeper.GetCounterpartyAddress(ctx, relayer.String(), packet.GetDestChannel()) + + return types.NewIncentivizedAcknowledgement(forwardRelayer, ack.Acknowledgement(), ack.Success()) +} + +// OnAcknowledgementPacket implements the IBCModule interface +// If fees are not enabled, this callback will default to the ibc-core packet callback +func (im IBCModule) OnAcknowledgementPacket( + ctx sdk.Context, + packet channeltypes.Packet, + acknowledgement []byte, + relayer sdk.AccAddress, +) error { + if !im.keeper.IsFeeEnabled(ctx, packet.SourcePort, packet.SourceChannel) { + return im.app.OnAcknowledgementPacket(ctx, packet, acknowledgement, relayer) + } + + ack := new(types.IncentivizedAcknowledgement) + if err := types.ModuleCdc.UnmarshalJSON(acknowledgement, ack); err != nil { + return sdkerrors.Wrapf(err, "cannot unmarshal ICS-29 incentivized packet acknowledgement: %v", ack) + } + + if im.keeper.IsLocked(ctx) { + // if the fee keeper is locked then fee logic should be skipped + // this may occur in the presence of a severe bug which leads to invalid state + // the fee keeper will be unlocked after manual intervention + // the acknowledgement has been unmarshalled into an ics29 acknowledgement + // since the counterparty is still sending incentivized acknowledgements + // for fee enabled channels + // + // Please see ADR 004 for more information. + return im.app.OnAcknowledgementPacket(ctx, packet, ack.Result, relayer) + } + + packetID := channeltypes.NewPacketId(packet.SourcePort, packet.SourceChannel, packet.Sequence) + feesInEscrow, found := im.keeper.GetFeesInEscrow(ctx, packetID) + if found { + im.keeper.DistributePacketFeesOnAcknowledgement(ctx, ack.ForwardRelayerAddress, relayer, feesInEscrow.PacketFees) + + // removes the fees from the store as fees are now paid + im.keeper.DeleteFeesInEscrow(ctx, packetID) + } + + // call underlying callback + return im.app.OnAcknowledgementPacket(ctx, packet, ack.Result, relayer) +} + +// OnTimeoutPacket implements the IBCModule interface +// If fees are not enabled, this callback will default to the ibc-core packet callback +func (im IBCModule) OnTimeoutPacket( + ctx sdk.Context, + packet channeltypes.Packet, + relayer sdk.AccAddress, +) error { + // if the fee keeper is locked then fee logic should be skipped + // this may occur in the presence of a severe bug which leads to invalid state + // the fee keeper will be unlocked after manual intervention + // + // Please see ADR 004 for more information. + if !im.keeper.IsFeeEnabled(ctx, packet.SourcePort, packet.SourceChannel) || im.keeper.IsLocked(ctx) { + return im.app.OnTimeoutPacket(ctx, packet, relayer) + } + + packetID := channeltypes.NewPacketId(packet.SourcePort, packet.SourceChannel, packet.Sequence) + feesInEscrow, found := im.keeper.GetFeesInEscrow(ctx, packetID) + if found { + im.keeper.DistributePacketFeesOnTimeout(ctx, relayer, feesInEscrow.PacketFees) + + // removes the fee from the store as fee is now paid + im.keeper.DeleteFeesInEscrow(ctx, packetID) + } + + // call underlying callback + return im.app.OnTimeoutPacket(ctx, packet, relayer) +} + +// GetAppVersion returns the application version of the underlying application +func (im IBCModule) GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) { + return im.keeper.GetAppVersion(ctx, portID, channelID) +} diff --git a/modules/apps/29-fee/ibc_module_test.go b/modules/apps/29-fee/ibc_module_test.go new file mode 100644 index 00000000000..3f5cf64635a --- /dev/null +++ b/modules/apps/29-fee/ibc_module_test.go @@ -0,0 +1,912 @@ +package fee_test + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + + fee "github.com/cosmos/ibc-go/v3/modules/apps/29-fee" + "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" + transfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" + channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + "github.com/cosmos/ibc-go/v3/modules/core/exported" + ibctesting "github.com/cosmos/ibc-go/v3/testing" + ibcmock "github.com/cosmos/ibc-go/v3/testing/mock" +) + +var ( + defaultRecvFee = sdk.Coins{sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: sdk.NewInt(100)}} + defaultAckFee = sdk.Coins{sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: sdk.NewInt(200)}} + defaultTimeoutFee = sdk.Coins{sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: sdk.NewInt(300)}} +) + +// Tests OnChanOpenInit on ChainA +func (suite *FeeTestSuite) TestOnChanOpenInit() { + testCases := []struct { + name string + version string + expPass bool + }{ + { + "success - valid fee middleware and mock version", + string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: ibcmock.Version})), + true, + }, + { + "success - fee version not included, only perform mock logic", + ibcmock.Version, + true, + }, + { + "invalid fee middleware version", + string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: "invalid-ics29-1", AppVersion: ibcmock.Version})), + false, + }, + { + "invalid mock version", + string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: "invalid-mock-version"})), + false, + }, + { + "mock version not wrapped", + types.Version, + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + // reset suite + suite.SetupTest() + suite.coordinator.SetupConnections(suite.path) + + // setup mock callback + suite.chainA.GetSimApp().FeeMockModule.IBCApp.OnChanOpenInit = func(ctx sdk.Context, order channeltypes.Order, connectionHops []string, + portID, channelID string, chanCap *capabilitytypes.Capability, + counterparty channeltypes.Counterparty, version string, + ) error { + if version != ibcmock.Version { + return fmt.Errorf("incorrect mock version") + } + return nil + } + + suite.path.EndpointA.ChannelID = ibctesting.FirstChannelID + + counterparty := channeltypes.NewCounterparty(suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID) + channel := &channeltypes.Channel{ + State: channeltypes.INIT, + Ordering: channeltypes.UNORDERED, + Counterparty: counterparty, + ConnectionHops: []string{suite.path.EndpointA.ConnectionID}, + Version: tc.version, + } + + module, _, err := suite.chainA.App.GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), ibctesting.MockFeePort) + suite.Require().NoError(err) + + chanCap, err := suite.chainA.App.GetScopedIBCKeeper().NewCapability(suite.chainA.GetContext(), host.ChannelCapabilityPath(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID)) + suite.Require().NoError(err) + + cbs, ok := suite.chainA.App.GetIBCKeeper().Router.GetRoute(module) + suite.Require().True(ok) + + err = cbs.OnChanOpenInit(suite.chainA.GetContext(), channel.Ordering, channel.GetConnectionHops(), + suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, chanCap, counterparty, channel.Version) + + if tc.expPass { + suite.Require().NoError(err, "unexpected error from version: %s", tc.version) + } else { + suite.Require().Error(err, "error not returned for version: %s", tc.version) + } + }) + } +} + +// Tests OnChanOpenTry on ChainA +func (suite *FeeTestSuite) TestOnChanOpenTry() { + testCases := []struct { + name string + cpVersion string + crossing bool + expPass bool + }{ + { + "success - valid fee middleware version", + string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: ibcmock.Version})), + false, + true, + }, + { + "success - valid mock version", + ibcmock.Version, + false, + true, + }, + { + "success - crossing hellos: valid fee middleware", + string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: ibcmock.Version})), + true, + true, + }, + { + "invalid fee middleware version", + string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: "invalid-ics29-1", AppVersion: ibcmock.Version})), + false, + false, + }, + { + "invalid mock version", + string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: "invalid-mock-version"})), + false, + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + // reset suite + suite.SetupTest() + suite.coordinator.SetupConnections(suite.path) + suite.path.EndpointB.ChanOpenInit() + + // setup mock callback + suite.chainA.GetSimApp().FeeMockModule.IBCApp.OnChanOpenTry = func(ctx sdk.Context, order channeltypes.Order, connectionHops []string, + portID, channelID string, chanCap *capabilitytypes.Capability, + counterparty channeltypes.Counterparty, counterpartyVersion string, + ) (string, error) { + if counterpartyVersion != ibcmock.Version { + return "", fmt.Errorf("incorrect mock version") + } + return ibcmock.Version, nil + } + + var ( + chanCap *capabilitytypes.Capability + ok bool + err error + ) + if tc.crossing { + suite.path.EndpointA.ChanOpenInit() + chanCap, ok = suite.chainA.GetSimApp().ScopedFeeMockKeeper.GetCapability(suite.chainA.GetContext(), host.ChannelCapabilityPath(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID)) + suite.Require().True(ok) + } else { + chanCap, err = suite.chainA.App.GetScopedIBCKeeper().NewCapability(suite.chainA.GetContext(), host.ChannelCapabilityPath(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID)) + suite.Require().NoError(err) + } + + suite.path.EndpointA.ChannelID = ibctesting.FirstChannelID + + counterparty := channeltypes.NewCounterparty(suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID) + channel := &channeltypes.Channel{ + State: channeltypes.INIT, + Ordering: channeltypes.UNORDERED, + Counterparty: counterparty, + ConnectionHops: []string{suite.path.EndpointA.ConnectionID}, + Version: tc.cpVersion, + } + + module, _, err := suite.chainA.App.GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), ibctesting.MockFeePort) + suite.Require().NoError(err) + + cbs, ok := suite.chainA.App.GetIBCKeeper().Router.GetRoute(module) + suite.Require().True(ok) + + _, err = cbs.OnChanOpenTry(suite.chainA.GetContext(), channel.Ordering, channel.GetConnectionHops(), + suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, chanCap, counterparty, tc.cpVersion) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +// Tests OnChanOpenAck on ChainA +func (suite *FeeTestSuite) TestOnChanOpenAck() { + testCases := []struct { + name string + cpVersion string + malleate func(suite *FeeTestSuite) + expPass bool + }{ + { + "success", + string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: ibcmock.Version})), + func(suite *FeeTestSuite) {}, + true, + }, + { + "invalid fee version", + string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: "invalid-ics29-1", AppVersion: ibcmock.Version})), + func(suite *FeeTestSuite) {}, + false, + }, + { + "invalid mock version", + string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: "invalid-mock-version"})), + func(suite *FeeTestSuite) {}, + false, + }, + { + "invalid version fails to unmarshal metadata", + "invalid-version", + func(suite *FeeTestSuite) {}, + false, + }, + { + "previous INIT set without fee, however counterparty set fee version", // note this can only happen with incompetent or malicious counterparty chain + string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: ibcmock.Version})), + func(suite *FeeTestSuite) { + // do the first steps without fee version, then pass the fee version as counterparty version in ChanOpenACK + suite.path.EndpointA.ChannelConfig.Version = ibcmock.Version + suite.path.EndpointB.ChannelConfig.Version = ibcmock.Version + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + suite.SetupTest() + suite.coordinator.SetupConnections(suite.path) + + // setup mock callback + suite.chainA.GetSimApp().FeeMockModule.IBCApp.OnChanOpenAck = func( + ctx sdk.Context, portID, channelID string, counterpartyChannelID string, counterpartyVersion string, + ) error { + if counterpartyVersion != ibcmock.Version { + return fmt.Errorf("incorrect mock version") + } + return nil + } + + // malleate test case + tc.malleate(suite) + + suite.path.EndpointA.ChanOpenInit() + suite.path.EndpointB.ChanOpenTry() + + module, _, err := suite.chainA.App.GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), ibctesting.MockFeePort) + suite.Require().NoError(err) + + cbs, ok := suite.chainA.App.GetIBCKeeper().Router.GetRoute(module) + suite.Require().True(ok) + + err = cbs.OnChanOpenAck(suite.chainA.GetContext(), suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, suite.path.EndpointA.Counterparty.ChannelID, tc.cpVersion) + if tc.expPass { + suite.Require().NoError(err, "unexpected error for case: %s", tc.name) + } else { + suite.Require().Error(err, "%s expected error but returned none", tc.name) + } + }) + } +} + +func (suite *FeeTestSuite) TestOnChanCloseInit() { + var ( + refundAcc sdk.AccAddress + fee types.Fee + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", func() {}, true, + }, + { + "application callback fails", func() { + suite.chainA.GetSimApp().FeeMockModule.IBCApp.OnChanCloseInit = func( + ctx sdk.Context, portID, channelID string, + ) error { + return fmt.Errorf("application callback fails") + } + }, false, + }, + { + "RefundFeesOnChannelClosure fails - invalid refund address", func() { + // store the fee in state & update escrow account balance + packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(1)) + packetFees := types.NewPacketFees([]types.PacketFee{types.NewPacketFee(fee, "invalid refund address", nil)}) + + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, packetFees) + err := suite.chainA.GetSimApp().BankKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), refundAcc, types.ModuleName, fee.Total()) + suite.Require().NoError(err) + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + suite.SetupTest() + suite.coordinator.Setup(suite.path) // setup channel + + packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) + fee = types.Fee{ + RecvFee: defaultRecvFee, + AckFee: defaultAckFee, + TimeoutFee: defaultTimeoutFee, + } + + refundAcc = suite.chainA.SenderAccount.GetAddress() + packetFee := types.NewPacketFee(fee, refundAcc.String(), []string{}) + + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, types.NewPacketFees([]types.PacketFee{packetFee})) + err := suite.chainA.GetSimApp().BankKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), refundAcc, types.ModuleName, fee.Total()) + suite.Require().NoError(err) + + tc.malleate() + + module, _, err := suite.chainA.App.GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), ibctesting.MockFeePort) + suite.Require().NoError(err) + + cbs, ok := suite.chainA.App.GetIBCKeeper().Router.GetRoute(module) + suite.Require().True(ok) + + err = cbs.OnChanCloseInit(suite.chainA.GetContext(), suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +// Tests OnChanCloseConfirm on chainA +func (suite *FeeTestSuite) TestOnChanCloseConfirm() { + var ( + refundAcc sdk.AccAddress + fee types.Fee + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", func() {}, true, + }, + { + "application callback fails", func() { + suite.chainA.GetSimApp().FeeMockModule.IBCApp.OnChanCloseConfirm = func( + ctx sdk.Context, portID, channelID string, + ) error { + return fmt.Errorf("application callback fails") + } + }, false, + }, + { + "RefundChannelFeesOnClosure fails - refund address is invalid", func() { + // store the fee in state & update escrow account balance + packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(1)) + packetFees := types.NewPacketFees([]types.PacketFee{types.NewPacketFee(fee, "invalid refund address", nil)}) + + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, packetFees) + err := suite.chainA.GetSimApp().BankKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), refundAcc, types.ModuleName, fee.Total()) + suite.Require().NoError(err) + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + suite.coordinator.Setup(suite.path) // setup channel + + packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) + fee = types.Fee{ + RecvFee: defaultRecvFee, + AckFee: defaultAckFee, + TimeoutFee: defaultTimeoutFee, + } + + refundAcc = suite.chainA.SenderAccount.GetAddress() + packetFee := types.NewPacketFee(fee, refundAcc.String(), []string{}) + + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, types.NewPacketFees([]types.PacketFee{packetFee})) + err := suite.chainA.GetSimApp().BankKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), refundAcc, types.ModuleName, fee.Total()) + suite.Require().NoError(err) + + tc.malleate() + + module, _, err := suite.chainA.App.GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), ibctesting.MockFeePort) + suite.Require().NoError(err) + + cbs, ok := suite.chainA.App.GetIBCKeeper().Router.GetRoute(module) + suite.Require().True(ok) + + err = cbs.OnChanCloseConfirm(suite.chainA.GetContext(), suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + + }) + } +} + +func (suite *FeeTestSuite) TestOnRecvPacket() { + testCases := []struct { + name string + malleate func() + // forwardRelayer bool indicates if there is a forwardRelayer address set + forwardRelayer bool + feeEnabled bool + }{ + { + "success", + func() {}, + true, + true, + }, + { + "async write acknowledgement: ack is nil", + func() { + // setup mock callback + suite.chainB.GetSimApp().FeeMockModule.IBCApp.OnRecvPacket = func( + ctx sdk.Context, + packet channeltypes.Packet, + relayer sdk.AccAddress, + ) exported.Acknowledgement { + return nil + } + }, + true, + true, + }, + { + "fee not enabled", + func() { + suite.chainB.GetSimApp().IBCFeeKeeper.DeleteFeeEnabled(suite.chainB.GetContext(), suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID) + }, + true, + false, + }, + { + "forward address is not found", + func() { + suite.chainB.GetSimApp().IBCFeeKeeper.SetCounterpartyAddress(suite.chainB.GetContext(), suite.chainA.SenderAccount.GetAddress().String(), "", suite.path.EndpointB.ChannelID) + }, + false, + true, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + suite.SetupTest() + // setup pathAToC (chainA -> chainC) first in order to have different channel IDs for chainA & chainB + suite.coordinator.Setup(suite.pathAToC) + // setup path for chainA -> chainB + suite.coordinator.Setup(suite.path) + + suite.chainB.GetSimApp().IBCFeeKeeper.SetFeeEnabled(suite.chainB.GetContext(), suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID) + + packet := suite.CreateMockPacket() + + // set up module and callbacks + module, _, err := suite.chainB.App.GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainB.GetContext(), ibctesting.MockFeePort) + suite.Require().NoError(err) + + cbs, ok := suite.chainB.App.GetIBCKeeper().Router.GetRoute(module) + suite.Require().True(ok) + + suite.chainB.GetSimApp().IBCFeeKeeper.SetCounterpartyAddress(suite.chainB.GetContext(), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), suite.path.EndpointB.ChannelID) + + // malleate test case + tc.malleate() + + result := cbs.OnRecvPacket(suite.chainB.GetContext(), packet, suite.chainA.SenderAccount.GetAddress()) + + switch { + case tc.name == "success": + forwardAddr, _ := suite.chainB.GetSimApp().IBCFeeKeeper.GetCounterpartyAddress(suite.chainB.GetContext(), suite.chainA.SenderAccount.GetAddress().String(), suite.path.EndpointB.ChannelID) + + expectedAck := types.IncentivizedAcknowledgement{ + Result: ibcmock.MockAcknowledgement.Acknowledgement(), + ForwardRelayerAddress: forwardAddr, + UnderlyingAppSuccess: true, + } + suite.Require().Equal(expectedAck, result) + + case !tc.feeEnabled: + suite.Require().Equal(ibcmock.MockAcknowledgement, result) + + case tc.forwardRelayer && result == nil: + suite.Require().Equal(nil, result) + packetID := channeltypes.NewPacketId(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + + // retrieve the forward relayer that was stored in `onRecvPacket` + relayer, _ := suite.chainB.GetSimApp().IBCFeeKeeper.GetRelayerAddressForAsyncAck(suite.chainB.GetContext(), packetID) + suite.Require().Equal(relayer, suite.chainA.SenderAccount.GetAddress().String()) + + case !tc.forwardRelayer: + expectedAck := types.IncentivizedAcknowledgement{ + Result: ibcmock.MockAcknowledgement.Acknowledgement(), + ForwardRelayerAddress: "", + UnderlyingAppSuccess: true, + } + suite.Require().Equal(expectedAck, result) + } + }) + } +} + +// different channel than sending chain +func (suite *FeeTestSuite) TestOnAcknowledgementPacket() { + var ( + ack []byte + packetFee types.PacketFee + originalBalance sdk.Coins + expectedBalance sdk.Coins + expectedRelayerBalance sdk.Coins + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() { + expectedRelayerBalance = packetFee.Fee.RecvFee.Add(packetFee.Fee.AckFee[0]) + }, + true, + }, + { + "no op success without a packet fee", + func() { + packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, suite.chainA.SenderAccount.GetSequence()) + suite.chainA.GetSimApp().IBCFeeKeeper.DeleteFeesInEscrow(suite.chainA.GetContext(), packetID) + + ack = types.IncentivizedAcknowledgement{ + Result: ibcmock.MockAcknowledgement.Acknowledgement(), + ForwardRelayerAddress: suite.chainA.SenderAccount.GetAddress().String(), + }.Acknowledgement() + + expectedBalance = originalBalance + }, + true, + }, + { + "ack wrong format", + func() { + ack = []byte("unsupported acknowledgement format") + + expectedBalance = originalBalance + }, + false, + }, + { + "channel is not fee not enabled, success", + func() { + suite.chainA.GetSimApp().IBCFeeKeeper.DeleteFeeEnabled(suite.chainA.GetContext(), suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID) + ack = ibcmock.MockAcknowledgement.Acknowledgement() + + expectedBalance = originalBalance + }, + true, + }, + { + "success: fee module is disabled, skip fee logic", + func() { + lockFeeModule(suite.chainA) + + expectedBalance = originalBalance + }, + true, + }, + { + "fail on distribute receive fee (blocked address)", + func() { + blockedAddr := suite.chainA.GetSimApp().AccountKeeper.GetModuleAccount(suite.chainA.GetContext(), transfertypes.ModuleName).GetAddress() + + ack = types.IncentivizedAcknowledgement{ + Result: ibcmock.MockAcknowledgement.Acknowledgement(), + ForwardRelayerAddress: blockedAddr.String(), + }.Acknowledgement() + + expectedRelayerBalance = packetFee.Fee.AckFee + expectedBalance = expectedBalance.Add(packetFee.Fee.RecvFee...) + }, + true, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + suite.SetupTest() + suite.coordinator.Setup(suite.path) + packet := suite.CreateMockPacket() + + expectedRelayerBalance = sdk.Coins{} // reset + + // set up module and callbacks + module, _, err := suite.chainA.App.GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), ibctesting.MockFeePort) + suite.Require().NoError(err) + + cbs, ok := suite.chainA.App.GetIBCKeeper().Router.GetRoute(module) + suite.Require().True(ok) + + // escrow the packet fee + packetID := channeltypes.NewPacketId(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + packetFee = types.NewPacketFee( + types.Fee{ + RecvFee: defaultRecvFee, + AckFee: defaultAckFee, + TimeoutFee: defaultTimeoutFee, + }, + suite.chainA.SenderAccount.GetAddress().String(), + []string{}, + ) + + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, types.NewPacketFees([]types.PacketFee{packetFee})) + err = suite.chainA.GetSimApp().BankKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), types.ModuleName, packetFee.Fee.Total()) + suite.Require().NoError(err) + + relayerAddr := suite.chainB.SenderAccount.GetAddress() + + // must be changed explicitly + ack = types.IncentivizedAcknowledgement{ + Result: ibcmock.MockAcknowledgement.Acknowledgement(), + ForwardRelayerAddress: relayerAddr.String(), + }.Acknowledgement() + + // log original sender balance + // NOTE: balance is logged after escrowing tokens + originalBalance = sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), ibctesting.TestCoin.Denom)) + + // default to success case + expectedBalance = originalBalance.Add(packetFee.Fee.TimeoutFee[0]) + + // malleate test case + tc.malleate() + + err = cbs.OnAcknowledgementPacket(suite.chainA.GetContext(), packet, ack, relayerAddr) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + + suite.Require().Equal( + expectedBalance, + sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), ibctesting.TestCoin.Denom)), + ) + + relayerBalance := sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), relayerAddr, ibctesting.TestCoin.Denom)) + suite.Require().Equal( + expectedRelayerBalance, + relayerBalance, + ) + }) + } +} + +func (suite *FeeTestSuite) TestOnTimeoutPacket() { + var ( + relayerAddr sdk.AccAddress + packetFee types.PacketFee + originalBalance sdk.Coins + expectedBalance sdk.Coins + ) + testCases := []struct { + name string + malleate func() + expFeeDistributed bool + }{ + { + "success", + func() {}, + true, + }, + { + "fee not enabled", + func() { + suite.chainA.GetSimApp().IBCFeeKeeper.DeleteFeeEnabled(suite.chainA.GetContext(), suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID) + + expectedBalance = originalBalance + }, + false, + }, + { + "fee module is disabled, skip fee logic", + func() { + lockFeeModule(suite.chainA) + + expectedBalance = originalBalance + }, + false, + }, + { + "no op if identified packet fee doesn't exist", + func() { + // delete packet fee + packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, suite.chainA.SenderAccount.GetSequence()) + suite.chainA.GetSimApp().IBCFeeKeeper.DeleteFeesInEscrow(suite.chainA.GetContext(), packetID) + + expectedBalance = originalBalance + }, + false, + }, + { + "distribute fee fails for timeout fee (blocked address)", + func() { + relayerAddr = suite.chainA.GetSimApp().AccountKeeper.GetModuleAccount(suite.chainA.GetContext(), transfertypes.ModuleName).GetAddress() + + expectedBalance = originalBalance. + Add(packetFee.Fee.RecvFee...). + Add(packetFee.Fee.AckFee...). + Add(packetFee.Fee.TimeoutFee...) + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + suite.SetupTest() + suite.coordinator.Setup(suite.path) + packet := suite.CreateMockPacket() + + // set up module and callbacks + module, _, err := suite.chainA.App.GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), ibctesting.MockFeePort) + suite.Require().NoError(err) + + cbs, ok := suite.chainA.App.GetIBCKeeper().Router.GetRoute(module) + suite.Require().True(ok) + + packetID := channeltypes.NewPacketId(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + + // must be explicitly changed + relayerAddr = suite.chainB.SenderAccount.GetAddress() + + packetFee = types.NewPacketFee( + types.Fee{ + RecvFee: defaultRecvFee, + AckFee: defaultAckFee, + TimeoutFee: defaultTimeoutFee, + }, + suite.chainA.SenderAccount.GetAddress().String(), + []string{}, + ) + + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, types.NewPacketFees([]types.PacketFee{packetFee})) + err = suite.chainA.GetSimApp().BankKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), types.ModuleName, packetFee.Fee.Total()) + suite.Require().NoError(err) + + // log original sender balance + // NOTE: balance is logged after escrowing tokens + originalBalance = sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), ibctesting.TestCoin.Denom)) + + // default to success case + expectedBalance = originalBalance. + Add(packetFee.Fee.RecvFee[0]). + Add(packetFee.Fee.AckFee[0]) + + // malleate test case + tc.malleate() + + err = cbs.OnTimeoutPacket(suite.chainA.GetContext(), packet, relayerAddr) + suite.Require().NoError(err) + + suite.Require().Equal( + expectedBalance, + sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), ibctesting.TestCoin.Denom)), + ) + + relayerBalance := sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), relayerAddr, ibctesting.TestCoin.Denom)) + if tc.expFeeDistributed { + // there should no longer be a fee in escrow for this packet + found := suite.chainA.GetSimApp().IBCFeeKeeper.HasFeesInEscrow(suite.chainA.GetContext(), packetID) + suite.Require().False(found) + + suite.Require().Equal(packetFee.Fee.TimeoutFee, relayerBalance) + } else { + suite.Require().Empty(relayerBalance) + } + }) + } +} + +func (suite *FeeTestSuite) TestGetAppVersion() { + var ( + portID string + channelID string + expAppVersion string + ) + testCases := []struct { + name string + malleate func() + expFound bool + }{ + { + "success for fee enabled channel", + func() { + expAppVersion = ibcmock.Version + }, + true, + }, + { + "success for non fee enabled channel", + func() { + path := ibctesting.NewPath(suite.chainA, suite.chainB) + path.EndpointA.ChannelConfig.PortID = ibctesting.MockFeePort + path.EndpointB.ChannelConfig.PortID = ibctesting.MockFeePort + // by default a new path uses a non fee channel + suite.coordinator.Setup(path) + portID = path.EndpointA.ChannelConfig.PortID + channelID = path.EndpointA.ChannelID + + expAppVersion = ibcmock.Version + }, + true, + }, + { + "channel does not exist", + func() { + channelID = "does not exist" + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + suite.SetupTest() + suite.coordinator.Setup(suite.path) + + portID = suite.path.EndpointA.ChannelConfig.PortID + channelID = suite.path.EndpointA.ChannelID + + // malleate test case + tc.malleate() + + module, _, err := suite.chainA.App.GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), ibctesting.MockFeePort) + suite.Require().NoError(err) + + cbs, ok := suite.chainA.App.GetIBCKeeper().Router.GetRoute(module) + suite.Require().True(ok) + + feeModule := cbs.(fee.IBCModule) + + appVersion, found := feeModule.GetAppVersion(suite.chainA.GetContext(), portID, channelID) + + if tc.expFound { + suite.Require().True(found) + suite.Require().Equal(expAppVersion, appVersion) + } else { + suite.Require().False(found) + suite.Require().Empty(appVersion) + } + }) + } +} diff --git a/modules/apps/29-fee/keeper/escrow.go b/modules/apps/29-fee/keeper/escrow.go new file mode 100644 index 00000000000..0ca84684440 --- /dev/null +++ b/modules/apps/29-fee/keeper/escrow.go @@ -0,0 +1,232 @@ +package keeper + +import ( + "bytes" + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" +) + +// escrowPacketFee sends the packet fee to the 29-fee module account to hold in escrow +func (k Keeper) escrowPacketFee(ctx sdk.Context, packetID channeltypes.PacketId, packetFee types.PacketFee) error { + // check if the refund address is valid + refundAddr, err := sdk.AccAddressFromBech32(packetFee.RefundAddress) + if err != nil { + return err + } + + refundAcc := k.authKeeper.GetAccount(ctx, refundAddr) + if refundAcc == nil { + return sdkerrors.Wrapf(types.ErrRefundAccNotFound, "account with address: %s not found", packetFee.RefundAddress) + } + + coins := packetFee.Fee.Total() + if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, refundAddr, types.ModuleName, coins); err != nil { + return err + } + + // multiple fees may be escrowed for a single packet, firstly create a slice containing the new fee + // retrieve any previous fees stored in escrow for the packet and append them to the list + fees := []types.PacketFee{packetFee} + if feesInEscrow, found := k.GetFeesInEscrow(ctx, packetID); found { + fees = append(fees, feesInEscrow.PacketFees...) + } + + packetFees := types.NewPacketFees(fees) + k.SetFeesInEscrow(ctx, packetID, packetFees) + + EmitIncentivizedPacket(ctx, packetID, packetFee) + + return nil +} + +// DistributePacketFeesOnAcknowledgement pays all the acknowledgement & receive fees for a given packetID while refunding the timeout fees to the refund account. +func (k Keeper) DistributePacketFeesOnAcknowledgement(ctx sdk.Context, forwardRelayer string, reverseRelayer sdk.AccAddress, packetFees []types.PacketFee) { + // cache context before trying to distribute fees + // if the escrow account has insufficient balance then we want to avoid partially distributing fees + cacheCtx, writeFn := ctx.CacheContext() + + forwardAddr, _ := sdk.AccAddressFromBech32(forwardRelayer) + + for _, packetFee := range packetFees { + if !k.EscrowAccountHasBalance(cacheCtx, packetFee.Fee.Total()) { + // if the escrow account does not have sufficient funds then there must exist a severe bug + // the fee module should be locked until manual intervention fixes the issue + // a locked fee module will simply skip fee logic, all channels will temporarily function as + // fee disabled channels + // NOTE: we use the uncached context to lock the fee module so that the state changes from + // locking the fee module are persisted + k.lockFeeModule(ctx) + + return + } + + // check if refundAcc address works + refundAddr, err := sdk.AccAddressFromBech32(packetFee.RefundAddress) + if err != nil { + panic(fmt.Sprintf("could not parse refundAcc %s to sdk.AccAddress", packetFee.RefundAddress)) + } + + k.distributePacketFeeOnAcknowledgement(cacheCtx, refundAddr, forwardAddr, reverseRelayer, packetFee) + } + + // write the cache + writeFn() + + // NOTE: The context returned by CacheContext() refers to a new EventManager, so it needs to explicitly set events to the original context. + ctx.EventManager().EmitEvents(cacheCtx.EventManager().Events()) +} + +// distributePacketFeeOnAcknowledgement pays the receive fee for a given packetID while refunding the timeout fee to the refund account associated with the Fee. +// If there was no forward relayer or the associated forward relayer address is blocked, the receive fee is refunded. +func (k Keeper) distributePacketFeeOnAcknowledgement(ctx sdk.Context, refundAddr, forwardRelayer, reverseRelayer sdk.AccAddress, packetFee types.PacketFee) { + // distribute fee to valid forward relayer address otherwise refund the fee + if !forwardRelayer.Empty() && !k.bankKeeper.BlockedAddr(forwardRelayer) { + // distribute fee for forward relaying + k.distributeFee(ctx, forwardRelayer, refundAddr, packetFee.Fee.RecvFee) + } else { + // refund onRecv fee as forward relayer is not valid address + k.distributeFee(ctx, refundAddr, refundAddr, packetFee.Fee.RecvFee) + } + + // distribute fee for reverse relaying + k.distributeFee(ctx, reverseRelayer, refundAddr, packetFee.Fee.AckFee) + + // refund timeout fee for unused timeout + k.distributeFee(ctx, refundAddr, refundAddr, packetFee.Fee.TimeoutFee) + +} + +// DistributePacketsFeesOnTimeout pays all the timeout fees for a given packetID while refunding the acknowledgement & receive fees to the refund account. +func (k Keeper) DistributePacketFeesOnTimeout(ctx sdk.Context, timeoutRelayer sdk.AccAddress, packetFees []types.PacketFee) { + // cache context before trying to distribute fees + // if the escrow account has insufficient balance then we want to avoid partially distributing fees + cacheCtx, writeFn := ctx.CacheContext() + + for _, packetFee := range packetFees { + if !k.EscrowAccountHasBalance(cacheCtx, packetFee.Fee.Total()) { + // if the escrow account does not have sufficient funds then there must exist a severe bug + // the fee module should be locked until manual intervention fixes the issue + // a locked fee module will simply skip fee logic, all channels will temporarily function as + // fee disabled channels + // NOTE: we use the uncached context to lock the fee module so that the state changes from + // locking the fee module are persisted + k.lockFeeModule(ctx) + + return + } + + // check if refundAcc address works + refundAddr, err := sdk.AccAddressFromBech32(packetFee.RefundAddress) + if err != nil { + panic(fmt.Sprintf("could not parse refundAcc %s to sdk.AccAddress", packetFee.RefundAddress)) + } + + k.distributePacketFeeOnTimeout(cacheCtx, refundAddr, timeoutRelayer, packetFee) + } + + // write the cache + writeFn() + + // NOTE: The context returned by CacheContext() refers to a new EventManager, so it needs to explicitly set events to the original context. + ctx.EventManager().EmitEvents(cacheCtx.EventManager().Events()) +} + +// distributePacketFeeOnTimeout pays the timeout fee to the timeout relayer and refunds the acknowledgement & receive fee. +func (k Keeper) distributePacketFeeOnTimeout(ctx sdk.Context, refundAddr, timeoutRelayer sdk.AccAddress, packetFee types.PacketFee) { + // refund receive fee for unused forward relaying + k.distributeFee(ctx, refundAddr, refundAddr, packetFee.Fee.RecvFee) + + // refund ack fee for unused reverse relaying + k.distributeFee(ctx, refundAddr, refundAddr, packetFee.Fee.AckFee) + + // distribute fee for timeout relaying + k.distributeFee(ctx, timeoutRelayer, refundAddr, packetFee.Fee.TimeoutFee) +} + +// distributeFee will attempt to distribute the escrowed fee to the receiver address. +// If the distribution fails for any reason (such as the receiving address being blocked), +// the state changes will be discarded. +func (k Keeper) distributeFee(ctx sdk.Context, receiver, refundAccAddress sdk.AccAddress, fee sdk.Coins) { + // cache context before trying to distribute fees + cacheCtx, writeFn := ctx.CacheContext() + + err := k.bankKeeper.SendCoinsFromModuleToAccount(cacheCtx, types.ModuleName, receiver, fee) + if err != nil { + if bytes.Equal(receiver, refundAccAddress) { + return // if sending to the refund address already failed, then return (no-op) + } + + // if an error is returned from x/bank and the receiver is not the refundAccAddress + // then attempt to refund the fee to the original sender + err := k.bankKeeper.SendCoinsFromModuleToAccount(cacheCtx, types.ModuleName, refundAccAddress, fee) + if err != nil { + return // if sending to the refund address fails, no-op + } + } + + // write the cache + writeFn() + + // NOTE: The context returned by CacheContext() refers to a new EventManager, so it needs to explicitly set events to the original context. + ctx.EventManager().EmitEvents(cacheCtx.EventManager().Events()) +} + +// RefundFeesOnChannelClosure will refund all fees associated with the given port and channel identifiers. +// If the escrow account runs out of balance then fee module will become locked as this implies the presence +// of a severe bug. When the fee module is locked, no fee distributions will be performed. +// Please see ADR 004 for more information. +func (k Keeper) RefundFeesOnChannelClosure(ctx sdk.Context, portID, channelID string) error { + identifiedPacketFees := k.GetIdentifiedPacketFeesForChannel(ctx, portID, channelID) + + // cache context before trying to distribute fees + // if the escrow account has insufficient balance then we want to avoid partially distributing fees + cacheCtx, writeFn := ctx.CacheContext() + + for _, identifiedPacketFee := range identifiedPacketFees { + for _, packetFee := range identifiedPacketFee.PacketFees { + + if !k.EscrowAccountHasBalance(cacheCtx, packetFee.Fee.Total()) { + // if the escrow account does not have sufficient funds then there must exist a severe bug + // the fee module should be locked until manual intervention fixes the issue + // a locked fee module will simply skip fee logic, all channels will temporarily function as + // fee disabled channels + // NOTE: we use the uncached context to lock the fee module so that the state changes from + // locking the fee module are persisted + k.lockFeeModule(ctx) + + // return a nil error so state changes are committed but distribution stops + return nil + } + + refundAddr, err := sdk.AccAddressFromBech32(packetFee.RefundAddress) + if err != nil { + return err + } + + // if the refund address is blocked, skip and continue distribution + if k.bankKeeper.BlockedAddr(refundAddr) { + continue + } + + // refund all fees to refund address + // Use SendCoins rather than the module account send functions since refund address may be a user account or module address. + moduleAcc := k.GetFeeModuleAddress() + if err = k.bankKeeper.SendCoins(cacheCtx, moduleAcc, refundAddr, packetFee.Fee.Total()); err != nil { + return err + } + + } + + k.DeleteFeesInEscrow(cacheCtx, identifiedPacketFee.PacketId) + } + + // write the cache + writeFn() + + return nil +} diff --git a/modules/apps/29-fee/keeper/escrow_test.go b/modules/apps/29-fee/keeper/escrow_test.go new file mode 100644 index 00000000000..e2a43afe586 --- /dev/null +++ b/modules/apps/29-fee/keeper/escrow_test.go @@ -0,0 +1,464 @@ +package keeper_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/tendermint/tendermint/crypto/secp256k1" + + "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" + transfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" + channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" +) + +func (suite *KeeperTestSuite) TestDistributeFee() { + var ( + forwardRelayer string + forwardRelayerBal sdk.Coin + reverseRelayer sdk.AccAddress + reverseRelayerBal sdk.Coin + refundAcc sdk.AccAddress + refundAccBal sdk.Coin + packetFee types.PacketFee + packetFees []types.PacketFee + ) + + testCases := []struct { + name string + malleate func() + expResult func() + }{ + { + "success", + func() {}, + func() { + // check if the reverse relayer is paid + expectedReverseAccBal := reverseRelayerBal.Add(defaultAckFee[0]).Add(defaultAckFee[0]) + balance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), reverseRelayer, sdk.DefaultBondDenom) + suite.Require().Equal(expectedReverseAccBal, balance) + + // check if the forward relayer is paid + forward, err := sdk.AccAddressFromBech32(forwardRelayer) + suite.Require().NoError(err) + + expectedForwardAccBal := forwardRelayerBal.Add(defaultRecvFee[0]).Add(defaultRecvFee[0]) + balance = suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), forward, sdk.DefaultBondDenom) + suite.Require().Equal(expectedForwardAccBal, balance) + + // check if the refund acc has been refunded the timeoutFee + expectedRefundAccBal := refundAccBal.Add(defaultTimeoutFee[0].Add(defaultTimeoutFee[0])) + balance = suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), refundAcc, sdk.DefaultBondDenom) + suite.Require().Equal(expectedRefundAccBal, balance) + + // check the module acc wallet is now empty + balance = suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.GetSimApp().IBCFeeKeeper.GetFeeModuleAddress(), sdk.DefaultBondDenom) + suite.Require().Equal(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(0)), balance) + }, + }, + { + "escrow account out of balance, fee module becomes locked - no distribution", func() { + // pass in an extra packet fee + packetFees = append(packetFees, packetFee) + }, + func() { + suite.Require().True(suite.chainA.GetSimApp().IBCFeeKeeper.IsLocked(suite.chainA.GetContext())) + + // check if the module acc contains all the fees + expectedModuleAccBal := packetFee.Fee.Total().Add(packetFee.Fee.Total()...) + balance := suite.chainA.GetSimApp().BankKeeper.GetAllBalances(suite.chainA.GetContext(), suite.chainA.GetSimApp().IBCFeeKeeper.GetFeeModuleAddress()) + suite.Require().Equal(expectedModuleAccBal, balance) + }, + }, + { + "invalid forward address", + func() { + forwardRelayer = "invalid address" + }, + func() { + // check if the refund acc has been refunded the timeoutFee & recvFee + expectedRefundAccBal := refundAccBal.Add(defaultTimeoutFee[0]).Add(defaultRecvFee[0]).Add(defaultTimeoutFee[0]).Add(defaultRecvFee[0]) + balance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), refundAcc, sdk.DefaultBondDenom) + suite.Require().Equal(expectedRefundAccBal, balance) + }, + }, + { + "invalid forward address: blocked address", + func() { + forwardRelayer = suite.chainA.GetSimApp().AccountKeeper.GetModuleAccount(suite.chainA.GetContext(), transfertypes.ModuleName).GetAddress().String() + }, + func() { + // check if the refund acc has been refunded the timeoutFee & recvFee + expectedRefundAccBal := refundAccBal.Add(defaultTimeoutFee[0]).Add(defaultRecvFee[0]).Add(defaultTimeoutFee[0]).Add(defaultRecvFee[0]) + balance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), refundAcc, sdk.DefaultBondDenom) + suite.Require().Equal(expectedRefundAccBal, balance) + }, + }, + { + "invalid receiver address: ack fee returned to sender", + func() { + reverseRelayer = suite.chainA.GetSimApp().AccountKeeper.GetModuleAccount(suite.chainA.GetContext(), transfertypes.ModuleName).GetAddress() + }, + func() { + // check if the refund acc has been refunded the timeoutFee & ackFee + expectedRefundAccBal := refundAccBal.Add(defaultTimeoutFee[0]).Add(defaultAckFee[0]).Add(defaultTimeoutFee[0]).Add(defaultAckFee[0]) + balance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), refundAcc, sdk.DefaultBondDenom) + suite.Require().Equal(expectedRefundAccBal, balance) + }, + }, + { + "invalid refund address: no-op, timeout fee remains in escrow", + func() { + packetFees[0].RefundAddress = suite.chainA.GetSimApp().AccountKeeper.GetModuleAccount(suite.chainA.GetContext(), transfertypes.ModuleName).GetAddress().String() + packetFees[1].RefundAddress = suite.chainA.GetSimApp().AccountKeeper.GetModuleAccount(suite.chainA.GetContext(), transfertypes.ModuleName).GetAddress().String() + }, + func() { + // check if the module acc contains the timeoutFee + expectedModuleAccBal := sdk.NewCoin(sdk.DefaultBondDenom, defaultTimeoutFee.Add(defaultTimeoutFee...).AmountOf(sdk.DefaultBondDenom)) + balance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.GetSimApp().IBCFeeKeeper.GetFeeModuleAddress(), sdk.DefaultBondDenom) + suite.Require().Equal(expectedModuleAccBal, balance) + }, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() // reset + suite.coordinator.Setup(suite.path) // setup channel + + // setup accounts + forwardRelayer = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()).String() + reverseRelayer = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) + refundAcc = suite.chainA.SenderAccount.GetAddress() + + packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) + fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) + + // escrow the packet fees & store the fees in state + packetFee = types.NewPacketFee(fee, refundAcc.String(), []string{}) + packetFees = []types.PacketFee{packetFee, packetFee} + + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, types.NewPacketFees(packetFees)) + err := suite.chainA.GetSimApp().BankKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), refundAcc, types.ModuleName, packetFee.Fee.Total().Add(packetFee.Fee.Total()...)) + suite.Require().NoError(err) + + tc.malleate() + + // fetch the account balances before fee distribution (forward, reverse, refund) + forwardAccAddress, _ := sdk.AccAddressFromBech32(forwardRelayer) + forwardRelayerBal = suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), forwardAccAddress, sdk.DefaultBondDenom) + reverseRelayerBal = suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), reverseRelayer, sdk.DefaultBondDenom) + refundAccBal = suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), refundAcc, sdk.DefaultBondDenom) + + suite.chainA.GetSimApp().IBCFeeKeeper.DistributePacketFeesOnAcknowledgement(suite.chainA.GetContext(), forwardRelayer, reverseRelayer, packetFees) + + tc.expResult() + }) + } +} + +func (suite *KeeperTestSuite) TestDistributePacketFeesOnTimeout() { + var ( + timeoutRelayer sdk.AccAddress + timeoutRelayerBal sdk.Coin + refundAcc sdk.AccAddress + refundAccBal sdk.Coin + packetFee types.PacketFee + packetFees []types.PacketFee + ) + + testCases := []struct { + name string + malleate func() + expResult func() + }{ + { + "success", + func() {}, + func() { + // check if the timeout relayer is paid + expectedTimeoutAccBal := timeoutRelayerBal.Add(defaultTimeoutFee[0]).Add(defaultTimeoutFee[0]) + balance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), timeoutRelayer, sdk.DefaultBondDenom) + suite.Require().Equal(expectedTimeoutAccBal, balance) + + // check if the refund acc has been refunded the recv/ack fees + expectedRefundAccBal := refundAccBal.Add(defaultAckFee[0]).Add(defaultAckFee[0]).Add(defaultRecvFee[0]).Add(defaultRecvFee[0]) + balance = suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), refundAcc, sdk.DefaultBondDenom) + suite.Require().Equal(expectedRefundAccBal, balance) + + // check the module acc wallet is now empty + balance = suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.GetSimApp().IBCFeeKeeper.GetFeeModuleAddress(), sdk.DefaultBondDenom) + suite.Require().Equal(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(0)), balance) + }, + }, + { + "escrow account out of balance, fee module becomes locked - no distribution", func() { + // pass in an extra packet fee + packetFees = append(packetFees, packetFee) + }, + func() { + suite.Require().True(suite.chainA.GetSimApp().IBCFeeKeeper.IsLocked(suite.chainA.GetContext())) + + // check if the module acc contains all the fees + expectedModuleAccBal := packetFee.Fee.Total().Add(packetFee.Fee.Total()...) + balance := suite.chainA.GetSimApp().BankKeeper.GetAllBalances(suite.chainA.GetContext(), suite.chainA.GetSimApp().IBCFeeKeeper.GetFeeModuleAddress()) + suite.Require().Equal(expectedModuleAccBal, balance) + }, + }, + { + "invalid timeout relayer address: timeout fee returned to sender", + func() { + timeoutRelayer = suite.chainA.GetSimApp().AccountKeeper.GetModuleAccount(suite.chainA.GetContext(), transfertypes.ModuleName).GetAddress() + }, + func() { + // check if the refund acc has been refunded the all the fees + expectedRefundAccBal := sdk.Coins{refundAccBal}.Add(packetFee.Fee.Total()...).Add(packetFee.Fee.Total()...)[0] + balance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), refundAcc, sdk.DefaultBondDenom) + suite.Require().Equal(expectedRefundAccBal, balance) + }, + }, + { + "invalid refund address: no-op, recv and ack fees remain in escrow", + func() { + packetFees[0].RefundAddress = suite.chainA.GetSimApp().AccountKeeper.GetModuleAccount(suite.chainA.GetContext(), transfertypes.ModuleName).GetAddress().String() + packetFees[1].RefundAddress = suite.chainA.GetSimApp().AccountKeeper.GetModuleAccount(suite.chainA.GetContext(), transfertypes.ModuleName).GetAddress().String() + }, + func() { + // check if the module acc contains the timeoutFee + expectedModuleAccBal := sdk.NewCoin(sdk.DefaultBondDenom, defaultRecvFee.Add(defaultRecvFee[0]).Add(defaultAckFee[0]).Add(defaultAckFee[0]).AmountOf(sdk.DefaultBondDenom)) + balance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.GetSimApp().IBCFeeKeeper.GetFeeModuleAddress(), sdk.DefaultBondDenom) + suite.Require().Equal(expectedModuleAccBal, balance) + }, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() // reset + suite.coordinator.Setup(suite.path) // setup channel + + // setup accounts + timeoutRelayer = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) + refundAcc = suite.chainA.SenderAccount.GetAddress() + + packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) + fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) + + // escrow the packet fees & store the fees in state + packetFee = types.NewPacketFee(fee, refundAcc.String(), []string{}) + packetFees = []types.PacketFee{packetFee, packetFee} + + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, types.NewPacketFees(packetFees)) + err := suite.chainA.GetSimApp().BankKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), refundAcc, types.ModuleName, packetFee.Fee.Total().Add(packetFee.Fee.Total()...)) + suite.Require().NoError(err) + + tc.malleate() + + // fetch the account balances before fee distribution (forward, reverse, refund) + timeoutRelayerBal = suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), timeoutRelayer, sdk.DefaultBondDenom) + refundAccBal = suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), refundAcc, sdk.DefaultBondDenom) + + suite.chainA.GetSimApp().IBCFeeKeeper.DistributePacketFeesOnTimeout(suite.chainA.GetContext(), timeoutRelayer, packetFees) + + tc.expResult() + }) + } +} + +func (suite *KeeperTestSuite) TestRefundFeesOnChannelClosure() { + var ( + expIdentifiedPacketFees []types.IdentifiedPacketFees + expEscrowBal sdk.Coins + expRefundBal sdk.Coins + refundAcc sdk.AccAddress + fee types.Fee + locked bool + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", func() { + for i := 1; i < 6; i++ { + // store the fee in state & update escrow account balance + packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(i)) + packetFees := types.NewPacketFees([]types.PacketFee{types.NewPacketFee(fee, refundAcc.String(), nil)}) + identifiedPacketFees := types.NewIdentifiedPacketFees(packetID, packetFees.PacketFees) + + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, packetFees) + + err := suite.chainA.GetSimApp().BankKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), refundAcc, types.ModuleName, fee.Total()) + suite.Require().NoError(err) + + expIdentifiedPacketFees = append(expIdentifiedPacketFees, identifiedPacketFees) + } + }, true, + }, + { + "success with undistributed packet fees on a different channel", func() { + for i := 1; i < 6; i++ { + // store the fee in state & update escrow account balance + packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(i)) + packetFees := types.NewPacketFees([]types.PacketFee{types.NewPacketFee(fee, refundAcc.String(), nil)}) + identifiedPacketFees := types.NewIdentifiedPacketFees(packetID, packetFees.PacketFees) + + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, packetFees) + + err := suite.chainA.GetSimApp().BankKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), refundAcc, types.ModuleName, fee.Total()) + suite.Require().NoError(err) + + expIdentifiedPacketFees = append(expIdentifiedPacketFees, identifiedPacketFees) + } + + // set packet fee for a different channel + packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, "channel-1", uint64(1)) + packetFees := types.NewPacketFees([]types.PacketFee{types.NewPacketFee(fee, refundAcc.String(), nil)}) + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeeEnabled(suite.chainA.GetContext(), suite.path.EndpointA.ChannelConfig.PortID, "channel-1") + + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, packetFees) + err := suite.chainA.GetSimApp().BankKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), refundAcc, types.ModuleName, fee.Total()) + suite.Require().NoError(err) + + expEscrowBal = fee.Total() + expRefundBal = expRefundBal.Sub(fee.Total()) + }, true, + }, + { + "escrow account empty, module should become locked", func() { + locked = true + + // store the fee in state without updating escrow account balance + packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(1)) + packetFees := types.NewPacketFees([]types.PacketFee{types.NewPacketFee(fee, refundAcc.String(), nil)}) + identifiedPacketFees := types.NewIdentifiedPacketFees(packetID, packetFees.PacketFees) + + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, packetFees) + + expIdentifiedPacketFees = []types.IdentifiedPacketFees{identifiedPacketFees} + }, + true, + }, + { + "escrow account goes negative on second packet, module should become locked", func() { + locked = true + + // store 2 fees in state + packetID1 := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(1)) + packetID2 := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(2)) + packetFees := types.NewPacketFees([]types.PacketFee{types.NewPacketFee(fee, refundAcc.String(), nil)}) + identifiedPacketFee1 := types.NewIdentifiedPacketFees(packetID1, packetFees.PacketFees) + identifiedPacketFee2 := types.NewIdentifiedPacketFees(packetID2, packetFees.PacketFees) + + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID1, packetFees) + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID2, packetFees) + + // update escrow account balance for 1 fee + err := suite.chainA.GetSimApp().BankKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), refundAcc, types.ModuleName, fee.Total()) + suite.Require().NoError(err) + + expIdentifiedPacketFees = []types.IdentifiedPacketFees{identifiedPacketFee1, identifiedPacketFee2} + }, true, + }, + { + "invalid refund acc address", func() { + // store the fee in state & update escrow account balance + packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(1)) + packetFees := types.NewPacketFees([]types.PacketFee{types.NewPacketFee(fee, "invalid refund address", nil)}) + identifiedPacketFees := types.NewIdentifiedPacketFees(packetID, packetFees.PacketFees) + + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, packetFees) + + err := suite.chainA.GetSimApp().BankKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), refundAcc, types.ModuleName, fee.Total()) + suite.Require().NoError(err) + + expIdentifiedPacketFees = []types.IdentifiedPacketFees{identifiedPacketFees} + }, false, + }, + { + "distributing to blocked address is skipped", func() { + blockedAddr := suite.chainA.GetSimApp().AccountKeeper.GetModuleAccount(suite.chainA.GetContext(), transfertypes.ModuleName).GetAddress().String() + + // store the fee in state & update escrow account balance + packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(1)) + packetFees := types.NewPacketFees([]types.PacketFee{types.NewPacketFee(fee, blockedAddr, nil)}) + identifiedPacketFees := types.NewIdentifiedPacketFees(packetID, packetFees.PacketFees) + + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, packetFees) + + err := suite.chainA.GetSimApp().BankKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), refundAcc, types.ModuleName, fee.Total()) + suite.Require().NoError(err) + + expIdentifiedPacketFees = []types.IdentifiedPacketFees{identifiedPacketFees} + + expEscrowBal = fee.Total() + expRefundBal = expRefundBal.Sub(fee.Total()) + }, true, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() // reset + suite.coordinator.Setup(suite.path) // setup channel + expIdentifiedPacketFees = []types.IdentifiedPacketFees{} + expEscrowBal = sdk.Coins{} + locked = false + + // setup + refundAcc = suite.chainA.SenderAccount.GetAddress() + moduleAcc := suite.chainA.GetSimApp().IBCFeeKeeper.GetFeeModuleAddress() + + // expected refund balance if the refunds are successful + // NOTE: tc.malleate() should transfer from refund balance to correctly set the escrow balance + expRefundBal = suite.chainA.GetSimApp().BankKeeper.GetAllBalances(suite.chainA.GetContext(), refundAcc) + + fee = types.Fee{ + RecvFee: defaultRecvFee, + AckFee: defaultAckFee, + TimeoutFee: defaultTimeoutFee, + } + + tc.malleate() + + // refundAcc balance before distribution + originalRefundBal := suite.chainA.GetSimApp().BankKeeper.GetAllBalances(suite.chainA.GetContext(), refundAcc) + originalEscrowBal := suite.chainA.GetSimApp().BankKeeper.GetAllBalances(suite.chainA.GetContext(), moduleAcc) + + err := suite.chainA.GetSimApp().IBCFeeKeeper.RefundFeesOnChannelClosure(suite.chainA.GetContext(), suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID) + + // refundAcc balance after RefundFeesOnChannelClosure + refundBal := suite.chainA.GetSimApp().BankKeeper.GetAllBalances(suite.chainA.GetContext(), refundAcc) + escrowBal := suite.chainA.GetSimApp().BankKeeper.GetAllBalances(suite.chainA.GetContext(), moduleAcc) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + + suite.Require().Equal(locked, suite.chainA.GetSimApp().IBCFeeKeeper.IsLocked(suite.chainA.GetContext())) + + if locked || !tc.expPass { + // refund account and escrow account balances should remain unchanged + suite.Require().Equal(originalRefundBal, refundBal) + suite.Require().Equal(originalEscrowBal, escrowBal) + + // ensure none of the fees were deleted + suite.Require().Equal(expIdentifiedPacketFees, suite.chainA.GetSimApp().IBCFeeKeeper.GetIdentifiedPacketFeesForChannel(suite.chainA.GetContext(), suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID)) + } else { + suite.Require().Equal(expEscrowBal, escrowBal) // escrow balance should be empty + suite.Require().Equal(expRefundBal, refundBal) // all packets should have been refunded + + // all fees in escrow should be deleted for this channel + suite.Require().Empty(suite.chainA.GetSimApp().IBCFeeKeeper.GetIdentifiedPacketFeesForChannel(suite.chainA.GetContext(), suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID)) + } + + }) + } +} diff --git a/modules/apps/29-fee/keeper/events.go b/modules/apps/29-fee/keeper/events.go new file mode 100644 index 00000000000..9ff6f320ffc --- /dev/null +++ b/modules/apps/29-fee/keeper/events.go @@ -0,0 +1,25 @@ +package keeper + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" +) + +// EmitIncentivizedPacket emits an event so that relayers know an incentivized packet is ready to be relayed +func EmitIncentivizedPacket(ctx sdk.Context, packetID channeltypes.PacketId, packetFee types.PacketFee) { + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeIncentivizedPacket, + sdk.NewAttribute(channeltypes.AttributeKeyPortID, packetID.PortId), + sdk.NewAttribute(channeltypes.AttributeKeyChannelID, packetID.ChannelId), + sdk.NewAttribute(channeltypes.AttributeKeySequence, fmt.Sprint(packetID.Sequence)), + sdk.NewAttribute(types.AttributeKeyRecvFee, packetFee.Fee.RecvFee.String()), + sdk.NewAttribute(types.AttributeKeyAckFee, packetFee.Fee.AckFee.String()), + sdk.NewAttribute(types.AttributeKeyTimeoutFee, packetFee.Fee.TimeoutFee.String()), + ), + ) +} diff --git a/modules/apps/29-fee/keeper/genesis.go b/modules/apps/29-fee/keeper/genesis.go new file mode 100644 index 00000000000..70b6a5012a2 --- /dev/null +++ b/modules/apps/29-fee/keeper/genesis.go @@ -0,0 +1,36 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" +) + +// InitGenesis initializes the fee middleware application state from a provided genesis state +func (k Keeper) InitGenesis(ctx sdk.Context, state types.GenesisState) { + for _, identifiedFees := range state.IdentifiedFees { + k.SetFeesInEscrow(ctx, identifiedFees.PacketId, types.NewPacketFees(identifiedFees.PacketFees)) + } + + for _, relayer := range state.RegisteredRelayers { + k.SetCounterpartyAddress(ctx, relayer.Address, relayer.CounterpartyAddress, relayer.ChannelId) + } + + for _, forwardAddr := range state.ForwardRelayers { + k.SetRelayerAddressForAsyncAck(ctx, forwardAddr.PacketId, forwardAddr.Address) + } + + for _, enabledChan := range state.FeeEnabledChannels { + k.SetFeeEnabled(ctx, enabledChan.PortId, enabledChan.ChannelId) + } +} + +// ExportGenesis returns the fee middleware application exported genesis +func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { + return &types.GenesisState{ + IdentifiedFees: k.GetAllIdentifiedPacketFees(ctx), + FeeEnabledChannels: k.GetAllFeeEnabledChannels(ctx), + RegisteredRelayers: k.GetAllRelayerAddresses(ctx), + ForwardRelayers: k.GetAllForwardRelayerAddresses(ctx), + } +} diff --git a/modules/apps/29-fee/keeper/genesis_test.go b/modules/apps/29-fee/keeper/genesis_test.go new file mode 100644 index 00000000000..8aa30385e58 --- /dev/null +++ b/modules/apps/29-fee/keeper/genesis_test.go @@ -0,0 +1,113 @@ +package keeper_test + +import ( + "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v3/testing" +) + +func (suite *KeeperTestSuite) TestInitGenesis() { + // build PacketId & Fee + refundAcc := suite.chainA.SenderAccount.GetAddress() + packetID := channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1) + fee := types.Fee{ + RecvFee: defaultRecvFee, + AckFee: defaultAckFee, + TimeoutFee: defaultTimeoutFee, + } + + // relayer addresses + sender := suite.chainA.SenderAccount.GetAddress().String() + counterparty := suite.chainB.SenderAccount.GetAddress().String() + + genesisState := types.GenesisState{ + IdentifiedFees: []types.IdentifiedPacketFees{ + { + PacketId: packetID, + PacketFees: []types.PacketFee{ + { + Fee: fee, + RefundAddress: refundAcc.String(), + Relayers: nil, + }, + }, + }, + }, + FeeEnabledChannels: []types.FeeEnabledChannel{ + { + PortId: ibctesting.MockFeePort, + ChannelId: ibctesting.FirstChannelID, + }, + }, + RegisteredRelayers: []types.RegisteredRelayerAddress{ + { + Address: sender, + CounterpartyAddress: counterparty, + ChannelId: ibctesting.FirstChannelID, + }, + }, + } + + suite.chainA.GetSimApp().IBCFeeKeeper.InitGenesis(suite.chainA.GetContext(), genesisState) + + // check fee + feesInEscrow, found := suite.chainA.GetSimApp().IBCFeeKeeper.GetFeesInEscrow(suite.chainA.GetContext(), packetID) + suite.Require().True(found) + suite.Require().Equal(genesisState.IdentifiedFees[0].PacketFees, feesInEscrow.PacketFees) + + // check fee is enabled + isEnabled := suite.chainA.GetSimApp().IBCFeeKeeper.IsFeeEnabled(suite.chainA.GetContext(), ibctesting.MockFeePort, ibctesting.FirstChannelID) + suite.Require().True(isEnabled) + + // check relayers + addr, found := suite.chainA.GetSimApp().IBCFeeKeeper.GetCounterpartyAddress(suite.chainA.GetContext(), sender, ibctesting.FirstChannelID) + suite.Require().True(found) + suite.Require().Equal(genesisState.RegisteredRelayers[0].CounterpartyAddress, addr) +} + +func (suite *KeeperTestSuite) TestExportGenesis() { + // set fee enabled + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeeEnabled(suite.chainA.GetContext(), ibctesting.MockFeePort, ibctesting.FirstChannelID) + + // setup & escrow the packet fee + refundAcc := suite.chainA.SenderAccount.GetAddress() + packetID := channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1) + fee := types.Fee{ + RecvFee: defaultRecvFee, + AckFee: defaultAckFee, + TimeoutFee: defaultTimeoutFee, + } + + packetFee := types.NewPacketFee(fee, refundAcc.String(), []string{}) + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, types.NewPacketFees([]types.PacketFee{packetFee})) + + // relayer addresses + sender := suite.chainA.SenderAccount.GetAddress().String() + counterparty := suite.chainB.SenderAccount.GetAddress().String() + // set counterparty address + suite.chainA.GetSimApp().IBCFeeKeeper.SetCounterpartyAddress(suite.chainA.GetContext(), sender, counterparty, ibctesting.FirstChannelID) + + // set forward relayer address + suite.chainA.GetSimApp().IBCFeeKeeper.SetRelayerAddressForAsyncAck(suite.chainA.GetContext(), packetID, sender) + + // export genesis + genesisState := suite.chainA.GetSimApp().IBCFeeKeeper.ExportGenesis(suite.chainA.GetContext()) + + // check fee enabled + suite.Require().Equal(ibctesting.FirstChannelID, genesisState.FeeEnabledChannels[0].ChannelId) + suite.Require().Equal(ibctesting.MockFeePort, genesisState.FeeEnabledChannels[0].PortId) + + // check fee + suite.Require().Equal(packetID, genesisState.IdentifiedFees[0].PacketId) + suite.Require().Equal(fee, genesisState.IdentifiedFees[0].PacketFees[0].Fee) + suite.Require().Equal(refundAcc.String(), genesisState.IdentifiedFees[0].PacketFees[0].RefundAddress) + suite.Require().Equal([]string(nil), genesisState.IdentifiedFees[0].PacketFees[0].Relayers) + + // check registered relayer addresses + suite.Require().Equal(sender, genesisState.RegisteredRelayers[0].Address) + suite.Require().Equal(counterparty, genesisState.RegisteredRelayers[0].CounterpartyAddress) + + // check registered relayer addresses + suite.Require().Equal(sender, genesisState.ForwardRelayers[0].Address) + suite.Require().Equal(packetID, genesisState.ForwardRelayers[0].PacketId) +} diff --git a/modules/apps/29-fee/keeper/grpc_query.go b/modules/apps/29-fee/keeper/grpc_query.go new file mode 100644 index 00000000000..10da5bc5595 --- /dev/null +++ b/modules/apps/29-fee/keeper/grpc_query.go @@ -0,0 +1,244 @@ +package keeper + +import ( + "context" + + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/query" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" +) + +var _ types.QueryServer = Keeper{} + +// IncentivizedPackets implements the Query/IncentivizedPackets gRPC method +func (k Keeper) IncentivizedPackets(goCtx context.Context, req *types.QueryIncentivizedPacketsRequest) (*types.QueryIncentivizedPacketsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + ctx := sdk.UnwrapSDKContext(goCtx).WithBlockHeight(int64(req.QueryHeight)) + + var identifiedPackets []types.IdentifiedPacketFees + store := prefix.NewStore(ctx.KVStore(k.storeKey), []byte(types.FeesInEscrowPrefix)) + _, err := query.Paginate(store, req.Pagination, func(key, value []byte) error { + packetID, err := types.ParseKeyFeesInEscrow(types.FeesInEscrowPrefix + string(key)) + if err != nil { + return err + } + + packetFees := k.MustUnmarshalFees(value) + identifiedPackets = append(identifiedPackets, types.NewIdentifiedPacketFees(packetID, packetFees.PacketFees)) + return nil + }) + + if err != nil { + return nil, status.Error(codes.NotFound, err.Error()) + } + + return &types.QueryIncentivizedPacketsResponse{ + IncentivizedPackets: identifiedPackets, + }, nil +} + +// IncentivizedPacket implements the Query/IncentivizedPacket gRPC method +func (k Keeper) IncentivizedPacket(goCtx context.Context, req *types.QueryIncentivizedPacketRequest) (*types.QueryIncentivizedPacketResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + ctx := sdk.UnwrapSDKContext(goCtx).WithBlockHeight(int64(req.QueryHeight)) + + feesInEscrow, exists := k.GetFeesInEscrow(ctx, req.PacketId) + if !exists { + return nil, status.Error( + codes.NotFound, + sdkerrors.Wrapf(types.ErrFeeNotFound, "channel: %s, port: %s, sequence: %d", req.PacketId.ChannelId, req.PacketId.PortId, req.PacketId.Sequence).Error()) + } + + return &types.QueryIncentivizedPacketResponse{ + IncentivizedPacket: types.NewIdentifiedPacketFees(req.PacketId, feesInEscrow.PacketFees), + }, nil +} + +// IncentivizedPacketsForChannel implements the Query/IncentivizedPacketsForChannel gRPC method +func (k Keeper) IncentivizedPacketsForChannel(goCtx context.Context, req *types.QueryIncentivizedPacketsForChannelRequest) (*types.QueryIncentivizedPacketsForChannelResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + ctx := sdk.UnwrapSDKContext(goCtx).WithBlockHeight(int64(req.QueryHeight)) + + var packets []*types.IdentifiedPacketFees + keyPrefix := types.KeyFeesInEscrowChannelPrefix(req.PortId, req.ChannelId) + store := prefix.NewStore(ctx.KVStore(k.storeKey), keyPrefix) + _, err := query.Paginate(store, req.Pagination, func(key, value []byte) error { + packetID, err := types.ParseKeyFeesInEscrow(string(keyPrefix) + string(key)) + if err != nil { + return err + } + + packetFees := k.MustUnmarshalFees(value) + + identifiedPacketFees := types.NewIdentifiedPacketFees(packetID, packetFees.PacketFees) + packets = append(packets, &identifiedPacketFees) + + return nil + }) + + if err != nil { + return nil, status.Error(codes.NotFound, err.Error()) + } + + return &types.QueryIncentivizedPacketsForChannelResponse{ + IncentivizedPackets: packets, + }, nil +} + +// TotalRecvFees implements the Query/TotalRecvFees gRPC method +func (k Keeper) TotalRecvFees(goCtx context.Context, req *types.QueryTotalRecvFeesRequest) (*types.QueryTotalRecvFeesResponse, error) { + + ctx := sdk.UnwrapSDKContext(goCtx) + + feesInEscrow, found := k.GetFeesInEscrow(ctx, req.PacketId) + if !found { + return nil, status.Errorf( + codes.NotFound, + sdkerrors.Wrapf(types.ErrFeeNotFound, "channel: %s, port: %s, sequence: %d", req.PacketId.ChannelId, req.PacketId.PortId, req.PacketId.Sequence).Error(), + ) + } + + var recvFees sdk.Coins + for _, packetFee := range feesInEscrow.PacketFees { + recvFees = recvFees.Add(packetFee.Fee.RecvFee...) + } + + return &types.QueryTotalRecvFeesResponse{ + RecvFees: recvFees, + }, nil +} + +// TotalAckFees implements the Query/TotalAckFees gRPC method +func (k Keeper) TotalAckFees(goCtx context.Context, req *types.QueryTotalAckFeesRequest) (*types.QueryTotalAckFeesResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + feesInEscrow, found := k.GetFeesInEscrow(ctx, req.PacketId) + if !found { + return nil, status.Errorf( + codes.NotFound, + sdkerrors.Wrapf(types.ErrFeeNotFound, "channel: %s, port: %s, sequence: %d", req.PacketId.ChannelId, req.PacketId.PortId, req.PacketId.Sequence).Error(), + ) + } + + var ackFees sdk.Coins + for _, packetFee := range feesInEscrow.PacketFees { + ackFees = ackFees.Add(packetFee.Fee.AckFee...) + } + + return &types.QueryTotalAckFeesResponse{ + AckFees: ackFees, + }, nil +} + +// TotalTimeoutFees implements the Query/TotalTimeoutFees gRPC method +func (k Keeper) TotalTimeoutFees(goCtx context.Context, req *types.QueryTotalTimeoutFeesRequest) (*types.QueryTotalTimeoutFeesResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + feesInEscrow, found := k.GetFeesInEscrow(ctx, req.PacketId) + if !found { + return nil, status.Errorf( + codes.NotFound, + sdkerrors.Wrapf(types.ErrFeeNotFound, "channel: %s, port: %s, sequence: %d", req.PacketId.ChannelId, req.PacketId.PortId, req.PacketId.Sequence).Error(), + ) + } + + var timeoutFees sdk.Coins + for _, packetFee := range feesInEscrow.PacketFees { + timeoutFees = timeoutFees.Add(packetFee.Fee.TimeoutFee...) + } + + return &types.QueryTotalTimeoutFeesResponse{ + TimeoutFees: timeoutFees, + }, nil +} + +// CounterpartyAddress implements the Query/CounterpartyAddress gRPC method and returns the registered counterparty address for forward relaying +func (k Keeper) CounterpartyAddress(goCtx context.Context, req *types.QueryCounterpartyAddressRequest) (*types.QueryCounterpartyAddressResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + counterpartyAddr, found := k.GetCounterpartyAddress(ctx, req.RelayerAddress, req.ChannelId) + if !found { + return nil, status.Errorf(codes.NotFound, "counterparty address not found for address: %s on channel: %s", req.RelayerAddress, req.ChannelId) + } + + return &types.QueryCounterpartyAddressResponse{ + CounterpartyAddress: counterpartyAddr, + }, nil +} + +// FeeEnabledChannels implements the Query/FeeEnabledChannels gRPC method and returns a list of fee enabled channels +func (k Keeper) FeeEnabledChannels(goCtx context.Context, req *types.QueryFeeEnabledChannelsRequest) (*types.QueryFeeEnabledChannelsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + ctx := sdk.UnwrapSDKContext(goCtx).WithBlockHeight(int64(req.QueryHeight)) + + var feeEnabledChannels []types.FeeEnabledChannel + store := prefix.NewStore(ctx.KVStore(k.storeKey), []byte(types.FeeEnabledKeyPrefix)) + _, err := query.Paginate(store, req.Pagination, func(key, value []byte) error { + portID, channelID, err := types.ParseKeyFeeEnabled(types.FeeEnabledKeyPrefix + string(key)) + if err != nil { + return err + } + + feeEnabledChannel := types.FeeEnabledChannel{ + PortId: portID, + ChannelId: channelID, + } + + feeEnabledChannels = append(feeEnabledChannels, feeEnabledChannel) + + return nil + }) + + if err != nil { + return nil, status.Error(codes.NotFound, err.Error()) + } + + return &types.QueryFeeEnabledChannelsResponse{ + FeeEnabledChannels: feeEnabledChannels, + }, nil +} + +// FeeEnabledChannel implements the Query/FeeEnabledChannel gRPC method and returns true if the provided +// port and channel identifiers belong to a fee enabled channel +func (k Keeper) FeeEnabledChannel(goCtx context.Context, req *types.QueryFeeEnabledChannelRequest) (*types.QueryFeeEnabledChannelResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + isFeeEnabled := k.IsFeeEnabled(ctx, req.PortId, req.ChannelId) + + return &types.QueryFeeEnabledChannelResponse{ + FeeEnabled: isFeeEnabled, + }, nil +} diff --git a/modules/apps/29-fee/keeper/grpc_query_test.go b/modules/apps/29-fee/keeper/grpc_query_test.go new file mode 100644 index 00000000000..d665c655ccf --- /dev/null +++ b/modules/apps/29-fee/keeper/grpc_query_test.go @@ -0,0 +1,641 @@ +package keeper_test + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" + "github.com/tendermint/tendermint/crypto/secp256k1" + + "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v3/testing" +) + +func (suite *KeeperTestSuite) TestQueryIncentivizedPackets() { + var ( + req *types.QueryIncentivizedPacketsRequest + expectedPackets []types.IdentifiedPacketFees + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() { + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeeEnabled(suite.chainA.GetContext(), ibctesting.MockFeePort, ibctesting.FirstChannelID) + + fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) + packetFee := types.NewPacketFee(fee, suite.chainA.SenderAccount.GetAddress().String(), []string(nil)) + + for i := 0; i < 3; i++ { + // escrow packet fees for three different packets + packetID := channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, uint64(i+1)) + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, types.NewPacketFees([]types.PacketFee{packetFee})) + + expectedPackets = append(expectedPackets, types.NewIdentifiedPacketFees(packetID, []types.PacketFee{packetFee})) + } + + req = &types.QueryIncentivizedPacketsRequest{ + Pagination: &query.PageRequest{ + Limit: 5, + CountTotal: false, + }, + QueryHeight: 0, + } + }, + true, + }, + { + "empty pagination", + func() { + expectedPackets = nil + req = &types.QueryIncentivizedPacketsRequest{} + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + suite.SetupTest() // reset + + tc.malleate() // malleate mutates test data + + ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) + res, err := suite.queryClient.IncentivizedPackets(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(expectedPackets, res.IncentivizedPackets) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestQueryIncentivizedPacket() { + var ( + req *types.QueryIncentivizedPacketRequest + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() {}, + true, + }, + { + "fees not found for packet id", + func() { + req = &types.QueryIncentivizedPacketRequest{ + PacketId: channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 100), + QueryHeight: 0, + } + }, + false, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + suite.SetupTest() // reset + + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeeEnabled(suite.chainA.GetContext(), ibctesting.MockFeePort, ibctesting.FirstChannelID) + + packetID := channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1) + fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) + packetFee := types.NewPacketFee(fee, suite.chainA.SenderAccount.GetAddress().String(), []string(nil)) + + packetFees := []types.PacketFee{packetFee, packetFee, packetFee} + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, types.NewPacketFees(packetFees)) + + req = &types.QueryIncentivizedPacketRequest{ + PacketId: packetID, + QueryHeight: 0, + } + + tc.malleate() // malleate mutates test data + + ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) + res, err := suite.queryClient.IncentivizedPacket(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(types.NewIdentifiedPacketFees(packetID, []types.PacketFee{packetFee, packetFee, packetFee}), res.IncentivizedPacket) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestQueryIncentivizedPacketsForChannel() { + var ( + req *types.QueryIncentivizedPacketsForChannelRequest + expIdentifiedPacketFees []*types.IdentifiedPacketFees + ) + + fee := types.Fee{ + AckFee: sdk.Coins{sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: sdk.NewInt(100)}}, + RecvFee: sdk.Coins{sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: sdk.NewInt(100)}}, + TimeoutFee: sdk.Coins{sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: sdk.NewInt(100)}}, + } + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty pagination", + func() { + expIdentifiedPacketFees = nil + req = &types.QueryIncentivizedPacketsForChannelRequest{} + }, + true, + }, + { + "success", + func() { + req = &types.QueryIncentivizedPacketsForChannelRequest{ + Pagination: &query.PageRequest{ + Limit: 5, + CountTotal: false, + }, + PortId: ibctesting.MockFeePort, + ChannelId: ibctesting.FirstChannelID, + QueryHeight: 0, + } + }, + true, + }, + { + "no packets for specified channel", + func() { + expIdentifiedPacketFees = nil + req = &types.QueryIncentivizedPacketsForChannelRequest{ + Pagination: &query.PageRequest{ + Limit: 5, + CountTotal: false, + }, + PortId: ibctesting.MockFeePort, + ChannelId: "channel-10", + QueryHeight: 0, + } + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + // setup + refundAcc := suite.chainA.SenderAccount.GetAddress() + packetFee := types.NewPacketFee(fee, refundAcc.String(), nil) + packetFees := types.NewPacketFees([]types.PacketFee{packetFee, packetFee, packetFee}) + + identifiedFees1 := types.NewIdentifiedPacketFees(channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1), packetFees.PacketFees) + identifiedFees2 := types.NewIdentifiedPacketFees(channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 2), packetFees.PacketFees) + identifiedFees3 := types.NewIdentifiedPacketFees(channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 3), packetFees.PacketFees) + + expIdentifiedPacketFees = append(expIdentifiedPacketFees, &identifiedFees1, &identifiedFees2, &identifiedFees3) + + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeeEnabled(suite.chainA.GetContext(), ibctesting.MockFeePort, ibctesting.FirstChannelID) + for _, identifiedPacketFees := range expIdentifiedPacketFees { + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), identifiedPacketFees.PacketId, types.NewPacketFees(identifiedPacketFees.PacketFees)) + } + + tc.malleate() + ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) + + res, err := suite.queryClient.IncentivizedPacketsForChannel(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(expIdentifiedPacketFees, res.IncentivizedPackets) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestQueryTotalRecvFees() { + var ( + req *types.QueryTotalRecvFeesRequest + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() {}, + true, + }, + { + "packet not found", + func() { + req.PacketId = channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 100) + }, + false, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + suite.SetupTest() // reset + + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeeEnabled(suite.chainA.GetContext(), ibctesting.MockFeePort, ibctesting.FirstChannelID) + + packetID := channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1) + + fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) + packetFee := types.NewPacketFee(fee, suite.chainA.SenderAccount.GetAddress().String(), []string(nil)) + + packetFees := []types.PacketFee{packetFee, packetFee, packetFee} + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, types.NewPacketFees(packetFees)) + + req = &types.QueryTotalRecvFeesRequest{ + PacketId: packetID, + } + + tc.malleate() + + ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) + res, err := suite.queryClient.TotalRecvFees(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + + // expected total is three times the default recv fee + expectedFees := defaultRecvFee.Add(defaultRecvFee...).Add(defaultRecvFee...) + suite.Require().Equal(expectedFees, res.RecvFees) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestQueryTotalAckFees() { + var ( + req *types.QueryTotalAckFeesRequest + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() {}, + true, + }, + { + "packet not found", + func() { + req.PacketId = channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 100) + }, + false, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + suite.SetupTest() // reset + + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeeEnabled(suite.chainA.GetContext(), ibctesting.MockFeePort, ibctesting.FirstChannelID) + + packetID := channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1) + + fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) + packetFee := types.NewPacketFee(fee, suite.chainA.SenderAccount.GetAddress().String(), []string(nil)) + + packetFees := []types.PacketFee{packetFee, packetFee, packetFee} + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, types.NewPacketFees(packetFees)) + + req = &types.QueryTotalAckFeesRequest{ + PacketId: packetID, + } + + tc.malleate() + + ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) + res, err := suite.queryClient.TotalAckFees(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + + // expected total is three times the default acknowledgement fee + expectedFees := defaultAckFee.Add(defaultAckFee...).Add(defaultAckFee...) + suite.Require().Equal(expectedFees, res.AckFees) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestQueryTotalTimeoutFees() { + var ( + req *types.QueryTotalTimeoutFeesRequest + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() {}, + true, + }, + { + "packet not found", + func() { + req.PacketId = channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 100) + }, + false, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + suite.SetupTest() // reset + + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeeEnabled(suite.chainA.GetContext(), ibctesting.MockFeePort, ibctesting.FirstChannelID) + + packetID := channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1) + + fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) + packetFee := types.NewPacketFee(fee, suite.chainA.SenderAccount.GetAddress().String(), []string(nil)) + + packetFees := []types.PacketFee{packetFee, packetFee, packetFee} + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, types.NewPacketFees(packetFees)) + + req = &types.QueryTotalTimeoutFeesRequest{ + PacketId: packetID, + } + + tc.malleate() + + ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) + res, err := suite.queryClient.TotalTimeoutFees(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + + // expected total is three times the default acknowledgement fee + expectedFees := defaultTimeoutFee.Add(defaultTimeoutFee...).Add(defaultTimeoutFee...) + suite.Require().Equal(expectedFees, res.TimeoutFees) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestQueryCounterpartyAddress() { + var ( + req *types.QueryCounterpartyAddressRequest + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() {}, + true, + }, + { + "counterparty address not found: invalid channel", + func() { + req.ChannelId = "invalid-channel-id" + }, + false, + }, + { + "counterparty address not found: invalid address", + func() { + req.RelayerAddress = "invalid-addr" + }, + false, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + suite.SetupTest() // reset + + pk := secp256k1.GenPrivKey().PubKey() + expectedCounterpartyAddr := sdk.AccAddress(pk.Address()) + + suite.chainA.GetSimApp().IBCFeeKeeper.SetCounterpartyAddress( + suite.chainA.GetContext(), + suite.chainA.SenderAccount.GetAddress().String(), + expectedCounterpartyAddr.String(), + suite.path.EndpointA.ChannelID, + ) + + req = &types.QueryCounterpartyAddressRequest{ + ChannelId: suite.path.EndpointA.ChannelID, + RelayerAddress: suite.chainA.SenderAccount.GetAddress().String(), + } + + tc.malleate() + + ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) + res, err := suite.queryClient.CounterpartyAddress(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(expectedCounterpartyAddr.String(), res.CounterpartyAddress) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestQueryFeeEnabledChannels() { + var ( + req *types.QueryFeeEnabledChannelsRequest + expFeeEnabledChannels []types.FeeEnabledChannel + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() {}, + true, + }, + { + "success: empty pagination", + func() { + req = &types.QueryFeeEnabledChannelsRequest{} + }, + true, + }, + { + "success: with multiple fee enabled channels", + func() { + suite.coordinator.Setup(suite.pathAToC) + + expChannel := types.FeeEnabledChannel{ + PortId: suite.pathAToC.EndpointA.ChannelConfig.PortID, + ChannelId: suite.pathAToC.EndpointA.ChannelID, + } + + expFeeEnabledChannels = append(expFeeEnabledChannels, expChannel) + }, + true, + }, + { + "success: pagination with multiple fee enabled channels", + func() { + // start at index 1, as channel-0 is already added to expFeeEnabledChannels below + for i := 1; i < 10; i++ { + channelID := channeltypes.FormatChannelIdentifier(uint64(i)) + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeeEnabled(suite.chainA.GetContext(), ibctesting.MockFeePort, channelID) + + expChannel := types.FeeEnabledChannel{ + PortId: ibctesting.MockFeePort, + ChannelId: channelID, + } + + if i < 5 { // add only the first 5 channels, as our default pagination limit is 5 + expFeeEnabledChannels = append(expFeeEnabledChannels, expChannel) + } + } + + suite.chainA.NextBlock() + }, + true, + }, + { + "empty response", + func() { + suite.chainA.GetSimApp().IBCFeeKeeper.DeleteFeeEnabled(suite.chainA.GetContext(), suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID) + expFeeEnabledChannels = nil + + suite.chainA.NextBlock() + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + suite.SetupTest() // reset + + suite.coordinator.Setup(suite.path) + + expChannel := types.FeeEnabledChannel{ + PortId: suite.path.EndpointA.ChannelConfig.PortID, + ChannelId: suite.path.EndpointA.ChannelID, + } + + expFeeEnabledChannels = []types.FeeEnabledChannel{expChannel} + + req = &types.QueryFeeEnabledChannelsRequest{ + Pagination: &query.PageRequest{ + Limit: 5, + CountTotal: false, + }, + QueryHeight: 0, + } + + tc.malleate() + + ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) + res, err := suite.queryClient.FeeEnabledChannels(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(expFeeEnabledChannels, res.FeeEnabledChannels) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestQueryFeeEnabledChannel() { + var ( + req *types.QueryFeeEnabledChannelRequest + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() {}, + true, + }, + { + "fee not enabled on channel", + func() { + req.ChannelId = "invalid-channel-id" + req.PortId = "invalid-port-id" + }, + false, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + suite.SetupTest() // reset + + suite.coordinator.Setup(suite.path) + + req = &types.QueryFeeEnabledChannelRequest{ + PortId: suite.path.EndpointA.ChannelConfig.PortID, + ChannelId: suite.path.EndpointA.ChannelID, + } + + tc.malleate() + + ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) + res, err := suite.queryClient.FeeEnabledChannel(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().True(res.FeeEnabled) + } else { + suite.Require().False(res.FeeEnabled) + } + }) + } +} diff --git a/modules/apps/29-fee/keeper/keeper.go b/modules/apps/29-fee/keeper/keeper.go new file mode 100644 index 00000000000..e0317d3660a --- /dev/null +++ b/modules/apps/29-fee/keeper/keeper.go @@ -0,0 +1,335 @@ +package keeper + +import ( + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v3/modules/core/24-host" +) + +// Middleware must implement types.ChannelKeeper and types.PortKeeper expected interfaces +// so that it can wrap IBC channel and port logic for underlying application. +var ( + _ types.ChannelKeeper = Keeper{} + _ types.PortKeeper = Keeper{} +) + +// Keeper defines the IBC fungible transfer keeper +type Keeper struct { + storeKey sdk.StoreKey + cdc codec.BinaryCodec + + authKeeper types.AccountKeeper + ics4Wrapper types.ICS4Wrapper + channelKeeper types.ChannelKeeper + portKeeper types.PortKeeper + bankKeeper types.BankKeeper +} + +// NewKeeper creates a new 29-fee Keeper instance +func NewKeeper( + cdc codec.BinaryCodec, key sdk.StoreKey, paramSpace paramtypes.Subspace, + ics4Wrapper types.ICS4Wrapper, channelKeeper types.ChannelKeeper, portKeeper types.PortKeeper, authKeeper types.AccountKeeper, bankKeeper types.BankKeeper, +) Keeper { + + return Keeper{ + cdc: cdc, + storeKey: key, + ics4Wrapper: ics4Wrapper, + channelKeeper: channelKeeper, + portKeeper: portKeeper, + authKeeper: authKeeper, + bankKeeper: bankKeeper, + } +} + +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", "x/"+host.ModuleName+"-"+types.ModuleName) +} + +// BindPort defines a wrapper function for the port Keeper's function in +// order to expose it to module's InitGenesis function +func (k Keeper) BindPort(ctx sdk.Context, portID string) *capabilitytypes.Capability { + return k.portKeeper.BindPort(ctx, portID) +} + +// GetChannel wraps IBC ChannelKeeper's GetChannel function +func (k Keeper) GetChannel(ctx sdk.Context, portID, channelID string) (channeltypes.Channel, bool) { + return k.channelKeeper.GetChannel(ctx, portID, channelID) +} + +// GetNextSequenceSend wraps IBC ChannelKeeper's GetNextSequenceSend function +func (k Keeper) GetNextSequenceSend(ctx sdk.Context, portID, channelID string) (uint64, bool) { + return k.channelKeeper.GetNextSequenceSend(ctx, portID, channelID) +} + +// GetFeeModuleAddress returns the ICS29 Fee ModuleAccount address +func (k Keeper) GetFeeModuleAddress() sdk.AccAddress { + return k.authKeeper.GetModuleAddress(types.ModuleName) +} + +// EscrowAccountHasBalance verifies if the escrow account has the provided fee. +func (k Keeper) EscrowAccountHasBalance(ctx sdk.Context, coins sdk.Coins) bool { + for _, coin := range coins { + if !k.bankKeeper.HasBalance(ctx, k.GetFeeModuleAddress(), coin) { + return false + } + } + + return true +} + +// lockFeeModule sets a flag to determine if fee handling logic should run for the given channel +// identified by channel and port identifiers. +// Please see ADR 004 for more information. +func (k Keeper) lockFeeModule(ctx sdk.Context) { + store := ctx.KVStore(k.storeKey) + store.Set(types.KeyLocked(), []byte{1}) +} + +// IsLocked indicates if the fee module is locked +// Please see ADR 004 for more information. +func (k Keeper) IsLocked(ctx sdk.Context) bool { + store := ctx.KVStore(k.storeKey) + return store.Has(types.KeyLocked()) +} + +// SetFeeEnabled sets a flag to determine if fee handling logic should run for the given channel +// identified by channel and port identifiers. +func (k Keeper) SetFeeEnabled(ctx sdk.Context, portID, channelID string) { + store := ctx.KVStore(k.storeKey) + store.Set(types.KeyFeeEnabled(portID, channelID), []byte{1}) +} + +// DeleteFeeEnabled deletes the fee enabled flag for a given portID and channelID +func (k Keeper) DeleteFeeEnabled(ctx sdk.Context, portID, channelID string) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.KeyFeeEnabled(portID, channelID)) +} + +// IsFeeEnabled returns whether fee handling logic should be run for the given port. It will check the +// fee enabled flag for the given port and channel identifiers +func (k Keeper) IsFeeEnabled(ctx sdk.Context, portID, channelID string) bool { + store := ctx.KVStore(k.storeKey) + return store.Get(types.KeyFeeEnabled(portID, channelID)) != nil +} + +// GetAllFeeEnabledChannels returns a list of all ics29 enabled channels containing portID & channelID that are stored in state +func (k Keeper) GetAllFeeEnabledChannels(ctx sdk.Context) []types.FeeEnabledChannel { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, []byte(types.FeeEnabledKeyPrefix)) + defer iterator.Close() + + var enabledChArr []types.FeeEnabledChannel + for ; iterator.Valid(); iterator.Next() { + portID, channelID, err := types.ParseKeyFeeEnabled(string(iterator.Key())) + if err != nil { + panic(err) + } + ch := types.FeeEnabledChannel{ + PortId: portID, + ChannelId: channelID, + } + + enabledChArr = append(enabledChArr, ch) + } + + return enabledChArr +} + +// SetCounterpartyAddress maps the destination chain relayer address to the source relayer address +// The receiving chain must store the mapping from: address -> counterpartyAddress for the given channel +func (k Keeper) SetCounterpartyAddress(ctx sdk.Context, address, counterpartyAddress, channelID string) { + store := ctx.KVStore(k.storeKey) + store.Set(types.KeyCounterpartyRelayer(address, channelID), []byte(counterpartyAddress)) +} + +// GetCounterpartyAddress gets the relayer counterparty address given a destination relayer address +func (k Keeper) GetCounterpartyAddress(ctx sdk.Context, address, channelID string) (string, bool) { + store := ctx.KVStore(k.storeKey) + key := types.KeyCounterpartyRelayer(address, channelID) + + if !store.Has(key) { + return "", false + } + + addr := string(store.Get(key)) + return addr, true +} + +// GetAllRelayerAddresses returns all registered relayer addresses +func (k Keeper) GetAllRelayerAddresses(ctx sdk.Context) []types.RegisteredRelayerAddress { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, []byte(types.CounterpartyRelayerAddressKeyPrefix)) + defer iterator.Close() + + var registeredAddrArr []types.RegisteredRelayerAddress + for ; iterator.Valid(); iterator.Next() { + address, channelID, err := types.ParseKeyCounterpartyRelayer(string(iterator.Key())) + if err != nil { + panic(err) + } + + addr := types.RegisteredRelayerAddress{ + Address: address, + CounterpartyAddress: string(iterator.Value()), + ChannelId: channelID, + } + + registeredAddrArr = append(registeredAddrArr, addr) + } + + return registeredAddrArr +} + +// SetRelayerAddressForAsyncAck sets the forward relayer address during OnRecvPacket in case of async acknowledgement +func (k Keeper) SetRelayerAddressForAsyncAck(ctx sdk.Context, packetID channeltypes.PacketId, address string) { + store := ctx.KVStore(k.storeKey) + store.Set(types.KeyForwardRelayerAddress(packetID), []byte(address)) +} + +// GetRelayerAddressForAsyncAck gets forward relayer address for a particular packet +func (k Keeper) GetRelayerAddressForAsyncAck(ctx sdk.Context, packetID channeltypes.PacketId) (string, bool) { + store := ctx.KVStore(k.storeKey) + key := types.KeyForwardRelayerAddress(packetID) + if !store.Has(key) { + return "", false + } + + addr := string(store.Get(key)) + return addr, true +} + +// GetAllForwardRelayerAddresses returns all forward relayer addresses stored for async acknowledgements +func (k Keeper) GetAllForwardRelayerAddresses(ctx sdk.Context) []types.ForwardRelayerAddress { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, []byte(types.ForwardRelayerPrefix)) + defer iterator.Close() + + var forwardRelayerAddr []types.ForwardRelayerAddress + for ; iterator.Valid(); iterator.Next() { + packetID, err := types.ParseKeyForwardRelayerAddress(string(iterator.Key())) + if err != nil { + panic(err) + } + + addr := types.ForwardRelayerAddress{ + Address: string(iterator.Value()), + PacketId: packetID, + } + + forwardRelayerAddr = append(forwardRelayerAddr, addr) + } + + return forwardRelayerAddr +} + +// Deletes the forwardRelayerAddr associated with the packetID +func (k Keeper) DeleteForwardRelayerAddress(ctx sdk.Context, packetID channeltypes.PacketId) { + store := ctx.KVStore(k.storeKey) + key := types.KeyForwardRelayerAddress(packetID) + store.Delete(key) +} + +// GetFeesInEscrow returns all escrowed packet fees for a given packetID +func (k Keeper) GetFeesInEscrow(ctx sdk.Context, packetID channeltypes.PacketId) (types.PacketFees, bool) { + store := ctx.KVStore(k.storeKey) + key := types.KeyFeesInEscrow(packetID) + bz := store.Get(key) + if bz == nil { + return types.PacketFees{}, false + } + + return k.MustUnmarshalFees(bz), true +} + +// HasFeesInEscrow returns true if packet fees exist for the provided packetID +func (k Keeper) HasFeesInEscrow(ctx sdk.Context, packetID channeltypes.PacketId) bool { + store := ctx.KVStore(k.storeKey) + key := types.KeyFeesInEscrow(packetID) + + return store.Has(key) +} + +// SetFeesInEscrow sets the given packet fees in escrow keyed by the packetID +func (k Keeper) SetFeesInEscrow(ctx sdk.Context, packetID channeltypes.PacketId, fees types.PacketFees) { + store := ctx.KVStore(k.storeKey) + bz := k.MustMarshalFees(fees) + store.Set(types.KeyFeesInEscrow(packetID), bz) +} + +// DeleteFeesInEscrow deletes the fee associated with the given packetID +func (k Keeper) DeleteFeesInEscrow(ctx sdk.Context, packetID channeltypes.PacketId) { + store := ctx.KVStore(k.storeKey) + key := types.KeyFeesInEscrow(packetID) + store.Delete(key) +} + +// GetIdentifiedPacketFeesForChannel returns all the currently escrowed fees on a given channel. +func (k Keeper) GetIdentifiedPacketFeesForChannel(ctx sdk.Context, portID, channelID string) []types.IdentifiedPacketFees { + var identifiedPacketFees []types.IdentifiedPacketFees + + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.KeyFeesInEscrowChannelPrefix(portID, channelID)) + + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + packetID, err := types.ParseKeyFeesInEscrow(string(iterator.Key())) + if err != nil { + panic(err) + } + + packetFees := k.MustUnmarshalFees(iterator.Value()) + + identifiedFee := types.NewIdentifiedPacketFees(packetID, packetFees.PacketFees) + identifiedPacketFees = append(identifiedPacketFees, identifiedFee) + } + + return identifiedPacketFees +} + +// GetAllIdentifiedPacketFees returns a list of all IdentifiedPacketFees that are stored in state +func (k Keeper) GetAllIdentifiedPacketFees(ctx sdk.Context) []types.IdentifiedPacketFees { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, []byte(types.FeesInEscrowPrefix)) + defer iterator.Close() + + var identifiedFees []types.IdentifiedPacketFees + for ; iterator.Valid(); iterator.Next() { + packetID, err := types.ParseKeyFeesInEscrow(string(iterator.Key())) + if err != nil { + panic(err) + } + + feesInEscrow := k.MustUnmarshalFees(iterator.Value()) + + identifiedFee := types.IdentifiedPacketFees{ + PacketId: packetID, + PacketFees: feesInEscrow.PacketFees, + } + + identifiedFees = append(identifiedFees, identifiedFee) + } + + return identifiedFees +} + +// MustMarshalFees attempts to encode a Fee object and returns the +// raw encoded bytes. It panics on error. +func (k Keeper) MustMarshalFees(fees types.PacketFees) []byte { + return k.cdc.MustMarshal(&fees) +} + +// MustUnmarshalFees attempts to decode and return a Fee object from +// raw encoded bytes. It panics on error. +func (k Keeper) MustUnmarshalFees(bz []byte) types.PacketFees { + var fees types.PacketFees + k.cdc.MustUnmarshal(bz, &fees) + return fees +} diff --git a/modules/apps/29-fee/keeper/keeper_test.go b/modules/apps/29-fee/keeper/keeper_test.go new file mode 100644 index 00000000000..7446ecbab64 --- /dev/null +++ b/modules/apps/29-fee/keeper/keeper_test.go @@ -0,0 +1,259 @@ +package keeper_test + +import ( + "fmt" + "testing" + + "github.com/cosmos/cosmos-sdk/baseapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/suite" + + "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v3/testing" + ibcmock "github.com/cosmos/ibc-go/v3/testing/mock" +) + +var ( + defaultRecvFee = sdk.Coins{sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: sdk.NewInt(100)}} + defaultAckFee = sdk.Coins{sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: sdk.NewInt(200)}} + defaultTimeoutFee = sdk.Coins{sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: sdk.NewInt(300)}} + invalidCoins = sdk.Coins{sdk.Coin{Denom: "invalidDenom", Amount: sdk.NewInt(100)}} +) + +type KeeperTestSuite struct { + suite.Suite + + coordinator *ibctesting.Coordinator + + // testing chains used for convenience and readability + chainA *ibctesting.TestChain + chainB *ibctesting.TestChain + chainC *ibctesting.TestChain + + path *ibctesting.Path + pathAToC *ibctesting.Path + + queryClient types.QueryClient +} + +func (suite *KeeperTestSuite) SetupTest() { + suite.coordinator = ibctesting.NewCoordinator(suite.T(), 3) + suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1)) + suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2)) + suite.chainC = suite.coordinator.GetChain(ibctesting.GetChainID(3)) + + path := ibctesting.NewPath(suite.chainA, suite.chainB) + mockFeeVersion := string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: ibcmock.Version})) + path.EndpointA.ChannelConfig.Version = mockFeeVersion + path.EndpointB.ChannelConfig.Version = mockFeeVersion + path.EndpointA.ChannelConfig.PortID = ibctesting.MockFeePort + path.EndpointB.ChannelConfig.PortID = ibctesting.MockFeePort + suite.path = path + + path = ibctesting.NewPath(suite.chainA, suite.chainC) + path.EndpointA.ChannelConfig.Version = mockFeeVersion + path.EndpointB.ChannelConfig.Version = mockFeeVersion + path.EndpointA.ChannelConfig.PortID = ibctesting.MockFeePort + path.EndpointB.ChannelConfig.PortID = ibctesting.MockFeePort + suite.pathAToC = path + + queryHelper := baseapp.NewQueryServerTestHelper(suite.chainA.GetContext(), suite.chainA.GetSimApp().InterfaceRegistry()) + types.RegisterQueryServer(queryHelper, suite.chainA.GetSimApp().IBCFeeKeeper) + suite.queryClient = types.NewQueryClient(queryHelper) +} + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(KeeperTestSuite)) +} + +// helper function +func lockFeeModule(chain *ibctesting.TestChain) { + ctx := chain.GetContext() + storeKey := chain.GetSimApp().GetKey(types.ModuleName) + store := ctx.KVStore(storeKey) + store.Set(types.KeyLocked(), []byte{1}) +} + +func (suite *KeeperTestSuite) TestEscrowAccountHasBalance() { + fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) + + suite.Require().False(suite.chainA.GetSimApp().IBCFeeKeeper.EscrowAccountHasBalance(suite.chainA.GetContext(), fee.Total())) + + // set fee in escrow account + err := suite.chainA.GetSimApp().BankKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), types.ModuleName, fee.Total()) + suite.Require().Nil(err) + + suite.Require().True(suite.chainA.GetSimApp().IBCFeeKeeper.EscrowAccountHasBalance(suite.chainA.GetContext(), fee.Total())) + + // increase ack fee + fee.AckFee = fee.AckFee.Add(defaultAckFee...) + suite.Require().False(suite.chainA.GetSimApp().IBCFeeKeeper.EscrowAccountHasBalance(suite.chainA.GetContext(), fee.Total())) +} + +func (suite *KeeperTestSuite) TestFeesInEscrow() { + suite.coordinator.Setup(suite.path) + + // escrow five fees for packet sequence 1 + packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) + fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) + + packetFee := types.NewPacketFee(fee, suite.chainA.SenderAccount.GetAddress().String(), nil) + packetFees := []types.PacketFee{packetFee, packetFee, packetFee, packetFee, packetFee} + + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, types.NewPacketFees(packetFees)) + + // retrieve the fees in escrow and assert the length of PacketFees + feesInEscrow, found := suite.chainA.GetSimApp().IBCFeeKeeper.GetFeesInEscrow(suite.chainA.GetContext(), packetID) + suite.Require().True(found) + suite.Require().Len(feesInEscrow.PacketFees, 5, fmt.Sprintf("expected length 5, but got %d", len(feesInEscrow.PacketFees))) + + // delete fees for packet sequence 1 + suite.chainA.GetSimApp().IBCFeeKeeper.DeleteFeesInEscrow(suite.chainA.GetContext(), packetID) + hasFeesInEscrow := suite.chainA.GetSimApp().IBCFeeKeeper.HasFeesInEscrow(suite.chainA.GetContext(), packetID) + suite.Require().False(hasFeesInEscrow) +} + +func (suite *KeeperTestSuite) TestIsLocked() { + ctx := suite.chainA.GetContext() + suite.Require().False(suite.chainA.GetSimApp().IBCFeeKeeper.IsLocked(ctx)) + + lockFeeModule(suite.chainA) + + suite.Require().True(suite.chainA.GetSimApp().IBCFeeKeeper.IsLocked(ctx)) +} + +func (suite *KeeperTestSuite) TestGetIdentifiedPacketFeesForChannel() { + suite.coordinator.Setup(suite.path) + + // escrow a fee + refundAcc := suite.chainA.SenderAccount.GetAddress() + packetID1 := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) + packetID2 := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 2) + packetID5 := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 51) + + fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) + + // escrow the packet fee + packetFee := types.NewPacketFee(fee, refundAcc.String(), []string{}) + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID1, types.NewPacketFees([]types.PacketFee{packetFee})) + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID2, types.NewPacketFees([]types.PacketFee{packetFee})) + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID5, types.NewPacketFees([]types.PacketFee{packetFee})) + + // set fees in escrow for packetIDs on different channel + diffChannel := "channel-1" + diffPacketID1 := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, diffChannel, 1) + diffPacketID2 := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, diffChannel, 2) + diffPacketID5 := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, diffChannel, 5) + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), diffPacketID1, types.NewPacketFees([]types.PacketFee{packetFee})) + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), diffPacketID2, types.NewPacketFees([]types.PacketFee{packetFee})) + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), diffPacketID5, types.NewPacketFees([]types.PacketFee{packetFee})) + + expectedFees := []types.IdentifiedPacketFees{ + { + PacketId: packetID1, + PacketFees: []types.PacketFee{ + { + Fee: fee, + RefundAddress: refundAcc.String(), + Relayers: nil, + }, + }, + }, + { + PacketId: packetID2, + PacketFees: []types.PacketFee{ + { + Fee: fee, + RefundAddress: refundAcc.String(), + Relayers: nil, + }, + }, + }, + { + PacketId: packetID5, + PacketFees: []types.PacketFee{ + { + Fee: fee, + RefundAddress: refundAcc.String(), + Relayers: nil, + }, + }, + }, + } + + identifiedFees := suite.chainA.GetSimApp().IBCFeeKeeper.GetIdentifiedPacketFeesForChannel(suite.chainA.GetContext(), suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID) + suite.Require().Len(identifiedFees, len(expectedFees)) + suite.Require().Equal(identifiedFees, expectedFees) +} + +func (suite *KeeperTestSuite) TestGetAllIdentifiedPacketFees() { + suite.coordinator.Setup(suite.path) + + // escrow a fee + refundAcc := suite.chainA.SenderAccount.GetAddress() + packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) + fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) + + // escrow the packet fee + packetFee := types.NewPacketFee(fee, refundAcc.String(), []string{}) + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, types.NewPacketFees([]types.PacketFee{packetFee})) + + expectedFees := []types.IdentifiedPacketFees{ + { + PacketId: packetID, + PacketFees: []types.PacketFee{ + { + Fee: fee, + RefundAddress: refundAcc.String(), + Relayers: nil, + }, + }, + }, + } + + identifiedFees := suite.chainA.GetSimApp().IBCFeeKeeper.GetAllIdentifiedPacketFees(suite.chainA.GetContext()) + suite.Require().Len(identifiedFees, len(expectedFees)) + suite.Require().Equal(identifiedFees, expectedFees) +} + +func (suite *KeeperTestSuite) TestGetAllFeeEnabledChannels() { + validPortId := "ibcmoduleport" + // set two channels enabled + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeeEnabled(suite.chainA.GetContext(), ibctesting.MockFeePort, ibctesting.FirstChannelID) + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeeEnabled(suite.chainA.GetContext(), validPortId, ibctesting.FirstChannelID) + + expectedCh := []types.FeeEnabledChannel{ + { + PortId: validPortId, + ChannelId: ibctesting.FirstChannelID, + }, + { + PortId: ibctesting.MockFeePort, + ChannelId: ibctesting.FirstChannelID, + }, + } + + ch := suite.chainA.GetSimApp().IBCFeeKeeper.GetAllFeeEnabledChannels(suite.chainA.GetContext()) + suite.Require().Len(ch, len(expectedCh)) + suite.Require().Equal(ch, expectedCh) +} + +func (suite *KeeperTestSuite) TestGetAllRelayerAddresses() { + sender := suite.chainA.SenderAccount.GetAddress().String() + counterparty := suite.chainB.SenderAccount.GetAddress().String() + + suite.chainA.GetSimApp().IBCFeeKeeper.SetCounterpartyAddress(suite.chainA.GetContext(), sender, counterparty, ibctesting.FirstChannelID) + + expectedAddr := []types.RegisteredRelayerAddress{ + { + Address: sender, + CounterpartyAddress: counterparty, + ChannelId: ibctesting.FirstChannelID, + }, + } + + addr := suite.chainA.GetSimApp().IBCFeeKeeper.GetAllRelayerAddresses(suite.chainA.GetContext()) + suite.Require().Len(addr, len(expectedAddr)) + suite.Require().Equal(addr, expectedAddr) +} diff --git a/modules/apps/29-fee/keeper/msg_server.go b/modules/apps/29-fee/keeper/msg_server.go new file mode 100644 index 00000000000..69f6520c759 --- /dev/null +++ b/modules/apps/29-fee/keeper/msg_server.go @@ -0,0 +1,79 @@ +package keeper + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" +) + +var _ types.MsgServer = Keeper{} + +// RegisterCounterpartyAddress is called by the relayer on each channelEnd and allows them to specify their counterparty address before relaying +// This ensures they will be properly compensated for forward relaying on the source chain since the destination chain must send back relayer's source address (counterparty address) in acknowledgement +// This function may be called more than once by relayers, in which case, the previous counterparty address will be overwritten by the new counterparty address +func (k Keeper) RegisterCounterpartyAddress(goCtx context.Context, msg *types.MsgRegisterCounterpartyAddress) (*types.MsgRegisterCounterpartyAddressResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + k.SetCounterpartyAddress( + ctx, msg.Address, msg.CounterpartyAddress, msg.ChannelId, + ) + + k.Logger(ctx).Info("Registering counterparty address for relayer.", "Address:", msg.Address, "Counterparty Address:", msg.CounterpartyAddress) + + return &types.MsgRegisterCounterpartyAddressResponse{}, nil +} + +// PayPacketFee defines a rpc handler method for MsgPayPacketFee +// PayPacketFee is an open callback that may be called by any module/user that wishes to escrow funds in order to relay the packet with the next sequence +func (k Keeper) PayPacketFee(goCtx context.Context, msg *types.MsgPayPacketFee) (*types.MsgPayPacketFeeResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + if !k.IsFeeEnabled(ctx, msg.SourcePortId, msg.SourceChannelId) { + // users may not escrow fees on this channel. Must send packets without a fee message + return nil, types.ErrFeeNotEnabled + } + + if k.IsLocked(ctx) { + return nil, types.ErrFeeModuleLocked + } + + // get the next sequence + sequence, found := k.GetNextSequenceSend(ctx, msg.SourcePortId, msg.SourceChannelId) + if !found { + return nil, channeltypes.ErrSequenceSendNotFound + } + + packetID := channeltypes.NewPacketId(msg.SourcePortId, msg.SourceChannelId, sequence) + packetFee := types.NewPacketFee(msg.Fee, msg.Signer, msg.Relayers) + + if err := k.escrowPacketFee(ctx, packetID, packetFee); err != nil { + return nil, err + } + + return &types.MsgPayPacketFeeResponse{}, nil +} + +// PayPacketFee defines a rpc handler method for MsgPayPacketFee +// PayPacketFee is an open callback that may be called by any module/user that wishes to escrow funds in order to +// incentivize the relaying of a known packet +func (k Keeper) PayPacketFeeAsync(goCtx context.Context, msg *types.MsgPayPacketFeeAsync) (*types.MsgPayPacketFeeAsyncResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + if !k.IsFeeEnabled(ctx, msg.PacketId.PortId, msg.PacketId.ChannelId) { + // users may not escrow fees on this channel. Must send packets without a fee message + return nil, types.ErrFeeNotEnabled + } + + if k.IsLocked(ctx) { + return nil, types.ErrFeeModuleLocked + } + + if err := k.escrowPacketFee(ctx, msg.PacketId, msg.PacketFee); err != nil { + return nil, err + } + + return &types.MsgPayPacketFeeAsyncResponse{}, nil +} diff --git a/modules/apps/29-fee/keeper/msg_server_test.go b/modules/apps/29-fee/keeper/msg_server_test.go new file mode 100644 index 00000000000..8a927559a51 --- /dev/null +++ b/modules/apps/29-fee/keeper/msg_server_test.go @@ -0,0 +1,310 @@ +package keeper_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v3/testing" +) + +func (suite *KeeperTestSuite) TestRegisterCounterpartyAddress() { + var ( + sender string + counterparty string + ) + + testCases := []struct { + name string + expPass bool + malleate func() + }{ + { + "success", + true, + func() {}, + }, + { + "counterparty is an arbitrary string", + true, + func() { counterparty = "arbitrary-string" }, + }, + } + + for _, tc := range testCases { + suite.SetupTest() + ctx := suite.chainA.GetContext() + + sender = suite.chainA.SenderAccount.GetAddress().String() + counterparty = suite.chainB.SenderAccount.GetAddress().String() + tc.malleate() + msg := types.NewMsgRegisterCounterpartyAddress(sender, counterparty, ibctesting.FirstChannelID) + + _, err := suite.chainA.SendMsgs(msg) + + if tc.expPass { + suite.Require().NoError(err) // message committed + + counterpartyAddress, _ := suite.chainA.GetSimApp().IBCFeeKeeper.GetCounterpartyAddress(ctx, suite.chainA.SenderAccount.GetAddress().String(), ibctesting.FirstChannelID) + suite.Require().Equal(counterparty, counterpartyAddress) + } else { + suite.Require().Error(err) + } + } +} + +func (suite *KeeperTestSuite) TestPayPacketFee() { + var ( + expEscrowBalance sdk.Coins + expFeesInEscrow []types.PacketFee + msg *types.MsgPayPacketFee + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() {}, + true, + }, + { + "success with existing packet fees in escrow", + func() { + fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) + + packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) + packetFee := types.NewPacketFee(fee, suite.chainA.SenderAccount.GetAddress().String(), nil) + feesInEscrow := types.NewPacketFees([]types.PacketFee{packetFee}) + + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, feesInEscrow) + err := suite.chainA.GetSimApp().BankKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), types.ModuleName, fee.Total()) + suite.Require().NoError(err) + + expEscrowBalance = expEscrowBalance.Add(fee.Total()...) + expFeesInEscrow = append(expFeesInEscrow, packetFee) + }, + true, + }, + { + "fee module is locked", + func() { + lockFeeModule(suite.chainA) + }, + false, + }, + { + "fee module disabled on channel", + func() { + msg.SourcePortId = "invalid-port" + msg.SourceChannelId = "invalid-channel" + }, + false, + }, + { + "invalid refund address", + func() { + msg.Signer = "invalid-address" + }, + false, + }, + { + "refund account does not exist", + func() { + msg.Signer = suite.chainB.SenderAccount.GetAddress().String() + }, + false, + }, + { + "acknowledgement fee balance not found", + func() { + msg.Fee.AckFee = invalidCoins + }, + false, + }, + { + "receive fee balance not found", + func() { + msg.Fee.RecvFee = invalidCoins + }, + false, + }, + { + "timeout fee balance not found", + func() { + msg.Fee.TimeoutFee = invalidCoins + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + suite.coordinator.Setup(suite.path) // setup channel + + fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) + msg = types.NewMsgPayPacketFee( + fee, + suite.path.EndpointA.ChannelConfig.PortID, + suite.path.EndpointA.ChannelID, + suite.chainA.SenderAccount.GetAddress().String(), + nil, + ) + + expEscrowBalance = fee.Total() + expPacketFee := types.NewPacketFee(fee, suite.chainA.SenderAccount.GetAddress().String(), nil) + expFeesInEscrow = []types.PacketFee{expPacketFee} + + tc.malleate() + + _, err := suite.chainA.GetSimApp().IBCFeeKeeper.PayPacketFee(sdk.WrapSDKContext(suite.chainA.GetContext()), msg) + + if tc.expPass { + suite.Require().NoError(err) // message committed + + packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) + feesInEscrow, found := suite.chainA.GetSimApp().IBCFeeKeeper.GetFeesInEscrow(suite.chainA.GetContext(), packetID) + suite.Require().True(found) + suite.Require().Equal(expFeesInEscrow, feesInEscrow.PacketFees) + + escrowBalance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.GetSimApp().IBCFeeKeeper.GetFeeModuleAddress(), sdk.DefaultBondDenom) + suite.Require().Equal(expEscrowBalance.AmountOf(sdk.DefaultBondDenom), escrowBalance.Amount) + } else { + suite.Require().Error(err) + + escrowBalance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.GetSimApp().IBCFeeKeeper.GetFeeModuleAddress(), sdk.DefaultBondDenom) + suite.Require().Equal(sdk.NewInt(0), escrowBalance.Amount) + } + }) + } +} + +func (suite *KeeperTestSuite) TestPayPacketFeeAsync() { + var ( + expEscrowBalance sdk.Coins + expFeesInEscrow []types.PacketFee + msg *types.MsgPayPacketFeeAsync + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() {}, + true, + }, + { + "success with existing packet fees in escrow", + func() { + fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) + + packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) + packetFee := types.NewPacketFee(fee, suite.chainA.SenderAccount.GetAddress().String(), nil) + feesInEscrow := types.NewPacketFees([]types.PacketFee{packetFee}) + + suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, feesInEscrow) + err := suite.chainA.GetSimApp().BankKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), types.ModuleName, fee.Total()) + suite.Require().NoError(err) + + expEscrowBalance = expEscrowBalance.Add(fee.Total()...) + expFeesInEscrow = append(expFeesInEscrow, packetFee) + }, + true, + }, + { + "fee module is locked", + func() { + lockFeeModule(suite.chainA) + }, + false, + }, + { + "fee module disabled on channel", + func() { + msg.PacketId.PortId = "invalid-port" + msg.PacketId.ChannelId = "invalid-channel" + }, + false, + }, + { + "invalid refund address", + func() { + msg.PacketFee.RefundAddress = "invalid-address" + }, + false, + }, + { + "refund account does not exist", + func() { + msg.PacketFee.RefundAddress = suite.chainB.SenderAccount.GetAddress().String() + }, + false, + }, + { + "acknowledgement fee balance not found", + func() { + msg.PacketFee.Fee.AckFee = invalidCoins + }, + false, + }, + { + "receive fee balance not found", + func() { + msg.PacketFee.Fee.RecvFee = invalidCoins + }, + false, + }, + { + "timeout fee balance not found", + func() { + msg.PacketFee.Fee.TimeoutFee = invalidCoins + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + suite.coordinator.Setup(suite.path) // setup channel + + packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) + fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) + packetFee := types.NewPacketFee(fee, suite.chainA.SenderAccount.GetAddress().String(), nil) + + expEscrowBalance = fee.Total() + expFeesInEscrow = []types.PacketFee{packetFee} + msg = types.NewMsgPayPacketFeeAsync(packetID, packetFee) + + tc.malleate() + + _, err := suite.chainA.GetSimApp().IBCFeeKeeper.PayPacketFeeAsync(sdk.WrapSDKContext(suite.chainA.GetContext()), msg) + + if tc.expPass { + suite.Require().NoError(err) // message committed + + feesInEscrow, found := suite.chainA.GetSimApp().IBCFeeKeeper.GetFeesInEscrow(suite.chainA.GetContext(), packetID) + suite.Require().True(found) + suite.Require().Equal(expFeesInEscrow, feesInEscrow.PacketFees) + + escrowBalance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.GetSimApp().IBCFeeKeeper.GetFeeModuleAddress(), sdk.DefaultBondDenom) + suite.Require().Equal(expEscrowBalance.AmountOf(sdk.DefaultBondDenom), escrowBalance.Amount) + } else { + suite.Require().Error(err) + + escrowBalance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.GetSimApp().IBCFeeKeeper.GetFeeModuleAddress(), sdk.DefaultBondDenom) + suite.Require().Equal(sdk.NewInt(0), escrowBalance.Amount) + } + }) + } +} diff --git a/modules/apps/29-fee/keeper/relay.go b/modules/apps/29-fee/keeper/relay.go new file mode 100644 index 00000000000..476497bfbbb --- /dev/null +++ b/modules/apps/29-fee/keeper/relay.go @@ -0,0 +1,65 @@ +package keeper + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + + "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + ibcexported "github.com/cosmos/ibc-go/v3/modules/core/exported" +) + +// SendPacket wraps IBC ChannelKeeper's SendPacket function +func (k Keeper) SendPacket(ctx sdk.Context, chanCap *capabilitytypes.Capability, packet ibcexported.PacketI) error { + return k.ics4Wrapper.SendPacket(ctx, chanCap, packet) +} + +// WriteAcknowledgement wraps IBC ChannelKeeper's WriteAcknowledgement function +// ICS29 WriteAcknowledgement is used for asynchronous acknowledgements +func (k Keeper) WriteAcknowledgement(ctx sdk.Context, chanCap *capabilitytypes.Capability, packet ibcexported.PacketI, acknowledgement ibcexported.Acknowledgement) error { + if !k.IsFeeEnabled(ctx, packet.GetDestPort(), packet.GetDestChannel()) { + // ics4Wrapper may be core IBC or higher-level middleware + return k.ics4Wrapper.WriteAcknowledgement(ctx, chanCap, packet, acknowledgement) + } + + packetID := channeltypes.NewPacketId(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + + // retrieve the forward relayer that was stored in `onRecvPacket` + relayer, found := k.GetRelayerAddressForAsyncAck(ctx, packetID) + if !found { + return sdkerrors.Wrapf(types.ErrRelayerNotFoundForAsyncAck, "no relayer address stored for async acknowledgement for packet with portID: %s, channelID: %s, sequence: %d", packetID.PortId, packetID.ChannelId, packetID.Sequence) + } + + // it is possible that a relayer has not registered a counterparty address. + // if there is no registered counterparty address then write acknowledgement with empty relayer address and refund recv_fee. + forwardRelayer, _ := k.GetCounterpartyAddress(ctx, relayer, packet.GetDestChannel()) + + ack := types.NewIncentivizedAcknowledgement(forwardRelayer, acknowledgement.Acknowledgement(), acknowledgement.Success()) + + k.DeleteForwardRelayerAddress(ctx, packetID) + + // ics4Wrapper may be core IBC or higher-level middleware + return k.ics4Wrapper.WriteAcknowledgement(ctx, chanCap, packet, ack) +} + +// GetAppVersion returns the underlying application version. +func (k Keeper) GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) { + version, found := k.ics4Wrapper.GetAppVersion(ctx, portID, channelID) + if !found { + return "", false + } + + if !k.IsFeeEnabled(ctx, portID, channelID) { + return version, true + } + + var metadata types.Metadata + if err := types.ModuleCdc.UnmarshalJSON([]byte(version), &metadata); err != nil { + panic(fmt.Errorf("unable to unmarshal metadata for fee enabled channel: %w", err)) + } + + return metadata.AppVersion, true +} diff --git a/modules/apps/29-fee/keeper/relay_test.go b/modules/apps/29-fee/keeper/relay_test.go new file mode 100644 index 00000000000..d0a9e620f9d --- /dev/null +++ b/modules/apps/29-fee/keeper/relay_test.go @@ -0,0 +1,172 @@ +package keeper_test + +import ( + "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" + clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v3/testing" + ibcmock "github.com/cosmos/ibc-go/v3/testing/mock" +) + +func (suite *KeeperTestSuite) TestWriteAcknowledgementAsync() { + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() { + suite.chainB.GetSimApp().IBCFeeKeeper.SetRelayerAddressForAsyncAck(suite.chainB.GetContext(), channeltypes.NewPacketId(suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, 1), suite.chainA.SenderAccount.GetAddress().String()) + suite.chainB.GetSimApp().IBCFeeKeeper.SetCounterpartyAddress(suite.chainB.GetContext(), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), suite.path.EndpointB.ChannelID) + }, + true, + }, + { + "relayer address not set for async WriteAcknowledgement", + func() {}, + false, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + suite.SetupTest() + + // open incentivized channels + // setup pathAToC (chainA -> chainC) first in order to have different channel IDs for chainA & chainB + suite.coordinator.Setup(suite.pathAToC) + // setup path for chainA -> chainB + suite.coordinator.Setup(suite.path) + + // build packet + timeoutTimestamp := ^uint64(0) + packet := channeltypes.NewPacket( + []byte("packetData"), + 1, + suite.path.EndpointA.ChannelConfig.PortID, + suite.path.EndpointA.ChannelID, + suite.path.EndpointB.ChannelConfig.PortID, + suite.path.EndpointB.ChannelID, + clienttypes.ZeroHeight(), + timeoutTimestamp, + ) + + ack := channeltypes.NewResultAcknowledgement([]byte("success")) + chanCap := suite.chainB.GetChannelCapability(suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID) + + // malleate test case + tc.malleate() + + err := suite.chainB.GetSimApp().IBCFeeKeeper.WriteAcknowledgement(suite.chainB.GetContext(), chanCap, packet, ack) + + if tc.expPass { + suite.Require().NoError(err) + _, found := suite.chainB.GetSimApp().IBCFeeKeeper.GetRelayerAddressForAsyncAck(suite.chainB.GetContext(), channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1)) + suite.Require().False(found) + + expectedAck := types.NewIncentivizedAcknowledgement(suite.chainB.SenderAccount.GetAddress().String(), ack.Acknowledgement(), ack.Success()) + commitedAck, _ := suite.chainB.GetSimApp().GetIBCKeeper().ChannelKeeper.GetPacketAcknowledgement(suite.chainB.GetContext(), packet.DestinationPort, packet.DestinationChannel, 1) + suite.Require().Equal(commitedAck, channeltypes.CommitAcknowledgement(expectedAck.Acknowledgement())) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestWriteAcknowledgementAsyncFeeDisabled() { + // open incentivized channel + suite.coordinator.Setup(suite.path) + suite.chainB.GetSimApp().IBCFeeKeeper.DeleteFeeEnabled(suite.chainB.GetContext(), suite.path.EndpointB.ChannelConfig.PortID, "channel-0") + + // build packet + timeoutTimestamp := ^uint64(0) + packet := channeltypes.NewPacket( + []byte("packetData"), + 1, + suite.path.EndpointA.ChannelConfig.PortID, + suite.path.EndpointA.ChannelID, + suite.path.EndpointB.ChannelConfig.PortID, + suite.path.EndpointB.ChannelID, + clienttypes.ZeroHeight(), + timeoutTimestamp, + ) + + ack := channeltypes.NewResultAcknowledgement([]byte("success")) + chanCap := suite.chainB.GetChannelCapability(suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID) + + err := suite.chainB.GetSimApp().IBCFeeKeeper.WriteAcknowledgement(suite.chainB.GetContext(), chanCap, packet, ack) + suite.Require().NoError(err) + + packetAck, _ := suite.chainB.GetSimApp().GetIBCKeeper().ChannelKeeper.GetPacketAcknowledgement(suite.chainB.GetContext(), packet.DestinationPort, packet.DestinationChannel, 1) + suite.Require().Equal(packetAck, channeltypes.CommitAcknowledgement(ack.Acknowledgement())) +} + +func (suite *KeeperTestSuite) TestGetAppVersion() { + var ( + portID string + channelID string + expAppVersion string + ) + testCases := []struct { + name string + malleate func() + expFound bool + }{ + { + "success for fee enabled channel", + func() { + expAppVersion = ibcmock.Version + }, + true, + }, + { + "success for non fee enabled channel", + func() { + path := ibctesting.NewPath(suite.chainA, suite.chainB) + path.EndpointA.ChannelConfig.PortID = ibctesting.MockFeePort + path.EndpointB.ChannelConfig.PortID = ibctesting.MockFeePort + // by default a new path uses a non fee channel + suite.coordinator.Setup(path) + portID = path.EndpointA.ChannelConfig.PortID + channelID = path.EndpointA.ChannelID + + expAppVersion = ibcmock.Version + }, + true, + }, + { + "channel does not exist", + func() { + channelID = "does not exist" + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + suite.SetupTest() + suite.coordinator.Setup(suite.path) + + portID = suite.path.EndpointA.ChannelConfig.PortID + channelID = suite.path.EndpointA.ChannelID + + // malleate test case + tc.malleate() + + appVersion, found := suite.chainA.GetSimApp().IBCFeeKeeper.GetAppVersion(suite.chainA.GetContext(), portID, channelID) + + if tc.expFound { + suite.Require().True(found) + suite.Require().Equal(expAppVersion, appVersion) + } else { + suite.Require().False(found) + suite.Require().Empty(appVersion) + } + }) + } +} diff --git a/modules/apps/29-fee/module.go b/modules/apps/29-fee/module.go new file mode 100644 index 00000000000..e493e047837 --- /dev/null +++ b/modules/apps/29-fee/module.go @@ -0,0 +1,172 @@ +package fee + +import ( + "context" + "encoding/json" + "fmt" + "math/rand" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/gorilla/mux" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/spf13/cobra" + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/client/cli" + "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/keeper" + "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" + porttypes "github.com/cosmos/ibc-go/v3/modules/core/05-port/types" +) + +var ( + _ module.AppModule = AppModule{} + _ porttypes.IBCModule = IBCModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +// AppModuleBasic is the 29-fee AppModuleBasic +type AppModuleBasic struct{} + +// Name implements AppModuleBasic interface +func (AppModuleBasic) Name() string { + return types.ModuleName +} + +// RegisterLegacyAminoCodec implements AppModuleBasic interface +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {} + +// RegisterInterfaces registers module concrete types into protobuf Any. +func (AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) { + types.RegisterInterfaces(registry) +} + +// DefaultGenesis returns default genesis state as raw bytes for the ibc +// 29-fee module. +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { + return cdc.MustMarshalJSON(types.DefaultGenesisState()) +} + +// ValidateGenesis performs genesis state validation for the 29-fee module. +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncodingConfig, bz json.RawMessage) error { + var gs types.GenesisState + if err := cdc.UnmarshalJSON(bz, &gs); err != nil { + return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) + } + + return gs.Validate() +} + +// RegisterRESTRoutes implements AppModuleBasic interface +func (AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Router) { +} + +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for ics29 fee module. +func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { + types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) +} + +// GetTxCmd implements AppModuleBasic interface +func (AppModuleBasic) GetTxCmd() *cobra.Command { + return cli.NewTxCmd() +} + +// GetQueryCmd implements AppModuleBasic interface +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd() +} + +// AppModule represents the AppModule for this module +type AppModule struct { + AppModuleBasic + keeper keeper.Keeper +} + +// NewAppModule creates a new 29-fee module +func NewAppModule(k keeper.Keeper) AppModule { + return AppModule{ + keeper: k, + } +} + +// RegisterInvariants implements the AppModule interface +func (AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { +} + +// Route implements the AppModule interface +func (am AppModule) Route() sdk.Route { + return sdk.Route{} +} + +// QuerierRoute implements the AppModule interface +func (AppModule) QuerierRoute() string { + return types.QuerierRoute +} + +// LegacyQuerierHandler implements the AppModule interface +func (am AppModule) LegacyQuerierHandler(*codec.LegacyAmino) sdk.Querier { + return nil +} + +// RegisterServices registers module services. +func (am AppModule) RegisterServices(cfg module.Configurator) { + types.RegisterMsgServer(cfg.MsgServer(), am.keeper) + types.RegisterQueryServer(cfg.QueryServer(), am.keeper) +} + +// InitGenesis performs genesis initialization for the ibc-29-fee module. It returns +// no validator updates. +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate { + var genesisState types.GenesisState + cdc.MustUnmarshalJSON(data, &genesisState) + am.keeper.InitGenesis(ctx, genesisState) + return []abci.ValidatorUpdate{} +} + +// ExportGenesis returns the exported genesis state as raw bytes for the ibc-29-fee +// module. +func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage { + gs := am.keeper.ExportGenesis(ctx) + return cdc.MustMarshalJSON(gs) +} + +// ConsensusVersion implements AppModule/ConsensusVersion. +func (AppModule) ConsensusVersion() uint64 { return 1 } + +// BeginBlock implements the AppModule interface +func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { +} + +// EndBlock implements the AppModule interface +func (am AppModule) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} + +// AppModuleSimulation functions + +// GenerateGenesisState creates a randomized GenState of the 29-fee module. +func (AppModule) GenerateGenesisState(_ *module.SimulationState) { +} + +// ProposalContents doesn't return any content functions for governance proposals. +func (AppModule) ProposalContents(_ module.SimulationState) []simtypes.WeightedProposalContent { + return nil +} + +// RandomizedParams creates randomized ibc-29-fee param changes for the simulator. +func (AppModule) RandomizedParams(_ *rand.Rand) []simtypes.ParamChange { + return nil +} + +// RegisterStoreDecoder registers a decoder for 29-fee module's types +func (am AppModule) RegisterStoreDecoder(_ sdk.StoreDecoderRegistry) { +} + +// WeightedOperations returns the all the 29-fee module operations with their respective weights. +func (am AppModule) WeightedOperations(_ module.SimulationState) []simtypes.WeightedOperation { + return nil +} diff --git a/modules/apps/29-fee/transfer_test.go b/modules/apps/29-fee/transfer_test.go new file mode 100644 index 00000000000..9d7557fd6c4 --- /dev/null +++ b/modules/apps/29-fee/transfer_test.go @@ -0,0 +1,72 @@ +package fee_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" + transfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + ibctesting "github.com/cosmos/ibc-go/v3/testing" +) + +// Integration test to ensure ics29 works with ics20 +func (suite *FeeTestSuite) TestFeeTransfer() { + path := ibctesting.NewPath(suite.chainA, suite.chainB) + feeTransferVersion := string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: transfertypes.Version})) + path.EndpointA.ChannelConfig.Version = feeTransferVersion + path.EndpointB.ChannelConfig.Version = feeTransferVersion + path.EndpointA.ChannelConfig.PortID = transfertypes.PortID + path.EndpointB.ChannelConfig.PortID = transfertypes.PortID + + suite.coordinator.Setup(path) + + // set up coin & ics20 packet + coin := ibctesting.TestCoin + fee := types.Fee{ + RecvFee: defaultRecvFee, + AckFee: defaultAckFee, + TimeoutFee: defaultTimeoutFee, + } + + msgs := []sdk.Msg{ + types.NewMsgPayPacketFee(fee, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, suite.chainA.SenderAccount.GetAddress().String(), nil), + transfertypes.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, coin, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), clienttypes.NewHeight(0, 100), 0), + } + res, err := suite.chainA.SendMsgs(msgs...) + suite.Require().NoError(err) // message committed + + // after incentivizing the packets + originalChainASenderAccountBalance := sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), ibctesting.TestCoin.Denom)) + + packet, err := ibctesting.ParsePacketFromEvents(res.GetEvents()) + suite.Require().NoError(err) + + // register counterparty address on chainB + // relayerAddress is address of sender account on chainB, but we will use it on chainA + // to differentiate from the chainA.SenderAccount for checking successful relay payouts + relayerAddress := suite.chainB.SenderAccount.GetAddress() + + msgRegister := types.NewMsgRegisterCounterpartyAddress(suite.chainB.SenderAccount.GetAddress().String(), relayerAddress.String(), ibctesting.FirstChannelID) + _, err = suite.chainB.SendMsgs(msgRegister) + suite.Require().NoError(err) // message committed + + // relay packet + err = path.RelayPacket(packet) + suite.Require().NoError(err) // relay committed + + // ensure relayers got paid + // relayer for forward relay: chainB.SenderAccount + // relayer for reverse relay: chainA.SenderAccount + + // check forward relay balance + suite.Require().Equal( + fee.RecvFee, + sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainB.SenderAccount.GetAddress(), ibctesting.TestCoin.Denom)), + ) + + suite.Require().Equal( + fee.AckFee.Add(fee.TimeoutFee...), // ack fee paid, timeout fee refunded + sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), ibctesting.TestCoin.Denom)).Sub(originalChainASenderAccountBalance), + ) + +} diff --git a/modules/apps/29-fee/types/ack.go b/modules/apps/29-fee/types/ack.go new file mode 100644 index 00000000000..229d8e4cc3f --- /dev/null +++ b/modules/apps/29-fee/types/ack.go @@ -0,0 +1,27 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// NewIncentivizedAcknowledgement creates a new instance of IncentivizedAcknowledgement +func NewIncentivizedAcknowledgement(relayer string, ack []byte, success bool) IncentivizedAcknowledgement { + return IncentivizedAcknowledgement{ + Result: ack, + ForwardRelayerAddress: relayer, + UnderlyingAppSuccess: success, + } +} + +// Success implements the Acknowledgement interface. The acknowledgement is +// considered successful if the forward relayer address is empty. Otherwise it is +// considered a failed acknowledgement. +func (ack IncentivizedAcknowledgement) Success() bool { + return ack.UnderlyingAppSuccess +} + +// Acknowledgement implements the Acknowledgement interface. It returns the +// acknowledgement serialised using JSON. +func (ack IncentivizedAcknowledgement) Acknowledgement() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&ack)) +} diff --git a/modules/apps/29-fee/types/ack.pb.go b/modules/apps/29-fee/types/ack.pb.go new file mode 100644 index 00000000000..4f6437da224 --- /dev/null +++ b/modules/apps/29-fee/types/ack.pb.go @@ -0,0 +1,423 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/applications/fee/v1/ack.proto + +package types + +import ( + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// IncentivizedAcknowledgement is the acknowledgement format to be used by applications wrapped in the fee middleware +type IncentivizedAcknowledgement struct { + // the underlying app acknowledgement result bytes + Result []byte `protobuf:"bytes,1,opt,name=result,proto3" json:"result,omitempty"` + // the relayer address which submits the recv packet message + ForwardRelayerAddress string `protobuf:"bytes,2,opt,name=forward_relayer_address,json=forwardRelayerAddress,proto3" json:"forward_relayer_address,omitempty" yaml:"forward_relayer_address"` + // success flag of the base application callback + UnderlyingAppSuccess bool `protobuf:"varint,3,opt,name=underlying_app_success,json=underlyingAppSuccess,proto3" json:"underlying_app_success,omitempty" yaml:"underlying_app_successl"` +} + +func (m *IncentivizedAcknowledgement) Reset() { *m = IncentivizedAcknowledgement{} } +func (m *IncentivizedAcknowledgement) String() string { return proto.CompactTextString(m) } +func (*IncentivizedAcknowledgement) ProtoMessage() {} +func (*IncentivizedAcknowledgement) Descriptor() ([]byte, []int) { + return fileDescriptor_ab2834946fb65ea4, []int{0} +} +func (m *IncentivizedAcknowledgement) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *IncentivizedAcknowledgement) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_IncentivizedAcknowledgement.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 *IncentivizedAcknowledgement) XXX_Merge(src proto.Message) { + xxx_messageInfo_IncentivizedAcknowledgement.Merge(m, src) +} +func (m *IncentivizedAcknowledgement) XXX_Size() int { + return m.Size() +} +func (m *IncentivizedAcknowledgement) XXX_DiscardUnknown() { + xxx_messageInfo_IncentivizedAcknowledgement.DiscardUnknown(m) +} + +var xxx_messageInfo_IncentivizedAcknowledgement proto.InternalMessageInfo + +func (m *IncentivizedAcknowledgement) GetResult() []byte { + if m != nil { + return m.Result + } + return nil +} + +func (m *IncentivizedAcknowledgement) GetForwardRelayerAddress() string { + if m != nil { + return m.ForwardRelayerAddress + } + return "" +} + +func (m *IncentivizedAcknowledgement) GetUnderlyingAppSuccess() bool { + if m != nil { + return m.UnderlyingAppSuccess + } + return false +} + +func init() { + proto.RegisterType((*IncentivizedAcknowledgement)(nil), "ibc.applications.fee.v1.IncentivizedAcknowledgement") +} + +func init() { proto.RegisterFile("ibc/applications/fee/v1/ack.proto", fileDescriptor_ab2834946fb65ea4) } + +var fileDescriptor_ab2834946fb65ea4 = []byte{ + // 315 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x90, 0xb1, 0x4e, 0xf3, 0x30, + 0x14, 0x85, 0xeb, 0xff, 0x97, 0x2a, 0x88, 0x98, 0xa2, 0xd2, 0x56, 0x20, 0x85, 0x92, 0xa9, 0x4b, + 0x63, 0x95, 0x8a, 0x01, 0xb6, 0x76, 0x63, 0x42, 0x0a, 0x0b, 0xea, 0x12, 0x39, 0xf6, 0x6d, 0xb0, + 0xea, 0xd8, 0x96, 0xed, 0xa4, 0x0a, 0x4f, 0xc1, 0x63, 0x31, 0x76, 0x64, 0x42, 0xa8, 0x1d, 0xd9, + 0x78, 0x02, 0x94, 0xa6, 0x12, 0x1d, 0x60, 0xbb, 0xf7, 0x9c, 0x4f, 0x67, 0xf8, 0xbc, 0x4b, 0x9e, + 0x52, 0x4c, 0xb4, 0x16, 0x9c, 0x12, 0xc7, 0x95, 0xb4, 0x78, 0x01, 0x80, 0xcb, 0x31, 0x26, 0x74, + 0x19, 0x69, 0xa3, 0x9c, 0xf2, 0x7b, 0x3c, 0xa5, 0xd1, 0x21, 0x12, 0x2d, 0x00, 0xa2, 0x72, 0x7c, + 0xd6, 0xc9, 0x54, 0xa6, 0x76, 0x0c, 0xae, 0xaf, 0x06, 0x0f, 0x3f, 0x91, 0x77, 0x7e, 0x27, 0x29, + 0x48, 0xc7, 0x4b, 0xfe, 0x0c, 0x6c, 0x4a, 0x97, 0x52, 0xad, 0x04, 0xb0, 0x0c, 0x72, 0x90, 0xce, + 0xef, 0x7a, 0x6d, 0x03, 0xb6, 0x10, 0xae, 0x8f, 0x06, 0x68, 0x78, 0x12, 0xef, 0x3f, 0x7f, 0xee, + 0xf5, 0x16, 0xca, 0xac, 0x88, 0x61, 0x89, 0x01, 0x41, 0x2a, 0x30, 0x09, 0x61, 0xcc, 0x80, 0xb5, + 0xfd, 0x7f, 0x03, 0x34, 0x3c, 0x9e, 0x85, 0x5f, 0xef, 0x17, 0x41, 0x45, 0x72, 0x71, 0x1b, 0xfe, + 0x01, 0x86, 0xf1, 0xe9, 0xbe, 0x89, 0x9b, 0x62, 0xda, 0xe4, 0xfe, 0xa3, 0xd7, 0x2d, 0x24, 0x03, + 0x23, 0x2a, 0x2e, 0xb3, 0x84, 0x68, 0x9d, 0xd8, 0x82, 0xd2, 0x7a, 0xfa, 0xff, 0x00, 0x0d, 0x8f, + 0x0e, 0xa7, 0x7f, 0xe7, 0x44, 0x18, 0x77, 0x7e, 0x9a, 0xa9, 0xd6, 0x0f, 0x4d, 0x3e, 0xbb, 0x7f, + 0xdd, 0x04, 0x68, 0xbd, 0x09, 0xd0, 0xc7, 0x26, 0x40, 0x2f, 0xdb, 0xa0, 0xb5, 0xde, 0x06, 0xad, + 0xb7, 0x6d, 0xd0, 0x9a, 0x5f, 0x67, 0xdc, 0x3d, 0x15, 0x69, 0x44, 0x55, 0x8e, 0xa9, 0xb2, 0xb9, + 0xb2, 0x98, 0xa7, 0x74, 0x94, 0x29, 0x5c, 0x4e, 0x70, 0xae, 0x58, 0x21, 0xc0, 0xd6, 0xe6, 0x2d, + 0xbe, 0xba, 0x19, 0xd5, 0xd2, 0x5d, 0xa5, 0xc1, 0xa6, 0xed, 0x9d, 0xc5, 0xc9, 0x77, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x2e, 0x38, 0x5f, 0x80, 0x99, 0x01, 0x00, 0x00, +} + +func (m *IncentivizedAcknowledgement) 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 *IncentivizedAcknowledgement) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *IncentivizedAcknowledgement) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.UnderlyingAppSuccess { + i-- + if m.UnderlyingAppSuccess { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if len(m.ForwardRelayerAddress) > 0 { + i -= len(m.ForwardRelayerAddress) + copy(dAtA[i:], m.ForwardRelayerAddress) + i = encodeVarintAck(dAtA, i, uint64(len(m.ForwardRelayerAddress))) + i-- + dAtA[i] = 0x12 + } + if len(m.Result) > 0 { + i -= len(m.Result) + copy(dAtA[i:], m.Result) + i = encodeVarintAck(dAtA, i, uint64(len(m.Result))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintAck(dAtA []byte, offset int, v uint64) int { + offset -= sovAck(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *IncentivizedAcknowledgement) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Result) + if l > 0 { + n += 1 + l + sovAck(uint64(l)) + } + l = len(m.ForwardRelayerAddress) + if l > 0 { + n += 1 + l + sovAck(uint64(l)) + } + if m.UnderlyingAppSuccess { + n += 2 + } + return n +} + +func sovAck(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozAck(x uint64) (n int) { + return sovAck(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *IncentivizedAcknowledgement) 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 ErrIntOverflowAck + } + 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: IncentivizedAcknowledgement: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: IncentivizedAcknowledgement: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Result", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAck + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthAck + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthAck + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Result = append(m.Result[:0], dAtA[iNdEx:postIndex]...) + if m.Result == nil { + m.Result = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ForwardRelayerAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAck + } + 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 ErrInvalidLengthAck + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAck + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ForwardRelayerAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UnderlyingAppSuccess", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAck + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.UnderlyingAppSuccess = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipAck(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAck + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipAck(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAck + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAck + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAck + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthAck + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupAck + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthAck + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthAck = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowAck = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupAck = fmt.Errorf("proto: unexpected end of group") +) diff --git a/modules/apps/29-fee/types/codec.go b/modules/apps/29-fee/types/codec.go new file mode 100644 index 00000000000..871f148b043 --- /dev/null +++ b/modules/apps/29-fee/types/codec.go @@ -0,0 +1,48 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" +) + +// RegisterLegacyAminoCodec registers the necessary x/ibc 29-fee interfaces and concrete types +// on the provided LegacyAmino codec. These types are used for Amino JSON serialization. +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(&MsgRegisterCounterpartyAddress{}, "cosmos-sdk/MsgRegisterCounterpartyAddress", nil) + cdc.RegisterConcrete(&MsgPayPacketFee{}, "cosmos-sdk/MsgPayPacketFee", nil) + cdc.RegisterConcrete(&MsgPayPacketFeeAsync{}, "cosmos-sdk/MsgPayPacketFeeAsync", nil) +} + +// RegisterInterfaces register the 29-fee module interfaces to protobuf +// Any. +func RegisterInterfaces(registry codectypes.InterfaceRegistry) { + registry.RegisterImplementations( + (*sdk.Msg)(nil), + &MsgRegisterCounterpartyAddress{}, + &MsgPayPacketFee{}, + &MsgPayPacketFeeAsync{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +var ( + amino = codec.NewLegacyAmino() + + // ModuleCdc references the global x/ibc-transfer module codec. Note, the codec + // should ONLY be used in certain instances of tests and for JSON encoding. + // + // The actual codec used for serialization should be provided to x/ibc transfer and + // defined at the application level. + ModuleCdc = codec.NewProtoCodec(codectypes.NewInterfaceRegistry()) + + // AminoCdc is a amino codec created to support amino json compatible msgs. + AminoCdc = codec.NewAminoCodec(amino) +) + +func init() { + RegisterLegacyAminoCodec(amino) + amino.Seal() +} diff --git a/modules/apps/29-fee/types/errors.go b/modules/apps/29-fee/types/errors.go new file mode 100644 index 00000000000..812db0a43a4 --- /dev/null +++ b/modules/apps/29-fee/types/errors.go @@ -0,0 +1,19 @@ +package types + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// 29-fee sentinel errors +var ( + ErrInvalidVersion = sdkerrors.Register(ModuleName, 2, "invalid ICS29 middleware version") + ErrRefundAccNotFound = sdkerrors.Register(ModuleName, 3, "no account found for given refund address") + ErrBalanceNotFound = sdkerrors.Register(ModuleName, 4, "balance not found for given account address") + ErrFeeNotFound = sdkerrors.Register(ModuleName, 5, "there is no fee escrowed for the given packetID") + ErrRelayersNotNil = sdkerrors.Register(ModuleName, 6, "relayers must be nil. This feature is not supported") + ErrCounterpartyAddressEmpty = sdkerrors.Register(ModuleName, 7, "counterparty address must not be empty") + ErrForwardRelayerAddressNotFound = sdkerrors.Register(ModuleName, 8, "forward relayer address not found") + ErrFeeNotEnabled = sdkerrors.Register(ModuleName, 9, "fee module is not enabled for this channel. If this error occurs after channel setup, fee module may not be enabled") + ErrRelayerNotFoundForAsyncAck = sdkerrors.Register(ModuleName, 10, "relayer address must be stored for async WriteAcknowledgement") + ErrFeeModuleLocked = sdkerrors.Register(ModuleName, 11, "the fee module is currently locked, a severe bug has been detected") +) diff --git a/modules/apps/29-fee/types/events.go b/modules/apps/29-fee/types/events.go new file mode 100644 index 00000000000..cac882d98d3 --- /dev/null +++ b/modules/apps/29-fee/types/events.go @@ -0,0 +1,10 @@ +package types + +// 29-fee events +const ( + EventTypeIncentivizedPacket = "incentivized_ibc_packet" + + AttributeKeyRecvFee = "recv_fee" + AttributeKeyAckFee = "ack_fee" + AttributeKeyTimeoutFee = "timeout_fee" +) diff --git a/modules/apps/29-fee/types/expected_keepers.go b/modules/apps/29-fee/types/expected_keepers.go new file mode 100644 index 00000000000..1d9d3439b07 --- /dev/null +++ b/modules/apps/29-fee/types/expected_keepers.go @@ -0,0 +1,42 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/types" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + ibcexported "github.com/cosmos/ibc-go/v3/modules/core/exported" +) + +// AccountKeeper defines the contract required for account APIs. +type AccountKeeper interface { + GetModuleAddress(name string) sdk.AccAddress + GetAccount(sdk.Context, sdk.AccAddress) types.AccountI +} + +// ICS4Wrapper defines the expected ICS4Wrapper for middleware +type ICS4Wrapper interface { + WriteAcknowledgement(ctx sdk.Context, chanCap *capabilitytypes.Capability, packet ibcexported.PacketI, acknowledgement ibcexported.Acknowledgement) error + SendPacket(ctx sdk.Context, channelCap *capabilitytypes.Capability, packet ibcexported.PacketI) error + GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) +} + +// ChannelKeeper defines the expected IBC channel keeper +type ChannelKeeper interface { + GetChannel(ctx sdk.Context, srcPort, srcChan string) (channel channeltypes.Channel, found bool) + GetNextSequenceSend(ctx sdk.Context, portID, channelID string) (uint64, bool) +} + +// PortKeeper defines the expected IBC port keeper +type PortKeeper interface { + BindPort(ctx sdk.Context, portID string) *capabilitytypes.Capability +} + +// BankKeeper defines the expected bank keeper +type BankKeeper interface { + HasBalance(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coin) bool + SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error + SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error + SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error + BlockedAddr(sdk.AccAddress) bool +} diff --git a/modules/apps/29-fee/types/fee.go b/modules/apps/29-fee/types/fee.go new file mode 100644 index 00000000000..1f979c732d8 --- /dev/null +++ b/modules/apps/29-fee/types/fee.go @@ -0,0 +1,92 @@ +package types + +import ( + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" +) + +// NewPacketFee creates and returns a new PacketFee struct including the incentivization fees, refund addres and relayers +func NewPacketFee(fee Fee, refundAddr string, relayers []string) PacketFee { + return PacketFee{ + Fee: fee, + RefundAddress: refundAddr, + Relayers: relayers, + } +} + +// Validate performs basic stateless validation of the associated PacketFee +func (p PacketFee) Validate() error { + _, err := sdk.AccAddressFromBech32(p.RefundAddress) + if err != nil { + return sdkerrors.Wrap(err, "failed to convert RefundAddress into sdk.AccAddress") + } + + // enforce relayer is nil + if p.Relayers != nil { + return ErrRelayersNotNil + } + + if err := p.Fee.Validate(); err != nil { + return err + } + + return nil +} + +// NewPacketFees creates and returns a new PacketFees struct including a list of type PacketFee +func NewPacketFees(packetFees []PacketFee) PacketFees { + return PacketFees{ + PacketFees: packetFees, + } +} + +// NewIdentifiedPacketFees creates and returns a new IdentifiedPacketFees struct containing a packet ID and packet fees +func NewIdentifiedPacketFees(packetID channeltypes.PacketId, packetFees []PacketFee) IdentifiedPacketFees { + return IdentifiedPacketFees{ + PacketId: packetID, + PacketFees: packetFees, + } +} + +// NewFee creates and returns a new Fee struct encapsulating the receive, acknowledgement and timeout fees as sdk.Coins +func NewFee(recvFee, ackFee, timeoutFee sdk.Coins) Fee { + return Fee{ + RecvFee: recvFee, + AckFee: ackFee, + TimeoutFee: timeoutFee, + } +} + +// Total returns the total amount for a given Fee +func (f Fee) Total() sdk.Coins { + return f.RecvFee.Add(f.AckFee...).Add(f.TimeoutFee...) +} + +// Validate asserts that each Fee is valid and all three Fees are not empty or zero +func (fee Fee) Validate() error { + var errFees []string + if !fee.AckFee.IsValid() { + errFees = append(errFees, "ack fee invalid") + } + if !fee.RecvFee.IsValid() { + errFees = append(errFees, "recv fee invalid") + } + if !fee.TimeoutFee.IsValid() { + errFees = append(errFees, "timeout fee invalid") + } + + if len(errFees) > 0 { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "contains invalid fees: %s", strings.Join(errFees, " , ")) + } + + // if all three fee's are zero or empty return an error + if fee.AckFee.IsZero() && fee.RecvFee.IsZero() && fee.TimeoutFee.IsZero() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, "all fees are zero") + } + + return nil +} diff --git a/modules/apps/29-fee/types/fee.pb.go b/modules/apps/29-fee/types/fee.pb.go new file mode 100644 index 00000000000..1867eb351e5 --- /dev/null +++ b/modules/apps/29-fee/types/fee.pb.go @@ -0,0 +1,1182 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/applications/fee/v1/fee.proto + +package types + +import ( + fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + types1 "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// Fee defines the ICS29 receive, acknowledgement and timeout fees +type Fee struct { + // the packet receive fee + RecvFee github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=recv_fee,json=recvFee,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"recv_fee" yaml:"recv_fee"` + // the packet acknowledgement fee + AckFee github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,2,rep,name=ack_fee,json=ackFee,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"ack_fee" yaml:"ack_fee"` + // the packet timeout fee + TimeoutFee github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=timeout_fee,json=timeoutFee,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"timeout_fee" yaml:"timeout_fee"` +} + +func (m *Fee) Reset() { *m = Fee{} } +func (m *Fee) String() string { return proto.CompactTextString(m) } +func (*Fee) ProtoMessage() {} +func (*Fee) Descriptor() ([]byte, []int) { + return fileDescriptor_cb3319f1af2a53e5, []int{0} +} +func (m *Fee) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Fee) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Fee.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 *Fee) XXX_Merge(src proto.Message) { + xxx_messageInfo_Fee.Merge(m, src) +} +func (m *Fee) XXX_Size() int { + return m.Size() +} +func (m *Fee) XXX_DiscardUnknown() { + xxx_messageInfo_Fee.DiscardUnknown(m) +} + +var xxx_messageInfo_Fee proto.InternalMessageInfo + +func (m *Fee) GetRecvFee() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.RecvFee + } + return nil +} + +func (m *Fee) GetAckFee() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.AckFee + } + return nil +} + +func (m *Fee) GetTimeoutFee() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.TimeoutFee + } + return nil +} + +// PacketFee contains ICS29 relayer fees, refund address and optional list of permitted relayers +type PacketFee struct { + // fee encapsulates the recv, ack and timeout fees associated with an IBC packet + Fee Fee `protobuf:"bytes,1,opt,name=fee,proto3" json:"fee"` + // the refund address for unspent fees + RefundAddress string `protobuf:"bytes,2,opt,name=refund_address,json=refundAddress,proto3" json:"refund_address,omitempty" yaml:"refund_address"` + // optional list of relayers permitted to receive fees + Relayers []string `protobuf:"bytes,3,rep,name=relayers,proto3" json:"relayers,omitempty"` +} + +func (m *PacketFee) Reset() { *m = PacketFee{} } +func (m *PacketFee) String() string { return proto.CompactTextString(m) } +func (*PacketFee) ProtoMessage() {} +func (*PacketFee) Descriptor() ([]byte, []int) { + return fileDescriptor_cb3319f1af2a53e5, []int{1} +} +func (m *PacketFee) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PacketFee) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PacketFee.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 *PacketFee) XXX_Merge(src proto.Message) { + xxx_messageInfo_PacketFee.Merge(m, src) +} +func (m *PacketFee) XXX_Size() int { + return m.Size() +} +func (m *PacketFee) XXX_DiscardUnknown() { + xxx_messageInfo_PacketFee.DiscardUnknown(m) +} + +var xxx_messageInfo_PacketFee proto.InternalMessageInfo + +func (m *PacketFee) GetFee() Fee { + if m != nil { + return m.Fee + } + return Fee{} +} + +func (m *PacketFee) GetRefundAddress() string { + if m != nil { + return m.RefundAddress + } + return "" +} + +func (m *PacketFee) GetRelayers() []string { + if m != nil { + return m.Relayers + } + return nil +} + +// PacketFees contains a list of type PacketFee +type PacketFees struct { + // list of packet fees + PacketFees []PacketFee `protobuf:"bytes,1,rep,name=packet_fees,json=packetFees,proto3" json:"packet_fees" yaml:"packet_fees"` +} + +func (m *PacketFees) Reset() { *m = PacketFees{} } +func (m *PacketFees) String() string { return proto.CompactTextString(m) } +func (*PacketFees) ProtoMessage() {} +func (*PacketFees) Descriptor() ([]byte, []int) { + return fileDescriptor_cb3319f1af2a53e5, []int{2} +} +func (m *PacketFees) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PacketFees) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PacketFees.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 *PacketFees) XXX_Merge(src proto.Message) { + xxx_messageInfo_PacketFees.Merge(m, src) +} +func (m *PacketFees) XXX_Size() int { + return m.Size() +} +func (m *PacketFees) XXX_DiscardUnknown() { + xxx_messageInfo_PacketFees.DiscardUnknown(m) +} + +var xxx_messageInfo_PacketFees proto.InternalMessageInfo + +func (m *PacketFees) GetPacketFees() []PacketFee { + if m != nil { + return m.PacketFees + } + return nil +} + +// IdentifiedPacketFees contains a list of type PacketFee and associated PacketId +type IdentifiedPacketFees struct { + // unique packet identifier comprised of the channel ID, port ID and sequence + PacketId types1.PacketId `protobuf:"bytes,1,opt,name=packet_id,json=packetId,proto3" json:"packet_id" yaml:"packet_id"` + // list of packet fees + PacketFees []PacketFee `protobuf:"bytes,2,rep,name=packet_fees,json=packetFees,proto3" json:"packet_fees" yaml:"packet_fees"` +} + +func (m *IdentifiedPacketFees) Reset() { *m = IdentifiedPacketFees{} } +func (m *IdentifiedPacketFees) String() string { return proto.CompactTextString(m) } +func (*IdentifiedPacketFees) ProtoMessage() {} +func (*IdentifiedPacketFees) Descriptor() ([]byte, []int) { + return fileDescriptor_cb3319f1af2a53e5, []int{3} +} +func (m *IdentifiedPacketFees) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *IdentifiedPacketFees) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_IdentifiedPacketFees.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 *IdentifiedPacketFees) XXX_Merge(src proto.Message) { + xxx_messageInfo_IdentifiedPacketFees.Merge(m, src) +} +func (m *IdentifiedPacketFees) XXX_Size() int { + return m.Size() +} +func (m *IdentifiedPacketFees) XXX_DiscardUnknown() { + xxx_messageInfo_IdentifiedPacketFees.DiscardUnknown(m) +} + +var xxx_messageInfo_IdentifiedPacketFees proto.InternalMessageInfo + +func (m *IdentifiedPacketFees) GetPacketId() types1.PacketId { + if m != nil { + return m.PacketId + } + return types1.PacketId{} +} + +func (m *IdentifiedPacketFees) GetPacketFees() []PacketFee { + if m != nil { + return m.PacketFees + } + return nil +} + +func init() { + proto.RegisterType((*Fee)(nil), "ibc.applications.fee.v1.Fee") + proto.RegisterType((*PacketFee)(nil), "ibc.applications.fee.v1.PacketFee") + proto.RegisterType((*PacketFees)(nil), "ibc.applications.fee.v1.PacketFees") + proto.RegisterType((*IdentifiedPacketFees)(nil), "ibc.applications.fee.v1.IdentifiedPacketFees") +} + +func init() { proto.RegisterFile("ibc/applications/fee/v1/fee.proto", fileDescriptor_cb3319f1af2a53e5) } + +var fileDescriptor_cb3319f1af2a53e5 = []byte{ + // 525 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x94, 0x31, 0x6f, 0x13, 0x31, + 0x14, 0xc7, 0x73, 0x09, 0x6a, 0x1b, 0x47, 0x14, 0x74, 0x2a, 0x22, 0x8d, 0xe0, 0x52, 0x3c, 0x65, + 0x89, 0xad, 0xa4, 0x30, 0xc0, 0x04, 0x57, 0x29, 0x52, 0x27, 0xd0, 0x89, 0x89, 0x25, 0xf2, 0xd9, + 0x2f, 0xa9, 0x95, 0xdc, 0xf9, 0x74, 0xbe, 0x44, 0xca, 0xca, 0x27, 0xe0, 0x1b, 0xb0, 0xf3, 0x49, + 0xba, 0x20, 0x75, 0x64, 0x0a, 0x28, 0xf9, 0x06, 0xdd, 0x91, 0x90, 0x7d, 0x4e, 0x94, 0x82, 0xaa, + 0xaa, 0x12, 0xd3, 0xf9, 0xd9, 0xef, 0xef, 0xdf, 0xb3, 0xdf, 0xff, 0x8c, 0x5e, 0xc8, 0x98, 0x53, + 0x96, 0x65, 0x53, 0xc9, 0x59, 0x21, 0x55, 0xaa, 0xe9, 0x08, 0x80, 0xce, 0x7b, 0xe6, 0x43, 0xb2, + 0x5c, 0x15, 0xca, 0x7f, 0x2a, 0x63, 0x4e, 0x76, 0x53, 0x88, 0x59, 0x9b, 0xf7, 0x5a, 0x01, 0x57, + 0x3a, 0x51, 0x9a, 0xc6, 0x4c, 0x1b, 0x49, 0x0c, 0x05, 0xeb, 0x51, 0xae, 0x64, 0x5a, 0x0a, 0x5b, + 0x47, 0x63, 0x35, 0x56, 0x76, 0x48, 0xcd, 0xc8, 0xcd, 0x5a, 0x22, 0x57, 0x39, 0x50, 0x7e, 0xc1, + 0xd2, 0x14, 0xa6, 0x86, 0xe6, 0x86, 0x65, 0x0a, 0xfe, 0x5d, 0x45, 0xb5, 0x01, 0x80, 0xbf, 0x40, + 0x07, 0x39, 0xf0, 0xf9, 0x70, 0x04, 0xd0, 0xf4, 0x4e, 0x6a, 0x9d, 0x46, 0xff, 0x98, 0x94, 0x4c, + 0x62, 0x98, 0xc4, 0x31, 0xc9, 0x99, 0x92, 0x69, 0x78, 0x76, 0xb9, 0x6c, 0x57, 0xae, 0x97, 0xed, + 0x47, 0x0b, 0x96, 0x4c, 0xdf, 0xe0, 0x8d, 0x10, 0x7f, 0xfb, 0xd9, 0xee, 0x8c, 0x65, 0x71, 0x31, + 0x8b, 0x09, 0x57, 0x09, 0x75, 0x35, 0x97, 0x9f, 0xae, 0x16, 0x13, 0x5a, 0x2c, 0x32, 0xd0, 0x76, + 0x0f, 0x1d, 0xed, 0x1b, 0x99, 0x41, 0xcf, 0xd1, 0x3e, 0xe3, 0x13, 0x4b, 0xae, 0xde, 0x45, 0x0e, + 0x1d, 0xf9, 0xb0, 0x24, 0x3b, 0xdd, 0xfd, 0xc0, 0x7b, 0x8c, 0x4f, 0x0c, 0xf7, 0xb3, 0x87, 0x1a, + 0x85, 0x4c, 0x40, 0xcd, 0x0a, 0x0b, 0xaf, 0xdd, 0x05, 0x1f, 0x38, 0xb8, 0x5f, 0xc2, 0x77, 0xb4, + 0xf7, 0x2b, 0x00, 0x39, 0xe5, 0x00, 0x00, 0x7f, 0xf5, 0x50, 0xfd, 0x03, 0xe3, 0x13, 0x30, 0x91, + 0xff, 0x12, 0xd5, 0xca, 0x06, 0x78, 0x9d, 0x46, 0xff, 0x19, 0xb9, 0xc5, 0x0d, 0x64, 0x00, 0x10, + 0x3e, 0x30, 0xc5, 0x44, 0x26, 0xdd, 0x7f, 0x8b, 0x0e, 0x73, 0x18, 0xcd, 0x52, 0x31, 0x64, 0x42, + 0xe4, 0xa0, 0x75, 0xb3, 0x7a, 0xe2, 0x75, 0xea, 0xe1, 0xf1, 0xf5, 0xb2, 0xfd, 0x64, 0xd3, 0xa2, + 0xdd, 0x75, 0x1c, 0x3d, 0x2c, 0x27, 0xde, 0x95, 0xb1, 0xdf, 0x32, 0xdd, 0x9f, 0xb2, 0x05, 0xe4, + 0xda, 0x5e, 0x43, 0x3d, 0xda, 0xc6, 0x38, 0x41, 0x68, 0x5b, 0xa0, 0xf6, 0x87, 0xa8, 0x91, 0xd9, + 0xc8, 0x1c, 0x5b, 0x3b, 0xab, 0xe0, 0x5b, 0x2b, 0xdd, 0x2a, 0xc3, 0xd6, 0xcd, 0xcb, 0xdb, 0xd9, + 0x04, 0x47, 0x28, 0xdb, 0x02, 0xf0, 0x77, 0x0f, 0x1d, 0x9d, 0x0b, 0x48, 0x0b, 0x39, 0x92, 0x20, + 0x76, 0xc8, 0x1f, 0x51, 0xdd, 0x89, 0xa4, 0x70, 0x37, 0xf4, 0xdc, 0x72, 0x8d, 0xc1, 0xc9, 0xc6, + 0xd5, 0x5b, 0xe6, 0xb9, 0x08, 0x9b, 0x0e, 0xf9, 0xf8, 0x06, 0x52, 0x0a, 0x1c, 0x1d, 0x64, 0x2e, + 0xe7, 0xef, 0xf3, 0x54, 0xff, 0xf7, 0x79, 0xc2, 0xf7, 0x97, 0xab, 0xc0, 0xbb, 0x5a, 0x05, 0xde, + 0xaf, 0x55, 0xe0, 0x7d, 0x59, 0x07, 0x95, 0xab, 0x75, 0x50, 0xf9, 0xb1, 0x0e, 0x2a, 0x9f, 0x5e, + 0xfd, 0x6b, 0x18, 0x19, 0xf3, 0xee, 0x58, 0xd1, 0xf9, 0x29, 0x4d, 0x94, 0x98, 0x4d, 0x41, 0x9b, + 0xf7, 0x42, 0xd3, 0xfe, 0xeb, 0xae, 0x79, 0x2a, 0xac, 0x87, 0xe2, 0x3d, 0xfb, 0xe3, 0x9e, 0xfe, + 0x09, 0x00, 0x00, 0xff, 0xff, 0xc1, 0x6c, 0xa2, 0x41, 0x4f, 0x04, 0x00, 0x00, +} + +func (m *Fee) 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 *Fee) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Fee) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.TimeoutFee) > 0 { + for iNdEx := len(m.TimeoutFee) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.TimeoutFee[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintFee(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.AckFee) > 0 { + for iNdEx := len(m.AckFee) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.AckFee[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintFee(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.RecvFee) > 0 { + for iNdEx := len(m.RecvFee) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.RecvFee[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintFee(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *PacketFee) 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 *PacketFee) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PacketFee) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Relayers) > 0 { + for iNdEx := len(m.Relayers) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Relayers[iNdEx]) + copy(dAtA[i:], m.Relayers[iNdEx]) + i = encodeVarintFee(dAtA, i, uint64(len(m.Relayers[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if len(m.RefundAddress) > 0 { + i -= len(m.RefundAddress) + copy(dAtA[i:], m.RefundAddress) + i = encodeVarintFee(dAtA, i, uint64(len(m.RefundAddress))) + i-- + dAtA[i] = 0x12 + } + { + size, err := m.Fee.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintFee(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *PacketFees) 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 *PacketFees) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PacketFees) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.PacketFees) > 0 { + for iNdEx := len(m.PacketFees) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.PacketFees[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintFee(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *IdentifiedPacketFees) 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 *IdentifiedPacketFees) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *IdentifiedPacketFees) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.PacketFees) > 0 { + for iNdEx := len(m.PacketFees) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.PacketFees[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintFee(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + { + size, err := m.PacketId.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintFee(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func encodeVarintFee(dAtA []byte, offset int, v uint64) int { + offset -= sovFee(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Fee) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.RecvFee) > 0 { + for _, e := range m.RecvFee { + l = e.Size() + n += 1 + l + sovFee(uint64(l)) + } + } + if len(m.AckFee) > 0 { + for _, e := range m.AckFee { + l = e.Size() + n += 1 + l + sovFee(uint64(l)) + } + } + if len(m.TimeoutFee) > 0 { + for _, e := range m.TimeoutFee { + l = e.Size() + n += 1 + l + sovFee(uint64(l)) + } + } + return n +} + +func (m *PacketFee) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Fee.Size() + n += 1 + l + sovFee(uint64(l)) + l = len(m.RefundAddress) + if l > 0 { + n += 1 + l + sovFee(uint64(l)) + } + if len(m.Relayers) > 0 { + for _, s := range m.Relayers { + l = len(s) + n += 1 + l + sovFee(uint64(l)) + } + } + return n +} + +func (m *PacketFees) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.PacketFees) > 0 { + for _, e := range m.PacketFees { + l = e.Size() + n += 1 + l + sovFee(uint64(l)) + } + } + return n +} + +func (m *IdentifiedPacketFees) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.PacketId.Size() + n += 1 + l + sovFee(uint64(l)) + if len(m.PacketFees) > 0 { + for _, e := range m.PacketFees { + l = e.Size() + n += 1 + l + sovFee(uint64(l)) + } + } + return n +} + +func sovFee(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozFee(x uint64) (n int) { + return sovFee(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Fee) 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 ErrIntOverflowFee + } + 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: Fee: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Fee: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RecvFee", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFee + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthFee + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthFee + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RecvFee = append(m.RecvFee, types.Coin{}) + if err := m.RecvFee[len(m.RecvFee)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AckFee", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFee + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthFee + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthFee + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AckFee = append(m.AckFee, types.Coin{}) + if err := m.AckFee[len(m.AckFee)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TimeoutFee", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFee + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthFee + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthFee + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TimeoutFee = append(m.TimeoutFee, types.Coin{}) + if err := m.TimeoutFee[len(m.TimeoutFee)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipFee(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthFee + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PacketFee) 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 ErrIntOverflowFee + } + 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: PacketFee: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PacketFee: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Fee", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFee + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthFee + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthFee + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Fee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RefundAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFee + } + 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 ErrInvalidLengthFee + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthFee + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RefundAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Relayers", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFee + } + 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 ErrInvalidLengthFee + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthFee + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Relayers = append(m.Relayers, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipFee(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthFee + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PacketFees) 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 ErrIntOverflowFee + } + 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: PacketFees: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PacketFees: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PacketFees", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFee + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthFee + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthFee + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PacketFees = append(m.PacketFees, PacketFee{}) + if err := m.PacketFees[len(m.PacketFees)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipFee(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthFee + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *IdentifiedPacketFees) 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 ErrIntOverflowFee + } + 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: IdentifiedPacketFees: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: IdentifiedPacketFees: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PacketId", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFee + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthFee + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthFee + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.PacketId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PacketFees", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowFee + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthFee + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthFee + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PacketFees = append(m.PacketFees, PacketFee{}) + if err := m.PacketFees[len(m.PacketFees)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipFee(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthFee + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipFee(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowFee + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowFee + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowFee + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthFee + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupFee + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthFee + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthFee = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowFee = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupFee = fmt.Errorf("proto: unexpected end of group") +) diff --git a/modules/apps/29-fee/types/fee_test.go b/modules/apps/29-fee/types/fee_test.go new file mode 100644 index 00000000000..2b02e5b857b --- /dev/null +++ b/modules/apps/29-fee/types/fee_test.go @@ -0,0 +1,123 @@ +package types_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto/secp256k1" + + "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" +) + +var ( + // defaultRecvFee is the default packet receive fee used for testing purposes + defaultRecvFee = sdk.NewCoins(sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: sdk.NewInt(100)}) + + // defaultAckFee is the default packet acknowledgement fee used for testing purposes + defaultAckFee = sdk.NewCoins(sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: sdk.NewInt(200)}) + + // defaultTimeoutFee is the default packet timeout fee used for testing purposes + defaultTimeoutFee = sdk.NewCoins(sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: sdk.NewInt(300)}) + + // invalidFee is an invalid coin set used to trigger error cases for testing purposes + invalidFee = sdk.Coins{sdk.Coin{Denom: "invalid-denom", Amount: sdk.NewInt(-2)}} + + // defaultAccAddress is the default account used for testing purposes + defaultAccAddress = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()).String() +) + +func TestFeeTotal(t *testing.T) { + fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) + + total := fee.Total() + require.Equal(t, sdk.NewInt(600), total.AmountOf(sdk.DefaultBondDenom)) +} + +func TestPacketFeeValidation(t *testing.T) { + var ( + packetFee types.PacketFee + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() {}, + true, + }, + { + "should fail when refund address is invalid", + func() { + packetFee.RefundAddress = "invalid-address" + }, + false, + }, + { + "should fail when all fees are invalid", + func() { + packetFee.Fee.AckFee = invalidFee + packetFee.Fee.RecvFee = invalidFee + packetFee.Fee.TimeoutFee = invalidFee + }, + false, + }, + { + "should fail with single invalid fee", + func() { + packetFee.Fee.AckFee = invalidFee + }, + false, + }, + { + "should fail with two invalid fees", + func() { + packetFee.Fee.TimeoutFee = invalidFee + packetFee.Fee.AckFee = invalidFee + }, + false, + }, + { + "should pass with two empty fees", + func() { + packetFee.Fee.TimeoutFee = sdk.Coins{} + packetFee.Fee.AckFee = sdk.Coins{} + }, + true, + }, + { + "should pass with one empty fee", + func() { + packetFee.Fee.TimeoutFee = sdk.Coins{} + }, + true, + }, + { + "should fail if all fees are empty", + func() { + packetFee.Fee.AckFee = sdk.Coins{} + packetFee.Fee.RecvFee = sdk.Coins{} + packetFee.Fee.TimeoutFee = sdk.Coins{} + }, + false, + }, + } + + for _, tc := range testCases { + fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) + packetFee = types.NewPacketFee(fee, defaultAccAddress, nil) + + tc.malleate() // malleate mutates test data + + err := packetFee.Validate() + + if tc.expPass { + require.NoError(t, err) + } else { + require.Error(t, err) + } + } +} diff --git a/modules/apps/29-fee/types/genesis.go b/modules/apps/29-fee/types/genesis.go new file mode 100644 index 00000000000..be9299fcd94 --- /dev/null +++ b/modules/apps/29-fee/types/genesis.go @@ -0,0 +1,81 @@ +package types + +import ( + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + host "github.com/cosmos/ibc-go/v3/modules/core/24-host" +) + +// NewGenesisState creates a 29-fee GenesisState instance. +func NewGenesisState(identifiedFees []IdentifiedPacketFees, feeEnabledChannels []FeeEnabledChannel, registeredRelayers []RegisteredRelayerAddress, forwardRelayers []ForwardRelayerAddress) *GenesisState { + return &GenesisState{ + IdentifiedFees: identifiedFees, + FeeEnabledChannels: feeEnabledChannels, + RegisteredRelayers: registeredRelayers, + ForwardRelayers: forwardRelayers, + } +} + +// DefaultGenesisState returns a GenesisState with "transfer" as the default PortID. +func DefaultGenesisState() *GenesisState { + return &GenesisState{ + IdentifiedFees: []IdentifiedPacketFees{}, + ForwardRelayers: []ForwardRelayerAddress{}, + FeeEnabledChannels: []FeeEnabledChannel{}, + RegisteredRelayers: []RegisteredRelayerAddress{}, + } +} + +// Validate performs basic genesis state validation returning an error upon any +// failure. +func (gs GenesisState) Validate() error { + // Validate IdentifiedPacketFees + for _, identifiedFees := range gs.IdentifiedFees { + if err := identifiedFees.PacketId.Validate(); err != nil { + return err + } + + for _, packetFee := range identifiedFees.PacketFees { + if err := packetFee.Validate(); err != nil { + return err + } + } + } + + // Validate FeeEnabledChannels + for _, feeCh := range gs.FeeEnabledChannels { + if err := host.PortIdentifierValidator(feeCh.PortId); err != nil { + return sdkerrors.Wrap(err, "invalid source port ID") + } + if err := host.ChannelIdentifierValidator(feeCh.ChannelId); err != nil { + return sdkerrors.Wrap(err, "invalid source channel ID") + } + } + + // Validate RegisteredRelayers + for _, rel := range gs.RegisteredRelayers { + if _, err := sdk.AccAddressFromBech32(rel.Address); err != nil { + return sdkerrors.Wrap(err, "failed to convert source relayer address into sdk.AccAddress") + } + + if strings.TrimSpace(rel.CounterpartyAddress) == "" { + return ErrCounterpartyAddressEmpty + } + } + + // Validate ForwardRelayers + for _, rel := range gs.ForwardRelayers { + if _, err := sdk.AccAddressFromBech32(rel.Address); err != nil { + return sdkerrors.Wrap(err, "failed to convert forward relayer address into sdk.AccAddress") + } + + if err := rel.PacketId.Validate(); err != nil { + return err + } + } + + return nil +} diff --git a/modules/apps/29-fee/types/genesis.pb.go b/modules/apps/29-fee/types/genesis.pb.go new file mode 100644 index 00000000000..af78add463e --- /dev/null +++ b/modules/apps/29-fee/types/genesis.pb.go @@ -0,0 +1,1273 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/applications/fee/v1/genesis.proto + +package types + +import ( + fmt "fmt" + types "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// GenesisState defines the ICS29 fee middleware genesis state +type GenesisState struct { + // list of identified packet fees + IdentifiedFees []IdentifiedPacketFees `protobuf:"bytes,1,rep,name=identified_fees,json=identifiedFees,proto3" json:"identified_fees" yaml:"identified_fees"` + // list of fee enabled channels + FeeEnabledChannels []FeeEnabledChannel `protobuf:"bytes,2,rep,name=fee_enabled_channels,json=feeEnabledChannels,proto3" json:"fee_enabled_channels" yaml:"fee_enabled_channels"` + // list of registered relayer addresses + RegisteredRelayers []RegisteredRelayerAddress `protobuf:"bytes,3,rep,name=registered_relayers,json=registeredRelayers,proto3" json:"registered_relayers" yaml:"registered_relayers"` + // list of forward relayer addresses + ForwardRelayers []ForwardRelayerAddress `protobuf:"bytes,4,rep,name=forward_relayers,json=forwardRelayers,proto3" json:"forward_relayers" yaml:"forward_relayers"` +} + +func (m *GenesisState) Reset() { *m = GenesisState{} } +func (m *GenesisState) String() string { return proto.CompactTextString(m) } +func (*GenesisState) ProtoMessage() {} +func (*GenesisState) Descriptor() ([]byte, []int) { + return fileDescriptor_7191992e856dff95, []int{0} +} +func (m *GenesisState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GenesisState.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 *GenesisState) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenesisState.Merge(m, src) +} +func (m *GenesisState) XXX_Size() int { + return m.Size() +} +func (m *GenesisState) XXX_DiscardUnknown() { + xxx_messageInfo_GenesisState.DiscardUnknown(m) +} + +var xxx_messageInfo_GenesisState proto.InternalMessageInfo + +func (m *GenesisState) GetIdentifiedFees() []IdentifiedPacketFees { + if m != nil { + return m.IdentifiedFees + } + return nil +} + +func (m *GenesisState) GetFeeEnabledChannels() []FeeEnabledChannel { + if m != nil { + return m.FeeEnabledChannels + } + return nil +} + +func (m *GenesisState) GetRegisteredRelayers() []RegisteredRelayerAddress { + if m != nil { + return m.RegisteredRelayers + } + return nil +} + +func (m *GenesisState) GetForwardRelayers() []ForwardRelayerAddress { + if m != nil { + return m.ForwardRelayers + } + return nil +} + +// FeeEnabledChannel contains the PortID & ChannelID for a fee enabled channel +type FeeEnabledChannel struct { + // unique port identifier + PortId string `protobuf:"bytes,1,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty" yaml:"port_id"` + // unique channel identifier + ChannelId string `protobuf:"bytes,2,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty" yaml:"channel_id"` +} + +func (m *FeeEnabledChannel) Reset() { *m = FeeEnabledChannel{} } +func (m *FeeEnabledChannel) String() string { return proto.CompactTextString(m) } +func (*FeeEnabledChannel) ProtoMessage() {} +func (*FeeEnabledChannel) Descriptor() ([]byte, []int) { + return fileDescriptor_7191992e856dff95, []int{1} +} +func (m *FeeEnabledChannel) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *FeeEnabledChannel) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_FeeEnabledChannel.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 *FeeEnabledChannel) XXX_Merge(src proto.Message) { + xxx_messageInfo_FeeEnabledChannel.Merge(m, src) +} +func (m *FeeEnabledChannel) XXX_Size() int { + return m.Size() +} +func (m *FeeEnabledChannel) XXX_DiscardUnknown() { + xxx_messageInfo_FeeEnabledChannel.DiscardUnknown(m) +} + +var xxx_messageInfo_FeeEnabledChannel proto.InternalMessageInfo + +func (m *FeeEnabledChannel) GetPortId() string { + if m != nil { + return m.PortId + } + return "" +} + +func (m *FeeEnabledChannel) GetChannelId() string { + if m != nil { + return m.ChannelId + } + return "" +} + +// RegisteredRelayerAddress contains the address and counterparty address for a specific relayer (for distributing fees) +type RegisteredRelayerAddress struct { + // the relayer address + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + // the counterparty relayer address + CounterpartyAddress string `protobuf:"bytes,2,opt,name=counterparty_address,json=counterpartyAddress,proto3" json:"counterparty_address,omitempty" yaml:"counterparty_address"` + // unique channel identifier + ChannelId string `protobuf:"bytes,3,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty" yaml:"channel_id"` +} + +func (m *RegisteredRelayerAddress) Reset() { *m = RegisteredRelayerAddress{} } +func (m *RegisteredRelayerAddress) String() string { return proto.CompactTextString(m) } +func (*RegisteredRelayerAddress) ProtoMessage() {} +func (*RegisteredRelayerAddress) Descriptor() ([]byte, []int) { + return fileDescriptor_7191992e856dff95, []int{2} +} +func (m *RegisteredRelayerAddress) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RegisteredRelayerAddress) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RegisteredRelayerAddress.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 *RegisteredRelayerAddress) XXX_Merge(src proto.Message) { + xxx_messageInfo_RegisteredRelayerAddress.Merge(m, src) +} +func (m *RegisteredRelayerAddress) XXX_Size() int { + return m.Size() +} +func (m *RegisteredRelayerAddress) XXX_DiscardUnknown() { + xxx_messageInfo_RegisteredRelayerAddress.DiscardUnknown(m) +} + +var xxx_messageInfo_RegisteredRelayerAddress proto.InternalMessageInfo + +func (m *RegisteredRelayerAddress) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *RegisteredRelayerAddress) GetCounterpartyAddress() string { + if m != nil { + return m.CounterpartyAddress + } + return "" +} + +func (m *RegisteredRelayerAddress) GetChannelId() string { + if m != nil { + return m.ChannelId + } + return "" +} + +// ForwardRelayerAddress contains the forward relayer address and PacketId used for async acknowledgements +type ForwardRelayerAddress struct { + // the forward relayer address + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + // unique packet identifer comprised of the channel ID, port ID and sequence + PacketId types.PacketId `protobuf:"bytes,2,opt,name=packet_id,json=packetId,proto3" json:"packet_id" yaml:"packet_id"` +} + +func (m *ForwardRelayerAddress) Reset() { *m = ForwardRelayerAddress{} } +func (m *ForwardRelayerAddress) String() string { return proto.CompactTextString(m) } +func (*ForwardRelayerAddress) ProtoMessage() {} +func (*ForwardRelayerAddress) Descriptor() ([]byte, []int) { + return fileDescriptor_7191992e856dff95, []int{3} +} +func (m *ForwardRelayerAddress) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ForwardRelayerAddress) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ForwardRelayerAddress.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 *ForwardRelayerAddress) XXX_Merge(src proto.Message) { + xxx_messageInfo_ForwardRelayerAddress.Merge(m, src) +} +func (m *ForwardRelayerAddress) XXX_Size() int { + return m.Size() +} +func (m *ForwardRelayerAddress) XXX_DiscardUnknown() { + xxx_messageInfo_ForwardRelayerAddress.DiscardUnknown(m) +} + +var xxx_messageInfo_ForwardRelayerAddress proto.InternalMessageInfo + +func (m *ForwardRelayerAddress) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *ForwardRelayerAddress) GetPacketId() types.PacketId { + if m != nil { + return m.PacketId + } + return types.PacketId{} +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "ibc.applications.fee.v1.GenesisState") + proto.RegisterType((*FeeEnabledChannel)(nil), "ibc.applications.fee.v1.FeeEnabledChannel") + proto.RegisterType((*RegisteredRelayerAddress)(nil), "ibc.applications.fee.v1.RegisteredRelayerAddress") + proto.RegisterType((*ForwardRelayerAddress)(nil), "ibc.applications.fee.v1.ForwardRelayerAddress") +} + +func init() { + proto.RegisterFile("ibc/applications/fee/v1/genesis.proto", fileDescriptor_7191992e856dff95) +} + +var fileDescriptor_7191992e856dff95 = []byte{ + // 579 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x54, 0x4d, 0x6f, 0xd4, 0x30, + 0x14, 0xdc, 0xb4, 0x55, 0x4b, 0x5d, 0xd4, 0x0f, 0xb7, 0xa5, 0x51, 0x11, 0x49, 0x31, 0x42, 0xaa, + 0x40, 0x4d, 0xb4, 0x2d, 0x1c, 0xe0, 0xc6, 0x22, 0x8a, 0xf6, 0x04, 0x32, 0x9c, 0xb8, 0x44, 0xf9, + 0x78, 0x49, 0x2d, 0xb2, 0x71, 0x64, 0xbb, 0x41, 0xcb, 0x8d, 0x0b, 0x1c, 0xe1, 0x17, 0x71, 0xee, + 0xb1, 0x47, 0x4e, 0x2b, 0xd4, 0xfe, 0x83, 0xfe, 0x02, 0x94, 0x38, 0xe9, 0x6e, 0x97, 0x0d, 0xe2, + 0xf6, 0x62, 0xcf, 0xbc, 0x19, 0x8f, 0xf3, 0x8c, 0x1e, 0xb2, 0x20, 0x74, 0xfd, 0x3c, 0x4f, 0x59, + 0xe8, 0x2b, 0xc6, 0x33, 0xe9, 0xc6, 0x00, 0x6e, 0xd1, 0x75, 0x13, 0xc8, 0x40, 0x32, 0xe9, 0xe4, + 0x82, 0x2b, 0x8e, 0x77, 0x58, 0x10, 0x3a, 0x93, 0x30, 0x27, 0x06, 0x70, 0x8a, 0xee, 0xee, 0x56, + 0xc2, 0x13, 0x5e, 0x61, 0xdc, 0xb2, 0xd2, 0xf0, 0xdd, 0xfb, 0x6d, 0x5d, 0x4b, 0xd6, 0x04, 0x24, + 0xe4, 0x02, 0xdc, 0xf0, 0xc4, 0xcf, 0x32, 0x48, 0xcb, 0xed, 0xba, 0xd4, 0x10, 0xf2, 0x7d, 0x01, + 0xdd, 0x7e, 0xad, 0x6d, 0xbc, 0x53, 0xbe, 0x02, 0x5c, 0xa0, 0x35, 0x16, 0x41, 0xa6, 0x58, 0xcc, + 0x20, 0xf2, 0x62, 0x00, 0x69, 0x1a, 0x7b, 0xf3, 0xfb, 0x2b, 0x87, 0x07, 0x4e, 0x8b, 0x3f, 0xa7, + 0x7f, 0x8d, 0x7f, 0xeb, 0x87, 0x1f, 0x41, 0x1d, 0x03, 0xc8, 0x9e, 0x75, 0x36, 0xb2, 0x3b, 0x57, + 0x23, 0xfb, 0xce, 0xd0, 0x1f, 0xa4, 0xcf, 0xc9, 0x54, 0x4f, 0x42, 0x57, 0xc7, 0x2b, 0x25, 0x1e, + 0x7f, 0x31, 0xd0, 0x56, 0x0c, 0xe0, 0x41, 0xe6, 0x07, 0x29, 0x44, 0x5e, 0x6d, 0x53, 0x9a, 0x73, + 0x95, 0xfa, 0xa3, 0x56, 0xf5, 0x63, 0x80, 0x57, 0x9a, 0xf3, 0x52, 0x53, 0x7a, 0x0f, 0x6a, 0xe9, + 0xbb, 0x5a, 0x7a, 0x56, 0x57, 0x42, 0x71, 0x3c, 0xcd, 0x93, 0xf8, 0xab, 0x81, 0x36, 0x05, 0x24, + 0x4c, 0x2a, 0x10, 0x10, 0x79, 0x02, 0x52, 0x7f, 0x08, 0x42, 0x9a, 0xf3, 0x95, 0x85, 0x6e, 0xab, + 0x05, 0x7a, 0xcd, 0xa1, 0x9a, 0xf2, 0x22, 0x8a, 0x04, 0x48, 0xd9, 0x23, 0xb5, 0x93, 0x5d, 0xed, + 0x64, 0x46, 0x6f, 0x42, 0xb1, 0x98, 0x66, 0x4b, 0xfc, 0x19, 0xad, 0xc7, 0x5c, 0x7c, 0xf2, 0xc5, + 0x84, 0x89, 0x85, 0xca, 0x84, 0xd3, 0x9e, 0x83, 0x26, 0x4c, 0x39, 0xb0, 0x6b, 0x07, 0x3b, 0x75, + 0x16, 0x53, 0x5d, 0x09, 0x5d, 0x8b, 0x6f, 0xf0, 0x24, 0x29, 0xd0, 0xc6, 0x5f, 0x91, 0xe2, 0xc7, + 0x68, 0x29, 0xe7, 0x42, 0x79, 0x2c, 0x32, 0x8d, 0x3d, 0x63, 0x7f, 0xb9, 0x87, 0xaf, 0x46, 0xf6, + 0xaa, 0xee, 0x59, 0x6f, 0x10, 0xba, 0x58, 0x56, 0xfd, 0x08, 0x3f, 0x41, 0xa8, 0xce, 0xb9, 0xc4, + 0xcf, 0x55, 0xf8, 0xed, 0xab, 0x91, 0xbd, 0xa1, 0xf1, 0xe3, 0x3d, 0x42, 0x97, 0xeb, 0x8f, 0x7e, + 0x44, 0x7e, 0x1a, 0xc8, 0x6c, 0x0b, 0x12, 0x9b, 0x68, 0xc9, 0xd7, 0xa5, 0xd6, 0xa7, 0xcd, 0x27, + 0xa6, 0x68, 0x2b, 0xe4, 0xa7, 0x99, 0x02, 0x91, 0xfb, 0x42, 0x0d, 0xbd, 0x06, 0xa6, 0x65, 0xed, + 0xf1, 0x6f, 0x30, 0x0b, 0x45, 0xe8, 0xe6, 0xe4, 0x72, 0xa3, 0x76, 0xf3, 0x00, 0xf3, 0xff, 0x79, + 0x80, 0x6f, 0x06, 0xda, 0x9e, 0x79, 0x09, 0xff, 0x70, 0xff, 0x1e, 0x2d, 0xe7, 0xd5, 0xcc, 0x34, + 0x49, 0xad, 0x1c, 0xde, 0xab, 0x6e, 0xb8, 0x9c, 0x5a, 0xa7, 0x19, 0xd5, 0xa2, 0xeb, 0xe8, 0xc9, + 0xea, 0x47, 0x3d, 0xb3, 0xbe, 0xd0, 0xf5, 0x3a, 0xfc, 0x86, 0x4d, 0xe8, 0xad, 0xbc, 0xc1, 0xbc, + 0x39, 0xbb, 0xb0, 0x8c, 0xf3, 0x0b, 0xcb, 0xf8, 0x7d, 0x61, 0x19, 0x3f, 0x2e, 0xad, 0xce, 0xf9, + 0xa5, 0xd5, 0xf9, 0x75, 0x69, 0x75, 0x3e, 0x3c, 0x4d, 0x98, 0x3a, 0x39, 0x0d, 0x9c, 0x90, 0x0f, + 0xdc, 0x90, 0xcb, 0x01, 0x97, 0x2e, 0x0b, 0xc2, 0x83, 0x84, 0xbb, 0xc5, 0x91, 0x3b, 0xe0, 0xd1, + 0x69, 0x0a, 0xb2, 0x7c, 0x54, 0xa4, 0x7b, 0xf8, 0xec, 0xa0, 0x7c, 0x4f, 0xd4, 0x30, 0x07, 0x19, + 0x2c, 0x56, 0x8f, 0xc5, 0xd1, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe9, 0x33, 0x0f, 0x78, 0xca, + 0x04, 0x00, 0x00, +} + +func (m *GenesisState) 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 *GenesisState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ForwardRelayers) > 0 { + for iNdEx := len(m.ForwardRelayers) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ForwardRelayers[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if len(m.RegisteredRelayers) > 0 { + for iNdEx := len(m.RegisteredRelayers) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.RegisteredRelayers[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.FeeEnabledChannels) > 0 { + for iNdEx := len(m.FeeEnabledChannels) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.FeeEnabledChannels[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.IdentifiedFees) > 0 { + for iNdEx := len(m.IdentifiedFees) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.IdentifiedFees[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *FeeEnabledChannel) 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 *FeeEnabledChannel) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *FeeEnabledChannel) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ChannelId) > 0 { + i -= len(m.ChannelId) + copy(dAtA[i:], m.ChannelId) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.ChannelId))) + i-- + dAtA[i] = 0x12 + } + if len(m.PortId) > 0 { + i -= len(m.PortId) + copy(dAtA[i:], m.PortId) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.PortId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *RegisteredRelayerAddress) 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 *RegisteredRelayerAddress) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RegisteredRelayerAddress) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ChannelId) > 0 { + i -= len(m.ChannelId) + copy(dAtA[i:], m.ChannelId) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.ChannelId))) + i-- + dAtA[i] = 0x1a + } + if len(m.CounterpartyAddress) > 0 { + i -= len(m.CounterpartyAddress) + copy(dAtA[i:], m.CounterpartyAddress) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.CounterpartyAddress))) + i-- + dAtA[i] = 0x12 + } + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ForwardRelayerAddress) 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 *ForwardRelayerAddress) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ForwardRelayerAddress) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.PacketId.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int { + offset -= sovGenesis(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *GenesisState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.IdentifiedFees) > 0 { + for _, e := range m.IdentifiedFees { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.FeeEnabledChannels) > 0 { + for _, e := range m.FeeEnabledChannels { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.RegisteredRelayers) > 0 { + for _, e := range m.RegisteredRelayers { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.ForwardRelayers) > 0 { + for _, e := range m.ForwardRelayers { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + return n +} + +func (m *FeeEnabledChannel) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PortId) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + l = len(m.ChannelId) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + return n +} + +func (m *RegisteredRelayerAddress) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + l = len(m.CounterpartyAddress) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + l = len(m.ChannelId) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + return n +} + +func (m *ForwardRelayerAddress) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + l = m.PacketId.Size() + n += 1 + l + sovGenesis(uint64(l)) + return n +} + +func sovGenesis(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozGenesis(x uint64) (n int) { + return sovGenesis(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *GenesisState) 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 ErrIntOverflowGenesis + } + 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: GenesisState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IdentifiedFees", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IdentifiedFees = append(m.IdentifiedFees, IdentifiedPacketFees{}) + if err := m.IdentifiedFees[len(m.IdentifiedFees)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FeeEnabledChannels", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FeeEnabledChannels = append(m.FeeEnabledChannels, FeeEnabledChannel{}) + if err := m.FeeEnabledChannels[len(m.FeeEnabledChannels)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RegisteredRelayers", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RegisteredRelayers = append(m.RegisteredRelayers, RegisteredRelayerAddress{}) + if err := m.RegisteredRelayers[len(m.RegisteredRelayers)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ForwardRelayers", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ForwardRelayers = append(m.ForwardRelayers, ForwardRelayerAddress{}) + if err := m.ForwardRelayers[len(m.ForwardRelayers)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *FeeEnabledChannel) 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 ErrIntOverflowGenesis + } + 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: FeeEnabledChannel: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FeeEnabledChannel: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PortId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + 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 ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PortId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChannelId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + 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 ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChannelId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RegisteredRelayerAddress) 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 ErrIntOverflowGenesis + } + 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: RegisteredRelayerAddress: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RegisteredRelayerAddress: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + 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 ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CounterpartyAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + 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 ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CounterpartyAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChannelId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + 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 ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChannelId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ForwardRelayerAddress) 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 ErrIntOverflowGenesis + } + 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: ForwardRelayerAddress: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ForwardRelayerAddress: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + 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 ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PacketId", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.PacketId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipGenesis(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthGenesis + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupGenesis + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthGenesis + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthGenesis = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowGenesis = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupGenesis = fmt.Errorf("proto: unexpected end of group") +) diff --git a/modules/apps/29-fee/types/genesis_test.go b/modules/apps/29-fee/types/genesis_test.go new file mode 100644 index 00000000000..d574257e706 --- /dev/null +++ b/modules/apps/29-fee/types/genesis_test.go @@ -0,0 +1,215 @@ +package types_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto/secp256k1" + + "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" + transfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" + channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v3/testing" +) + +var ( + addr1 = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()).String() + addr2 = sdk.AccAddress("testaddr2").String() + validCoins = sdk.Coins{sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: sdk.NewInt(100)}} + validCoins2 = sdk.Coins{sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: sdk.NewInt(200)}} + validCoins3 = sdk.Coins{sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: sdk.NewInt(300)}} +) + +func TestValidateGenesis(t *testing.T) { + var ( + packetID channeltypes.PacketId + fee types.Fee + refundAcc string + sender string + forwardAddr string + counterparty string + portID string + channelID string + packetChannelID string + seq uint64 + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "valid genesis", + func() {}, + true, + }, + { + "invalid packetID: invalid channel", + func() { + packetID = channeltypes.NewPacketId( + portID, + "", + seq, + ) + }, + false, + }, + { + "invalid packetID: invalid port", + func() { + packetID = channeltypes.NewPacketId( + "", + channelID, + seq, + ) + }, + false, + }, + { + "invalid packetID: invalid sequence", + func() { + packetID = channeltypes.NewPacketId( + portID, + channelID, + 0, + ) + }, + false, + }, + { + "invalid packetID: invalid fee", + func() { + fee = types.Fee{ + sdk.Coins{}, + sdk.Coins{}, + sdk.Coins{}, + } + }, + false, + }, + { + "invalid packetID: invalid refundAcc", + func() { + refundAcc = "" + }, + false, + }, + { + "invalid FeeEnabledChannel: invalid ChannelID", + func() { + channelID = "" + }, + false, + }, + { + "invalid FeeEnabledChannel: invalid PortID", + func() { + portID = "" + }, + false, + }, + { + "invalid RegisteredRelayers: invalid sender", + func() { + sender = "" + }, + false, + }, + { + "invalid RegisteredRelayers: invalid counterparty", + func() { + counterparty = " " + }, + false, + }, + { + "invalid ForwardRelayerAddress: invalid forwardAddr", + func() { + forwardAddr = "" + }, + false, + }, + { + "invalid ForwardRelayerAddress: invalid packet", + func() { + packetChannelID = "1" + }, + false, + }, + } + + for _, tc := range testCases { + portID = transfertypes.PortID + channelID = ibctesting.FirstChannelID + packetChannelID = ibctesting.FirstChannelID + seq = uint64(1) + + // build PacketId & Fee + packetID = channeltypes.NewPacketId( + portID, + channelID, + seq, + ) + fee = types.Fee{ + validCoins, + validCoins2, + validCoins3, + } + + refundAcc = addr1 + + // relayer addresses + sender = addr1 + counterparty = addr2 + forwardAddr = addr2 + + tc.malleate() + + genState := types.GenesisState{ + IdentifiedFees: []types.IdentifiedPacketFees{ + { + PacketId: packetID, + PacketFees: []types.PacketFee{ + { + Fee: fee, + RefundAddress: refundAcc, + Relayers: nil, + }, + }, + }, + }, + FeeEnabledChannels: []types.FeeEnabledChannel{ + { + PortId: portID, + ChannelId: channelID, + }, + }, + RegisteredRelayers: []types.RegisteredRelayerAddress{ + { + Address: sender, + CounterpartyAddress: counterparty, + }, + }, + ForwardRelayers: []types.ForwardRelayerAddress{ + { + Address: forwardAddr, + PacketId: channeltypes.NewPacketId(portID, packetChannelID, 1), + }, + }, + } + + err := genState.Validate() + if tc.expPass { + require.NoError(t, err, tc.name) + } else { + require.Error(t, err, tc.name) + } + } +} + +func TestValidateDefaultGenesis(t *testing.T) { + err := types.DefaultGenesisState().Validate() + require.NoError(t, err) +} diff --git a/modules/apps/29-fee/types/keys.go b/modules/apps/29-fee/types/keys.go new file mode 100644 index 00000000000..188a75e7ac3 --- /dev/null +++ b/modules/apps/29-fee/types/keys.go @@ -0,0 +1,139 @@ +package types + +import ( + "fmt" + "strconv" + "strings" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" +) + +const ( + // ModuleName defines the 29-fee name + ModuleName = "feeibc" + + // StoreKey is the store key string for IBC fee module + StoreKey = ModuleName + + // RouterKey is the message route for IBC fee module + RouterKey = ModuleName + + // QuerierRoute is the querier route for IBC fee module + QuerierRoute = ModuleName + + Version = "fee29-1" + + // FeeEnabledPrefix is the key prefix for storing fee enabled flag + FeeEnabledKeyPrefix = "feeEnabled" + + // CounterpartyRelayerAddressKeyPrefix is the key prefix for relayer address mapping + CounterpartyRelayerAddressKeyPrefix = "relayerAddress" + + // FeesInEscrowPrefix is the key prefix for fee in escrow mapping + FeesInEscrowPrefix = "feesInEscrow" + + // ForwardRelayerPrefix is the key prefix for forward relayer addresses stored in state for async acknowledgements + ForwardRelayerPrefix = "forwardRelayer" +) + +// KeyLocked returns the key used to lock and unlock the fee module. This key is used +// in the presence of a severe bug. +func KeyLocked() []byte { + return []byte("locked") +} + +// KeyFeeEnabled returns the key that stores a flag to determine if fee logic should +// be enabled for the given port and channel identifiers. +func KeyFeeEnabled(portID, channelID string) []byte { + return []byte(fmt.Sprintf("%s/%s/%s", FeeEnabledKeyPrefix, portID, channelID)) +} + +// ParseKeyFeeEnabled parses the key used to indicate if the fee logic should be +// enabled for the given port and channel identifiers. +func ParseKeyFeeEnabled(key string) (portID, channelID string, err error) { + keySplit := strings.Split(key, "/") + if len(keySplit) != 3 { + return "", "", sdkerrors.Wrapf( + sdkerrors.ErrLogic, "key provided is incorrect: the key split has incorrect length, expected %d, got %d", 3, len(keySplit), + ) + } + + if keySplit[0] != FeeEnabledKeyPrefix { + return "", "", sdkerrors.Wrapf(sdkerrors.ErrLogic, "key prefix is incorrect: expected %s, got %s", FeeEnabledKeyPrefix, keySplit[0]) + } + + portID = keySplit[1] + channelID = keySplit[2] + + return portID, channelID, nil +} + +// KeyCounterpartyRelayer returns the key for relayer address -> counterparty address mapping +func KeyCounterpartyRelayer(address, channelID string) []byte { + return []byte(fmt.Sprintf("%s/%s/%s", CounterpartyRelayerAddressKeyPrefix, address, channelID)) +} + +// ParseKeyCounterpartyRelayer returns the registered relayer address and channelID used to store the counterpartyrelayer address +func ParseKeyCounterpartyRelayer(key string) (address string, channelID string, error error) { + keySplit := strings.Split(key, "/") + if len(keySplit) != 3 { + return "", "", sdkerrors.Wrapf( + sdkerrors.ErrLogic, "key provided is incorrect: the key split has incorrect length, expected %d, got %d", 3, len(keySplit), + ) + } + + return keySplit[1], keySplit[2], nil +} + +// KeyForwardRelayerAddress returns the key for packetID -> forwardAddress mapping +func KeyForwardRelayerAddress(packetID channeltypes.PacketId) []byte { + return []byte(fmt.Sprintf("%s/%s/%s/%d", ForwardRelayerPrefix, packetID.PortId, packetID.ChannelId, packetID.Sequence)) +} + +// ParseKeyForwardRelayerAddress parses the key used to store the forward relayer address and returns the packetID +func ParseKeyForwardRelayerAddress(key string) (channeltypes.PacketId, error) { + keySplit := strings.Split(key, "/") + if len(keySplit) != 4 { + return channeltypes.PacketId{}, sdkerrors.Wrapf( + sdkerrors.ErrLogic, "key provided is incorrect: the key split has incorrect length, expected %d, got %d", 4, len(keySplit), + ) + } + + seq, err := strconv.ParseUint(keySplit[3], 10, 64) + if err != nil { + return channeltypes.PacketId{}, err + } + + packetID := channeltypes.NewPacketId(keySplit[1], keySplit[2], seq) + return packetID, nil +} + +// KeyFeesInEscrow returns the key for escrowed fees +func KeyFeesInEscrow(packetID channeltypes.PacketId) []byte { + return []byte(fmt.Sprintf("%s/%d", KeyFeesInEscrowChannelPrefix(packetID.PortId, packetID.ChannelId), packetID.Sequence)) +} + +// ParseKeyFeesInEscrow parses the key used to store fees in escrow and returns the packet id +func ParseKeyFeesInEscrow(key string) (channeltypes.PacketId, error) { + keySplit := strings.Split(key, "/") + if len(keySplit) != 4 { + return channeltypes.PacketId{}, sdkerrors.Wrapf( + sdkerrors.ErrLogic, "key provided is incorrect: the key split has incorrect length, expected %d, got %d", 4, len(keySplit), + ) + } + + seq, err := strconv.ParseUint(keySplit[3], 10, 64) + if err != nil { + return channeltypes.PacketId{}, err + } + + packetID := channeltypes.NewPacketId(keySplit[1], keySplit[2], seq) + return packetID, nil +} + +// KeyFeesInEscrowChannelPrefix returns the key prefix for escrowed fees on the given channel +func KeyFeesInEscrowChannelPrefix(portID, channelID string) []byte { + return []byte(fmt.Sprintf("%s/%s/%s", FeesInEscrowPrefix, portID, channelID)) +} diff --git a/modules/apps/29-fee/types/keys_test.go b/modules/apps/29-fee/types/keys_test.go new file mode 100644 index 00000000000..22dc91b6a57 --- /dev/null +++ b/modules/apps/29-fee/types/keys_test.go @@ -0,0 +1,176 @@ +package types_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v3/testing" +) + +var ( + validPacketID = channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1) +) + +func TestKeyCounterpartyRelayer(t *testing.T) { + var ( + relayerAddress = "relayer_address" + channelID = "channel-0" + ) + + key := types.KeyCounterpartyRelayer(relayerAddress, channelID) + require.Equal(t, string(key), fmt.Sprintf("%s/%s/%s", types.CounterpartyRelayerAddressKeyPrefix, relayerAddress, channelID)) +} + +func TestKeyFeesInEscrow(t *testing.T) { + key := types.KeyFeesInEscrow(validPacketID) + require.Equal(t, string(key), fmt.Sprintf("%s/%s/%s/%d", types.FeesInEscrowPrefix, ibctesting.MockFeePort, ibctesting.FirstChannelID, 1)) +} + +func TestParseKeyFeeEnabled(t *testing.T) { + testCases := []struct { + name string + key string + expPass bool + }{ + { + "success", + string(types.KeyFeeEnabled(ibctesting.MockPort, ibctesting.FirstChannelID)), + true, + }, + { + "incorrect key - key split has incorrect length", + string(types.KeyFeesInEscrow(validPacketID)), + false, + }, + { + "incorrect key - key split has incorrect length", + fmt.Sprintf("%s/%s/%s", "fee", ibctesting.MockPort, ibctesting.FirstChannelID), + false, + }, + } + + for _, tc := range testCases { + portID, channelID, err := types.ParseKeyFeeEnabled(tc.key) + + if tc.expPass { + require.NoError(t, err) + require.Equal(t, ibctesting.MockPort, portID) + require.Equal(t, ibctesting.FirstChannelID, channelID) + } else { + require.Error(t, err) + require.Empty(t, portID) + require.Empty(t, channelID) + } + } +} + +func TestParseKeyFeesInEscrow(t *testing.T) { + + testCases := []struct { + name string + key string + expPass bool + }{ + { + "success", + string(types.KeyFeesInEscrow(validPacketID)), + true, + }, + { + "incorrect key - key split has incorrect length", + string(types.KeyFeeEnabled(validPacketID.PortId, validPacketID.ChannelId)), + false, + }, + { + "incorrect key - sequence cannot be parsed", + fmt.Sprintf("%s/%s", types.KeyFeesInEscrowChannelPrefix(validPacketID.PortId, validPacketID.ChannelId), "sequence"), + false, + }, + } + + for _, tc := range testCases { + packetID, err := types.ParseKeyFeesInEscrow(tc.key) + + if tc.expPass { + require.NoError(t, err) + require.Equal(t, validPacketID, packetID) + } else { + require.Error(t, err) + } + } +} + +func TestParseKeyForwardRelayerAddress(t *testing.T) { + + testCases := []struct { + name string + key string + expPass bool + }{ + { + "success", + string(types.KeyForwardRelayerAddress(validPacketID)), + true, + }, + { + "incorrect key - key split has incorrect length", + "forwardRelayer/transfer/channel-0", + false, + }, + { + "incorrect key - sequence is not correct", + "forwardRelayer/transfer/channel-0/sequence", + false, + }, + } + + for _, tc := range testCases { + packetID, err := types.ParseKeyForwardRelayerAddress(tc.key) + + if tc.expPass { + require.NoError(t, err) + require.Equal(t, validPacketID, packetID) + } else { + require.Error(t, err) + } + } +} + +func TestParseKeyCounterpartyRelayer(t *testing.T) { + var ( + relayerAddress = "relayer_address" + ) + + testCases := []struct { + name string + key string + expPass bool + }{ + { + "success", + string(types.KeyCounterpartyRelayer(relayerAddress, ibctesting.FirstChannelID)), + true, + }, + { + "incorrect key - key split has incorrect length", + "relayerAddress/relayer_address/transfer/channel-0", + false, + }, + } + + for _, tc := range testCases { + address, channelID, err := types.ParseKeyCounterpartyRelayer(tc.key) + + if tc.expPass { + require.NoError(t, err) + require.Equal(t, relayerAddress, address) + require.Equal(t, ibctesting.FirstChannelID, channelID) + } else { + require.Error(t, err) + } + } +} diff --git a/modules/apps/29-fee/types/metadata.pb.go b/modules/apps/29-fee/types/metadata.pb.go new file mode 100644 index 00000000000..95bb9244946 --- /dev/null +++ b/modules/apps/29-fee/types/metadata.pb.go @@ -0,0 +1,378 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/applications/fee/v1/metadata.proto + +package types + +import ( + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// Metadata defines the ICS29 channel specific metadata encoded into the channel version bytestring +// See ICS004: https://github.com/cosmos/ibc/tree/master/spec/core/ics-004-channel-and-packet-semantics#Versioning +type Metadata struct { + // fee_version defines the ICS29 fee version + FeeVersion string `protobuf:"bytes,1,opt,name=fee_version,json=feeVersion,proto3" json:"fee_version,omitempty" yaml:"fee_version"` + // app_version defines the underlying application version, which may or may not be a JSON encoded bytestring + AppVersion string `protobuf:"bytes,2,opt,name=app_version,json=appVersion,proto3" json:"app_version,omitempty" yaml:"app_version"` +} + +func (m *Metadata) Reset() { *m = Metadata{} } +func (m *Metadata) String() string { return proto.CompactTextString(m) } +func (*Metadata) ProtoMessage() {} +func (*Metadata) Descriptor() ([]byte, []int) { + return fileDescriptor_03d0f000eda681ce, []int{0} +} +func (m *Metadata) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Metadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Metadata.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 *Metadata) XXX_Merge(src proto.Message) { + xxx_messageInfo_Metadata.Merge(m, src) +} +func (m *Metadata) XXX_Size() int { + return m.Size() +} +func (m *Metadata) XXX_DiscardUnknown() { + xxx_messageInfo_Metadata.DiscardUnknown(m) +} + +var xxx_messageInfo_Metadata proto.InternalMessageInfo + +func (m *Metadata) GetFeeVersion() string { + if m != nil { + return m.FeeVersion + } + return "" +} + +func (m *Metadata) GetAppVersion() string { + if m != nil { + return m.AppVersion + } + return "" +} + +func init() { + proto.RegisterType((*Metadata)(nil), "ibc.applications.fee.v1.Metadata") +} + +func init() { + proto.RegisterFile("ibc/applications/fee/v1/metadata.proto", fileDescriptor_03d0f000eda681ce) +} + +var fileDescriptor_03d0f000eda681ce = []byte{ + // 248 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0xcb, 0x4c, 0x4a, 0xd6, + 0x4f, 0x2c, 0x28, 0xc8, 0xc9, 0x4c, 0x4e, 0x2c, 0xc9, 0xcc, 0xcf, 0x2b, 0xd6, 0x4f, 0x4b, 0x4d, + 0xd5, 0x2f, 0x33, 0xd4, 0xcf, 0x4d, 0x2d, 0x49, 0x4c, 0x49, 0x2c, 0x49, 0xd4, 0x2b, 0x28, 0xca, + 0x2f, 0xc9, 0x17, 0x12, 0xcf, 0x4c, 0x4a, 0xd6, 0x43, 0x56, 0xa7, 0x97, 0x96, 0x9a, 0xaa, 0x57, + 0x66, 0x28, 0x25, 0x92, 0x9e, 0x9f, 0x9e, 0x0f, 0x56, 0xa3, 0x0f, 0x62, 0x41, 0x94, 0x2b, 0xd5, + 0x70, 0x71, 0xf8, 0x42, 0x0d, 0x10, 0x32, 0xe7, 0xe2, 0x4e, 0x4b, 0x4d, 0x8d, 0x2f, 0x4b, 0x2d, + 0x2a, 0xce, 0xcc, 0xcf, 0x93, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x74, 0x12, 0xfb, 0x74, 0x4f, 0x5e, + 0xa8, 0x32, 0x31, 0x37, 0xc7, 0x4a, 0x09, 0x49, 0x52, 0x29, 0x88, 0x2b, 0x2d, 0x35, 0x35, 0x0c, + 0xc2, 0x01, 0x69, 0x4c, 0x2c, 0x28, 0x80, 0x6b, 0x64, 0x42, 0xd7, 0x88, 0x24, 0xa9, 0x14, 0xc4, + 0x95, 0x58, 0x50, 0x00, 0xd5, 0xe8, 0xe4, 0x7f, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, + 0x0f, 0x1e, 0xc9, 0x31, 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, + 0x0c, 0x51, 0xa6, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, 0xfa, 0xc9, 0xf9, + 0xc5, 0xb9, 0xf9, 0xc5, 0xfa, 0x99, 0x49, 0xc9, 0xba, 0xe9, 0xf9, 0xfa, 0x65, 0xc6, 0xfa, 0xb9, + 0xf9, 0x29, 0xa5, 0x39, 0xa9, 0xc5, 0xa0, 0xe0, 0x28, 0xd6, 0x37, 0xb2, 0xd4, 0x05, 0x85, 0x44, + 0x49, 0x65, 0x41, 0x6a, 0x71, 0x12, 0x1b, 0xd8, 0x57, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x04, 0x84, 0x58, 0xe0, 0x2e, 0x01, 0x00, 0x00, +} + +func (m *Metadata) 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 *Metadata) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Metadata) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.AppVersion) > 0 { + i -= len(m.AppVersion) + copy(dAtA[i:], m.AppVersion) + i = encodeVarintMetadata(dAtA, i, uint64(len(m.AppVersion))) + i-- + dAtA[i] = 0x12 + } + if len(m.FeeVersion) > 0 { + i -= len(m.FeeVersion) + copy(dAtA[i:], m.FeeVersion) + i = encodeVarintMetadata(dAtA, i, uint64(len(m.FeeVersion))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintMetadata(dAtA []byte, offset int, v uint64) int { + offset -= sovMetadata(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Metadata) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.FeeVersion) + if l > 0 { + n += 1 + l + sovMetadata(uint64(l)) + } + l = len(m.AppVersion) + if l > 0 { + n += 1 + l + sovMetadata(uint64(l)) + } + return n +} + +func sovMetadata(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozMetadata(x uint64) (n int) { + return sovMetadata(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Metadata) 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 ErrIntOverflowMetadata + } + 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: Metadata: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Metadata: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FeeVersion", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetadata + } + 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 ErrInvalidLengthMetadata + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMetadata + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FeeVersion = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AppVersion", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetadata + } + 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 ErrInvalidLengthMetadata + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMetadata + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AppVersion = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMetadata(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetadata + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipMetadata(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMetadata + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMetadata + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMetadata + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthMetadata + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupMetadata + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthMetadata + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthMetadata = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowMetadata = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupMetadata = fmt.Errorf("proto: unexpected end of group") +) diff --git a/modules/apps/29-fee/types/msgs.go b/modules/apps/29-fee/types/msgs.go new file mode 100644 index 00000000000..d2fec3e542f --- /dev/null +++ b/modules/apps/29-fee/types/msgs.go @@ -0,0 +1,164 @@ +package types + +import ( + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v3/modules/core/24-host" +) + +// msg types +const ( + TypeMsgPayPacketFee = "payPacketFee" + TypeMsgPayPacketFeeAsync = "payPacketFeeAsync" +) + +// NewMsgRegisterCounterpartyAddress creates a new instance of MsgRegisterCounterpartyAddress +func NewMsgRegisterCounterpartyAddress(address, counterpartyAddress, channelID string) *MsgRegisterCounterpartyAddress { + return &MsgRegisterCounterpartyAddress{ + Address: address, + CounterpartyAddress: counterpartyAddress, + ChannelId: channelID, + } +} + +// ValidateBasic performs a basic check of the MsgRegisterCounterpartyAddress fields +func (msg MsgRegisterCounterpartyAddress) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Address) + if err != nil { + return sdkerrors.Wrap(err, "failed to convert msg.Address into sdk.AccAddress") + } + + if strings.TrimSpace(msg.CounterpartyAddress) == "" { + return ErrCounterpartyAddressEmpty + } + + // validate channelId + if err := host.ChannelIdentifierValidator(msg.ChannelId); err != nil { + return err + } + + return nil +} + +// GetSigners implements sdk.Msg +func (msg MsgRegisterCounterpartyAddress) GetSigners() []sdk.AccAddress { + signer, err := sdk.AccAddressFromBech32(msg.Address) + if err != nil { + panic(err) + } + return []sdk.AccAddress{signer} +} + +// NewMsgPayPacketFee creates a new instance of MsgPayPacketFee +func NewMsgPayPacketFee(fee Fee, sourcePortId, sourceChannelId, signer string, relayers []string) *MsgPayPacketFee { + return &MsgPayPacketFee{ + Fee: fee, + SourcePortId: sourcePortId, + SourceChannelId: sourceChannelId, + Signer: signer, + Relayers: relayers, + } +} + +// ValidateBasic performs a basic check of the MsgPayPacketFee fields +func (msg MsgPayPacketFee) ValidateBasic() error { + // validate channelId + if err := host.ChannelIdentifierValidator(msg.SourceChannelId); err != nil { + return err + } + + // validate portId + if err := host.PortIdentifierValidator(msg.SourcePortId); err != nil { + return err + } + + // signer check + if _, err := sdk.AccAddressFromBech32(msg.Signer); err != nil { + return sdkerrors.Wrap(err, "failed to convert msg.Signer into sdk.AccAddress") + } + + // enforce relayer is nil + if msg.Relayers != nil { + return ErrRelayersNotNil + } + + if err := msg.Fee.Validate(); err != nil { + return err + } + + return nil +} + +// GetSigners implements sdk.Msg +func (msg MsgPayPacketFee) GetSigners() []sdk.AccAddress { + signer, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + panic(err) + } + return []sdk.AccAddress{signer} +} + +// Route implements sdk.Msg +func (msg MsgPayPacketFee) Route() string { + return RouterKey +} + +// Type implements sdk.Msg +func (msg MsgPayPacketFee) Type() string { + return TypeMsgPayPacketFee +} + +// GetSignBytes implements sdk.Msg. +func (msg MsgPayPacketFee) GetSignBytes() []byte { + return sdk.MustSortJSON(AminoCdc.MustMarshalJSON(&msg)) +} + +// NewMsgPayPacketAsync creates a new instance of MsgPayPacketFee +func NewMsgPayPacketFeeAsync(packetID channeltypes.PacketId, packetFee PacketFee) *MsgPayPacketFeeAsync { + return &MsgPayPacketFeeAsync{ + PacketId: packetID, + PacketFee: packetFee, + } +} + +// ValidateBasic performs a basic check of the MsgPayPacketFeeAsync fields +func (msg MsgPayPacketFeeAsync) ValidateBasic() error { + if err := msg.PacketId.Validate(); err != nil { + return err + } + + if err := msg.PacketFee.Validate(); err != nil { + return err + } + + return nil +} + +// GetSigners implements sdk.Msg +// The signer of the fee message must be the refund address +func (msg MsgPayPacketFeeAsync) GetSigners() []sdk.AccAddress { + signer, err := sdk.AccAddressFromBech32(msg.PacketFee.RefundAddress) + if err != nil { + panic(err) + } + return []sdk.AccAddress{signer} +} + +// Route implements sdk.Msg +func (msg MsgPayPacketFeeAsync) Route() string { + return RouterKey +} + +// Type implements sdk.Msg +func (msg MsgPayPacketFeeAsync) Type() string { + return TypeMsgPayPacketFeeAsync +} + +// GetSignBytes implements sdk.Msg. +func (msg MsgPayPacketFeeAsync) GetSignBytes() []byte { + return sdk.MustSortJSON(AminoCdc.MustMarshalJSON(&msg)) +} diff --git a/modules/apps/29-fee/types/msgs_test.go b/modules/apps/29-fee/types/msgs_test.go new file mode 100644 index 00000000000..d314a4f4193 --- /dev/null +++ b/modules/apps/29-fee/types/msgs_test.go @@ -0,0 +1,319 @@ +package types_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v3/testing" + + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto/secp256k1" +) + +func TestMsgRegisterCountepartyAddressValidation(t *testing.T) { + var ( + msg *types.MsgRegisterCounterpartyAddress + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() {}, + true, + }, + { + "validate with incorrect destination relayer address", + func() { + msg.Address = "invalid-address" + }, + false, + }, + { + "invalid counterparty address", + func() { + msg.CounterpartyAddress = "" + }, + false, + }, + { + "invalid counterparty address: whitespaced empty string", + func() { + msg.CounterpartyAddress = " " + }, + false, + }, + { + "invalid channelID", + func() { + msg.ChannelId = "" + }, + false, + }, + } + + for i, tc := range testCases { + msg = types.NewMsgRegisterCounterpartyAddress(defaultAccAddress, defaultAccAddress, ibctesting.FirstChannelID) + + tc.malleate() + + err := msg.ValidateBasic() + + if tc.expPass { + require.NoError(t, err, "valid test case %d failed: %s", i, tc.name) + } else { + require.Error(t, err, "invalid test case %d passed: %s", i, tc.name) + } + } +} + +func TestRegisterCountepartyAddressGetSigners(t *testing.T) { + accAddress := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) + msg := types.NewMsgRegisterCounterpartyAddress(accAddress.String(), defaultAccAddress, ibctesting.FirstChannelID) + require.Equal(t, []sdk.AccAddress{sdk.AccAddress(accAddress)}, msg.GetSigners()) +} + +func TestMsgPayPacketFeeValidation(t *testing.T) { + var ( + msg *types.MsgPayPacketFee + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() {}, + true, + }, + { + "invalid channelID", + func() { + msg.SourceChannelId = "" + }, + false, + }, + { + "invalid portID", + func() { + msg.SourcePortId = "" + }, + false, + }, + { + "relayers is not nil", + func() { + msg.Relayers = []string{defaultAccAddress} + }, + false, + }, + { + "invalid signer address", + func() { + msg.Signer = "invalid-address" + }, + false, + }, + } + + for _, tc := range testCases { + fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) + msg = types.NewMsgPayPacketFee(fee, ibctesting.MockFeePort, ibctesting.FirstChannelID, defaultAccAddress, nil) + + tc.malleate() // malleate mutates test data + + err := msg.ValidateBasic() + + if tc.expPass { + require.NoError(t, err) + } else { + require.Error(t, err) + } + } +} + +func TestPayPacketFeeGetSigners(t *testing.T) { + refundAddr := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) + fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) + msg := types.NewMsgPayPacketFee(fee, ibctesting.MockFeePort, ibctesting.FirstChannelID, refundAddr.String(), nil) + + require.Equal(t, []sdk.AccAddress{refundAddr}, msg.GetSigners()) +} + +func TestMsgPayPacketFeeRoute(t *testing.T) { + var msg types.MsgPayPacketFee + require.Equal(t, types.RouterKey, msg.Route()) +} + +func TestMsgPayPacketFeeType(t *testing.T) { + var msg types.MsgPayPacketFee + require.Equal(t, "payPacketFee", msg.Type()) +} + +func TestMsgPayPacketFeeGetSignBytes(t *testing.T) { + fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) + msg := types.NewMsgPayPacketFee(fee, ibctesting.MockFeePort, ibctesting.FirstChannelID, defaultAccAddress, nil) + + require.NotPanics(t, func() { + _ = msg.GetSignBytes() + }) +} + +func TestMsgPayPacketFeeAsyncValidation(t *testing.T) { + var ( + msg *types.MsgPayPacketFeeAsync + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() {}, + true, + }, + { + "invalid channelID", + func() { + msg.PacketId.ChannelId = "" + }, + false, + }, + { + "invalid portID", + func() { + msg.PacketId.PortId = "" + }, + false, + }, + { + "invalid sequence", + func() { + msg.PacketId.Sequence = 0 + }, + false, + }, + { + "relayers is not nil", + func() { + msg.PacketFee.Relayers = []string{defaultAccAddress} + }, + false, + }, + { + "invalid signer address", + func() { + msg.PacketFee.RefundAddress = "invalid-addr" + }, + false, + }, + { + "should fail when all fees are invalid", + func() { + msg.PacketFee.Fee.AckFee = invalidFee + msg.PacketFee.Fee.RecvFee = invalidFee + msg.PacketFee.Fee.TimeoutFee = invalidFee + }, + false, + }, + { + "should fail with single invalid fee", + func() { + msg.PacketFee.Fee.AckFee = invalidFee + }, + false, + }, + { + "should fail with two invalid fees", + func() { + msg.PacketFee.Fee.AckFee = invalidFee + msg.PacketFee.Fee.TimeoutFee = invalidFee + }, + false, + }, + { + "should pass with two empty fees", + func() { + msg.PacketFee.Fee.AckFee = sdk.Coins{} + msg.PacketFee.Fee.TimeoutFee = sdk.Coins{} + }, + true, + }, + { + "should pass with one empty fee", + func() { + msg.PacketFee.Fee.TimeoutFee = sdk.Coins{} + }, + true, + }, + { + "should fail if all fees are empty", + func() { + msg.PacketFee.Fee.AckFee = sdk.Coins{} + msg.PacketFee.Fee.RecvFee = sdk.Coins{} + msg.PacketFee.Fee.TimeoutFee = sdk.Coins{} + }, + false, + }, + } + + for _, tc := range testCases { + packetID := channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1) + fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) + packetFee := types.NewPacketFee(fee, defaultAccAddress, nil) + + msg = types.NewMsgPayPacketFeeAsync(packetID, packetFee) + + tc.malleate() // malleate mutates test data + + err := msg.ValidateBasic() + + if tc.expPass { + require.NoError(t, err) + } else { + require.Error(t, err) + } + } +} + +func TestPayPacketFeeAsyncGetSigners(t *testing.T) { + refundAddr := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) + packetID := channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1) + fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) + packetFee := types.NewPacketFee(fee, refundAddr.String(), nil) + + msg := types.NewMsgPayPacketFeeAsync(packetID, packetFee) + + require.Equal(t, []sdk.AccAddress{refundAddr}, msg.GetSigners()) +} + +func TestMsgPayPacketFeeAsyncRoute(t *testing.T) { + var msg types.MsgPayPacketFeeAsync + require.Equal(t, types.RouterKey, msg.Route()) +} + +func TestMsgPayPacketFeeAsyncType(t *testing.T) { + var msg types.MsgPayPacketFeeAsync + require.Equal(t, "payPacketFeeAsync", msg.Type()) +} + +func TestMsgPayPacketFeeAsyncGetSignBytes(t *testing.T) { + packetID := channeltypes.NewPacketId(ibctesting.MockFeePort, ibctesting.FirstChannelID, 1) + fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) + packetFee := types.NewPacketFee(fee, defaultAccAddress, nil) + + msg := types.NewMsgPayPacketFeeAsync(packetID, packetFee) + + require.NotPanics(t, func() { + _ = msg.GetSignBytes() + }) +} diff --git a/modules/apps/29-fee/types/query.pb.go b/modules/apps/29-fee/types/query.pb.go new file mode 100644 index 00000000000..32677378fe3 --- /dev/null +++ b/modules/apps/29-fee/types/query.pb.go @@ -0,0 +1,4162 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/applications/fee/v1/query.proto + +package types + +import ( + context "context" + fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types1 "github.com/cosmos/cosmos-sdk/types" + query "github.com/cosmos/cosmos-sdk/types/query" + types "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + _ "github.com/gogo/protobuf/gogoproto" + grpc1 "github.com/gogo/protobuf/grpc" + proto "github.com/gogo/protobuf/proto" + _ "google.golang.org/genproto/googleapis/api/annotations" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// QueryIncentivizedPacketsRequest defines the request type for the IncentivizedPackets rpc +type QueryIncentivizedPacketsRequest struct { + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,1,opt,name=pagination,proto3" json:"pagination,omitempty"` + // block height at which to query + QueryHeight uint64 `protobuf:"varint,2,opt,name=query_height,json=queryHeight,proto3" json:"query_height,omitempty"` +} + +func (m *QueryIncentivizedPacketsRequest) Reset() { *m = QueryIncentivizedPacketsRequest{} } +func (m *QueryIncentivizedPacketsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryIncentivizedPacketsRequest) ProtoMessage() {} +func (*QueryIncentivizedPacketsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_0638a8a78ca2503c, []int{0} +} +func (m *QueryIncentivizedPacketsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryIncentivizedPacketsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryIncentivizedPacketsRequest.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 *QueryIncentivizedPacketsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryIncentivizedPacketsRequest.Merge(m, src) +} +func (m *QueryIncentivizedPacketsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryIncentivizedPacketsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryIncentivizedPacketsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryIncentivizedPacketsRequest proto.InternalMessageInfo + +func (m *QueryIncentivizedPacketsRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +func (m *QueryIncentivizedPacketsRequest) GetQueryHeight() uint64 { + if m != nil { + return m.QueryHeight + } + return 0 +} + +// QueryIncentivizedPacketsResponse defines the response type for the IncentivizedPackets rpc +type QueryIncentivizedPacketsResponse struct { + // list of identified fees for incentivized packets + IncentivizedPackets []IdentifiedPacketFees `protobuf:"bytes,1,rep,name=incentivized_packets,json=incentivizedPackets,proto3" json:"incentivized_packets"` +} + +func (m *QueryIncentivizedPacketsResponse) Reset() { *m = QueryIncentivizedPacketsResponse{} } +func (m *QueryIncentivizedPacketsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryIncentivizedPacketsResponse) ProtoMessage() {} +func (*QueryIncentivizedPacketsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_0638a8a78ca2503c, []int{1} +} +func (m *QueryIncentivizedPacketsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryIncentivizedPacketsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryIncentivizedPacketsResponse.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 *QueryIncentivizedPacketsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryIncentivizedPacketsResponse.Merge(m, src) +} +func (m *QueryIncentivizedPacketsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryIncentivizedPacketsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryIncentivizedPacketsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryIncentivizedPacketsResponse proto.InternalMessageInfo + +func (m *QueryIncentivizedPacketsResponse) GetIncentivizedPackets() []IdentifiedPacketFees { + if m != nil { + return m.IncentivizedPackets + } + return nil +} + +// QueryIncentivizedPacketRequest defines the request type for the IncentivizedPacket rpc +type QueryIncentivizedPacketRequest struct { + // unique packet identifier comprised of channel ID, port ID and sequence + PacketId types.PacketId `protobuf:"bytes,1,opt,name=packet_id,json=packetId,proto3" json:"packet_id"` + // block height at which to query + QueryHeight uint64 `protobuf:"varint,2,opt,name=query_height,json=queryHeight,proto3" json:"query_height,omitempty"` +} + +func (m *QueryIncentivizedPacketRequest) Reset() { *m = QueryIncentivizedPacketRequest{} } +func (m *QueryIncentivizedPacketRequest) String() string { return proto.CompactTextString(m) } +func (*QueryIncentivizedPacketRequest) ProtoMessage() {} +func (*QueryIncentivizedPacketRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_0638a8a78ca2503c, []int{2} +} +func (m *QueryIncentivizedPacketRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryIncentivizedPacketRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryIncentivizedPacketRequest.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 *QueryIncentivizedPacketRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryIncentivizedPacketRequest.Merge(m, src) +} +func (m *QueryIncentivizedPacketRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryIncentivizedPacketRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryIncentivizedPacketRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryIncentivizedPacketRequest proto.InternalMessageInfo + +func (m *QueryIncentivizedPacketRequest) GetPacketId() types.PacketId { + if m != nil { + return m.PacketId + } + return types.PacketId{} +} + +func (m *QueryIncentivizedPacketRequest) GetQueryHeight() uint64 { + if m != nil { + return m.QueryHeight + } + return 0 +} + +// QueryIncentivizedPacketsResponse defines the response type for the IncentivizedPacket rpc +type QueryIncentivizedPacketResponse struct { + // the identified fees for the incentivized packet + IncentivizedPacket IdentifiedPacketFees `protobuf:"bytes,1,opt,name=incentivized_packet,json=incentivizedPacket,proto3" json:"incentivized_packet"` +} + +func (m *QueryIncentivizedPacketResponse) Reset() { *m = QueryIncentivizedPacketResponse{} } +func (m *QueryIncentivizedPacketResponse) String() string { return proto.CompactTextString(m) } +func (*QueryIncentivizedPacketResponse) ProtoMessage() {} +func (*QueryIncentivizedPacketResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_0638a8a78ca2503c, []int{3} +} +func (m *QueryIncentivizedPacketResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryIncentivizedPacketResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryIncentivizedPacketResponse.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 *QueryIncentivizedPacketResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryIncentivizedPacketResponse.Merge(m, src) +} +func (m *QueryIncentivizedPacketResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryIncentivizedPacketResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryIncentivizedPacketResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryIncentivizedPacketResponse proto.InternalMessageInfo + +func (m *QueryIncentivizedPacketResponse) GetIncentivizedPacket() IdentifiedPacketFees { + if m != nil { + return m.IncentivizedPacket + } + return IdentifiedPacketFees{} +} + +// QueryIncentivizedPacketsForChannelRequest defines the request type for querying for all incentivized packets +// for a specific channel +type QueryIncentivizedPacketsForChannelRequest struct { + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,1,opt,name=pagination,proto3" json:"pagination,omitempty"` + PortId string `protobuf:"bytes,2,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty"` + ChannelId string `protobuf:"bytes,3,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty"` + // Height to query at + QueryHeight uint64 `protobuf:"varint,4,opt,name=query_height,json=queryHeight,proto3" json:"query_height,omitempty"` +} + +func (m *QueryIncentivizedPacketsForChannelRequest) Reset() { + *m = QueryIncentivizedPacketsForChannelRequest{} +} +func (m *QueryIncentivizedPacketsForChannelRequest) String() string { + return proto.CompactTextString(m) +} +func (*QueryIncentivizedPacketsForChannelRequest) ProtoMessage() {} +func (*QueryIncentivizedPacketsForChannelRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_0638a8a78ca2503c, []int{4} +} +func (m *QueryIncentivizedPacketsForChannelRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryIncentivizedPacketsForChannelRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryIncentivizedPacketsForChannelRequest.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 *QueryIncentivizedPacketsForChannelRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryIncentivizedPacketsForChannelRequest.Merge(m, src) +} +func (m *QueryIncentivizedPacketsForChannelRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryIncentivizedPacketsForChannelRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryIncentivizedPacketsForChannelRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryIncentivizedPacketsForChannelRequest proto.InternalMessageInfo + +func (m *QueryIncentivizedPacketsForChannelRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +func (m *QueryIncentivizedPacketsForChannelRequest) GetPortId() string { + if m != nil { + return m.PortId + } + return "" +} + +func (m *QueryIncentivizedPacketsForChannelRequest) GetChannelId() string { + if m != nil { + return m.ChannelId + } + return "" +} + +func (m *QueryIncentivizedPacketsForChannelRequest) GetQueryHeight() uint64 { + if m != nil { + return m.QueryHeight + } + return 0 +} + +// QueryIncentivizedPacketsResponse defines the response type for the incentivized packets RPC +type QueryIncentivizedPacketsForChannelResponse struct { + // Map of all incentivized_packets + IncentivizedPackets []*IdentifiedPacketFees `protobuf:"bytes,1,rep,name=incentivized_packets,json=incentivizedPackets,proto3" json:"incentivized_packets,omitempty"` +} + +func (m *QueryIncentivizedPacketsForChannelResponse) Reset() { + *m = QueryIncentivizedPacketsForChannelResponse{} +} +func (m *QueryIncentivizedPacketsForChannelResponse) String() string { + return proto.CompactTextString(m) +} +func (*QueryIncentivizedPacketsForChannelResponse) ProtoMessage() {} +func (*QueryIncentivizedPacketsForChannelResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_0638a8a78ca2503c, []int{5} +} +func (m *QueryIncentivizedPacketsForChannelResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryIncentivizedPacketsForChannelResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryIncentivizedPacketsForChannelResponse.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 *QueryIncentivizedPacketsForChannelResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryIncentivizedPacketsForChannelResponse.Merge(m, src) +} +func (m *QueryIncentivizedPacketsForChannelResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryIncentivizedPacketsForChannelResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryIncentivizedPacketsForChannelResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryIncentivizedPacketsForChannelResponse proto.InternalMessageInfo + +func (m *QueryIncentivizedPacketsForChannelResponse) GetIncentivizedPackets() []*IdentifiedPacketFees { + if m != nil { + return m.IncentivizedPackets + } + return nil +} + +// QueryTotalRecvFeesRequest defines the request type for the TotalRecvFees rpc +type QueryTotalRecvFeesRequest struct { + // the packet identifier for the associated fees + PacketId types.PacketId `protobuf:"bytes,1,opt,name=packet_id,json=packetId,proto3" json:"packet_id"` +} + +func (m *QueryTotalRecvFeesRequest) Reset() { *m = QueryTotalRecvFeesRequest{} } +func (m *QueryTotalRecvFeesRequest) String() string { return proto.CompactTextString(m) } +func (*QueryTotalRecvFeesRequest) ProtoMessage() {} +func (*QueryTotalRecvFeesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_0638a8a78ca2503c, []int{6} +} +func (m *QueryTotalRecvFeesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryTotalRecvFeesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryTotalRecvFeesRequest.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 *QueryTotalRecvFeesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryTotalRecvFeesRequest.Merge(m, src) +} +func (m *QueryTotalRecvFeesRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryTotalRecvFeesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryTotalRecvFeesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryTotalRecvFeesRequest proto.InternalMessageInfo + +func (m *QueryTotalRecvFeesRequest) GetPacketId() types.PacketId { + if m != nil { + return m.PacketId + } + return types.PacketId{} +} + +// QueryTotalRecvFeesResponse defines the response type for the TotalRecvFees rpc +type QueryTotalRecvFeesResponse struct { + // the total packet receive fees + RecvFees github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=recv_fees,json=recvFees,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"recv_fees" yaml:"recv_fees"` +} + +func (m *QueryTotalRecvFeesResponse) Reset() { *m = QueryTotalRecvFeesResponse{} } +func (m *QueryTotalRecvFeesResponse) String() string { return proto.CompactTextString(m) } +func (*QueryTotalRecvFeesResponse) ProtoMessage() {} +func (*QueryTotalRecvFeesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_0638a8a78ca2503c, []int{7} +} +func (m *QueryTotalRecvFeesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryTotalRecvFeesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryTotalRecvFeesResponse.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 *QueryTotalRecvFeesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryTotalRecvFeesResponse.Merge(m, src) +} +func (m *QueryTotalRecvFeesResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryTotalRecvFeesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryTotalRecvFeesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryTotalRecvFeesResponse proto.InternalMessageInfo + +func (m *QueryTotalRecvFeesResponse) GetRecvFees() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.RecvFees + } + return nil +} + +// QueryTotalAckFeesRequest defines the request type for the TotalAckFees rpc +type QueryTotalAckFeesRequest struct { + // the packet identifier for the associated fees + PacketId types.PacketId `protobuf:"bytes,1,opt,name=packet_id,json=packetId,proto3" json:"packet_id"` +} + +func (m *QueryTotalAckFeesRequest) Reset() { *m = QueryTotalAckFeesRequest{} } +func (m *QueryTotalAckFeesRequest) String() string { return proto.CompactTextString(m) } +func (*QueryTotalAckFeesRequest) ProtoMessage() {} +func (*QueryTotalAckFeesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_0638a8a78ca2503c, []int{8} +} +func (m *QueryTotalAckFeesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryTotalAckFeesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryTotalAckFeesRequest.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 *QueryTotalAckFeesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryTotalAckFeesRequest.Merge(m, src) +} +func (m *QueryTotalAckFeesRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryTotalAckFeesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryTotalAckFeesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryTotalAckFeesRequest proto.InternalMessageInfo + +func (m *QueryTotalAckFeesRequest) GetPacketId() types.PacketId { + if m != nil { + return m.PacketId + } + return types.PacketId{} +} + +// QueryTotalAckFeesResponse defines the response type for the TotalAckFees rpc +type QueryTotalAckFeesResponse struct { + // the total packet acknowledgement fees + AckFees github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=ack_fees,json=ackFees,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"ack_fees" yaml:"ack_fees"` +} + +func (m *QueryTotalAckFeesResponse) Reset() { *m = QueryTotalAckFeesResponse{} } +func (m *QueryTotalAckFeesResponse) String() string { return proto.CompactTextString(m) } +func (*QueryTotalAckFeesResponse) ProtoMessage() {} +func (*QueryTotalAckFeesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_0638a8a78ca2503c, []int{9} +} +func (m *QueryTotalAckFeesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryTotalAckFeesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryTotalAckFeesResponse.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 *QueryTotalAckFeesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryTotalAckFeesResponse.Merge(m, src) +} +func (m *QueryTotalAckFeesResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryTotalAckFeesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryTotalAckFeesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryTotalAckFeesResponse proto.InternalMessageInfo + +func (m *QueryTotalAckFeesResponse) GetAckFees() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.AckFees + } + return nil +} + +// QueryTotalTimeoutFeesRequest defines the request type for the TotalTimeoutFees rpc +type QueryTotalTimeoutFeesRequest struct { + // the packet identifier for the associated fees + PacketId types.PacketId `protobuf:"bytes,1,opt,name=packet_id,json=packetId,proto3" json:"packet_id"` +} + +func (m *QueryTotalTimeoutFeesRequest) Reset() { *m = QueryTotalTimeoutFeesRequest{} } +func (m *QueryTotalTimeoutFeesRequest) String() string { return proto.CompactTextString(m) } +func (*QueryTotalTimeoutFeesRequest) ProtoMessage() {} +func (*QueryTotalTimeoutFeesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_0638a8a78ca2503c, []int{10} +} +func (m *QueryTotalTimeoutFeesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryTotalTimeoutFeesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryTotalTimeoutFeesRequest.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 *QueryTotalTimeoutFeesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryTotalTimeoutFeesRequest.Merge(m, src) +} +func (m *QueryTotalTimeoutFeesRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryTotalTimeoutFeesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryTotalTimeoutFeesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryTotalTimeoutFeesRequest proto.InternalMessageInfo + +func (m *QueryTotalTimeoutFeesRequest) GetPacketId() types.PacketId { + if m != nil { + return m.PacketId + } + return types.PacketId{} +} + +// QueryTotalTimeoutFeesResponse defines the response type for the TotalTimeoutFees rpc +type QueryTotalTimeoutFeesResponse struct { + // the total packet timeout fees + TimeoutFees github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=timeout_fees,json=timeoutFees,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"timeout_fees" yaml:"timeout_fees"` +} + +func (m *QueryTotalTimeoutFeesResponse) Reset() { *m = QueryTotalTimeoutFeesResponse{} } +func (m *QueryTotalTimeoutFeesResponse) String() string { return proto.CompactTextString(m) } +func (*QueryTotalTimeoutFeesResponse) ProtoMessage() {} +func (*QueryTotalTimeoutFeesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_0638a8a78ca2503c, []int{11} +} +func (m *QueryTotalTimeoutFeesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryTotalTimeoutFeesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryTotalTimeoutFeesResponse.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 *QueryTotalTimeoutFeesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryTotalTimeoutFeesResponse.Merge(m, src) +} +func (m *QueryTotalTimeoutFeesResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryTotalTimeoutFeesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryTotalTimeoutFeesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryTotalTimeoutFeesResponse proto.InternalMessageInfo + +func (m *QueryTotalTimeoutFeesResponse) GetTimeoutFees() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.TimeoutFees + } + return nil +} + +// QueryCounterpartyAddressRequest defines the request type for the CounterpartyAddress rpc +type QueryCounterpartyAddressRequest struct { + // unique channel identifier + ChannelId string `protobuf:"bytes,1,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty" yaml:"channel_id"` + // the relayer address to which the counterparty is registered + RelayerAddress string `protobuf:"bytes,2,opt,name=relayer_address,json=relayerAddress,proto3" json:"relayer_address,omitempty" yaml:"relayer_address"` +} + +func (m *QueryCounterpartyAddressRequest) Reset() { *m = QueryCounterpartyAddressRequest{} } +func (m *QueryCounterpartyAddressRequest) String() string { return proto.CompactTextString(m) } +func (*QueryCounterpartyAddressRequest) ProtoMessage() {} +func (*QueryCounterpartyAddressRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_0638a8a78ca2503c, []int{12} +} +func (m *QueryCounterpartyAddressRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryCounterpartyAddressRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryCounterpartyAddressRequest.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 *QueryCounterpartyAddressRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryCounterpartyAddressRequest.Merge(m, src) +} +func (m *QueryCounterpartyAddressRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryCounterpartyAddressRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryCounterpartyAddressRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryCounterpartyAddressRequest proto.InternalMessageInfo + +func (m *QueryCounterpartyAddressRequest) GetChannelId() string { + if m != nil { + return m.ChannelId + } + return "" +} + +func (m *QueryCounterpartyAddressRequest) GetRelayerAddress() string { + if m != nil { + return m.RelayerAddress + } + return "" +} + +// QueryCounterpartyAddressResponse defines the response type for the CounterpartyAddress rpc +type QueryCounterpartyAddressResponse struct { + // the counterparty address used to compensate forward relaying + CounterpartyAddress string `protobuf:"bytes,1,opt,name=counterparty_address,json=counterpartyAddress,proto3" json:"counterparty_address,omitempty" yaml:"counterparty_address"` +} + +func (m *QueryCounterpartyAddressResponse) Reset() { *m = QueryCounterpartyAddressResponse{} } +func (m *QueryCounterpartyAddressResponse) String() string { return proto.CompactTextString(m) } +func (*QueryCounterpartyAddressResponse) ProtoMessage() {} +func (*QueryCounterpartyAddressResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_0638a8a78ca2503c, []int{13} +} +func (m *QueryCounterpartyAddressResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryCounterpartyAddressResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryCounterpartyAddressResponse.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 *QueryCounterpartyAddressResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryCounterpartyAddressResponse.Merge(m, src) +} +func (m *QueryCounterpartyAddressResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryCounterpartyAddressResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryCounterpartyAddressResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryCounterpartyAddressResponse proto.InternalMessageInfo + +func (m *QueryCounterpartyAddressResponse) GetCounterpartyAddress() string { + if m != nil { + return m.CounterpartyAddress + } + return "" +} + +// QueryFeeEnabledChannelsRequest defines the request type for the FeeEnabledChannels rpc +type QueryFeeEnabledChannelsRequest struct { + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,1,opt,name=pagination,proto3" json:"pagination,omitempty"` + // block height at which to query + QueryHeight uint64 `protobuf:"varint,2,opt,name=query_height,json=queryHeight,proto3" json:"query_height,omitempty"` +} + +func (m *QueryFeeEnabledChannelsRequest) Reset() { *m = QueryFeeEnabledChannelsRequest{} } +func (m *QueryFeeEnabledChannelsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryFeeEnabledChannelsRequest) ProtoMessage() {} +func (*QueryFeeEnabledChannelsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_0638a8a78ca2503c, []int{14} +} +func (m *QueryFeeEnabledChannelsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryFeeEnabledChannelsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryFeeEnabledChannelsRequest.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 *QueryFeeEnabledChannelsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryFeeEnabledChannelsRequest.Merge(m, src) +} +func (m *QueryFeeEnabledChannelsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryFeeEnabledChannelsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryFeeEnabledChannelsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryFeeEnabledChannelsRequest proto.InternalMessageInfo + +func (m *QueryFeeEnabledChannelsRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +func (m *QueryFeeEnabledChannelsRequest) GetQueryHeight() uint64 { + if m != nil { + return m.QueryHeight + } + return 0 +} + +// QueryFeeEnabledChannelsResponse defines the response type for the FeeEnabledChannels rpc +type QueryFeeEnabledChannelsResponse struct { + // list of fee enabled channels + FeeEnabledChannels []FeeEnabledChannel `protobuf:"bytes,1,rep,name=fee_enabled_channels,json=feeEnabledChannels,proto3" json:"fee_enabled_channels" yaml:"fee_enabled_channels"` +} + +func (m *QueryFeeEnabledChannelsResponse) Reset() { *m = QueryFeeEnabledChannelsResponse{} } +func (m *QueryFeeEnabledChannelsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryFeeEnabledChannelsResponse) ProtoMessage() {} +func (*QueryFeeEnabledChannelsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_0638a8a78ca2503c, []int{15} +} +func (m *QueryFeeEnabledChannelsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryFeeEnabledChannelsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryFeeEnabledChannelsResponse.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 *QueryFeeEnabledChannelsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryFeeEnabledChannelsResponse.Merge(m, src) +} +func (m *QueryFeeEnabledChannelsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryFeeEnabledChannelsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryFeeEnabledChannelsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryFeeEnabledChannelsResponse proto.InternalMessageInfo + +func (m *QueryFeeEnabledChannelsResponse) GetFeeEnabledChannels() []FeeEnabledChannel { + if m != nil { + return m.FeeEnabledChannels + } + return nil +} + +// QueryFeeEnabledChannelRequest defines the request type for the FeeEnabledChannel rpc +type QueryFeeEnabledChannelRequest struct { + // unique port identifier + PortId string `protobuf:"bytes,1,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty" yaml:"port_id"` + // unique channel identifier + ChannelId string `protobuf:"bytes,2,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty" yaml:"channel_id"` +} + +func (m *QueryFeeEnabledChannelRequest) Reset() { *m = QueryFeeEnabledChannelRequest{} } +func (m *QueryFeeEnabledChannelRequest) String() string { return proto.CompactTextString(m) } +func (*QueryFeeEnabledChannelRequest) ProtoMessage() {} +func (*QueryFeeEnabledChannelRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_0638a8a78ca2503c, []int{16} +} +func (m *QueryFeeEnabledChannelRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryFeeEnabledChannelRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryFeeEnabledChannelRequest.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 *QueryFeeEnabledChannelRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryFeeEnabledChannelRequest.Merge(m, src) +} +func (m *QueryFeeEnabledChannelRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryFeeEnabledChannelRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryFeeEnabledChannelRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryFeeEnabledChannelRequest proto.InternalMessageInfo + +func (m *QueryFeeEnabledChannelRequest) GetPortId() string { + if m != nil { + return m.PortId + } + return "" +} + +func (m *QueryFeeEnabledChannelRequest) GetChannelId() string { + if m != nil { + return m.ChannelId + } + return "" +} + +// QueryFeeEnabledChannelResponse defines the response type for the FeeEnabledChannel rpc +type QueryFeeEnabledChannelResponse struct { + // boolean flag representing the fee enabled channel status + FeeEnabled bool `protobuf:"varint,1,opt,name=fee_enabled,json=feeEnabled,proto3" json:"fee_enabled,omitempty" yaml:"fee_enabled"` +} + +func (m *QueryFeeEnabledChannelResponse) Reset() { *m = QueryFeeEnabledChannelResponse{} } +func (m *QueryFeeEnabledChannelResponse) String() string { return proto.CompactTextString(m) } +func (*QueryFeeEnabledChannelResponse) ProtoMessage() {} +func (*QueryFeeEnabledChannelResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_0638a8a78ca2503c, []int{17} +} +func (m *QueryFeeEnabledChannelResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryFeeEnabledChannelResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryFeeEnabledChannelResponse.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 *QueryFeeEnabledChannelResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryFeeEnabledChannelResponse.Merge(m, src) +} +func (m *QueryFeeEnabledChannelResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryFeeEnabledChannelResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryFeeEnabledChannelResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryFeeEnabledChannelResponse proto.InternalMessageInfo + +func (m *QueryFeeEnabledChannelResponse) GetFeeEnabled() bool { + if m != nil { + return m.FeeEnabled + } + return false +} + +func init() { + proto.RegisterType((*QueryIncentivizedPacketsRequest)(nil), "ibc.applications.fee.v1.QueryIncentivizedPacketsRequest") + proto.RegisterType((*QueryIncentivizedPacketsResponse)(nil), "ibc.applications.fee.v1.QueryIncentivizedPacketsResponse") + proto.RegisterType((*QueryIncentivizedPacketRequest)(nil), "ibc.applications.fee.v1.QueryIncentivizedPacketRequest") + proto.RegisterType((*QueryIncentivizedPacketResponse)(nil), "ibc.applications.fee.v1.QueryIncentivizedPacketResponse") + proto.RegisterType((*QueryIncentivizedPacketsForChannelRequest)(nil), "ibc.applications.fee.v1.QueryIncentivizedPacketsForChannelRequest") + proto.RegisterType((*QueryIncentivizedPacketsForChannelResponse)(nil), "ibc.applications.fee.v1.QueryIncentivizedPacketsForChannelResponse") + proto.RegisterType((*QueryTotalRecvFeesRequest)(nil), "ibc.applications.fee.v1.QueryTotalRecvFeesRequest") + proto.RegisterType((*QueryTotalRecvFeesResponse)(nil), "ibc.applications.fee.v1.QueryTotalRecvFeesResponse") + proto.RegisterType((*QueryTotalAckFeesRequest)(nil), "ibc.applications.fee.v1.QueryTotalAckFeesRequest") + proto.RegisterType((*QueryTotalAckFeesResponse)(nil), "ibc.applications.fee.v1.QueryTotalAckFeesResponse") + proto.RegisterType((*QueryTotalTimeoutFeesRequest)(nil), "ibc.applications.fee.v1.QueryTotalTimeoutFeesRequest") + proto.RegisterType((*QueryTotalTimeoutFeesResponse)(nil), "ibc.applications.fee.v1.QueryTotalTimeoutFeesResponse") + proto.RegisterType((*QueryCounterpartyAddressRequest)(nil), "ibc.applications.fee.v1.QueryCounterpartyAddressRequest") + proto.RegisterType((*QueryCounterpartyAddressResponse)(nil), "ibc.applications.fee.v1.QueryCounterpartyAddressResponse") + proto.RegisterType((*QueryFeeEnabledChannelsRequest)(nil), "ibc.applications.fee.v1.QueryFeeEnabledChannelsRequest") + proto.RegisterType((*QueryFeeEnabledChannelsResponse)(nil), "ibc.applications.fee.v1.QueryFeeEnabledChannelsResponse") + proto.RegisterType((*QueryFeeEnabledChannelRequest)(nil), "ibc.applications.fee.v1.QueryFeeEnabledChannelRequest") + proto.RegisterType((*QueryFeeEnabledChannelResponse)(nil), "ibc.applications.fee.v1.QueryFeeEnabledChannelResponse") +} + +func init() { + proto.RegisterFile("ibc/applications/fee/v1/query.proto", fileDescriptor_0638a8a78ca2503c) +} + +var fileDescriptor_0638a8a78ca2503c = []byte{ + // 1281 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x58, 0x4f, 0x6f, 0x1b, 0x45, + 0x1c, 0xcd, 0xa4, 0xa1, 0x4d, 0x26, 0xa1, 0x4d, 0xc7, 0xa1, 0x4d, 0x4d, 0x62, 0xa7, 0x53, 0x0a, + 0x21, 0x28, 0xbb, 0x8a, 0x43, 0x9b, 0x96, 0x53, 0x6b, 0x43, 0x68, 0x04, 0x82, 0x76, 0x95, 0x0b, + 0x08, 0xe4, 0xae, 0x77, 0xc7, 0xce, 0x2a, 0xce, 0xce, 0x76, 0x77, 0x6d, 0x70, 0xd3, 0x20, 0x35, + 0x52, 0x84, 0x04, 0x15, 0x42, 0x42, 0xe2, 0x80, 0xb8, 0x22, 0x24, 0x24, 0xb8, 0xf3, 0x0d, 0x7a, + 0xaa, 0x2a, 0x71, 0xe1, 0x64, 0xaa, 0x84, 0x4f, 0x60, 0x71, 0x40, 0xe2, 0x82, 0x76, 0x66, 0x76, + 0xbd, 0xee, 0xee, 0x26, 0xde, 0x92, 0x88, 0x53, 0xec, 0xf9, 0xfd, 0x99, 0xf7, 0xde, 0xfc, 0x3c, + 0xf3, 0x14, 0x78, 0xc1, 0xa8, 0x68, 0xb2, 0x6a, 0x59, 0x75, 0x43, 0x53, 0x5d, 0x83, 0x9a, 0x8e, + 0x5c, 0x25, 0x44, 0x6e, 0x2e, 0xc8, 0x77, 0x1a, 0xc4, 0x6e, 0x49, 0x96, 0x4d, 0x5d, 0x8a, 0xce, + 0x1a, 0x15, 0x4d, 0x0a, 0x27, 0x49, 0x55, 0x42, 0xa4, 0xe6, 0x42, 0x76, 0xa2, 0x46, 0x6b, 0x94, + 0xe5, 0xc8, 0xde, 0x27, 0x9e, 0x9e, 0x9d, 0xaa, 0x51, 0x5a, 0xab, 0x13, 0x59, 0xb5, 0x0c, 0x59, + 0x35, 0x4d, 0xea, 0x8a, 0x22, 0x1e, 0xcd, 0x69, 0xd4, 0xd9, 0xa0, 0x8e, 0x5c, 0x51, 0x1d, 0x6f, + 0xa3, 0x0a, 0x71, 0xd5, 0x05, 0x59, 0xa3, 0x86, 0x29, 0xe2, 0x73, 0xe1, 0x38, 0x43, 0x11, 0x64, + 0x59, 0x6a, 0xcd, 0x30, 0x59, 0x33, 0x91, 0x7b, 0x3e, 0x09, 0xbd, 0x87, 0x8f, 0xa7, 0x5c, 0x4c, + 0x4a, 0xa9, 0x11, 0x93, 0x38, 0x86, 0x13, 0xee, 0xa4, 0x51, 0x9b, 0xc8, 0xda, 0x9a, 0x6a, 0x9a, + 0xa4, 0xee, 0xa5, 0x88, 0x8f, 0x3c, 0x05, 0x3f, 0x00, 0x30, 0x7f, 0xcb, 0xc3, 0xb3, 0x62, 0x6a, + 0xc4, 0x74, 0x8d, 0xa6, 0x71, 0x97, 0xe8, 0x37, 0x55, 0x6d, 0x9d, 0xb8, 0x8e, 0x42, 0xee, 0x34, + 0x88, 0xe3, 0xa2, 0x65, 0x08, 0xbb, 0x20, 0x27, 0xc1, 0x0c, 0x98, 0x1d, 0x2d, 0xbc, 0x2c, 0x71, + 0x46, 0x92, 0xc7, 0x48, 0xe2, 0xba, 0x0a, 0x46, 0xd2, 0x4d, 0xb5, 0x46, 0x44, 0xad, 0x12, 0xaa, + 0x44, 0xe7, 0xe1, 0x18, 0x4b, 0x2c, 0xaf, 0x11, 0xa3, 0xb6, 0xe6, 0x4e, 0x0e, 0xce, 0x80, 0xd9, + 0x21, 0x65, 0x94, 0xad, 0xdd, 0x60, 0x4b, 0xf8, 0x0b, 0x00, 0x67, 0x92, 0xe1, 0x38, 0x16, 0x35, + 0x1d, 0x82, 0xaa, 0x70, 0xc2, 0x08, 0x85, 0xcb, 0x16, 0x8f, 0x4f, 0x82, 0x99, 0x63, 0xb3, 0xa3, + 0x85, 0x79, 0x29, 0xe1, 0x60, 0xa5, 0x15, 0xdd, 0xab, 0xa9, 0x1a, 0x7e, 0xc7, 0x65, 0x42, 0x9c, + 0xe2, 0xd0, 0xc3, 0x76, 0x7e, 0x40, 0xc9, 0x18, 0xd1, 0xfd, 0xf0, 0x0e, 0x80, 0xb9, 0x04, 0x30, + 0xbe, 0x34, 0xd7, 0xe0, 0x08, 0xdf, 0xbd, 0x6c, 0xe8, 0x42, 0x99, 0x69, 0xb6, 0xbf, 0xa7, 0xba, + 0xe4, 0x4b, 0xdd, 0xf4, 0x34, 0xf1, 0xb2, 0x56, 0x74, 0xb1, 0xdf, 0xb0, 0x25, 0xbe, 0xf7, 0x23, + 0xca, 0xe7, 0xc9, 0x67, 0x14, 0x68, 0xa2, 0xc3, 0x4c, 0x8c, 0x26, 0x02, 0xd2, 0x33, 0x49, 0x82, + 0xa2, 0x92, 0xe0, 0x47, 0x00, 0xbe, 0x9a, 0x74, 0x3c, 0xcb, 0xd4, 0x2e, 0x71, 0xbe, 0x87, 0x3d, + 0x37, 0x67, 0xe1, 0x09, 0x8b, 0xda, 0x4c, 0x62, 0x4f, 0x9d, 0x11, 0xe5, 0xb8, 0xf7, 0x75, 0x45, + 0x47, 0xd3, 0x10, 0x0a, 0x89, 0xbd, 0xd8, 0x31, 0x16, 0x1b, 0x11, 0x2b, 0x31, 0xd2, 0x0e, 0x45, + 0xa5, 0xfd, 0x0a, 0xc0, 0xb9, 0x7e, 0x08, 0x09, 0x95, 0x6f, 0x1f, 0xe2, 0xe4, 0xc5, 0xcf, 0xdc, + 0xc7, 0xf0, 0x1c, 0xc3, 0xb3, 0x4a, 0x5d, 0xb5, 0xae, 0x10, 0xad, 0xc9, 0x52, 0x0f, 0x6b, 0xda, + 0xf0, 0x77, 0x00, 0x66, 0xe3, 0xfa, 0x0b, 0x7e, 0xf7, 0xe0, 0x88, 0x4d, 0xb4, 0x66, 0xb9, 0x4a, + 0x88, 0x4f, 0xea, 0x5c, 0xcf, 0x81, 0xf9, 0x47, 0x55, 0xa2, 0x86, 0x59, 0x7c, 0xd3, 0x6b, 0xde, + 0x69, 0xe7, 0xc7, 0x5b, 0xea, 0x46, 0xfd, 0x0d, 0x1c, 0x54, 0xe2, 0x9f, 0xfe, 0xc8, 0xcf, 0xd6, + 0x0c, 0x77, 0xad, 0x51, 0x91, 0x34, 0xba, 0x21, 0x8b, 0xbb, 0x8f, 0xff, 0x99, 0x77, 0xf4, 0x75, + 0xd9, 0x6d, 0x59, 0xc4, 0x61, 0x4d, 0x1c, 0x65, 0xd8, 0x16, 0x28, 0xf0, 0x47, 0x70, 0xb2, 0x8b, + 0xed, 0xba, 0xb6, 0x7e, 0xb8, 0xd4, 0xbf, 0x05, 0x61, 0x69, 0x83, 0xf6, 0x82, 0x79, 0x0b, 0x0e, + 0xab, 0xda, 0x7a, 0x9f, 0xc4, 0x4b, 0x82, 0xf8, 0x29, 0x4e, 0xdc, 0x2f, 0x4c, 0xc7, 0xfb, 0x84, + 0xca, 0x21, 0xe0, 0xdb, 0x70, 0xaa, 0x8b, 0x6b, 0xd5, 0xd8, 0x20, 0xb4, 0xe1, 0x1e, 0x2e, 0xf5, + 0x1f, 0x01, 0x9c, 0x4e, 0xd8, 0x42, 0xd0, 0xdf, 0x01, 0x70, 0xcc, 0xe5, 0xeb, 0x7d, 0x6a, 0xf0, + 0xb6, 0xd0, 0x20, 0xc3, 0x35, 0x08, 0x17, 0xa7, 0xd3, 0x61, 0xd4, 0xed, 0xe2, 0xc1, 0xdf, 0xfb, + 0x57, 0x5d, 0x89, 0x36, 0x4c, 0x97, 0xd8, 0x96, 0x6a, 0xbb, 0xad, 0xeb, 0xba, 0x6e, 0x13, 0x27, + 0xd0, 0xe3, 0xf5, 0x9e, 0x5f, 0xbd, 0x27, 0xc8, 0x48, 0xf1, 0x85, 0x4e, 0x3b, 0x7f, 0x9a, 0x23, + 0xe9, 0xc6, 0x70, 0xf8, 0x32, 0x28, 0xc1, 0x53, 0x36, 0xa9, 0xab, 0x2d, 0x62, 0x97, 0x55, 0xde, + 0x8f, 0x5f, 0x26, 0xc5, 0x6c, 0xa7, 0x9d, 0x3f, 0xe3, 0x4f, 0x70, 0x4f, 0x02, 0x56, 0x4e, 0x8a, + 0x15, 0x81, 0x00, 0x37, 0xc5, 0xeb, 0x14, 0x8b, 0x4e, 0x48, 0xa9, 0xc0, 0x09, 0x2d, 0x14, 0x0e, + 0x76, 0xe3, 0x40, 0xf3, 0x9d, 0x76, 0xfe, 0x45, 0x01, 0x34, 0x26, 0x0b, 0x2b, 0x19, 0x2d, 0xda, + 0x1b, 0x7f, 0xe9, 0xbf, 0x44, 0xcb, 0x84, 0xbc, 0x65, 0xaa, 0x95, 0x3a, 0xd1, 0xc5, 0xd5, 0xf4, + 0x7f, 0x3c, 0xd2, 0x3f, 0xf8, 0x87, 0x14, 0x87, 0x46, 0xa8, 0x70, 0x1f, 0xc0, 0x89, 0x2a, 0x21, + 0x65, 0xc2, 0xe3, 0x65, 0x71, 0x10, 0xfe, 0x60, 0xcd, 0x25, 0x5e, 0x95, 0x91, 0x9e, 0xc5, 0x0b, + 0x62, 0xd2, 0x84, 0x6c, 0x71, 0x5d, 0xb1, 0x82, 0xaa, 0x11, 0x2c, 0x78, 0xdb, 0x1f, 0xfb, 0x48, + 0x4f, 0x5f, 0xb4, 0xd7, 0xba, 0x2f, 0x0b, 0x3f, 0x1e, 0xd4, 0x69, 0xe7, 0x4f, 0xf2, 0x7d, 0x44, + 0x00, 0x07, 0xaf, 0x4d, 0xef, 0xdc, 0x0d, 0xf6, 0x37, 0x77, 0xf8, 0x83, 0xa4, 0x93, 0x0b, 0xa4, + 0x5a, 0x82, 0xa3, 0x21, 0x4e, 0x0c, 0xc8, 0x70, 0xf1, 0x4c, 0xa7, 0x9d, 0x47, 0x11, 0xc2, 0x58, + 0x81, 0x5d, 0x9e, 0x85, 0x5f, 0xc6, 0xe1, 0x73, 0xac, 0x37, 0xfa, 0x15, 0xc0, 0x4c, 0xcc, 0x0b, + 0x86, 0xae, 0x24, 0xca, 0x7c, 0x80, 0xe7, 0xcb, 0x5e, 0x7d, 0x86, 0x4a, 0xce, 0x07, 0xcf, 0x6f, + 0xff, 0xf6, 0xe7, 0x37, 0x83, 0xaf, 0xa0, 0x8b, 0xb2, 0x70, 0xa9, 0x81, 0x3b, 0x8d, 0x7b, 0x3b, + 0xd1, 0x83, 0x41, 0x88, 0xa2, 0xed, 0xd0, 0x52, 0x5a, 0x00, 0x3e, 0xf2, 0x2b, 0xe9, 0x0b, 0x05, + 0xf0, 0x6d, 0xc0, 0x90, 0xdf, 0x43, 0x77, 0xfb, 0x41, 0x2e, 0x7b, 0x63, 0x21, 0x6f, 0x06, 0x57, + 0xb3, 0x24, 0x06, 0x66, 0x2b, 0x30, 0xdb, 0xa1, 0x58, 0x77, 0x38, 0xb6, 0x64, 0xc7, 0x03, 0x6a, + 0x6a, 0x24, 0x1c, 0xf7, 0xd7, 0xb6, 0xd0, 0x3f, 0x00, 0x4e, 0xef, 0x6b, 0x46, 0x50, 0x31, 0xf5, + 0xd1, 0x44, 0xac, 0x59, 0xb6, 0xf4, 0x9f, 0x7a, 0x08, 0xbd, 0x6e, 0x31, 0xb9, 0xde, 0x41, 0x2b, + 0x7d, 0x1d, 0xb4, 0xaf, 0x57, 0x44, 0xa5, 0x90, 0x36, 0xe8, 0x6f, 0x00, 0x9f, 0xef, 0xb1, 0x26, + 0xa8, 0xb0, 0x3f, 0xd2, 0x38, 0x9f, 0x94, 0x5d, 0x4c, 0x55, 0x23, 0xd8, 0x7c, 0xc6, 0xd8, 0x7c, + 0x8a, 0x9a, 0x11, 0x36, 0xae, 0x97, 0x5f, 0x0e, 0xec, 0xcd, 0x11, 0x1d, 0xfc, 0x5f, 0x00, 0x8e, + 0x85, 0xad, 0x09, 0x5a, 0xe8, 0x83, 0x45, 0xaf, 0x4b, 0xca, 0x16, 0xd2, 0x94, 0x08, 0xde, 0x5b, + 0x8c, 0xf7, 0x27, 0xa8, 0x91, 0xc0, 0xdb, 0x77, 0x37, 0x47, 0x44, 0x7b, 0x67, 0x10, 0x8e, 0x3f, + 0x6d, 0x4b, 0xd0, 0xa5, 0x3e, 0x78, 0x44, 0x9d, 0x52, 0xf6, 0x72, 0xda, 0x32, 0x21, 0xc1, 0x7d, + 0xfe, 0xc3, 0xdf, 0x44, 0xad, 0x04, 0x0d, 0xc2, 0xee, 0xe6, 0x88, 0x74, 0x78, 0x02, 0x60, 0x26, + 0xc6, 0x56, 0x1c, 0x74, 0x85, 0x27, 0xfb, 0xa4, 0x83, 0xae, 0xf0, 0x7d, 0x3c, 0x0c, 0x5e, 0x65, + 0x7a, 0xbc, 0x87, 0xde, 0x8d, 0xe8, 0x11, 0x67, 0x5a, 0xe4, 0xcd, 0xa7, 0x8c, 0x53, 0xc2, 0x8f, + 0xfb, 0x67, 0x00, 0x51, 0xd4, 0x32, 0x1c, 0x74, 0xd3, 0x27, 0x5a, 0x9e, 0x83, 0x6e, 0xfa, 0x64, + 0x77, 0x82, 0x5f, 0x62, 0xfc, 0x72, 0x68, 0x2a, 0xc2, 0x2f, 0xf4, 0xd8, 0xa2, 0x47, 0x00, 0x9e, + 0x8e, 0x34, 0x41, 0x97, 0x53, 0xee, 0xea, 0xa3, 0x5d, 0x4a, 0x5d, 0x27, 0xc0, 0xde, 0x60, 0x60, + 0x8b, 0xe8, 0xda, 0x7e, 0x60, 0xfb, 0xb9, 0x5d, 0x8b, 0xef, 0x3f, 0xdc, 0xcd, 0x81, 0xc7, 0xbb, + 0x39, 0xf0, 0x64, 0x37, 0x07, 0xbe, 0xde, 0xcb, 0x0d, 0x3c, 0xde, 0xcb, 0x0d, 0xfc, 0xbe, 0x97, + 0x1b, 0xf8, 0xf0, 0x52, 0xd4, 0xae, 0x1b, 0x15, 0x6d, 0xbe, 0x46, 0xe5, 0xe6, 0xa2, 0xbc, 0x41, + 0xf5, 0x46, 0x9d, 0x38, 0x7c, 0xeb, 0xc2, 0xd5, 0x79, 0x6f, 0x77, 0xe6, 0xe0, 0x2b, 0xc7, 0xd9, + 0x3f, 0x91, 0x16, 0xff, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x61, 0x8e, 0x05, 0x2f, 0x71, 0x13, 0x00, + 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// QueryClient is the client API for Query service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type QueryClient interface { + // IncentivizedPackets returns all incentivized packets and their associated fees + IncentivizedPackets(ctx context.Context, in *QueryIncentivizedPacketsRequest, opts ...grpc.CallOption) (*QueryIncentivizedPacketsResponse, error) + // IncentivizedPacket returns all packet fees for a packet given its identifier + IncentivizedPacket(ctx context.Context, in *QueryIncentivizedPacketRequest, opts ...grpc.CallOption) (*QueryIncentivizedPacketResponse, error) + // Gets all incentivized packets for a specific channel + IncentivizedPacketsForChannel(ctx context.Context, in *QueryIncentivizedPacketsForChannelRequest, opts ...grpc.CallOption) (*QueryIncentivizedPacketsForChannelResponse, error) + // TotalRecvFees returns the total receive fees for a packet given its identifier + TotalRecvFees(ctx context.Context, in *QueryTotalRecvFeesRequest, opts ...grpc.CallOption) (*QueryTotalRecvFeesResponse, error) + // TotalAckFees returns the total acknowledgement fees for a packet given its identifier + TotalAckFees(ctx context.Context, in *QueryTotalAckFeesRequest, opts ...grpc.CallOption) (*QueryTotalAckFeesResponse, error) + // TotalTimeoutFees returns the total timeout fees for a packet given its identifier + TotalTimeoutFees(ctx context.Context, in *QueryTotalTimeoutFeesRequest, opts ...grpc.CallOption) (*QueryTotalTimeoutFeesResponse, error) + // CounterpartyAddress returns the registered counterparty address for forward relaying + CounterpartyAddress(ctx context.Context, in *QueryCounterpartyAddressRequest, opts ...grpc.CallOption) (*QueryCounterpartyAddressResponse, error) + // FeeEnabledChannels returns a list of all fee enabled channels + FeeEnabledChannels(ctx context.Context, in *QueryFeeEnabledChannelsRequest, opts ...grpc.CallOption) (*QueryFeeEnabledChannelsResponse, error) + // FeeEnabledChannel returns true if the provided port and channel identifiers belong to a fee enabled channel + FeeEnabledChannel(ctx context.Context, in *QueryFeeEnabledChannelRequest, opts ...grpc.CallOption) (*QueryFeeEnabledChannelResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) IncentivizedPackets(ctx context.Context, in *QueryIncentivizedPacketsRequest, opts ...grpc.CallOption) (*QueryIncentivizedPacketsResponse, error) { + out := new(QueryIncentivizedPacketsResponse) + err := c.cc.Invoke(ctx, "/ibc.applications.fee.v1.Query/IncentivizedPackets", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) IncentivizedPacket(ctx context.Context, in *QueryIncentivizedPacketRequest, opts ...grpc.CallOption) (*QueryIncentivizedPacketResponse, error) { + out := new(QueryIncentivizedPacketResponse) + err := c.cc.Invoke(ctx, "/ibc.applications.fee.v1.Query/IncentivizedPacket", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) IncentivizedPacketsForChannel(ctx context.Context, in *QueryIncentivizedPacketsForChannelRequest, opts ...grpc.CallOption) (*QueryIncentivizedPacketsForChannelResponse, error) { + out := new(QueryIncentivizedPacketsForChannelResponse) + err := c.cc.Invoke(ctx, "/ibc.applications.fee.v1.Query/IncentivizedPacketsForChannel", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) TotalRecvFees(ctx context.Context, in *QueryTotalRecvFeesRequest, opts ...grpc.CallOption) (*QueryTotalRecvFeesResponse, error) { + out := new(QueryTotalRecvFeesResponse) + err := c.cc.Invoke(ctx, "/ibc.applications.fee.v1.Query/TotalRecvFees", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) TotalAckFees(ctx context.Context, in *QueryTotalAckFeesRequest, opts ...grpc.CallOption) (*QueryTotalAckFeesResponse, error) { + out := new(QueryTotalAckFeesResponse) + err := c.cc.Invoke(ctx, "/ibc.applications.fee.v1.Query/TotalAckFees", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) TotalTimeoutFees(ctx context.Context, in *QueryTotalTimeoutFeesRequest, opts ...grpc.CallOption) (*QueryTotalTimeoutFeesResponse, error) { + out := new(QueryTotalTimeoutFeesResponse) + err := c.cc.Invoke(ctx, "/ibc.applications.fee.v1.Query/TotalTimeoutFees", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) CounterpartyAddress(ctx context.Context, in *QueryCounterpartyAddressRequest, opts ...grpc.CallOption) (*QueryCounterpartyAddressResponse, error) { + out := new(QueryCounterpartyAddressResponse) + err := c.cc.Invoke(ctx, "/ibc.applications.fee.v1.Query/CounterpartyAddress", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) FeeEnabledChannels(ctx context.Context, in *QueryFeeEnabledChannelsRequest, opts ...grpc.CallOption) (*QueryFeeEnabledChannelsResponse, error) { + out := new(QueryFeeEnabledChannelsResponse) + err := c.cc.Invoke(ctx, "/ibc.applications.fee.v1.Query/FeeEnabledChannels", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) FeeEnabledChannel(ctx context.Context, in *QueryFeeEnabledChannelRequest, opts ...grpc.CallOption) (*QueryFeeEnabledChannelResponse, error) { + out := new(QueryFeeEnabledChannelResponse) + err := c.cc.Invoke(ctx, "/ibc.applications.fee.v1.Query/FeeEnabledChannel", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // IncentivizedPackets returns all incentivized packets and their associated fees + IncentivizedPackets(context.Context, *QueryIncentivizedPacketsRequest) (*QueryIncentivizedPacketsResponse, error) + // IncentivizedPacket returns all packet fees for a packet given its identifier + IncentivizedPacket(context.Context, *QueryIncentivizedPacketRequest) (*QueryIncentivizedPacketResponse, error) + // Gets all incentivized packets for a specific channel + IncentivizedPacketsForChannel(context.Context, *QueryIncentivizedPacketsForChannelRequest) (*QueryIncentivizedPacketsForChannelResponse, error) + // TotalRecvFees returns the total receive fees for a packet given its identifier + TotalRecvFees(context.Context, *QueryTotalRecvFeesRequest) (*QueryTotalRecvFeesResponse, error) + // TotalAckFees returns the total acknowledgement fees for a packet given its identifier + TotalAckFees(context.Context, *QueryTotalAckFeesRequest) (*QueryTotalAckFeesResponse, error) + // TotalTimeoutFees returns the total timeout fees for a packet given its identifier + TotalTimeoutFees(context.Context, *QueryTotalTimeoutFeesRequest) (*QueryTotalTimeoutFeesResponse, error) + // CounterpartyAddress returns the registered counterparty address for forward relaying + CounterpartyAddress(context.Context, *QueryCounterpartyAddressRequest) (*QueryCounterpartyAddressResponse, error) + // FeeEnabledChannels returns a list of all fee enabled channels + FeeEnabledChannels(context.Context, *QueryFeeEnabledChannelsRequest) (*QueryFeeEnabledChannelsResponse, error) + // FeeEnabledChannel returns true if the provided port and channel identifiers belong to a fee enabled channel + FeeEnabledChannel(context.Context, *QueryFeeEnabledChannelRequest) (*QueryFeeEnabledChannelResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) IncentivizedPackets(ctx context.Context, req *QueryIncentivizedPacketsRequest) (*QueryIncentivizedPacketsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method IncentivizedPackets not implemented") +} +func (*UnimplementedQueryServer) IncentivizedPacket(ctx context.Context, req *QueryIncentivizedPacketRequest) (*QueryIncentivizedPacketResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method IncentivizedPacket not implemented") +} +func (*UnimplementedQueryServer) IncentivizedPacketsForChannel(ctx context.Context, req *QueryIncentivizedPacketsForChannelRequest) (*QueryIncentivizedPacketsForChannelResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method IncentivizedPacketsForChannel not implemented") +} +func (*UnimplementedQueryServer) TotalRecvFees(ctx context.Context, req *QueryTotalRecvFeesRequest) (*QueryTotalRecvFeesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method TotalRecvFees not implemented") +} +func (*UnimplementedQueryServer) TotalAckFees(ctx context.Context, req *QueryTotalAckFeesRequest) (*QueryTotalAckFeesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method TotalAckFees not implemented") +} +func (*UnimplementedQueryServer) TotalTimeoutFees(ctx context.Context, req *QueryTotalTimeoutFeesRequest) (*QueryTotalTimeoutFeesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method TotalTimeoutFees not implemented") +} +func (*UnimplementedQueryServer) CounterpartyAddress(ctx context.Context, req *QueryCounterpartyAddressRequest) (*QueryCounterpartyAddressResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CounterpartyAddress not implemented") +} +func (*UnimplementedQueryServer) FeeEnabledChannels(ctx context.Context, req *QueryFeeEnabledChannelsRequest) (*QueryFeeEnabledChannelsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method FeeEnabledChannels not implemented") +} +func (*UnimplementedQueryServer) FeeEnabledChannel(ctx context.Context, req *QueryFeeEnabledChannelRequest) (*QueryFeeEnabledChannelResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method FeeEnabledChannel not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_IncentivizedPackets_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryIncentivizedPacketsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).IncentivizedPackets(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.applications.fee.v1.Query/IncentivizedPackets", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).IncentivizedPackets(ctx, req.(*QueryIncentivizedPacketsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_IncentivizedPacket_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryIncentivizedPacketRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).IncentivizedPacket(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.applications.fee.v1.Query/IncentivizedPacket", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).IncentivizedPacket(ctx, req.(*QueryIncentivizedPacketRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_IncentivizedPacketsForChannel_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryIncentivizedPacketsForChannelRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).IncentivizedPacketsForChannel(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.applications.fee.v1.Query/IncentivizedPacketsForChannel", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).IncentivizedPacketsForChannel(ctx, req.(*QueryIncentivizedPacketsForChannelRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_TotalRecvFees_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryTotalRecvFeesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).TotalRecvFees(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.applications.fee.v1.Query/TotalRecvFees", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).TotalRecvFees(ctx, req.(*QueryTotalRecvFeesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_TotalAckFees_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryTotalAckFeesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).TotalAckFees(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.applications.fee.v1.Query/TotalAckFees", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).TotalAckFees(ctx, req.(*QueryTotalAckFeesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_TotalTimeoutFees_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryTotalTimeoutFeesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).TotalTimeoutFees(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.applications.fee.v1.Query/TotalTimeoutFees", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).TotalTimeoutFees(ctx, req.(*QueryTotalTimeoutFeesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_CounterpartyAddress_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryCounterpartyAddressRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).CounterpartyAddress(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.applications.fee.v1.Query/CounterpartyAddress", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).CounterpartyAddress(ctx, req.(*QueryCounterpartyAddressRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_FeeEnabledChannels_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryFeeEnabledChannelsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).FeeEnabledChannels(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.applications.fee.v1.Query/FeeEnabledChannels", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).FeeEnabledChannels(ctx, req.(*QueryFeeEnabledChannelsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_FeeEnabledChannel_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryFeeEnabledChannelRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).FeeEnabledChannel(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.applications.fee.v1.Query/FeeEnabledChannel", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).FeeEnabledChannel(ctx, req.(*QueryFeeEnabledChannelRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "ibc.applications.fee.v1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "IncentivizedPackets", + Handler: _Query_IncentivizedPackets_Handler, + }, + { + MethodName: "IncentivizedPacket", + Handler: _Query_IncentivizedPacket_Handler, + }, + { + MethodName: "IncentivizedPacketsForChannel", + Handler: _Query_IncentivizedPacketsForChannel_Handler, + }, + { + MethodName: "TotalRecvFees", + Handler: _Query_TotalRecvFees_Handler, + }, + { + MethodName: "TotalAckFees", + Handler: _Query_TotalAckFees_Handler, + }, + { + MethodName: "TotalTimeoutFees", + Handler: _Query_TotalTimeoutFees_Handler, + }, + { + MethodName: "CounterpartyAddress", + Handler: _Query_CounterpartyAddress_Handler, + }, + { + MethodName: "FeeEnabledChannels", + Handler: _Query_FeeEnabledChannels_Handler, + }, + { + MethodName: "FeeEnabledChannel", + Handler: _Query_FeeEnabledChannel_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "ibc/applications/fee/v1/query.proto", +} + +func (m *QueryIncentivizedPacketsRequest) 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 *QueryIncentivizedPacketsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryIncentivizedPacketsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.QueryHeight != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.QueryHeight)) + i-- + dAtA[i] = 0x10 + } + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryIncentivizedPacketsResponse) 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 *QueryIncentivizedPacketsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryIncentivizedPacketsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.IncentivizedPackets) > 0 { + for iNdEx := len(m.IncentivizedPackets) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.IncentivizedPackets[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *QueryIncentivizedPacketRequest) 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 *QueryIncentivizedPacketRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryIncentivizedPacketRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.QueryHeight != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.QueryHeight)) + i-- + dAtA[i] = 0x10 + } + { + size, err := m.PacketId.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *QueryIncentivizedPacketResponse) 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 *QueryIncentivizedPacketResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryIncentivizedPacketResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.IncentivizedPacket.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *QueryIncentivizedPacketsForChannelRequest) 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 *QueryIncentivizedPacketsForChannelRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryIncentivizedPacketsForChannelRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.QueryHeight != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.QueryHeight)) + i-- + dAtA[i] = 0x20 + } + if len(m.ChannelId) > 0 { + i -= len(m.ChannelId) + copy(dAtA[i:], m.ChannelId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ChannelId))) + i-- + dAtA[i] = 0x1a + } + if len(m.PortId) > 0 { + i -= len(m.PortId) + copy(dAtA[i:], m.PortId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.PortId))) + i-- + dAtA[i] = 0x12 + } + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryIncentivizedPacketsForChannelResponse) 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 *QueryIncentivizedPacketsForChannelResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryIncentivizedPacketsForChannelResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.IncentivizedPackets) > 0 { + for iNdEx := len(m.IncentivizedPackets) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.IncentivizedPackets[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *QueryTotalRecvFeesRequest) 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 *QueryTotalRecvFeesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryTotalRecvFeesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.PacketId.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *QueryTotalRecvFeesResponse) 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 *QueryTotalRecvFeesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryTotalRecvFeesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.RecvFees) > 0 { + for iNdEx := len(m.RecvFees) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.RecvFees[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *QueryTotalAckFeesRequest) 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 *QueryTotalAckFeesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryTotalAckFeesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.PacketId.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *QueryTotalAckFeesResponse) 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 *QueryTotalAckFeesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryTotalAckFeesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.AckFees) > 0 { + for iNdEx := len(m.AckFees) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.AckFees[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *QueryTotalTimeoutFeesRequest) 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 *QueryTotalTimeoutFeesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryTotalTimeoutFeesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.PacketId.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *QueryTotalTimeoutFeesResponse) 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 *QueryTotalTimeoutFeesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryTotalTimeoutFeesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.TimeoutFees) > 0 { + for iNdEx := len(m.TimeoutFees) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.TimeoutFees[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *QueryCounterpartyAddressRequest) 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 *QueryCounterpartyAddressRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryCounterpartyAddressRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.RelayerAddress) > 0 { + i -= len(m.RelayerAddress) + copy(dAtA[i:], m.RelayerAddress) + i = encodeVarintQuery(dAtA, i, uint64(len(m.RelayerAddress))) + i-- + dAtA[i] = 0x12 + } + if len(m.ChannelId) > 0 { + i -= len(m.ChannelId) + copy(dAtA[i:], m.ChannelId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ChannelId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryCounterpartyAddressResponse) 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 *QueryCounterpartyAddressResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryCounterpartyAddressResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.CounterpartyAddress) > 0 { + i -= len(m.CounterpartyAddress) + copy(dAtA[i:], m.CounterpartyAddress) + i = encodeVarintQuery(dAtA, i, uint64(len(m.CounterpartyAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryFeeEnabledChannelsRequest) 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 *QueryFeeEnabledChannelsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryFeeEnabledChannelsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.QueryHeight != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.QueryHeight)) + i-- + dAtA[i] = 0x10 + } + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryFeeEnabledChannelsResponse) 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 *QueryFeeEnabledChannelsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryFeeEnabledChannelsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.FeeEnabledChannels) > 0 { + for iNdEx := len(m.FeeEnabledChannels) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.FeeEnabledChannels[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *QueryFeeEnabledChannelRequest) 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 *QueryFeeEnabledChannelRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryFeeEnabledChannelRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ChannelId) > 0 { + i -= len(m.ChannelId) + copy(dAtA[i:], m.ChannelId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ChannelId))) + i-- + dAtA[i] = 0x12 + } + if len(m.PortId) > 0 { + i -= len(m.PortId) + copy(dAtA[i:], m.PortId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.PortId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryFeeEnabledChannelResponse) 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 *QueryFeeEnabledChannelResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryFeeEnabledChannelResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.FeeEnabled { + i-- + if m.FeeEnabled { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { + offset -= sovQuery(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *QueryIncentivizedPacketsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + if m.QueryHeight != 0 { + n += 1 + sovQuery(uint64(m.QueryHeight)) + } + return n +} + +func (m *QueryIncentivizedPacketsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.IncentivizedPackets) > 0 { + for _, e := range m.IncentivizedPackets { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *QueryIncentivizedPacketRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.PacketId.Size() + n += 1 + l + sovQuery(uint64(l)) + if m.QueryHeight != 0 { + n += 1 + sovQuery(uint64(m.QueryHeight)) + } + return n +} + +func (m *QueryIncentivizedPacketResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.IncentivizedPacket.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryIncentivizedPacketsForChannelRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.PortId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.ChannelId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.QueryHeight != 0 { + n += 1 + sovQuery(uint64(m.QueryHeight)) + } + return n +} + +func (m *QueryIncentivizedPacketsForChannelResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.IncentivizedPackets) > 0 { + for _, e := range m.IncentivizedPackets { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *QueryTotalRecvFeesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.PacketId.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryTotalRecvFeesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.RecvFees) > 0 { + for _, e := range m.RecvFees { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *QueryTotalAckFeesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.PacketId.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryTotalAckFeesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.AckFees) > 0 { + for _, e := range m.AckFees { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *QueryTotalTimeoutFeesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.PacketId.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryTotalTimeoutFeesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.TimeoutFees) > 0 { + for _, e := range m.TimeoutFees { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *QueryCounterpartyAddressRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ChannelId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.RelayerAddress) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryCounterpartyAddressResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.CounterpartyAddress) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryFeeEnabledChannelsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + if m.QueryHeight != 0 { + n += 1 + sovQuery(uint64(m.QueryHeight)) + } + return n +} + +func (m *QueryFeeEnabledChannelsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.FeeEnabledChannels) > 0 { + for _, e := range m.FeeEnabledChannels { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *QueryFeeEnabledChannelRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PortId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.ChannelId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryFeeEnabledChannelResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.FeeEnabled { + n += 2 + } + return n +} + +func sovQuery(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozQuery(x uint64) (n int) { + return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *QueryIncentivizedPacketsRequest) 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 ErrIntOverflowQuery + } + 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: QueryIncentivizedPacketsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryIncentivizedPacketsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field QueryHeight", wireType) + } + m.QueryHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.QueryHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryIncentivizedPacketsResponse) 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 ErrIntOverflowQuery + } + 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: QueryIncentivizedPacketsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryIncentivizedPacketsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IncentivizedPackets", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IncentivizedPackets = append(m.IncentivizedPackets, IdentifiedPacketFees{}) + if err := m.IncentivizedPackets[len(m.IncentivizedPackets)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryIncentivizedPacketRequest) 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 ErrIntOverflowQuery + } + 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: QueryIncentivizedPacketRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryIncentivizedPacketRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PacketId", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.PacketId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field QueryHeight", wireType) + } + m.QueryHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.QueryHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryIncentivizedPacketResponse) 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 ErrIntOverflowQuery + } + 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: QueryIncentivizedPacketResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryIncentivizedPacketResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IncentivizedPacket", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.IncentivizedPacket.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryIncentivizedPacketsForChannelRequest) 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 ErrIntOverflowQuery + } + 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: QueryIncentivizedPacketsForChannelRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryIncentivizedPacketsForChannelRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PortId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PortId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChannelId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChannelId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field QueryHeight", wireType) + } + m.QueryHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.QueryHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryIncentivizedPacketsForChannelResponse) 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 ErrIntOverflowQuery + } + 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: QueryIncentivizedPacketsForChannelResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryIncentivizedPacketsForChannelResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IncentivizedPackets", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IncentivizedPackets = append(m.IncentivizedPackets, &IdentifiedPacketFees{}) + if err := m.IncentivizedPackets[len(m.IncentivizedPackets)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryTotalRecvFeesRequest) 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 ErrIntOverflowQuery + } + 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: QueryTotalRecvFeesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryTotalRecvFeesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PacketId", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.PacketId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryTotalRecvFeesResponse) 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 ErrIntOverflowQuery + } + 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: QueryTotalRecvFeesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryTotalRecvFeesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RecvFees", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RecvFees = append(m.RecvFees, types1.Coin{}) + if err := m.RecvFees[len(m.RecvFees)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryTotalAckFeesRequest) 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 ErrIntOverflowQuery + } + 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: QueryTotalAckFeesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryTotalAckFeesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PacketId", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.PacketId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryTotalAckFeesResponse) 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 ErrIntOverflowQuery + } + 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: QueryTotalAckFeesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryTotalAckFeesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AckFees", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AckFees = append(m.AckFees, types1.Coin{}) + if err := m.AckFees[len(m.AckFees)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryTotalTimeoutFeesRequest) 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 ErrIntOverflowQuery + } + 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: QueryTotalTimeoutFeesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryTotalTimeoutFeesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PacketId", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.PacketId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryTotalTimeoutFeesResponse) 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 ErrIntOverflowQuery + } + 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: QueryTotalTimeoutFeesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryTotalTimeoutFeesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TimeoutFees", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TimeoutFees = append(m.TimeoutFees, types1.Coin{}) + if err := m.TimeoutFees[len(m.TimeoutFees)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryCounterpartyAddressRequest) 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 ErrIntOverflowQuery + } + 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: QueryCounterpartyAddressRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryCounterpartyAddressRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChannelId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChannelId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RelayerAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RelayerAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryCounterpartyAddressResponse) 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 ErrIntOverflowQuery + } + 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: QueryCounterpartyAddressResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryCounterpartyAddressResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CounterpartyAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CounterpartyAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryFeeEnabledChannelsRequest) 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 ErrIntOverflowQuery + } + 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: QueryFeeEnabledChannelsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryFeeEnabledChannelsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field QueryHeight", wireType) + } + m.QueryHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.QueryHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryFeeEnabledChannelsResponse) 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 ErrIntOverflowQuery + } + 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: QueryFeeEnabledChannelsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryFeeEnabledChannelsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FeeEnabledChannels", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FeeEnabledChannels = append(m.FeeEnabledChannels, FeeEnabledChannel{}) + if err := m.FeeEnabledChannels[len(m.FeeEnabledChannels)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryFeeEnabledChannelRequest) 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 ErrIntOverflowQuery + } + 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: QueryFeeEnabledChannelRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryFeeEnabledChannelRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PortId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PortId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChannelId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChannelId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryFeeEnabledChannelResponse) 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 ErrIntOverflowQuery + } + 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: QueryFeeEnabledChannelResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryFeeEnabledChannelResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field FeeEnabled", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.FeeEnabled = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipQuery(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthQuery + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupQuery + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthQuery + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthQuery = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowQuery = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupQuery = fmt.Errorf("proto: unexpected end of group") +) diff --git a/modules/apps/29-fee/types/query.pb.gw.go b/modules/apps/29-fee/types/query.pb.gw.go new file mode 100644 index 00000000000..9b9e47c5939 --- /dev/null +++ b/modules/apps/29-fee/types/query.pb.gw.go @@ -0,0 +1,1264 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: ibc/applications/fee/v1/query.proto + +/* +Package types is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package types + +import ( + "context" + "io" + "net/http" + + "github.com/golang/protobuf/descriptor" + "github.com/golang/protobuf/proto" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/grpc-ecosystem/grpc-gateway/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/status" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = descriptor.ForMessage + +var ( + filter_Query_IncentivizedPackets_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_IncentivizedPackets_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryIncentivizedPacketsRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_IncentivizedPackets_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.IncentivizedPackets(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_IncentivizedPackets_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryIncentivizedPacketsRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_IncentivizedPackets_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.IncentivizedPackets(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_IncentivizedPacket_0 = &utilities.DoubleArray{Encoding: map[string]int{"packet_id": 0, "port_id": 1, "channel_id": 2, "sequence": 3}, Base: []int{1, 1, 1, 2, 3, 0, 0, 0}, Check: []int{0, 1, 2, 2, 2, 3, 4, 5}} +) + +func request_Query_IncentivizedPacket_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryIncentivizedPacketRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["packet_id.port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "packet_id.port_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "packet_id.port_id", val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "packet_id.port_id", err) + } + + val, ok = pathParams["packet_id.channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "packet_id.channel_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "packet_id.channel_id", val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "packet_id.channel_id", err) + } + + val, ok = pathParams["packet_id.sequence"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "packet_id.sequence") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "packet_id.sequence", val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "packet_id.sequence", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_IncentivizedPacket_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.IncentivizedPacket(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_IncentivizedPacket_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryIncentivizedPacketRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["packet_id.port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "packet_id.port_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "packet_id.port_id", val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "packet_id.port_id", err) + } + + val, ok = pathParams["packet_id.channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "packet_id.channel_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "packet_id.channel_id", val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "packet_id.channel_id", err) + } + + val, ok = pathParams["packet_id.sequence"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "packet_id.sequence") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "packet_id.sequence", val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "packet_id.sequence", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_IncentivizedPacket_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.IncentivizedPacket(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_IncentivizedPacketsForChannel_0 = &utilities.DoubleArray{Encoding: map[string]int{"port_id": 0, "channel_id": 1}, Base: []int{1, 1, 2, 0, 0}, Check: []int{0, 1, 1, 2, 3}} +) + +func request_Query_IncentivizedPacketsForChannel_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryIncentivizedPacketsForChannelRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "port_id") + } + + protoReq.PortId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "port_id", err) + } + + val, ok = pathParams["channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "channel_id") + } + + protoReq.ChannelId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "channel_id", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_IncentivizedPacketsForChannel_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.IncentivizedPacketsForChannel(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_IncentivizedPacketsForChannel_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryIncentivizedPacketsForChannelRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "port_id") + } + + protoReq.PortId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "port_id", err) + } + + val, ok = pathParams["channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "channel_id") + } + + protoReq.ChannelId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "channel_id", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_IncentivizedPacketsForChannel_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.IncentivizedPacketsForChannel(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_TotalRecvFees_0 = &utilities.DoubleArray{Encoding: map[string]int{"packet_id": 0, "port_id": 1, "channel_id": 2, "sequence": 3}, Base: []int{1, 1, 1, 2, 3, 0, 0, 0}, Check: []int{0, 1, 2, 2, 2, 3, 4, 5}} +) + +func request_Query_TotalRecvFees_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryTotalRecvFeesRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["packet_id.port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "packet_id.port_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "packet_id.port_id", val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "packet_id.port_id", err) + } + + val, ok = pathParams["packet_id.channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "packet_id.channel_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "packet_id.channel_id", val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "packet_id.channel_id", err) + } + + val, ok = pathParams["packet_id.sequence"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "packet_id.sequence") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "packet_id.sequence", val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "packet_id.sequence", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_TotalRecvFees_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.TotalRecvFees(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_TotalRecvFees_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryTotalRecvFeesRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["packet_id.port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "packet_id.port_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "packet_id.port_id", val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "packet_id.port_id", err) + } + + val, ok = pathParams["packet_id.channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "packet_id.channel_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "packet_id.channel_id", val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "packet_id.channel_id", err) + } + + val, ok = pathParams["packet_id.sequence"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "packet_id.sequence") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "packet_id.sequence", val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "packet_id.sequence", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_TotalRecvFees_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.TotalRecvFees(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_TotalAckFees_0 = &utilities.DoubleArray{Encoding: map[string]int{"packet_id": 0, "port_id": 1, "channel_id": 2, "sequence": 3}, Base: []int{1, 1, 1, 2, 3, 0, 0, 0}, Check: []int{0, 1, 2, 2, 2, 3, 4, 5}} +) + +func request_Query_TotalAckFees_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryTotalAckFeesRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["packet_id.port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "packet_id.port_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "packet_id.port_id", val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "packet_id.port_id", err) + } + + val, ok = pathParams["packet_id.channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "packet_id.channel_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "packet_id.channel_id", val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "packet_id.channel_id", err) + } + + val, ok = pathParams["packet_id.sequence"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "packet_id.sequence") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "packet_id.sequence", val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "packet_id.sequence", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_TotalAckFees_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.TotalAckFees(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_TotalAckFees_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryTotalAckFeesRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["packet_id.port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "packet_id.port_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "packet_id.port_id", val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "packet_id.port_id", err) + } + + val, ok = pathParams["packet_id.channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "packet_id.channel_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "packet_id.channel_id", val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "packet_id.channel_id", err) + } + + val, ok = pathParams["packet_id.sequence"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "packet_id.sequence") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "packet_id.sequence", val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "packet_id.sequence", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_TotalAckFees_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.TotalAckFees(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_TotalTimeoutFees_0 = &utilities.DoubleArray{Encoding: map[string]int{"packet_id": 0, "port_id": 1, "channel_id": 2, "sequence": 3}, Base: []int{1, 1, 1, 2, 3, 0, 0, 0}, Check: []int{0, 1, 2, 2, 2, 3, 4, 5}} +) + +func request_Query_TotalTimeoutFees_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryTotalTimeoutFeesRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["packet_id.port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "packet_id.port_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "packet_id.port_id", val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "packet_id.port_id", err) + } + + val, ok = pathParams["packet_id.channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "packet_id.channel_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "packet_id.channel_id", val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "packet_id.channel_id", err) + } + + val, ok = pathParams["packet_id.sequence"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "packet_id.sequence") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "packet_id.sequence", val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "packet_id.sequence", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_TotalTimeoutFees_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.TotalTimeoutFees(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_TotalTimeoutFees_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryTotalTimeoutFeesRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["packet_id.port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "packet_id.port_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "packet_id.port_id", val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "packet_id.port_id", err) + } + + val, ok = pathParams["packet_id.channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "packet_id.channel_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "packet_id.channel_id", val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "packet_id.channel_id", err) + } + + val, ok = pathParams["packet_id.sequence"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "packet_id.sequence") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "packet_id.sequence", val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "packet_id.sequence", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_TotalTimeoutFees_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.TotalTimeoutFees(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_CounterpartyAddress_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryCounterpartyAddressRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["relayer_address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "relayer_address") + } + + protoReq.RelayerAddress, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "relayer_address", err) + } + + val, ok = pathParams["channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "channel_id") + } + + protoReq.ChannelId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "channel_id", err) + } + + msg, err := client.CounterpartyAddress(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_CounterpartyAddress_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryCounterpartyAddressRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["relayer_address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "relayer_address") + } + + protoReq.RelayerAddress, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "relayer_address", err) + } + + val, ok = pathParams["channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "channel_id") + } + + protoReq.ChannelId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "channel_id", err) + } + + msg, err := server.CounterpartyAddress(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_FeeEnabledChannels_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_FeeEnabledChannels_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryFeeEnabledChannelsRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_FeeEnabledChannels_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.FeeEnabledChannels(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_FeeEnabledChannels_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryFeeEnabledChannelsRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_FeeEnabledChannels_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.FeeEnabledChannels(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_FeeEnabledChannel_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryFeeEnabledChannelRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "port_id") + } + + protoReq.PortId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "port_id", err) + } + + val, ok = pathParams["channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "channel_id") + } + + protoReq.ChannelId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "channel_id", err) + } + + msg, err := client.FeeEnabledChannel(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_FeeEnabledChannel_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryFeeEnabledChannelRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "port_id") + } + + protoReq.PortId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "port_id", err) + } + + val, ok = pathParams["channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "channel_id") + } + + protoReq.ChannelId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "channel_id", err) + } + + msg, err := server.FeeEnabledChannel(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterQueryHandlerServer registers the http handlers for service Query to "mux". +// UnaryRPC :call QueryServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features (such as grpc.SendHeader, etc) to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. +func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { + + mux.Handle("GET", pattern_Query_IncentivizedPackets_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_IncentivizedPackets_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_IncentivizedPackets_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_IncentivizedPacket_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_IncentivizedPacket_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_IncentivizedPacket_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_IncentivizedPacketsForChannel_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_IncentivizedPacketsForChannel_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_IncentivizedPacketsForChannel_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_TotalRecvFees_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_TotalRecvFees_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_TotalRecvFees_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_TotalAckFees_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_TotalAckFees_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_TotalAckFees_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_TotalTimeoutFees_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_TotalTimeoutFees_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_TotalTimeoutFees_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_CounterpartyAddress_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_CounterpartyAddress_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_CounterpartyAddress_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_FeeEnabledChannels_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_FeeEnabledChannels_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_FeeEnabledChannels_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_FeeEnabledChannel_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_FeeEnabledChannel_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_FeeEnabledChannel_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterQueryHandlerFromEndpoint is same as RegisterQueryHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterQueryHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterQueryHandler(ctx, mux, conn) +} + +// RegisterQueryHandler registers the http handlers for service Query to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterQueryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterQueryHandlerClient(ctx, mux, NewQueryClient(conn)) +} + +// RegisterQueryHandlerClient registers the http handlers for service Query +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "QueryClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "QueryClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "QueryClient" to call the correct interceptors. +func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, client QueryClient) error { + + mux.Handle("GET", pattern_Query_IncentivizedPackets_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_IncentivizedPackets_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_IncentivizedPackets_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_IncentivizedPacket_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_IncentivizedPacket_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_IncentivizedPacket_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_IncentivizedPacketsForChannel_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_IncentivizedPacketsForChannel_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_IncentivizedPacketsForChannel_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_TotalRecvFees_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_TotalRecvFees_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_TotalRecvFees_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_TotalAckFees_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_TotalAckFees_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_TotalAckFees_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_TotalTimeoutFees_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_TotalTimeoutFees_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_TotalTimeoutFees_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_CounterpartyAddress_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_CounterpartyAddress_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_CounterpartyAddress_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_FeeEnabledChannels_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_FeeEnabledChannels_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_FeeEnabledChannels_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_FeeEnabledChannel_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_FeeEnabledChannel_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_FeeEnabledChannel_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_IncentivizedPackets_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "apps", "fee", "v1", "incentivized_packets"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_IncentivizedPacket_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 2, 5, 1, 0, 4, 1, 5, 6, 2, 7, 1, 0, 4, 1, 5, 8, 2, 9, 1, 0, 4, 1, 5, 10}, []string{"ibc", "apps", "fee", "v1", "incentivized_packet", "port", "packet_id.port_id", "channel", "packet_id.channel_id", "sequence", "packet_id.sequence"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_IncentivizedPacketsForChannel_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 2, 5, 1, 0, 4, 1, 5, 6, 2, 7, 1, 0, 4, 1, 5, 8}, []string{"ibc", "apps", "fee", "v1", "incentivized_packets", "port", "port_id", "channel", "channel_id"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_TotalRecvFees_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 2, 5, 1, 0, 4, 1, 5, 6, 2, 7, 1, 0, 4, 1, 5, 8, 2, 9, 1, 0, 4, 1, 5, 10}, []string{"ibc", "apps", "fee", "v1", "total_recv_fees", "port", "packet_id.port_id", "channel", "packet_id.channel_id", "sequence", "packet_id.sequence"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_TotalAckFees_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 2, 5, 1, 0, 4, 1, 5, 6, 2, 7, 1, 0, 4, 1, 5, 8, 2, 9, 1, 0, 4, 1, 5, 10}, []string{"ibc", "apps", "fee", "v1", "total_ack_fees", "port", "packet_id.port_id", "channel", "packet_id.channel_id", "sequence", "packet_id.sequence"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_TotalTimeoutFees_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 2, 5, 1, 0, 4, 1, 5, 6, 2, 7, 1, 0, 4, 1, 5, 8, 2, 9, 1, 0, 4, 1, 5, 10}, []string{"ibc", "apps", "fee", "v1", "total_timeout_fees", "port", "packet_id.port_id", "channel", "packet_id.channel_id", "sequence", "packet_id.sequence"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_CounterpartyAddress_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7}, []string{"ibc", "apps", "fee", "v1", "counterparty_address", "relayer_address", "channel", "channel_id"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_FeeEnabledChannels_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "apps", "fee", "v1", "fee_enabled"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_FeeEnabledChannel_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 2, 5, 1, 0, 4, 1, 5, 6, 2, 7, 1, 0, 4, 1, 5, 8}, []string{"ibc", "apps", "fee", "v1", "fee_enabled", "port", "port_id", "channel", "channel_id"}, "", runtime.AssumeColonVerbOpt(true))) +) + +var ( + forward_Query_IncentivizedPackets_0 = runtime.ForwardResponseMessage + + forward_Query_IncentivizedPacket_0 = runtime.ForwardResponseMessage + + forward_Query_IncentivizedPacketsForChannel_0 = runtime.ForwardResponseMessage + + forward_Query_TotalRecvFees_0 = runtime.ForwardResponseMessage + + forward_Query_TotalAckFees_0 = runtime.ForwardResponseMessage + + forward_Query_TotalTimeoutFees_0 = runtime.ForwardResponseMessage + + forward_Query_CounterpartyAddress_0 = runtime.ForwardResponseMessage + + forward_Query_FeeEnabledChannels_0 = runtime.ForwardResponseMessage + + forward_Query_FeeEnabledChannel_0 = runtime.ForwardResponseMessage +) diff --git a/modules/apps/29-fee/types/tx.pb.go b/modules/apps/29-fee/types/tx.pb.go new file mode 100644 index 00000000000..1cecc607dc9 --- /dev/null +++ b/modules/apps/29-fee/types/tx.pb.go @@ -0,0 +1,1544 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/applications/fee/v1/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + types "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + _ "github.com/gogo/protobuf/gogoproto" + grpc1 "github.com/gogo/protobuf/grpc" + proto "github.com/gogo/protobuf/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// MsgRegisterCounterpartyAddress defines the request type for the RegisterCounterpartyAddress rpc +type MsgRegisterCounterpartyAddress struct { + // the relayer address + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + // the counterparty relayer address + CounterpartyAddress string `protobuf:"bytes,2,opt,name=counterparty_address,json=counterpartyAddress,proto3" json:"counterparty_address,omitempty" yaml:"counterparty_address"` + // unique channel identifier + ChannelId string `protobuf:"bytes,3,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty" yaml:"channel_id"` +} + +func (m *MsgRegisterCounterpartyAddress) Reset() { *m = MsgRegisterCounterpartyAddress{} } +func (m *MsgRegisterCounterpartyAddress) String() string { return proto.CompactTextString(m) } +func (*MsgRegisterCounterpartyAddress) ProtoMessage() {} +func (*MsgRegisterCounterpartyAddress) Descriptor() ([]byte, []int) { + return fileDescriptor_05c93128649f1b96, []int{0} +} +func (m *MsgRegisterCounterpartyAddress) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgRegisterCounterpartyAddress) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgRegisterCounterpartyAddress.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 *MsgRegisterCounterpartyAddress) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgRegisterCounterpartyAddress.Merge(m, src) +} +func (m *MsgRegisterCounterpartyAddress) XXX_Size() int { + return m.Size() +} +func (m *MsgRegisterCounterpartyAddress) XXX_DiscardUnknown() { + xxx_messageInfo_MsgRegisterCounterpartyAddress.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgRegisterCounterpartyAddress proto.InternalMessageInfo + +// MsgRegisterCounterpartyAddressResponse defines the response type for the RegisterCounterpartyAddress rpc +type MsgRegisterCounterpartyAddressResponse struct { +} + +func (m *MsgRegisterCounterpartyAddressResponse) Reset() { + *m = MsgRegisterCounterpartyAddressResponse{} +} +func (m *MsgRegisterCounterpartyAddressResponse) String() string { return proto.CompactTextString(m) } +func (*MsgRegisterCounterpartyAddressResponse) ProtoMessage() {} +func (*MsgRegisterCounterpartyAddressResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_05c93128649f1b96, []int{1} +} +func (m *MsgRegisterCounterpartyAddressResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgRegisterCounterpartyAddressResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgRegisterCounterpartyAddressResponse.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 *MsgRegisterCounterpartyAddressResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgRegisterCounterpartyAddressResponse.Merge(m, src) +} +func (m *MsgRegisterCounterpartyAddressResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgRegisterCounterpartyAddressResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgRegisterCounterpartyAddressResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgRegisterCounterpartyAddressResponse proto.InternalMessageInfo + +// MsgPayPacketFee defines the request type for the PayPacketFee rpc +// This Msg can be used to pay for a packet at the next sequence send & should be combined with the Msg that will be +// paid for +type MsgPayPacketFee struct { + // fee encapsulates the recv, ack and timeout fees associated with an IBC packet + Fee Fee `protobuf:"bytes,1,opt,name=fee,proto3" json:"fee"` + // the source port unique identifier + SourcePortId string `protobuf:"bytes,2,opt,name=source_port_id,json=sourcePortId,proto3" json:"source_port_id,omitempty" yaml:"source_port_id"` + // the source channel unique identifer + SourceChannelId string `protobuf:"bytes,3,opt,name=source_channel_id,json=sourceChannelId,proto3" json:"source_channel_id,omitempty" yaml:"source_channel_id"` + // account address to refund fee if necessary + Signer string `protobuf:"bytes,4,opt,name=signer,proto3" json:"signer,omitempty"` + // optional list of relayers permitted to the receive packet fees + Relayers []string `protobuf:"bytes,5,rep,name=relayers,proto3" json:"relayers,omitempty"` +} + +func (m *MsgPayPacketFee) Reset() { *m = MsgPayPacketFee{} } +func (m *MsgPayPacketFee) String() string { return proto.CompactTextString(m) } +func (*MsgPayPacketFee) ProtoMessage() {} +func (*MsgPayPacketFee) Descriptor() ([]byte, []int) { + return fileDescriptor_05c93128649f1b96, []int{2} +} +func (m *MsgPayPacketFee) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgPayPacketFee) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgPayPacketFee.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 *MsgPayPacketFee) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgPayPacketFee.Merge(m, src) +} +func (m *MsgPayPacketFee) XXX_Size() int { + return m.Size() +} +func (m *MsgPayPacketFee) XXX_DiscardUnknown() { + xxx_messageInfo_MsgPayPacketFee.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgPayPacketFee proto.InternalMessageInfo + +// MsgPayPacketFeeResponse defines the response type for the PayPacketFee rpc +type MsgPayPacketFeeResponse struct { +} + +func (m *MsgPayPacketFeeResponse) Reset() { *m = MsgPayPacketFeeResponse{} } +func (m *MsgPayPacketFeeResponse) String() string { return proto.CompactTextString(m) } +func (*MsgPayPacketFeeResponse) ProtoMessage() {} +func (*MsgPayPacketFeeResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_05c93128649f1b96, []int{3} +} +func (m *MsgPayPacketFeeResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgPayPacketFeeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgPayPacketFeeResponse.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 *MsgPayPacketFeeResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgPayPacketFeeResponse.Merge(m, src) +} +func (m *MsgPayPacketFeeResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgPayPacketFeeResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgPayPacketFeeResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgPayPacketFeeResponse proto.InternalMessageInfo + +// MsgPayPacketFeeAsync defines the request type for the PayPacketFeeAsync rpc +// This Msg can be used to pay for a packet at a specified sequence (instead of the next sequence send) +type MsgPayPacketFeeAsync struct { + // unique packet identifier comprised of the channel ID, port ID and sequence + PacketId types.PacketId `protobuf:"bytes,1,opt,name=packet_id,json=packetId,proto3" json:"packet_id" yaml:"packet_id"` + // the packet fee associated with a particular IBC packet + PacketFee PacketFee `protobuf:"bytes,2,opt,name=packet_fee,json=packetFee,proto3" json:"packet_fee" yaml:"packet_fee"` +} + +func (m *MsgPayPacketFeeAsync) Reset() { *m = MsgPayPacketFeeAsync{} } +func (m *MsgPayPacketFeeAsync) String() string { return proto.CompactTextString(m) } +func (*MsgPayPacketFeeAsync) ProtoMessage() {} +func (*MsgPayPacketFeeAsync) Descriptor() ([]byte, []int) { + return fileDescriptor_05c93128649f1b96, []int{4} +} +func (m *MsgPayPacketFeeAsync) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgPayPacketFeeAsync) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgPayPacketFeeAsync.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 *MsgPayPacketFeeAsync) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgPayPacketFeeAsync.Merge(m, src) +} +func (m *MsgPayPacketFeeAsync) XXX_Size() int { + return m.Size() +} +func (m *MsgPayPacketFeeAsync) XXX_DiscardUnknown() { + xxx_messageInfo_MsgPayPacketFeeAsync.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgPayPacketFeeAsync proto.InternalMessageInfo + +// MsgPayPacketFeeAsyncResponse defines the response type for the PayPacketFeeAsync rpc +type MsgPayPacketFeeAsyncResponse struct { +} + +func (m *MsgPayPacketFeeAsyncResponse) Reset() { *m = MsgPayPacketFeeAsyncResponse{} } +func (m *MsgPayPacketFeeAsyncResponse) String() string { return proto.CompactTextString(m) } +func (*MsgPayPacketFeeAsyncResponse) ProtoMessage() {} +func (*MsgPayPacketFeeAsyncResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_05c93128649f1b96, []int{5} +} +func (m *MsgPayPacketFeeAsyncResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgPayPacketFeeAsyncResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgPayPacketFeeAsyncResponse.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 *MsgPayPacketFeeAsyncResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgPayPacketFeeAsyncResponse.Merge(m, src) +} +func (m *MsgPayPacketFeeAsyncResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgPayPacketFeeAsyncResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgPayPacketFeeAsyncResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgPayPacketFeeAsyncResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgRegisterCounterpartyAddress)(nil), "ibc.applications.fee.v1.MsgRegisterCounterpartyAddress") + proto.RegisterType((*MsgRegisterCounterpartyAddressResponse)(nil), "ibc.applications.fee.v1.MsgRegisterCounterpartyAddressResponse") + proto.RegisterType((*MsgPayPacketFee)(nil), "ibc.applications.fee.v1.MsgPayPacketFee") + proto.RegisterType((*MsgPayPacketFeeResponse)(nil), "ibc.applications.fee.v1.MsgPayPacketFeeResponse") + proto.RegisterType((*MsgPayPacketFeeAsync)(nil), "ibc.applications.fee.v1.MsgPayPacketFeeAsync") + proto.RegisterType((*MsgPayPacketFeeAsyncResponse)(nil), "ibc.applications.fee.v1.MsgPayPacketFeeAsyncResponse") +} + +func init() { proto.RegisterFile("ibc/applications/fee/v1/tx.proto", fileDescriptor_05c93128649f1b96) } + +var fileDescriptor_05c93128649f1b96 = []byte{ + // 630 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x54, 0x4d, 0x4f, 0xdb, 0x40, + 0x10, 0x8d, 0x09, 0xa5, 0x64, 0x8b, 0x4a, 0xb3, 0x85, 0x62, 0x0c, 0xb5, 0xa9, 0x0f, 0x55, 0x2e, + 0xd8, 0xe5, 0x4b, 0x55, 0xb9, 0x20, 0x82, 0x84, 0xca, 0x01, 0x15, 0x59, 0x3d, 0x55, 0x95, 0x90, + 0xb3, 0x9e, 0x18, 0xb7, 0x89, 0xd7, 0xda, 0x75, 0xa2, 0xfa, 0x0f, 0x54, 0x3d, 0x72, 0xeb, 0x95, + 0x9f, 0xc3, 0xa9, 0xe2, 0xd0, 0x43, 0x4f, 0x56, 0x05, 0x97, 0x9e, 0xf3, 0x0b, 0xaa, 0xf5, 0x97, + 0x1c, 0x48, 0x22, 0xda, 0x9b, 0x77, 0xe6, 0xcd, 0xdb, 0x37, 0x6f, 0xc6, 0x8b, 0xd6, 0xbc, 0x16, + 0x31, 0xed, 0x20, 0xe8, 0x78, 0xc4, 0x0e, 0x3d, 0xea, 0x73, 0xb3, 0x0d, 0x60, 0xf6, 0x37, 0xcc, + 0xf0, 0x8b, 0x11, 0x30, 0x1a, 0x52, 0xbc, 0xe4, 0xb5, 0x88, 0x51, 0x46, 0x18, 0x6d, 0x00, 0xa3, + 0xbf, 0xa1, 0x2c, 0xb8, 0xd4, 0xa5, 0x09, 0xc6, 0x14, 0x5f, 0x29, 0x5c, 0x79, 0x31, 0x8e, 0x50, + 0x54, 0x95, 0x20, 0x84, 0x32, 0x30, 0xc9, 0x99, 0xed, 0xfb, 0xd0, 0x11, 0xe9, 0xec, 0x33, 0x85, + 0xe8, 0x3f, 0x24, 0xa4, 0x1e, 0x73, 0xd7, 0x02, 0xd7, 0xe3, 0x21, 0xb0, 0x03, 0xda, 0xf3, 0x43, + 0x60, 0x81, 0xcd, 0xc2, 0x68, 0xdf, 0x71, 0x18, 0x70, 0x8e, 0x65, 0xf4, 0xd0, 0x4e, 0x3f, 0x65, + 0x69, 0x4d, 0x6a, 0xd4, 0xac, 0xfc, 0x88, 0x2d, 0xb4, 0x40, 0x4a, 0x05, 0xa7, 0x39, 0x6c, 0x4a, + 0xc0, 0x9a, 0xda, 0x20, 0xd6, 0x56, 0x22, 0xbb, 0xdb, 0xd9, 0xd5, 0x47, 0xa1, 0x74, 0xeb, 0x29, + 0x19, 0x71, 0xdb, 0x36, 0x42, 0x99, 0xc2, 0x53, 0xcf, 0x91, 0xab, 0x09, 0xd3, 0xe2, 0x20, 0xd6, + 0xea, 0x19, 0x53, 0x91, 0xd3, 0xad, 0x5a, 0x76, 0x38, 0x72, 0x76, 0x67, 0xbf, 0x5d, 0x68, 0x95, + 0x3f, 0x17, 0x5a, 0x45, 0x6f, 0xa0, 0x97, 0x93, 0xfb, 0xb1, 0x80, 0x07, 0xd4, 0xe7, 0xa0, 0x9f, + 0x4f, 0xa1, 0xf9, 0x63, 0xee, 0x9e, 0xd8, 0xd1, 0x89, 0x4d, 0x3e, 0x43, 0x78, 0x08, 0x80, 0xb7, + 0x51, 0xb5, 0x0d, 0x90, 0xf4, 0xf9, 0x68, 0x73, 0xd5, 0x18, 0x33, 0x11, 0xe3, 0x10, 0xa0, 0x39, + 0x7d, 0x19, 0x6b, 0x15, 0x4b, 0xc0, 0xf1, 0x1e, 0x7a, 0xcc, 0x69, 0x8f, 0x11, 0x38, 0x0d, 0x28, + 0x0b, 0x85, 0xee, 0xd4, 0x81, 0xe5, 0x41, 0xac, 0x2d, 0xa6, 0xba, 0x87, 0xf3, 0xba, 0x35, 0x97, + 0x06, 0x4e, 0x28, 0x0b, 0x8f, 0x1c, 0xfc, 0x16, 0xd5, 0x33, 0xc0, 0x9d, 0xde, 0x57, 0x07, 0xb1, + 0x26, 0x0f, 0x71, 0x94, 0x2d, 0x98, 0x4f, 0x63, 0x07, 0xb9, 0x11, 0xf8, 0x19, 0x9a, 0xe1, 0x9e, + 0xeb, 0x03, 0x93, 0xa7, 0x93, 0x59, 0x65, 0x27, 0xac, 0xa0, 0x59, 0x06, 0x1d, 0x3b, 0x02, 0xc6, + 0xe5, 0x07, 0x6b, 0xd5, 0x46, 0xcd, 0x2a, 0xce, 0x25, 0xf3, 0x96, 0xd1, 0xd2, 0x2d, 0x47, 0x0a, + 0xb7, 0x7e, 0x4a, 0x68, 0xe1, 0x56, 0x6e, 0x9f, 0x47, 0x3e, 0xc1, 0xef, 0x51, 0x2d, 0x48, 0x22, + 0x42, 0x73, 0x6a, 0xdc, 0xf3, 0xc4, 0x38, 0xb1, 0x78, 0x46, 0xbe, 0x6d, 0xfd, 0x0d, 0x23, 0xad, + 0x3b, 0x72, 0x9a, 0xb2, 0x70, 0x6e, 0x10, 0x6b, 0x4f, 0xd2, 0xb6, 0x8a, 0x6a, 0xdd, 0x9a, 0x0d, + 0x32, 0x0c, 0xfe, 0x88, 0x50, 0x16, 0x17, 0xf3, 0x98, 0x4a, 0x68, 0xf5, 0xb1, 0xf3, 0x28, 0x24, + 0x35, 0x97, 0x33, 0xee, 0xfa, 0x10, 0x77, 0x1b, 0x40, 0xb7, 0x32, 0x99, 0x87, 0x00, 0xa5, 0x8e, + 0x55, 0xb4, 0x3a, 0xaa, 0xab, 0xbc, 0xed, 0xcd, 0xaf, 0x55, 0x54, 0x3d, 0xe6, 0x2e, 0xfe, 0x2e, + 0xa1, 0x95, 0x49, 0x3f, 0xc9, 0xeb, 0xb1, 0xda, 0x26, 0x6f, 0xa3, 0xb2, 0xf7, 0x9f, 0x85, 0xb9, + 0x42, 0xfc, 0x09, 0xcd, 0x0d, 0xad, 0x70, 0x63, 0x12, 0x61, 0x19, 0xa9, 0xbc, 0xba, 0x2f, 0xb2, + 0xb8, 0x2b, 0x42, 0xf5, 0xbb, 0x0b, 0xb0, 0x7e, 0x5f, 0x9a, 0x04, 0xae, 0xec, 0xfc, 0x13, 0x3c, + 0xbf, 0xba, 0xf9, 0xee, 0xf2, 0x5a, 0x95, 0xae, 0xae, 0x55, 0xe9, 0xf7, 0xb5, 0x2a, 0x9d, 0xdf, + 0xa8, 0x95, 0xab, 0x1b, 0xb5, 0xf2, 0xeb, 0x46, 0xad, 0x7c, 0xd8, 0x71, 0xbd, 0xf0, 0xac, 0xd7, + 0x32, 0x08, 0xed, 0x9a, 0x84, 0xf2, 0x2e, 0xe5, 0xa6, 0xd7, 0x22, 0xeb, 0x2e, 0x35, 0xfb, 0x5b, + 0x66, 0x97, 0x3a, 0xbd, 0x0e, 0x70, 0xf1, 0x50, 0x72, 0x73, 0xf3, 0xcd, 0xba, 0x78, 0x23, 0xc3, + 0x28, 0x00, 0xde, 0x9a, 0x49, 0x1e, 0xc0, 0xad, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xf7, + 0x58, 0xab, 0x99, 0x05, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// MsgClient is the client API for Msg service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type MsgClient interface { + // RegisterCounterpartyAddress defines a rpc handler method for MsgRegisterCounterpartyAddress + // RegisterCounterpartyAddress is called by the relayer on each channelEnd and allows them to specify their + // counterparty address before relaying. This ensures they will be properly compensated for forward relaying since + // destination chain must send back relayer's source address (counterparty address) in acknowledgement. This function + // may be called more than once by a relayer, in which case, latest counterparty address is always used. + RegisterCounterpartyAddress(ctx context.Context, in *MsgRegisterCounterpartyAddress, opts ...grpc.CallOption) (*MsgRegisterCounterpartyAddressResponse, error) + // PayPacketFee defines a rpc handler method for MsgPayPacketFee + // PayPacketFee is an open callback that may be called by any module/user that wishes to escrow funds in order to + // incentivize the relaying of the packet at the next sequence + // NOTE: This method is intended to be used within a multi msg transaction, where the subsequent msg that follows + // initiates the lifecycle of the incentivized packet + PayPacketFee(ctx context.Context, in *MsgPayPacketFee, opts ...grpc.CallOption) (*MsgPayPacketFeeResponse, error) + // PayPacketFeeAsync defines a rpc handler method for MsgPayPacketFeeAsync + // PayPacketFeeAsync is an open callback that may be called by any module/user that wishes to escrow funds in order to + // incentivize the relaying of a known packet (i.e. at a particular sequence) + PayPacketFeeAsync(ctx context.Context, in *MsgPayPacketFeeAsync, opts ...grpc.CallOption) (*MsgPayPacketFeeAsyncResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) RegisterCounterpartyAddress(ctx context.Context, in *MsgRegisterCounterpartyAddress, opts ...grpc.CallOption) (*MsgRegisterCounterpartyAddressResponse, error) { + out := new(MsgRegisterCounterpartyAddressResponse) + err := c.cc.Invoke(ctx, "/ibc.applications.fee.v1.Msg/RegisterCounterpartyAddress", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) PayPacketFee(ctx context.Context, in *MsgPayPacketFee, opts ...grpc.CallOption) (*MsgPayPacketFeeResponse, error) { + out := new(MsgPayPacketFeeResponse) + err := c.cc.Invoke(ctx, "/ibc.applications.fee.v1.Msg/PayPacketFee", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) PayPacketFeeAsync(ctx context.Context, in *MsgPayPacketFeeAsync, opts ...grpc.CallOption) (*MsgPayPacketFeeAsyncResponse, error) { + out := new(MsgPayPacketFeeAsyncResponse) + err := c.cc.Invoke(ctx, "/ibc.applications.fee.v1.Msg/PayPacketFeeAsync", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + // RegisterCounterpartyAddress defines a rpc handler method for MsgRegisterCounterpartyAddress + // RegisterCounterpartyAddress is called by the relayer on each channelEnd and allows them to specify their + // counterparty address before relaying. This ensures they will be properly compensated for forward relaying since + // destination chain must send back relayer's source address (counterparty address) in acknowledgement. This function + // may be called more than once by a relayer, in which case, latest counterparty address is always used. + RegisterCounterpartyAddress(context.Context, *MsgRegisterCounterpartyAddress) (*MsgRegisterCounterpartyAddressResponse, error) + // PayPacketFee defines a rpc handler method for MsgPayPacketFee + // PayPacketFee is an open callback that may be called by any module/user that wishes to escrow funds in order to + // incentivize the relaying of the packet at the next sequence + // NOTE: This method is intended to be used within a multi msg transaction, where the subsequent msg that follows + // initiates the lifecycle of the incentivized packet + PayPacketFee(context.Context, *MsgPayPacketFee) (*MsgPayPacketFeeResponse, error) + // PayPacketFeeAsync defines a rpc handler method for MsgPayPacketFeeAsync + // PayPacketFeeAsync is an open callback that may be called by any module/user that wishes to escrow funds in order to + // incentivize the relaying of a known packet (i.e. at a particular sequence) + PayPacketFeeAsync(context.Context, *MsgPayPacketFeeAsync) (*MsgPayPacketFeeAsyncResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) RegisterCounterpartyAddress(ctx context.Context, req *MsgRegisterCounterpartyAddress) (*MsgRegisterCounterpartyAddressResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RegisterCounterpartyAddress not implemented") +} +func (*UnimplementedMsgServer) PayPacketFee(ctx context.Context, req *MsgPayPacketFee) (*MsgPayPacketFeeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PayPacketFee not implemented") +} +func (*UnimplementedMsgServer) PayPacketFeeAsync(ctx context.Context, req *MsgPayPacketFeeAsync) (*MsgPayPacketFeeAsyncResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PayPacketFeeAsync not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_RegisterCounterpartyAddress_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgRegisterCounterpartyAddress) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).RegisterCounterpartyAddress(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.applications.fee.v1.Msg/RegisterCounterpartyAddress", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).RegisterCounterpartyAddress(ctx, req.(*MsgRegisterCounterpartyAddress)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_PayPacketFee_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgPayPacketFee) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).PayPacketFee(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.applications.fee.v1.Msg/PayPacketFee", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).PayPacketFee(ctx, req.(*MsgPayPacketFee)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_PayPacketFeeAsync_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgPayPacketFeeAsync) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).PayPacketFeeAsync(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.applications.fee.v1.Msg/PayPacketFeeAsync", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).PayPacketFeeAsync(ctx, req.(*MsgPayPacketFeeAsync)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "ibc.applications.fee.v1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "RegisterCounterpartyAddress", + Handler: _Msg_RegisterCounterpartyAddress_Handler, + }, + { + MethodName: "PayPacketFee", + Handler: _Msg_PayPacketFee_Handler, + }, + { + MethodName: "PayPacketFeeAsync", + Handler: _Msg_PayPacketFeeAsync_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "ibc/applications/fee/v1/tx.proto", +} + +func (m *MsgRegisterCounterpartyAddress) 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 *MsgRegisterCounterpartyAddress) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgRegisterCounterpartyAddress) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ChannelId) > 0 { + i -= len(m.ChannelId) + copy(dAtA[i:], m.ChannelId) + i = encodeVarintTx(dAtA, i, uint64(len(m.ChannelId))) + i-- + dAtA[i] = 0x1a + } + if len(m.CounterpartyAddress) > 0 { + i -= len(m.CounterpartyAddress) + copy(dAtA[i:], m.CounterpartyAddress) + i = encodeVarintTx(dAtA, i, uint64(len(m.CounterpartyAddress))) + i-- + dAtA[i] = 0x12 + } + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintTx(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgRegisterCounterpartyAddressResponse) 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 *MsgRegisterCounterpartyAddressResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgRegisterCounterpartyAddressResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgPayPacketFee) 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 *MsgPayPacketFee) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgPayPacketFee) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Relayers) > 0 { + for iNdEx := len(m.Relayers) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Relayers[iNdEx]) + copy(dAtA[i:], m.Relayers[iNdEx]) + i = encodeVarintTx(dAtA, i, uint64(len(m.Relayers[iNdEx]))) + i-- + dAtA[i] = 0x2a + } + } + 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] = 0x22 + } + if len(m.SourceChannelId) > 0 { + i -= len(m.SourceChannelId) + copy(dAtA[i:], m.SourceChannelId) + i = encodeVarintTx(dAtA, i, uint64(len(m.SourceChannelId))) + i-- + dAtA[i] = 0x1a + } + if len(m.SourcePortId) > 0 { + i -= len(m.SourcePortId) + copy(dAtA[i:], m.SourcePortId) + i = encodeVarintTx(dAtA, i, uint64(len(m.SourcePortId))) + i-- + dAtA[i] = 0x12 + } + { + size, err := m.Fee.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 *MsgPayPacketFeeResponse) 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 *MsgPayPacketFeeResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgPayPacketFeeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgPayPacketFeeAsync) 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 *MsgPayPacketFeeAsync) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgPayPacketFeeAsync) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.PacketFee.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size, err := m.PacketId.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 *MsgPayPacketFeeAsyncResponse) 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 *MsgPayPacketFeeAsyncResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgPayPacketFeeAsyncResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func encodeVarintTx(dAtA []byte, offset int, v uint64) int { + offset -= sovTx(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgRegisterCounterpartyAddress) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.CounterpartyAddress) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ChannelId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgRegisterCounterpartyAddressResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgPayPacketFee) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Fee.Size() + n += 1 + l + sovTx(uint64(l)) + l = len(m.SourcePortId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.SourceChannelId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if len(m.Relayers) > 0 { + for _, s := range m.Relayers { + l = len(s) + n += 1 + l + sovTx(uint64(l)) + } + } + return n +} + +func (m *MsgPayPacketFeeResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgPayPacketFeeAsync) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.PacketId.Size() + n += 1 + l + sovTx(uint64(l)) + l = m.PacketFee.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgPayPacketFeeAsyncResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func sovTx(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTx(x uint64) (n int) { + return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MsgRegisterCounterpartyAddress) 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: MsgRegisterCounterpartyAddress: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgRegisterCounterpartyAddress: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Address", 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.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CounterpartyAddress", 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.CounterpartyAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChannelId", 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.ChannelId = 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 *MsgRegisterCounterpartyAddressResponse) 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: MsgRegisterCounterpartyAddressResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgRegisterCounterpartyAddressResponse: 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 *MsgPayPacketFee) 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: MsgPayPacketFee: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgPayPacketFee: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Fee", 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.Fee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SourcePortId", 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.SourcePortId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SourceChannelId", 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.SourceChannelId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + 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 + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Relayers", 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.Relayers = append(m.Relayers, 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 *MsgPayPacketFeeResponse) 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: MsgPayPacketFeeResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgPayPacketFeeResponse: 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 *MsgPayPacketFeeAsync) 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: MsgPayPacketFeeAsync: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgPayPacketFeeAsync: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PacketId", 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.PacketId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PacketFee", 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.PacketFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + 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 *MsgPayPacketFeeAsyncResponse) 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: MsgPayPacketFeeAsyncResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgPayPacketFeeAsyncResponse: 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 skipTx(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthTx + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTx + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTx + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTx = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTx = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTx = fmt.Errorf("proto: unexpected end of group") +) diff --git a/modules/apps/transfer/ibc_module_test.go b/modules/apps/transfer/ibc_module_test.go index 92d0f30d4c7..3dcb5518cbb 100644 --- a/modules/apps/transfer/ibc_module_test.go +++ b/modules/apps/transfer/ibc_module_test.go @@ -13,9 +13,10 @@ import ( func (suite *TransferTestSuite) TestOnChanOpenInit() { var ( - channel *channeltypes.Channel - path *ibctesting.Path - chanCap *capabilitytypes.Capability + channel *channeltypes.Channel + path *ibctesting.Path + chanCap *capabilitytypes.Capability + counterparty channeltypes.Counterparty ) testCases := []struct { @@ -64,7 +65,7 @@ func (suite *TransferTestSuite) TestOnChanOpenInit() { suite.coordinator.SetupConnections(path) path.EndpointA.ChannelID = ibctesting.FirstChannelID - counterparty := channeltypes.NewCounterparty(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) + counterparty = channeltypes.NewCounterparty(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) channel = &channeltypes.Channel{ State: channeltypes.INIT, Ordering: channeltypes.UNORDERED, @@ -85,7 +86,7 @@ func (suite *TransferTestSuite) TestOnChanOpenInit() { tc.malleate() // explicitly change fields in channel and testChannel err = cbs.OnChanOpenInit(suite.chainA.GetContext(), channel.Ordering, channel.GetConnectionHops(), - path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, chanCap, channel.Counterparty, channel.GetVersion(), + path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, chanCap, counterparty, channel.GetVersion(), ) if tc.expPass { @@ -103,6 +104,7 @@ func (suite *TransferTestSuite) TestOnChanOpenTry() { channel *channeltypes.Channel chanCap *capabilitytypes.Capability path *ibctesting.Path + counterparty channeltypes.Counterparty counterpartyVersion string ) @@ -153,7 +155,7 @@ func (suite *TransferTestSuite) TestOnChanOpenTry() { suite.coordinator.SetupConnections(path) path.EndpointA.ChannelID = ibctesting.FirstChannelID - counterparty := channeltypes.NewCounterparty(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) + counterparty = channeltypes.NewCounterparty(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) channel = &channeltypes.Channel{ State: channeltypes.TRYOPEN, Ordering: channeltypes.UNORDERED, diff --git a/modules/apps/transfer/keeper/genesis.go b/modules/apps/transfer/keeper/genesis.go index 35bbd231a59..d4020508cc9 100644 --- a/modules/apps/transfer/keeper/genesis.go +++ b/modules/apps/transfer/keeper/genesis.go @@ -28,12 +28,6 @@ func (k Keeper) InitGenesis(ctx sdk.Context, state types.GenesisState) { } k.SetParams(ctx, state.Params) - - // check if the module account exists - moduleAcc := k.GetTransferAccount(ctx) - if moduleAcc == nil { - panic(fmt.Sprintf("%s module account has not been set", types.ModuleName)) - } } // ExportGenesis exports ibc-transfer module's portID and denom trace info into its genesis state. diff --git a/modules/apps/transfer/keeper/keeper.go b/modules/apps/transfer/keeper/keeper.go index 46e3c51aa2a..d3f32d95df7 100644 --- a/modules/apps/transfer/keeper/keeper.go +++ b/modules/apps/transfer/keeper/keeper.go @@ -4,7 +4,6 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" @@ -64,11 +63,6 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", "x/"+host.ModuleName+"-"+types.ModuleName) } -// GetTransferAccount returns the ICS20 - transfers ModuleAccount -func (k Keeper) GetTransferAccount(ctx sdk.Context) authtypes.ModuleAccountI { - return k.authKeeper.GetModuleAccount(ctx, types.ModuleName) -} - // IsBound checks if the transfer module is already bound to the desired port func (k Keeper) IsBound(ctx sdk.Context, portID string) bool { _, ok := k.scopedKeeper.GetCapability(ctx, host.PortPath(portID)) diff --git a/modules/apps/transfer/keeper/keeper_test.go b/modules/apps/transfer/keeper/keeper_test.go index 1e29626c987..40ceb6d782f 100644 --- a/modules/apps/transfer/keeper/keeper_test.go +++ b/modules/apps/transfer/keeper/keeper_test.go @@ -4,9 +4,7 @@ import ( "testing" "github.com/cosmos/cosmos-sdk/baseapp" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/suite" - "github.com/tendermint/tendermint/crypto" "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" ibctesting "github.com/cosmos/ibc-go/v3/testing" @@ -46,16 +44,6 @@ func NewTransferPath(chainA, chainB *ibctesting.TestChain) *ibctesting.Path { return path } -func (suite *KeeperTestSuite) TestGetTransferAccount() { - expectedMaccAddr := sdk.AccAddress(crypto.AddressHash([]byte(types.ModuleName))) - - macc := suite.chainA.GetSimApp().TransferKeeper.GetTransferAccount(suite.chainA.GetContext()) - - suite.Require().NotNil(macc) - suite.Require().Equal(types.ModuleName, macc.GetName()) - suite.Require().Equal(expectedMaccAddr, macc.GetAddress()) -} - func TestKeeperTestSuite(t *testing.T) { suite.Run(t, new(KeeperTestSuite)) } diff --git a/modules/core/02-client/keeper/keeper_test.go b/modules/core/02-client/keeper/keeper_test.go index 535b2f1685f..f56607e4dcf 100644 --- a/modules/core/02-client/keeper/keeper_test.go +++ b/modules/core/02-client/keeper/keeper_test.go @@ -66,7 +66,8 @@ type KeeperTestSuite struct { now time.Time past time.Time solomachine *ibctesting.Solomachine - signers map[string]tmtypes.PrivValidator + + signers map[string]tmtypes.PrivValidator // TODO: deprecate queryClient types.QueryClient diff --git a/modules/core/04-channel/keeper/events.go b/modules/core/04-channel/keeper/events.go index d299d6afeda..731d298a2ae 100644 --- a/modules/core/04-channel/keeper/events.go +++ b/modules/core/04-channel/keeper/events.go @@ -20,6 +20,7 @@ func EmitChannelOpenInitEvent(ctx sdk.Context, portID string, channelID string, sdk.NewAttribute(types.AttributeCounterpartyPortID, channel.Counterparty.PortId), sdk.NewAttribute(types.AttributeCounterpartyChannelID, channel.Counterparty.ChannelId), sdk.NewAttribute(types.AttributeKeyConnectionID, channel.ConnectionHops[0]), + sdk.NewAttribute(types.AttributeVersion, channel.Version), ), }) @@ -41,6 +42,7 @@ func EmitChannelOpenTryEvent(ctx sdk.Context, portID string, channelID string, c sdk.NewAttribute(types.AttributeCounterpartyPortID, channel.Counterparty.PortId), sdk.NewAttribute(types.AttributeCounterpartyChannelID, channel.Counterparty.ChannelId), sdk.NewAttribute(types.AttributeKeyConnectionID, channel.ConnectionHops[0]), + sdk.NewAttribute(types.AttributeVersion, channel.Version), ), }) ctx.EventManager().EmitEvents(sdk.Events{ diff --git a/modules/core/04-channel/keeper/keeper.go b/modules/core/04-channel/keeper/keeper.go index 65378039ad9..ca2f824ad48 100644 --- a/modules/core/04-channel/keeper/keeper.go +++ b/modules/core/04-channel/keeper/keeper.go @@ -86,6 +86,16 @@ func (k Keeper) SetChannel(ctx sdk.Context, portID, channelID string, channel ty store.Set(host.ChannelKey(portID, channelID), bz) } +// GetAppVersion gets the version for the specified channel. +func (k Keeper) GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) { + channel, found := k.GetChannel(ctx, portID, channelID) + if !found { + return "", false + } + + return channel.Version, true +} + // GetNextChannelSequence gets the next channel sequence from the store. func (k Keeper) GetNextChannelSequence(ctx sdk.Context) uint64 { store := ctx.KVStore(k.storeKey) diff --git a/modules/core/04-channel/keeper/keeper_test.go b/modules/core/04-channel/keeper/keeper_test.go index 60888f11c3c..f04664d71f4 100644 --- a/modules/core/04-channel/keeper/keeper_test.go +++ b/modules/core/04-channel/keeper/keeper_test.go @@ -7,6 +7,7 @@ import ( "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" ibctesting "github.com/cosmos/ibc-go/v3/testing" + ibcmock "github.com/cosmos/ibc-go/v3/testing/mock" ) // KeeperTestSuite is a testing suite to test keeper functions. @@ -62,6 +63,24 @@ func (suite *KeeperTestSuite) TestSetChannel() { suite.Equal(expectedCounterparty, storedChannel.Counterparty) } +func (suite *KeeperTestSuite) TestGetAppVersion() { + // create client and connections on both chains + path := ibctesting.NewPath(suite.chainA, suite.chainB) + suite.coordinator.SetupConnections(path) + + version, found := suite.chainA.App.GetIBCKeeper().ChannelKeeper.GetAppVersion(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) + suite.Require().False(found) + suite.Require().Empty(version) + + // init channel + err := path.EndpointA.ChanOpenInit() + suite.NoError(err) + + channelVersion, found := suite.chainA.App.GetIBCKeeper().ChannelKeeper.GetAppVersion(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) + suite.Require().True(found) + suite.Require().Equal(ibcmock.Version, channelVersion) +} + // TestGetAllChannels creates multiple channels on chain A through various connections // and tests their retrieval. 2 channels are on connA0 and 1 channel is on connA1 func (suite KeeperTestSuite) TestGetAllChannels() { diff --git a/modules/core/04-channel/keeper/packet.go b/modules/core/04-channel/keeper/packet.go index 51b02f9bece..c3e8e45b89c 100644 --- a/modules/core/04-channel/keeper/packet.go +++ b/modules/core/04-channel/keeper/packet.go @@ -2,6 +2,7 @@ package keeper import ( "bytes" + "strconv" "time" sdk "github.com/cosmos/cosmos-sdk/types" @@ -129,7 +130,7 @@ func (k Keeper) SendPacket( k.Logger(ctx).Info( "packet sent", - "sequence", packet.GetSequence(), + "sequence", strconv.FormatUint(packet.GetSequence(), 10), "src_port", packet.GetSourcePort(), "src_channel", packet.GetSourceChannel(), "dst_port", packet.GetDestPort(), @@ -284,7 +285,7 @@ func (k Keeper) RecvPacket( // log that a packet has been received & executed k.Logger(ctx).Info( "packet received", - "sequence", packet.GetSequence(), + "sequence", strconv.FormatUint(packet.GetSequence(), 10), "src_port", packet.GetSourcePort(), "src_channel", packet.GetSourceChannel(), "dst_port", packet.GetDestPort(), @@ -494,7 +495,7 @@ func (k Keeper) AcknowledgePacket( // log that a packet has been acknowledged k.Logger(ctx).Info( "packet acknowledged", - "sequence", packet.GetSequence(), + "sequence", strconv.FormatUint(packet.GetSequence(), 10), "src_port", packet.GetSourcePort(), "src_channel", packet.GetSourceChannel(), "dst_port", packet.GetDestPort(), diff --git a/modules/core/04-channel/keeper/timeout.go b/modules/core/04-channel/keeper/timeout.go index b446aa7ea4c..5a14ef85b6b 100644 --- a/modules/core/04-channel/keeper/timeout.go +++ b/modules/core/04-channel/keeper/timeout.go @@ -2,6 +2,7 @@ package keeper import ( "bytes" + "strconv" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -159,7 +160,7 @@ func (k Keeper) TimeoutExecuted( k.Logger(ctx).Info( "packet timed-out", - "sequence", packet.GetSequence(), + "sequence", strconv.FormatUint(packet.GetSequence(), 10), "src_port", packet.GetSourcePort(), "src_channel", packet.GetSourceChannel(), "dst_port", packet.GetDestPort(), diff --git a/modules/core/04-channel/types/channel.pb.go b/modules/core/04-channel/types/channel.pb.go index 4ebbe01c977..3ce5ce3a2e1 100644 --- a/modules/core/04-channel/types/channel.pb.go +++ b/modules/core/04-channel/types/channel.pb.go @@ -347,6 +347,51 @@ func (m *PacketState) XXX_DiscardUnknown() { var xxx_messageInfo_PacketState proto.InternalMessageInfo +// PacketId is an identifer for a unique Packet +// Source chains refer to packets by source port/channel +// Destination chains refer to packets by destination port/channel +type PacketId struct { + // channel port identifier + PortId string `protobuf:"bytes,1,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty" yaml:"port_id"` + // channel unique identifier + ChannelId string `protobuf:"bytes,2,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty" yaml:"channel_id"` + // packet sequence + Sequence uint64 `protobuf:"varint,3,opt,name=sequence,proto3" json:"sequence,omitempty"` +} + +func (m *PacketId) Reset() { *m = PacketId{} } +func (m *PacketId) String() string { return proto.CompactTextString(m) } +func (*PacketId) ProtoMessage() {} +func (*PacketId) Descriptor() ([]byte, []int) { + return fileDescriptor_c3a07336710636a0, []int{5} +} +func (m *PacketId) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PacketId) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PacketId.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 *PacketId) XXX_Merge(src proto.Message) { + xxx_messageInfo_PacketId.Merge(m, src) +} +func (m *PacketId) XXX_Size() int { + return m.Size() +} +func (m *PacketId) XXX_DiscardUnknown() { + xxx_messageInfo_PacketId.DiscardUnknown(m) +} + +var xxx_messageInfo_PacketId proto.InternalMessageInfo + // Acknowledgement is the recommended acknowledgement format to be used by // app-specific protocols. // NOTE: The field numbers 21 and 22 were explicitly chosen to avoid accidental @@ -367,7 +412,7 @@ func (m *Acknowledgement) Reset() { *m = Acknowledgement{} } func (m *Acknowledgement) String() string { return proto.CompactTextString(m) } func (*Acknowledgement) ProtoMessage() {} func (*Acknowledgement) Descriptor() ([]byte, []int) { - return fileDescriptor_c3a07336710636a0, []int{5} + return fileDescriptor_c3a07336710636a0, []int{6} } func (m *Acknowledgement) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -449,70 +494,72 @@ func init() { proto.RegisterType((*Counterparty)(nil), "ibc.core.channel.v1.Counterparty") proto.RegisterType((*Packet)(nil), "ibc.core.channel.v1.Packet") proto.RegisterType((*PacketState)(nil), "ibc.core.channel.v1.PacketState") + proto.RegisterType((*PacketId)(nil), "ibc.core.channel.v1.PacketId") proto.RegisterType((*Acknowledgement)(nil), "ibc.core.channel.v1.Acknowledgement") } func init() { proto.RegisterFile("ibc/core/channel/v1/channel.proto", fileDescriptor_c3a07336710636a0) } var fileDescriptor_c3a07336710636a0 = []byte{ - // 911 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x55, 0xcd, 0x8e, 0xda, 0x56, - 0x14, 0xc6, 0x83, 0xf9, 0x3b, 0x0c, 0x0c, 0x73, 0xd3, 0x21, 0xae, 0x9b, 0x60, 0x62, 0x75, 0x31, - 0x4a, 0x15, 0xc8, 0x24, 0x51, 0xab, 0x66, 0xd5, 0xe1, 0x27, 0x1a, 0xab, 0x11, 0x8c, 0x0c, 0xb3, - 0x68, 0x36, 0x14, 0xec, 0x5b, 0xb0, 0x02, 0xbe, 0xd4, 0xbe, 0x30, 0x9a, 0x37, 0x88, 0x58, 0xf5, - 0x05, 0x90, 0x2a, 0x55, 0xed, 0x2b, 0xf4, 0x15, 0xb2, 0xcc, 0xb2, 0x2b, 0xab, 0x9a, 0x59, 0x74, - 0xcf, 0x0b, 0xb4, 0xf2, 0xbd, 0xd7, 0xfc, 0x4c, 0xa2, 0x2c, 0xbb, 0xca, 0x8a, 0x7b, 0xbe, 0xef, - 0x3b, 0x3f, 0x3e, 0xe7, 0x70, 0x2f, 0x3c, 0x70, 0x06, 0x56, 0xd5, 0x22, 0x1e, 0xae, 0x5a, 0xa3, - 0xbe, 0xeb, 0xe2, 0x71, 0x75, 0x7e, 0x12, 0x1d, 0x2b, 0x53, 0x8f, 0x50, 0x82, 0xee, 0x38, 0x03, - 0xab, 0x12, 0x4a, 0x2a, 0x11, 0x3e, 0x3f, 0x51, 0x3f, 0x1b, 0x92, 0x21, 0x61, 0x7c, 0x35, 0x3c, - 0x71, 0xa9, 0xaa, 0x6d, 0xa2, 0x8d, 0x1d, 0xec, 0x52, 0x16, 0x8c, 0x9d, 0xb8, 0x40, 0xff, 0x7d, - 0x0f, 0x52, 0x75, 0x1e, 0x05, 0x3d, 0x86, 0x84, 0x4f, 0xfb, 0x14, 0x2b, 0x52, 0x59, 0x3a, 0xce, - 0x3f, 0x51, 0x2b, 0x1f, 0xc8, 0x53, 0xe9, 0x84, 0x0a, 0x93, 0x0b, 0xd1, 0xd7, 0x90, 0x26, 0x9e, - 0x8d, 0x3d, 0xc7, 0x1d, 0x2a, 0x7b, 0x1f, 0x71, 0x6a, 0x87, 0x22, 0x73, 0xad, 0x45, 0xdf, 0xc3, - 0xbe, 0x45, 0x66, 0x2e, 0xc5, 0xde, 0xb4, 0xef, 0xd1, 0x2b, 0x25, 0x5e, 0x96, 0x8e, 0xb3, 0x4f, - 0x1e, 0x7c, 0xd0, 0xb7, 0xbe, 0x25, 0xac, 0xc9, 0x6f, 0x03, 0x2d, 0x66, 0xee, 0x38, 0xa3, 0x3a, - 0x1c, 0x58, 0xc4, 0x75, 0xb1, 0x45, 0x1d, 0xe2, 0xf6, 0x46, 0x64, 0xea, 0x2b, 0x72, 0x39, 0x7e, - 0x9c, 0xa9, 0xa9, 0xab, 0x40, 0x2b, 0x5e, 0xf5, 0x27, 0xe3, 0xe7, 0xfa, 0x2d, 0x81, 0x6e, 0xe6, - 0x37, 0xc8, 0x19, 0x99, 0xfa, 0x48, 0x81, 0xd4, 0x1c, 0x7b, 0xbe, 0x43, 0x5c, 0x25, 0x51, 0x96, - 0x8e, 0x33, 0x66, 0x64, 0x3e, 0x97, 0xdf, 0xfc, 0xaa, 0xc5, 0xf4, 0x7f, 0xf6, 0xe0, 0xd0, 0xb0, - 0xb1, 0x4b, 0x9d, 0x9f, 0x1c, 0x6c, 0x7f, 0xea, 0xd8, 0x47, 0x3a, 0x86, 0xee, 0x42, 0x6a, 0x4a, - 0x3c, 0xda, 0x73, 0x6c, 0x25, 0xc9, 0x98, 0x64, 0x68, 0x1a, 0x36, 0xba, 0x0f, 0x20, 0xca, 0x0c, - 0xb9, 0x14, 0xe3, 0x32, 0x02, 0x31, 0x6c, 0xd1, 0xe9, 0x4b, 0xd8, 0xdf, 0xfe, 0x00, 0xf4, 0xd5, - 0x26, 0x5a, 0xd8, 0xe5, 0x4c, 0x0d, 0xad, 0x02, 0x2d, 0xcf, 0x8b, 0x14, 0x84, 0xbe, 0xce, 0xf0, - 0x6c, 0x27, 0xc3, 0x1e, 0xd3, 0x1f, 0xad, 0x02, 0xed, 0x50, 0x7c, 0xd4, 0x9a, 0xd3, 0xdf, 0x4f, - 0xfc, 0x6f, 0x1c, 0x92, 0xe7, 0x7d, 0xeb, 0x35, 0xa6, 0x48, 0x85, 0xb4, 0x8f, 0x7f, 0x9e, 0x61, - 0xd7, 0xe2, 0xa3, 0x95, 0xcd, 0xb5, 0x8d, 0xbe, 0x81, 0xac, 0x4f, 0x66, 0x9e, 0x85, 0x7b, 0x61, - 0x4e, 0x91, 0xa3, 0xb8, 0x0a, 0x34, 0xc4, 0x73, 0x6c, 0x91, 0xba, 0x09, 0xdc, 0x3a, 0x27, 0x1e, - 0x45, 0xdf, 0x41, 0x5e, 0x70, 0x22, 0x33, 0x1b, 0x62, 0xa6, 0xf6, 0xf9, 0x2a, 0xd0, 0x8e, 0x76, - 0x7c, 0x05, 0xaf, 0x9b, 0x39, 0x0e, 0x44, 0xeb, 0xf6, 0x02, 0x0a, 0x36, 0xf6, 0xa9, 0xe3, 0xf6, - 0xd9, 0x5c, 0x58, 0x7e, 0x99, 0xc5, 0xf8, 0x62, 0x15, 0x68, 0x77, 0x79, 0x8c, 0xdb, 0x0a, 0xdd, - 0x3c, 0xd8, 0x82, 0x58, 0x25, 0x6d, 0xb8, 0xb3, 0xad, 0x8a, 0xca, 0x61, 0x63, 0xac, 0x95, 0x56, - 0x81, 0xa6, 0xbe, 0x1f, 0x6a, 0x5d, 0x13, 0xda, 0x42, 0xa3, 0xc2, 0x10, 0xc8, 0x76, 0x9f, 0xf6, - 0xd9, 0xb8, 0xf7, 0x4d, 0x76, 0x46, 0x3f, 0x42, 0x9e, 0x3a, 0x13, 0x4c, 0x66, 0xb4, 0x37, 0xc2, - 0xce, 0x70, 0x44, 0xd9, 0xc0, 0xb3, 0x3b, 0xfb, 0xce, 0x6f, 0xa2, 0xf9, 0x49, 0xe5, 0x8c, 0x29, - 0x6a, 0xf7, 0xc3, 0x65, 0xdd, 0xb4, 0x63, 0xd7, 0x5f, 0x37, 0x73, 0x02, 0xe0, 0x6a, 0x64, 0xc0, - 0x61, 0xa4, 0x08, 0x7f, 0x7d, 0xda, 0x9f, 0x4c, 0x95, 0x74, 0x38, 0xae, 0xda, 0xbd, 0x55, 0xa0, - 0x29, 0xbb, 0x41, 0xd6, 0x12, 0xdd, 0x2c, 0x08, 0xac, 0x1b, 0x41, 0x62, 0x03, 0xfe, 0x90, 0x20, - 0xcb, 0x37, 0x80, 0xfd, 0x67, 0xff, 0x87, 0xd5, 0xdb, 0xd9, 0xb4, 0xf8, 0xad, 0x4d, 0x8b, 0xba, - 0x2a, 0x6f, 0xba, 0x2a, 0x0a, 0x6d, 0xc3, 0xc1, 0xa9, 0xf5, 0xda, 0x25, 0x97, 0x63, 0x6c, 0x0f, - 0xf1, 0x04, 0xbb, 0x14, 0x29, 0x90, 0xf4, 0xb0, 0x3f, 0x1b, 0x53, 0xe5, 0x28, 0x94, 0x9f, 0xc5, - 0x4c, 0x61, 0xa3, 0x22, 0x24, 0xb0, 0xe7, 0x11, 0x4f, 0x29, 0x86, 0x35, 0x9d, 0xc5, 0x4c, 0x6e, - 0xd6, 0x00, 0xd2, 0x1e, 0xf6, 0xa7, 0xc4, 0xf5, 0xf1, 0xc3, 0x3f, 0x25, 0x48, 0x74, 0xc4, 0x05, - 0xa5, 0x75, 0xba, 0xa7, 0xdd, 0x66, 0xef, 0xa2, 0x65, 0xb4, 0x8c, 0xae, 0x71, 0xfa, 0xd2, 0x78, - 0xd5, 0x6c, 0xf4, 0x2e, 0x5a, 0x9d, 0xf3, 0x66, 0xdd, 0x78, 0x61, 0x34, 0x1b, 0x85, 0x98, 0x7a, - 0xb8, 0x58, 0x96, 0x73, 0x3b, 0x02, 0xa4, 0x00, 0x70, 0xbf, 0x10, 0x2c, 0x48, 0x6a, 0x7a, 0xb1, - 0x2c, 0xcb, 0xe1, 0x19, 0x95, 0x20, 0xc7, 0x99, 0xae, 0xf9, 0x43, 0xfb, 0xbc, 0xd9, 0x2a, 0xec, - 0xa9, 0xd9, 0xc5, 0xb2, 0x9c, 0x12, 0xe6, 0xc6, 0x93, 0x91, 0x71, 0xee, 0xc9, 0x98, 0x7b, 0xb0, - 0xcf, 0x99, 0xfa, 0xcb, 0x76, 0xa7, 0xd9, 0x28, 0xc8, 0x2a, 0x2c, 0x96, 0xe5, 0x24, 0xb7, 0x54, - 0xf9, 0xcd, 0x6f, 0xa5, 0xd8, 0xc3, 0x4b, 0x48, 0xb0, 0xbb, 0x12, 0x7d, 0x09, 0xc5, 0xb6, 0xd9, - 0x68, 0x9a, 0xbd, 0x56, 0xbb, 0xd5, 0xbc, 0x55, 0x2f, 0x0b, 0x19, 0xe2, 0x48, 0x87, 0x03, 0xae, - 0xba, 0x68, 0xb1, 0xdf, 0x66, 0xa3, 0x20, 0xa9, 0xb9, 0xc5, 0xb2, 0x9c, 0x59, 0x03, 0x61, 0xc1, - 0x5c, 0x13, 0x29, 0x44, 0xc1, 0xc2, 0xe4, 0x89, 0x6b, 0x9d, 0xb7, 0xd7, 0x25, 0xe9, 0xdd, 0x75, - 0x49, 0xfa, 0xfb, 0xba, 0x24, 0xfd, 0x72, 0x53, 0x8a, 0xbd, 0xbb, 0x29, 0xc5, 0xfe, 0xba, 0x29, - 0xc5, 0x5e, 0x7d, 0x3b, 0x74, 0xe8, 0x68, 0x36, 0xa8, 0x58, 0x64, 0x52, 0xb5, 0x88, 0x3f, 0x21, - 0x7e, 0xd5, 0x19, 0x58, 0x8f, 0x86, 0xa4, 0x3a, 0x7f, 0x5a, 0x9d, 0x10, 0x7b, 0x36, 0xc6, 0x3e, - 0x7f, 0x94, 0x1f, 0x3f, 0x7b, 0x14, 0xbd, 0xf2, 0xf4, 0x6a, 0x8a, 0xfd, 0x41, 0x92, 0xbd, 0xca, - 0x4f, 0xff, 0x0b, 0x00, 0x00, 0xff, 0xff, 0xa8, 0x31, 0x31, 0xe5, 0x06, 0x08, 0x00, 0x00, + // 925 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x56, 0xcd, 0x8e, 0x1a, 0x47, + 0x10, 0x66, 0x60, 0xf8, 0x2b, 0x16, 0x96, 0x6d, 0x67, 0xf1, 0x64, 0x62, 0x33, 0x78, 0x94, 0xc3, + 0xca, 0x91, 0xc1, 0x6b, 0x5b, 0x89, 0xe2, 0x53, 0x96, 0x1f, 0x6b, 0x47, 0xb1, 0x60, 0x35, 0xb0, + 0x87, 0xf8, 0x42, 0x60, 0xa6, 0x03, 0x23, 0xc3, 0x34, 0x99, 0x69, 0x58, 0xed, 0x1b, 0x58, 0x5c, + 0x92, 0x17, 0x40, 0x8a, 0x14, 0x25, 0xaf, 0x90, 0x57, 0xf0, 0xd1, 0xc7, 0x9c, 0x50, 0xb4, 0x7b, + 0xc8, 0x9d, 0x17, 0x48, 0x34, 0xdd, 0x3d, 0xfc, 0xac, 0xad, 0x3d, 0x26, 0x17, 0x9f, 0xe8, 0xaa, + 0xef, 0xab, 0xaa, 0x6f, 0xaa, 0x8a, 0x56, 0xc3, 0x03, 0xa7, 0x6f, 0x55, 0x2c, 0xe2, 0xe1, 0x8a, + 0x35, 0xec, 0xb9, 0x2e, 0x1e, 0x55, 0x66, 0xc7, 0xe1, 0xb1, 0x3c, 0xf1, 0x08, 0x25, 0xe8, 0x8e, + 0xd3, 0xb7, 0xca, 0x01, 0xa5, 0x1c, 0xfa, 0x67, 0xc7, 0xea, 0x27, 0x03, 0x32, 0x20, 0x0c, 0xaf, + 0x04, 0x27, 0x4e, 0x55, 0xb5, 0x4d, 0xb6, 0x91, 0x83, 0x5d, 0xca, 0x92, 0xb1, 0x13, 0x27, 0xe8, + 0xbf, 0x45, 0x21, 0x59, 0xe3, 0x59, 0xd0, 0x63, 0x88, 0xfb, 0xb4, 0x47, 0xb1, 0x22, 0x95, 0xa4, + 0xa3, 0xdc, 0x13, 0xb5, 0xfc, 0x81, 0x3a, 0xe5, 0x76, 0xc0, 0x30, 0x39, 0x11, 0x7d, 0x09, 0x29, + 0xe2, 0xd9, 0xd8, 0x73, 0xdc, 0x81, 0x12, 0xbd, 0x25, 0xa8, 0x15, 0x90, 0xcc, 0x35, 0x17, 0x7d, + 0x0b, 0x7b, 0x16, 0x99, 0xba, 0x14, 0x7b, 0x93, 0x9e, 0x47, 0x2f, 0x95, 0x58, 0x49, 0x3a, 0xca, + 0x3c, 0x79, 0xf0, 0xc1, 0xd8, 0xda, 0x16, 0xb1, 0x2a, 0xbf, 0x5d, 0x6a, 0x11, 0x73, 0x27, 0x18, + 0xd5, 0x60, 0xdf, 0x22, 0xae, 0x8b, 0x2d, 0xea, 0x10, 0xb7, 0x3b, 0x24, 0x13, 0x5f, 0x91, 0x4b, + 0xb1, 0xa3, 0x74, 0x55, 0x5d, 0x2d, 0xb5, 0xc2, 0x65, 0x6f, 0x3c, 0x7a, 0xae, 0xdf, 0x20, 0xe8, + 0x66, 0x6e, 0xe3, 0x39, 0x25, 0x13, 0x1f, 0x29, 0x90, 0x9c, 0x61, 0xcf, 0x77, 0x88, 0xab, 0xc4, + 0x4b, 0xd2, 0x51, 0xda, 0x0c, 0xcd, 0xe7, 0xf2, 0x9b, 0x5f, 0xb4, 0x88, 0xfe, 0x77, 0x14, 0x0e, + 0x0c, 0x1b, 0xbb, 0xd4, 0xf9, 0xc1, 0xc1, 0xf6, 0xc7, 0x8e, 0xdd, 0xd2, 0x31, 0x74, 0x17, 0x92, + 0x13, 0xe2, 0xd1, 0xae, 0x63, 0x2b, 0x09, 0x86, 0x24, 0x02, 0xd3, 0xb0, 0xd1, 0x7d, 0x00, 0x21, + 0x33, 0xc0, 0x92, 0x0c, 0x4b, 0x0b, 0x8f, 0x61, 0x8b, 0x4e, 0x5f, 0xc0, 0xde, 0xf6, 0x07, 0xa0, + 0x2f, 0x36, 0xd9, 0x82, 0x2e, 0xa7, 0xab, 0x68, 0xb5, 0xd4, 0x72, 0x5c, 0xa4, 0x00, 0xf4, 0x75, + 0x85, 0x67, 0x3b, 0x15, 0xa2, 0x8c, 0x7f, 0xb8, 0x5a, 0x6a, 0x07, 0xe2, 0xa3, 0xd6, 0x98, 0xfe, + 0x7e, 0xe1, 0x7f, 0x62, 0x90, 0x38, 0xeb, 0x59, 0xaf, 0x31, 0x45, 0x2a, 0xa4, 0x7c, 0xfc, 0xe3, + 0x14, 0xbb, 0x16, 0x1f, 0xad, 0x6c, 0xae, 0x6d, 0xf4, 0x15, 0x64, 0x7c, 0x32, 0xf5, 0x2c, 0xdc, + 0x0d, 0x6a, 0x8a, 0x1a, 0x85, 0xd5, 0x52, 0x43, 0xbc, 0xc6, 0x16, 0xa8, 0x9b, 0xc0, 0xad, 0x33, + 0xe2, 0x51, 0xf4, 0x0d, 0xe4, 0x04, 0x26, 0x2a, 0xb3, 0x21, 0xa6, 0xab, 0x9f, 0xae, 0x96, 0xda, + 0xe1, 0x4e, 0xac, 0xc0, 0x75, 0x33, 0xcb, 0x1d, 0xe1, 0xba, 0xbd, 0x80, 0xbc, 0x8d, 0x7d, 0xea, + 0xb8, 0x3d, 0x36, 0x17, 0x56, 0x5f, 0x66, 0x39, 0x3e, 0x5b, 0x2d, 0xb5, 0xbb, 0x3c, 0xc7, 0x4d, + 0x86, 0x6e, 0xee, 0x6f, 0xb9, 0x98, 0x92, 0x16, 0xdc, 0xd9, 0x66, 0x85, 0x72, 0xd8, 0x18, 0xab, + 0xc5, 0xd5, 0x52, 0x53, 0xdf, 0x4f, 0xb5, 0xd6, 0x84, 0xb6, 0xbc, 0xa1, 0x30, 0x04, 0xb2, 0xdd, + 0xa3, 0x3d, 0x36, 0xee, 0x3d, 0x93, 0x9d, 0xd1, 0xf7, 0x90, 0xa3, 0xce, 0x18, 0x93, 0x29, 0xed, + 0x0e, 0xb1, 0x33, 0x18, 0x52, 0x36, 0xf0, 0xcc, 0xce, 0xbe, 0xf3, 0x9b, 0x68, 0x76, 0x5c, 0x3e, + 0x65, 0x8c, 0xea, 0xfd, 0x60, 0x59, 0x37, 0xed, 0xd8, 0x8d, 0xd7, 0xcd, 0xac, 0x70, 0x70, 0x36, + 0x32, 0xe0, 0x20, 0x64, 0x04, 0xbf, 0x3e, 0xed, 0x8d, 0x27, 0x4a, 0x2a, 0x18, 0x57, 0xf5, 0xde, + 0x6a, 0xa9, 0x29, 0xbb, 0x49, 0xd6, 0x14, 0xdd, 0xcc, 0x0b, 0x5f, 0x27, 0x74, 0x89, 0x0d, 0xf8, + 0x5d, 0x82, 0x0c, 0xdf, 0x00, 0xf6, 0x9f, 0xfd, 0x0f, 0x56, 0x6f, 0x67, 0xd3, 0x62, 0x37, 0x36, + 0x2d, 0xec, 0xaa, 0xbc, 0xe9, 0xaa, 0x10, 0xfa, 0x93, 0x04, 0x29, 0x2e, 0xd4, 0xb0, 0xff, 0x67, + 0x95, 0x42, 0x51, 0x0b, 0xf6, 0x4f, 0xac, 0xd7, 0x2e, 0xb9, 0x18, 0x61, 0x7b, 0x80, 0xc7, 0xd8, + 0xa5, 0x48, 0x81, 0x84, 0x87, 0xfd, 0xe9, 0x88, 0x2a, 0x87, 0xc1, 0x07, 0x9c, 0x46, 0x4c, 0x61, + 0xa3, 0x02, 0xc4, 0xb1, 0xe7, 0x11, 0x4f, 0x29, 0x04, 0xf5, 0x4f, 0x23, 0x26, 0x37, 0xab, 0x00, + 0x29, 0x0f, 0xfb, 0x13, 0xe2, 0xfa, 0xf8, 0xe1, 0x1f, 0x12, 0xc4, 0xdb, 0xe2, 0xca, 0xd4, 0xda, + 0x9d, 0x93, 0x4e, 0xa3, 0x7b, 0xde, 0x34, 0x9a, 0x46, 0xc7, 0x38, 0x79, 0x69, 0xbc, 0x6a, 0xd4, + 0xbb, 0xe7, 0xcd, 0xf6, 0x59, 0xa3, 0x66, 0xbc, 0x30, 0x1a, 0xf5, 0x7c, 0x44, 0x3d, 0x98, 0x2f, + 0x4a, 0xd9, 0x1d, 0x02, 0x52, 0x00, 0x78, 0x5c, 0xe0, 0xcc, 0x4b, 0x6a, 0x6a, 0xbe, 0x28, 0xc9, + 0xc1, 0x19, 0x15, 0x21, 0xcb, 0x91, 0x8e, 0xf9, 0x5d, 0xeb, 0xac, 0xd1, 0xcc, 0x47, 0xd5, 0xcc, + 0x7c, 0x51, 0x4a, 0x0a, 0x73, 0x13, 0xc9, 0xc0, 0x18, 0x8f, 0x64, 0xc8, 0x3d, 0xd8, 0xe3, 0x48, + 0xed, 0x65, 0xab, 0xdd, 0xa8, 0xe7, 0x65, 0x15, 0xe6, 0x8b, 0x52, 0x82, 0x5b, 0xaa, 0xfc, 0xe6, + 0xd7, 0x62, 0xe4, 0xe1, 0x05, 0xc4, 0xd9, 0xed, 0x8d, 0x3e, 0x87, 0x42, 0xcb, 0xac, 0x37, 0xcc, + 0x6e, 0xb3, 0xd5, 0x6c, 0xdc, 0xd0, 0xcb, 0x52, 0x06, 0x7e, 0xa4, 0xc3, 0x3e, 0x67, 0x9d, 0x37, + 0xd9, 0x6f, 0xa3, 0x9e, 0x97, 0xd4, 0xec, 0x7c, 0x51, 0x4a, 0xaf, 0x1d, 0x81, 0x60, 0xce, 0x09, + 0x19, 0x42, 0xb0, 0x30, 0x79, 0xe1, 0x6a, 0xfb, 0xed, 0x55, 0x51, 0x7a, 0x77, 0x55, 0x94, 0xfe, + 0xba, 0x2a, 0x4a, 0x3f, 0x5f, 0x17, 0x23, 0xef, 0xae, 0x8b, 0x91, 0x3f, 0xaf, 0x8b, 0x91, 0x57, + 0x5f, 0x0f, 0x1c, 0x3a, 0x9c, 0xf6, 0xcb, 0x16, 0x19, 0x57, 0x2c, 0xe2, 0x8f, 0x89, 0x5f, 0x71, + 0xfa, 0xd6, 0xa3, 0x01, 0xa9, 0xcc, 0x9e, 0x56, 0xc6, 0xc4, 0x9e, 0x8e, 0xb0, 0xcf, 0x9f, 0x09, + 0x8f, 0x9f, 0x3d, 0x0a, 0xdf, 0x1d, 0xf4, 0x72, 0x82, 0xfd, 0x7e, 0x82, 0xbd, 0x13, 0x9e, 0xfe, + 0x1b, 0x00, 0x00, 0xff, 0xff, 0x47, 0xf5, 0x82, 0xa6, 0x98, 0x08, 0x00, 0x00, } func (m *Channel) Marshal() (dAtA []byte, err error) { @@ -811,6 +858,48 @@ func (m *PacketState) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *PacketId) 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 *PacketId) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PacketId) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Sequence != 0 { + i = encodeVarintChannel(dAtA, i, uint64(m.Sequence)) + i-- + dAtA[i] = 0x18 + } + if len(m.ChannelId) > 0 { + i -= len(m.ChannelId) + copy(dAtA[i:], m.ChannelId) + i = encodeVarintChannel(dAtA, i, uint64(len(m.ChannelId))) + i-- + dAtA[i] = 0x12 + } + if len(m.PortId) > 0 { + i -= len(m.PortId) + copy(dAtA[i:], m.PortId) + i = encodeVarintChannel(dAtA, i, uint64(len(m.PortId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *Acknowledgement) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -1028,6 +1117,26 @@ func (m *PacketState) Size() (n int) { return n } +func (m *PacketId) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PortId) + if l > 0 { + n += 1 + l + sovChannel(uint64(l)) + } + l = len(m.ChannelId) + if l > 0 { + n += 1 + l + sovChannel(uint64(l)) + } + if m.Sequence != 0 { + n += 1 + sovChannel(uint64(m.Sequence)) + } + return n +} + func (m *Acknowledgement) Size() (n int) { if m == nil { return 0 @@ -2067,6 +2176,139 @@ func (m *PacketState) Unmarshal(dAtA []byte) error { } return nil } +func (m *PacketId) 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 ErrIntOverflowChannel + } + 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: PacketId: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PacketId: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PortId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChannel + } + 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 ErrInvalidLengthChannel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthChannel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PortId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChannelId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChannel + } + 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 ErrInvalidLengthChannel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthChannel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChannelId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) + } + m.Sequence = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChannel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Sequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipChannel(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthChannel + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *Acknowledgement) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/modules/core/04-channel/types/events.go b/modules/core/04-channel/types/events.go index 4154b856c5d..e9f909a695d 100644 --- a/modules/core/04-channel/types/events.go +++ b/modules/core/04-channel/types/events.go @@ -11,6 +11,7 @@ const ( AttributeKeyConnectionID = "connection_id" AttributeKeyPortID = "port_id" AttributeKeyChannelID = "channel_id" + AttributeVersion = "version" AttributeCounterpartyPortID = "counterparty_port_id" AttributeCounterpartyChannelID = "counterparty_channel_id" diff --git a/modules/core/04-channel/types/packet.go b/modules/core/04-channel/types/packet.go index 0df4d8edd8d..dd59c8bfde9 100644 --- a/modules/core/04-channel/types/packet.go +++ b/modules/core/04-channel/types/packet.go @@ -111,3 +111,25 @@ func (p Packet) ValidateBasic() error { } return nil } + +// Validates a PacketId +func (p PacketId) Validate() error { + if err := host.PortIdentifierValidator(p.PortId); err != nil { + return sdkerrors.Wrap(err, "invalid source port ID") + } + + if err := host.ChannelIdentifierValidator(p.ChannelId); err != nil { + return sdkerrors.Wrap(err, "invalid source channel ID") + } + + if p.Sequence == 0 { + return sdkerrors.Wrap(ErrInvalidPacket, "packet sequence cannot be 0") + } + + return nil +} + +// NewPacketId returns a new instance of PacketId +func NewPacketId(portId, channelId string, seq uint64) PacketId { + return PacketId{PortId: portId, ChannelId: channelId, Sequence: seq} +} diff --git a/modules/core/04-channel/types/tx.pb.go b/modules/core/04-channel/types/tx.pb.go index 8f9ebe6ec63..fe6f344384d 100644 --- a/modules/core/04-channel/types/tx.pb.go +++ b/modules/core/04-channel/types/tx.pb.go @@ -105,6 +105,7 @@ var xxx_messageInfo_MsgChannelOpenInit proto.InternalMessageInfo // MsgChannelOpenInitResponse defines the Msg/ChannelOpenInit response type. type MsgChannelOpenInitResponse struct { ChannelId string `protobuf:"bytes,1,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty" yaml:"channel_id"` + Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` } func (m *MsgChannelOpenInitResponse) Reset() { *m = MsgChannelOpenInitResponse{} } @@ -147,6 +148,13 @@ func (m *MsgChannelOpenInitResponse) GetChannelId() string { return "" } +func (m *MsgChannelOpenInitResponse) GetVersion() string { + if m != nil { + return m.Version + } + return "" +} + // MsgChannelOpenInit defines a msg sent by a Relayer to try to open a channel // on Chain B. The version field within the Channel field has been deprecated. Its // value will be ignored by core IBC. @@ -198,6 +206,7 @@ var xxx_messageInfo_MsgChannelOpenTry proto.InternalMessageInfo // MsgChannelOpenTryResponse defines the Msg/ChannelOpenTry response type. type MsgChannelOpenTryResponse struct { + Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` } func (m *MsgChannelOpenTryResponse) Reset() { *m = MsgChannelOpenTryResponse{} } @@ -233,6 +242,13 @@ func (m *MsgChannelOpenTryResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgChannelOpenTryResponse proto.InternalMessageInfo +func (m *MsgChannelOpenTryResponse) GetVersion() string { + if m != nil { + return m.Version + } + return "" +} + // MsgChannelOpenAck defines a msg sent by a Relayer to Chain A to acknowledge // the change of channel state to TRYOPEN on Chain B. type MsgChannelOpenAck struct { @@ -902,87 +918,88 @@ func init() { func init() { proto.RegisterFile("ibc/core/channel/v1/tx.proto", fileDescriptor_bc4637e0ac3fc7b7) } var fileDescriptor_bc4637e0ac3fc7b7 = []byte{ - // 1275 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0xcd, 0x6e, 0xdb, 0x46, - 0x10, 0xd6, 0x5f, 0x64, 0x67, 0xe4, 0xda, 0x32, 0x65, 0x3b, 0x32, 0x65, 0x8b, 0x2a, 0x0f, 0xb1, - 0xe1, 0xc2, 0x52, 0x6c, 0xa7, 0x28, 0x62, 0x14, 0x28, 0x2c, 0x55, 0x41, 0x8d, 0xd6, 0x3f, 0xa0, - 0xe4, 0x02, 0x75, 0x8b, 0x0a, 0x12, 0xb5, 0x91, 0x09, 0x49, 0x5c, 0x95, 0xa4, 0x94, 0xe8, 0x0d, - 0x02, 0x9f, 0x72, 0x0e, 0x60, 0x20, 0x45, 0x4f, 0x45, 0x0f, 0xe9, 0x63, 0xe4, 0x98, 0x53, 0x5b, - 0xf4, 0x20, 0x14, 0xf6, 0xa5, 0x67, 0x3d, 0x41, 0xc1, 0xe5, 0x92, 0xa2, 0x24, 0x12, 0xa6, 0x13, - 0xdb, 0xcd, 0x6d, 0x77, 0xe6, 0xdb, 0xd9, 0xd9, 0xef, 0x1b, 0xee, 0x0f, 0x61, 0x49, 0xaa, 0x88, - 0x19, 0x11, 0x2b, 0x28, 0x23, 0x9e, 0x94, 0x65, 0x19, 0x35, 0x32, 0x9d, 0x8d, 0x8c, 0xf6, 0x2c, - 0xdd, 0x52, 0xb0, 0x86, 0x99, 0x98, 0x54, 0x11, 0xd3, 0xba, 0x37, 0x4d, 0xbd, 0xe9, 0xce, 0x06, - 0x3b, 0x57, 0xc3, 0x35, 0x4c, 0xfc, 0x19, 0xbd, 0x65, 0x40, 0x59, 0x6e, 0x10, 0xa8, 0x21, 0x21, - 0x59, 0xd3, 0xe3, 0x18, 0x2d, 0x0a, 0xf8, 0xd8, 0x69, 0x26, 0x33, 0x2c, 0x81, 0xf0, 0x3f, 0xfb, - 0x81, 0xd9, 0x53, 0x6b, 0x39, 0xc3, 0x78, 0xd0, 0x42, 0xf2, 0xae, 0x2c, 0x69, 0xcc, 0x27, 0x30, - 0xd1, 0xc2, 0x8a, 0x56, 0x92, 0xaa, 0x71, 0x7f, 0xca, 0xbf, 0x7a, 0x37, 0xcb, 0xf4, 0x7b, 0xdc, - 0x74, 0xb7, 0xdc, 0x6c, 0x6c, 0xf3, 0xd4, 0xc1, 0x0b, 0x61, 0xbd, 0xb5, 0x5b, 0x65, 0x3e, 0x87, - 0x09, 0x1a, 0x34, 0x1e, 0x48, 0xf9, 0x57, 0x23, 0x9b, 0x4b, 0x69, 0x87, 0x45, 0xa4, 0xe9, 0x1c, - 0xd9, 0xd0, 0x9b, 0x1e, 0xe7, 0x13, 0xcc, 0x21, 0xcc, 0x02, 0x84, 0x55, 0xa9, 0x26, 0x23, 0x25, - 0x1e, 0xd4, 0x67, 0x12, 0x68, 0x6f, 0x7b, 0xf2, 0xf9, 0x2b, 0xce, 0xf7, 0xef, 0x2b, 0xce, 0xc7, - 0x0b, 0xc0, 0x8e, 0xa7, 0x28, 0x20, 0xb5, 0x85, 0x65, 0x15, 0x31, 0x0f, 0x01, 0x68, 0xa8, 0x41, - 0xb6, 0xf3, 0xfd, 0x1e, 0x37, 0x6b, 0x64, 0x3b, 0xf0, 0xf1, 0xc2, 0x5d, 0xda, 0xd9, 0xad, 0xf2, - 0x7f, 0x04, 0x61, 0x76, 0x38, 0x68, 0x51, 0xe9, 0x5e, 0x6d, 0xd9, 0xfb, 0x10, 0x6b, 0x29, 0xa8, - 0x23, 0xe1, 0xb6, 0x5a, 0xb2, 0x65, 0x10, 0x20, 0x03, 0x93, 0xfd, 0x1e, 0xc7, 0xd2, 0x81, 0xe3, - 0x20, 0x5e, 0x98, 0x35, 0xad, 0x39, 0x33, 0x25, 0x3b, 0x8d, 0xc1, 0xab, 0xd3, 0x28, 0xc0, 0x9c, - 0x88, 0xdb, 0xb2, 0x86, 0x94, 0x56, 0x59, 0xd1, 0xba, 0xa5, 0x0e, 0x52, 0x54, 0x09, 0xcb, 0xf1, - 0x10, 0x49, 0x87, 0xeb, 0xf7, 0xb8, 0x04, 0x25, 0xc4, 0x01, 0xc5, 0x0b, 0x31, 0xbb, 0xf9, 0x5b, - 0xc3, 0xaa, 0x53, 0xdb, 0x52, 0x30, 0x7e, 0x52, 0x92, 0x64, 0x49, 0x8b, 0xdf, 0x49, 0xf9, 0x57, - 0xa7, 0xec, 0xd4, 0x0e, 0x7c, 0xbc, 0x70, 0x97, 0x74, 0x48, 0xed, 0x1c, 0xc3, 0x94, 0xe1, 0x39, - 0x41, 0x52, 0xed, 0x44, 0x8b, 0x87, 0xc9, 0x62, 0x58, 0xdb, 0x62, 0x8c, 0x1a, 0xed, 0x6c, 0xa4, - 0xbf, 0x22, 0x88, 0x6c, 0x42, 0x5f, 0x4a, 0xbf, 0xc7, 0xc5, 0xec, 0x71, 0x8d, 0xd1, 0xbc, 0x10, - 0x21, 0x5d, 0x03, 0x69, 0x2b, 0x96, 0x09, 0x97, 0x62, 0x49, 0xc0, 0xe2, 0x98, 0xae, 0x66, 0xad, - 0xf0, 0x7f, 0x8e, 0xa9, 0xbe, 0x23, 0xd6, 0xaf, 0xa6, 0xfa, 0x70, 0xb9, 0x05, 0xbc, 0x95, 0x1b, - 0x73, 0x0c, 0xf7, 0x86, 0x78, 0xb7, 0x85, 0x20, 0x55, 0x9f, 0xe5, 0xfb, 0x3d, 0x2e, 0xe9, 0x20, - 0x90, 0x3d, 0xde, 0xbc, 0xdd, 0x33, 0xa8, 0x9b, 0x9b, 0x50, 0x7e, 0x03, 0x0c, 0x41, 0x4b, 0x9a, - 0xd2, 0xa5, 0xc2, 0xcf, 0xf5, 0x7b, 0x5c, 0xd4, 0x2e, 0x90, 0xa6, 0x74, 0x79, 0x61, 0x92, 0xb4, - 0xf5, 0x6f, 0xe7, 0x03, 0x93, 0x7d, 0x47, 0xac, 0x5b, 0xb2, 0xff, 0x16, 0x80, 0xf9, 0x61, 0x6f, - 0x0e, 0xcb, 0x4f, 0x24, 0xa5, 0x79, 0x1b, 0xd2, 0x5b, 0x54, 0x96, 0xc5, 0x3a, 0x11, 0xdb, 0x81, - 0xca, 0xb2, 0x58, 0x37, 0xa9, 0xd4, 0x0b, 0x72, 0x94, 0xca, 0xd0, 0x8d, 0x50, 0x79, 0xc7, 0x85, - 0x4a, 0x0e, 0x96, 0x1d, 0xc9, 0xb2, 0xe8, 0x7c, 0xe9, 0x87, 0xd8, 0x00, 0x91, 0x6b, 0x60, 0x15, - 0x5d, 0xfd, 0xd0, 0x78, 0x37, 0x32, 0x2f, 0x3f, 0x2c, 0x96, 0x21, 0xe1, 0x90, 0x9b, 0x95, 0xfb, - 0xeb, 0x00, 0x2c, 0x8c, 0xf8, 0x6f, 0xb1, 0x16, 0x86, 0x37, 0xd4, 0xe0, 0x3b, 0x6e, 0xa8, 0xb7, - 0x5b, 0x0e, 0x29, 0x48, 0x3a, 0x13, 0x66, 0x71, 0xfa, 0x22, 0x00, 0x1f, 0xed, 0xa9, 0x35, 0x01, - 0x89, 0x9d, 0xc3, 0xb2, 0x58, 0x47, 0x1a, 0xf3, 0x08, 0xc2, 0x2d, 0xd2, 0x22, 0x4c, 0x46, 0x36, - 0x13, 0x8e, 0x27, 0x99, 0x01, 0xa6, 0x07, 0x19, 0x1d, 0xc0, 0x3c, 0x86, 0xa8, 0x91, 0xae, 0x88, - 0x9b, 0x4d, 0x49, 0x6b, 0x22, 0x59, 0x23, 0xf4, 0x4e, 0x65, 0x13, 0xfd, 0x1e, 0x77, 0xcf, 0xbe, - 0xa0, 0x01, 0x82, 0x17, 0x66, 0x88, 0x29, 0x67, 0x59, 0xc6, 0x48, 0x0b, 0xde, 0x08, 0x69, 0x21, - 0x17, 0xd2, 0x7e, 0x24, 0x1b, 0xce, 0x80, 0x11, 0xeb, 0xb6, 0xf2, 0x05, 0x84, 0x15, 0xa4, 0xb6, - 0x1b, 0x06, 0x33, 0xd3, 0x9b, 0x2b, 0x8e, 0xcc, 0x98, 0x70, 0x81, 0x40, 0x8b, 0xdd, 0x16, 0x12, - 0xe8, 0xb0, 0xed, 0x90, 0x3e, 0x07, 0xff, 0x77, 0x00, 0x60, 0x4f, 0xad, 0x15, 0xa5, 0x26, 0xc2, - 0xed, 0xeb, 0xe1, 0xbb, 0x2d, 0x2b, 0x48, 0x44, 0x52, 0x07, 0x55, 0xdd, 0xf8, 0x1e, 0x20, 0x4c, - 0xbe, 0x8f, 0x2c, 0xcb, 0x8d, 0xf2, 0xfd, 0x35, 0x30, 0x32, 0x7a, 0xa6, 0x95, 0x54, 0xf4, 0x53, - 0x1b, 0xc9, 0x22, 0x2a, 0x29, 0x48, 0xec, 0x10, 0xee, 0x43, 0xd9, 0xe5, 0x7e, 0x8f, 0x5b, 0x34, - 0x22, 0x8c, 0x63, 0x78, 0x21, 0xaa, 0x1b, 0x0b, 0xd4, 0xa6, 0xeb, 0xe1, 0xa1, 0xe2, 0xbf, 0x27, - 0x57, 0x62, 0xca, 0xed, 0x75, 0x2b, 0xf7, 0xd2, 0xb8, 0x82, 0xd0, 0xe8, 0x07, 0x32, 0xf9, 0xa2, - 0x3e, 0x04, 0x01, 0x3f, 0x83, 0x08, 0xfd, 0xac, 0xf4, 0x8c, 0xe8, 0xe6, 0xb4, 0xd0, 0xef, 0x71, - 0xcc, 0xd0, 0x37, 0xa7, 0x3b, 0x79, 0xc1, 0xd8, 0xc6, 0x8c, 0xdc, 0x6f, 0x72, 0x7b, 0x72, 0x56, - 0xfe, 0xce, 0xfb, 0x2a, 0x1f, 0x76, 0x51, 0xbe, 0x42, 0x6e, 0x11, 0xc3, 0xda, 0x5c, 0x77, 0x01, - 0xfc, 0x1e, 0x20, 0xe5, 0xb5, 0x23, 0xd6, 0x65, 0xfc, 0xb4, 0x81, 0xaa, 0x35, 0x44, 0xf6, 0xab, - 0xf7, 0xa8, 0x80, 0x55, 0x98, 0x29, 0x0f, 0x47, 0x33, 0x0a, 0x40, 0x18, 0x35, 0x0f, 0x34, 0xd6, - 0x07, 0x56, 0xdd, 0x34, 0x26, 0x4e, 0x53, 0xe3, 0x1d, 0xbd, 0xf3, 0x3f, 0x1f, 0x41, 0x22, 0x79, - 0x00, 0x8e, 0x30, 0x76, 0xcd, 0xba, 0xac, 0xfd, 0xea, 0x07, 0x66, 0x1c, 0xc4, 0x7c, 0x0a, 0x29, - 0x21, 0x5f, 0x38, 0x3c, 0xd8, 0x2f, 0xe4, 0x4b, 0x42, 0xbe, 0x70, 0xf4, 0x4d, 0xb1, 0x54, 0xfc, - 0xee, 0x30, 0x5f, 0x3a, 0xda, 0x2f, 0x1c, 0xe6, 0x73, 0xbb, 0x8f, 0x77, 0xf3, 0x5f, 0x46, 0x7d, - 0xec, 0xcc, 0xe9, 0x59, 0x2a, 0x62, 0x33, 0x31, 0x2b, 0xb0, 0xe8, 0x38, 0x6c, 0xff, 0xe0, 0xe0, - 0x30, 0xea, 0x67, 0x27, 0x4f, 0xcf, 0x52, 0x21, 0xbd, 0xcd, 0xac, 0xc3, 0x92, 0x23, 0xb0, 0x70, - 0x94, 0xcb, 0xe5, 0x0b, 0x85, 0x68, 0x80, 0x8d, 0x9c, 0x9e, 0xa5, 0x26, 0x68, 0x97, 0x0d, 0x3d, - 0xff, 0x25, 0xe9, 0xdb, 0x7c, 0x3d, 0x09, 0xc1, 0x3d, 0xb5, 0xc6, 0xd4, 0x61, 0x66, 0xf4, 0xe5, - 0xee, 0xbc, 0xfa, 0xf1, 0xf7, 0x33, 0x9b, 0xf1, 0x08, 0xb4, 0x78, 0x3e, 0x81, 0xe9, 0x91, 0xe7, - 0xf2, 0x7d, 0x0f, 0x21, 0x8a, 0x4a, 0x97, 0x4d, 0x7b, 0xc3, 0xb9, 0xcc, 0xa4, 0xdf, 0x88, 0xbd, - 0xcc, 0xb4, 0x23, 0xd6, 0x3d, 0xcd, 0x64, 0x7b, 0x19, 0x30, 0x1a, 0x30, 0x0e, 0xaf, 0x82, 0x35, - 0x0f, 0x51, 0x28, 0x96, 0xdd, 0xf4, 0x8e, 0xb5, 0x66, 0x95, 0x21, 0x3a, 0x76, 0x79, 0x5e, 0xbd, - 0x24, 0x8e, 0x85, 0x64, 0x1f, 0x78, 0x45, 0x5a, 0xf3, 0x3d, 0x85, 0x98, 0xe3, 0x85, 0xd7, 0x4b, - 0x20, 0x73, 0x9d, 0x5b, 0x57, 0x00, 0x5b, 0x13, 0xff, 0x00, 0x60, 0xbb, 0x15, 0xf2, 0x6e, 0x21, - 0x06, 0x18, 0x76, 0xed, 0x72, 0x8c, 0x15, 0xbd, 0x00, 0x13, 0xe6, 0x05, 0x88, 0x73, 0x1b, 0x46, - 0x01, 0xec, 0xca, 0x25, 0x00, 0x7b, 0xed, 0x8d, 0x9c, 0xcd, 0xf7, 0x2f, 0x19, 0x4a, 0x71, 0xee, - 0xb5, 0xe7, 0x72, 0x9e, 0xd4, 0x61, 0x66, 0xf4, 0x10, 0x70, 0xcd, 0x72, 0x04, 0xe8, 0xfe, 0xf1, - 0xba, 0x6c, 0x92, 0xd9, 0xc2, 0x9b, 0xf3, 0xa4, 0xff, 0xed, 0x79, 0xd2, 0xff, 0xcf, 0x79, 0xd2, - 0xff, 0xe2, 0x22, 0xe9, 0x7b, 0x7b, 0x91, 0xf4, 0xfd, 0x75, 0x91, 0xf4, 0x1d, 0x3f, 0xaa, 0x49, - 0xda, 0x49, 0xbb, 0x92, 0x16, 0x71, 0x33, 0x23, 0x62, 0xb5, 0x89, 0xd5, 0x8c, 0x54, 0x11, 0xd7, - 0x6b, 0x38, 0xd3, 0xd9, 0xca, 0x34, 0x71, 0xb5, 0xdd, 0x40, 0xaa, 0xf1, 0x13, 0xf1, 0xc1, 0xc3, - 0x75, 0xf3, 0x3f, 0xa2, 0xd6, 0x6d, 0x21, 0xb5, 0x12, 0x26, 0xff, 0x10, 0xb7, 0xfe, 0x0b, 0x00, - 0x00, 0xff, 0xff, 0xec, 0xba, 0x10, 0x62, 0xd2, 0x14, 0x00, 0x00, + // 1294 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0x4d, 0x6f, 0xe2, 0x56, + 0x17, 0xc6, 0x40, 0x20, 0x39, 0xe4, 0x4d, 0x88, 0x49, 0x32, 0xc4, 0x24, 0x98, 0xd7, 0x8b, 0x49, + 0x94, 0x2a, 0x30, 0x49, 0x66, 0x54, 0x4d, 0x54, 0xa9, 0x0a, 0x94, 0x51, 0xa3, 0x36, 0x1f, 0x32, + 0xa4, 0x52, 0xd3, 0xaa, 0x08, 0xcc, 0x1d, 0x62, 0x01, 0x36, 0xb5, 0x0d, 0x33, 0xfc, 0x83, 0x51, + 0x56, 0xb3, 0x1e, 0x29, 0xd2, 0x54, 0x5d, 0x55, 0x5d, 0x4c, 0x7f, 0xc6, 0x2c, 0x67, 0xd5, 0x56, + 0x5d, 0xa0, 0x2a, 0xd9, 0x74, 0xcd, 0x2f, 0xa8, 0x7c, 0x7d, 0x6d, 0x0c, 0xd8, 0x8a, 0x33, 0x93, + 0x64, 0xba, 0xf3, 0xbd, 0xe7, 0xb9, 0xe7, 0x9c, 0xfb, 0x9c, 0xe7, 0x7e, 0x19, 0x96, 0xc5, 0x8a, + 0x90, 0x11, 0x64, 0x05, 0x65, 0x84, 0xd3, 0xb2, 0x24, 0xa1, 0x46, 0xa6, 0xb3, 0x99, 0xd1, 0x9e, + 0xa7, 0x5b, 0x8a, 0xac, 0xc9, 0x74, 0x4c, 0xac, 0x08, 0x69, 0xdd, 0x9a, 0x26, 0xd6, 0x74, 0x67, + 0x93, 0x99, 0xaf, 0xc9, 0x35, 0x19, 0xdb, 0x33, 0xfa, 0x97, 0x01, 0x65, 0xd8, 0x81, 0xa3, 0x86, + 0x88, 0x24, 0x4d, 0xf7, 0x63, 0x7c, 0x11, 0xc0, 0xff, 0x9d, 0x22, 0x99, 0x6e, 0x31, 0x84, 0xfb, + 0x89, 0x02, 0x7a, 0x5f, 0xad, 0xe5, 0x8c, 0xce, 0xc3, 0x16, 0x92, 0xf6, 0x24, 0x51, 0xa3, 0x3f, + 0x81, 0x70, 0x4b, 0x56, 0xb4, 0x92, 0x58, 0x8d, 0x53, 0x29, 0x6a, 0x6d, 0x2a, 0x4b, 0xf7, 0x7b, + 0xec, 0x4c, 0xb7, 0xdc, 0x6c, 0xec, 0x70, 0xc4, 0xc0, 0xf1, 0x21, 0xfd, 0x6b, 0xaf, 0x4a, 0x7f, + 0x06, 0x61, 0xe2, 0x34, 0xee, 0x4f, 0x51, 0x6b, 0x91, 0xad, 0xe5, 0xb4, 0xc3, 0x24, 0xd2, 0x24, + 0x46, 0x36, 0xf8, 0xb6, 0xc7, 0xfa, 0x78, 0x73, 0x08, 0xbd, 0x08, 0x21, 0x55, 0xac, 0x49, 0x48, + 0x89, 0x07, 0xf4, 0x48, 0x3c, 0x69, 0xed, 0x4c, 0xbe, 0x78, 0xcd, 0xfa, 0xfe, 0x79, 0xcd, 0xfa, + 0xb8, 0x06, 0x30, 0xe3, 0x29, 0xf2, 0x48, 0x6d, 0xc9, 0x92, 0x8a, 0xe8, 0x87, 0x00, 0xc4, 0xd5, + 0x20, 0xdb, 0x85, 0x7e, 0x8f, 0x9d, 0x33, 0xb2, 0x1d, 0xd8, 0x38, 0x7e, 0x8a, 0x34, 0xf6, 0xaa, + 0x74, 0x1c, 0xc2, 0x1d, 0xa4, 0xa8, 0xa2, 0x2c, 0xe1, 0x9c, 0xa7, 0x78, 0xb3, 0xc9, 0xfd, 0x1e, + 0x80, 0xb9, 0xe1, 0x70, 0x45, 0xa5, 0x7b, 0x3d, 0x42, 0x0e, 0x20, 0xd6, 0x52, 0x50, 0x47, 0x94, + 0xdb, 0x6a, 0xc9, 0x96, 0x1b, 0x0e, 0x94, 0x4d, 0xf6, 0x7b, 0x2c, 0x43, 0x06, 0x8e, 0x83, 0x38, + 0x7e, 0xce, 0xec, 0xcd, 0x59, 0xc9, 0xda, 0x08, 0x0e, 0x5c, 0x9f, 0x60, 0x1e, 0xe6, 0x05, 0xb9, + 0x2d, 0x69, 0x48, 0x69, 0x95, 0x15, 0xad, 0x5b, 0x32, 0xe7, 0x1d, 0xc4, 0xe9, 0xb0, 0xfd, 0x1e, + 0x9b, 0x20, 0x54, 0x39, 0xa0, 0x38, 0x3e, 0x66, 0xef, 0xfe, 0xc6, 0xe8, 0xd5, 0x49, 0x6f, 0x29, + 0xb2, 0xfc, 0xb4, 0x24, 0x4a, 0xa2, 0x16, 0x9f, 0x48, 0x51, 0x6b, 0xd3, 0x76, 0xd2, 0x07, 0x36, + 0x8e, 0x9f, 0xc2, 0x0d, 0xac, 0xaa, 0x13, 0x98, 0x36, 0x2c, 0xa7, 0x48, 0xac, 0x9d, 0x6a, 0xf1, + 0x10, 0x9e, 0x0c, 0x63, 0x9b, 0x8c, 0xa1, 0xde, 0xce, 0x66, 0xfa, 0x4b, 0x8c, 0xc8, 0x26, 0xf4, + 0xa9, 0xf4, 0x7b, 0x6c, 0xcc, 0xee, 0xd7, 0x18, 0xcd, 0xf1, 0x11, 0xdc, 0x34, 0x90, 0x36, 0x19, + 0x85, 0x5d, 0x64, 0xf4, 0x08, 0x96, 0xc6, 0xea, 0x6a, 0xa9, 0xc8, 0xa6, 0x07, 0x6a, 0x58, 0x0f, + 0x7f, 0x8c, 0xe9, 0x61, 0x57, 0xa8, 0x5f, 0x4f, 0x0f, 0xc3, 0x12, 0xf5, 0x7b, 0x94, 0xe8, 0x09, + 0xdc, 0x1b, 0xaa, 0x88, 0xcd, 0x05, 0x5e, 0x29, 0x59, 0xae, 0xdf, 0x63, 0x93, 0x0e, 0xa5, 0xb3, + 0xfb, 0x5b, 0xb0, 0x5b, 0x06, 0x8a, 0xba, 0x0d, 0x4d, 0x6c, 0x82, 0x51, 0xea, 0x92, 0xa6, 0x74, + 0x89, 0x24, 0xe6, 0xfb, 0x3d, 0x36, 0x6a, 0x2f, 0x9d, 0xa6, 0x74, 0x39, 0x7e, 0x12, 0x7f, 0xeb, + 0xab, 0xea, 0xe3, 0x0a, 0x22, 0x31, 0x2a, 0x88, 0x5d, 0xa1, 0x6e, 0x0a, 0x82, 0xfb, 0xd5, 0x0f, + 0x0b, 0xc3, 0xd6, 0x9c, 0x2c, 0x3d, 0x15, 0x95, 0xe6, 0x5d, 0x94, 0xde, 0xa2, 0xb2, 0x2c, 0xd4, + 0x71, 0xb1, 0x1d, 0xa8, 0x2c, 0x0b, 0x75, 0x93, 0x4a, 0x5d, 0x90, 0xa3, 0x54, 0x06, 0x6f, 0x85, + 0xca, 0x09, 0x17, 0x2a, 0x59, 0x58, 0x71, 0x24, 0xcb, 0xa2, 0xf3, 0x15, 0x05, 0xb1, 0x01, 0x22, + 0xd7, 0x90, 0x55, 0x74, 0xfd, 0x83, 0xe6, 0xfd, 0xc8, 0xbc, 0xfa, 0x80, 0x59, 0x81, 0x84, 0x43, + 0x6e, 0x56, 0xee, 0x6f, 0xfc, 0xb0, 0x38, 0x62, 0xbf, 0x43, 0x2d, 0x0c, 0x6f, 0xb5, 0x81, 0xf7, + 0xdc, 0x6a, 0xef, 0x56, 0x0e, 0x29, 0x48, 0x3a, 0x13, 0x66, 0x71, 0xfa, 0xd2, 0x0f, 0xff, 0xdb, + 0x57, 0x6b, 0x3c, 0x12, 0x3a, 0x47, 0x65, 0xa1, 0x8e, 0x34, 0xfa, 0x31, 0x84, 0x5a, 0xf8, 0x0b, + 0x33, 0x19, 0xd9, 0x4a, 0x38, 0x9e, 0x71, 0x06, 0x98, 0x1c, 0x71, 0x64, 0x00, 0xfd, 0x04, 0xa2, + 0x46, 0xba, 0x82, 0xdc, 0x6c, 0x8a, 0x5a, 0x13, 0x49, 0x1a, 0xa6, 0x77, 0x3a, 0x9b, 0xe8, 0xf7, + 0xd8, 0x7b, 0xf6, 0x09, 0x0d, 0x10, 0x1c, 0x3f, 0x8b, 0xbb, 0x72, 0x56, 0xcf, 0x18, 0x69, 0x81, + 0x5b, 0x21, 0x2d, 0xe8, 0x42, 0xda, 0x0f, 0x78, 0xc3, 0x19, 0x30, 0x62, 0x9d, 0x4d, 0x9f, 0x43, + 0x48, 0x41, 0x6a, 0xbb, 0x61, 0x30, 0x33, 0xb3, 0xb5, 0xea, 0xc8, 0x8c, 0x09, 0xe7, 0x31, 0xb4, + 0xd8, 0x6d, 0x21, 0x9e, 0x0c, 0xdb, 0x09, 0xea, 0x31, 0xb8, 0xbf, 0xfc, 0x00, 0xfb, 0x6a, 0xad, + 0x28, 0x36, 0x91, 0xdc, 0xbe, 0x19, 0xbe, 0xdb, 0x92, 0x82, 0x04, 0x24, 0x76, 0x50, 0xd5, 0x8d, + 0xef, 0x01, 0xc2, 0xe4, 0xfb, 0xd8, 0xea, 0xb9, 0x55, 0xbe, 0xbf, 0x02, 0x5a, 0x42, 0xcf, 0xb5, + 0x92, 0x8a, 0x7e, 0x6c, 0x23, 0x49, 0x40, 0x25, 0x05, 0x09, 0x1d, 0xcc, 0x7d, 0x30, 0xbb, 0xd2, + 0xef, 0xb1, 0x4b, 0x86, 0x87, 0x71, 0x0c, 0xc7, 0x47, 0xf5, 0xce, 0x02, 0xe9, 0xd3, 0xeb, 0xe1, + 0x41, 0xf1, 0xdf, 0xe1, 0x6b, 0x34, 0xe1, 0xf6, 0xa6, 0x2b, 0xf7, 0xca, 0xb8, 0x82, 0x10, 0xef, + 0x87, 0x12, 0x5e, 0x51, 0xff, 0x85, 0x02, 0x7e, 0x0a, 0x11, 0xb2, 0xac, 0xf4, 0x8c, 0xc8, 0xe6, + 0xb4, 0xd8, 0xef, 0xb1, 0xf4, 0xd0, 0x9a, 0xd3, 0x8d, 0x1c, 0x6f, 0x6c, 0x63, 0x46, 0xee, 0xb7, + 0xb9, 0x3d, 0x39, 0x57, 0x7e, 0xe2, 0x43, 0x2b, 0x1f, 0x72, 0xa9, 0x7c, 0x05, 0xdf, 0x22, 0x86, + 0x6b, 0x73, 0xd3, 0x02, 0xf8, 0xcd, 0x8f, 0xe5, 0xb5, 0x2b, 0xd4, 0x25, 0xf9, 0x59, 0x03, 0x55, + 0x6b, 0x08, 0xef, 0x57, 0x1f, 0xa0, 0x80, 0x35, 0x98, 0x2d, 0x0f, 0x7b, 0x33, 0x04, 0xc0, 0x8f, + 0x76, 0x0f, 0x6a, 0xac, 0x0f, 0xac, 0xba, 0xd5, 0x18, 0x1b, 0xcd, 0x1a, 0xef, 0xea, 0x8d, 0x8f, + 0x7c, 0x04, 0x09, 0xf8, 0xd1, 0x38, 0xc2, 0xd8, 0x0d, 0xd7, 0x65, 0xfd, 0x17, 0x0a, 0xe8, 0x71, + 0x10, 0xfd, 0x08, 0x52, 0x7c, 0xbe, 0x70, 0x74, 0x78, 0x50, 0xc8, 0x97, 0xf8, 0x7c, 0xe1, 0xf8, + 0xeb, 0x62, 0xa9, 0xf8, 0xed, 0x51, 0xbe, 0x74, 0x7c, 0x50, 0x38, 0xca, 0xe7, 0xf6, 0x9e, 0xec, + 0xe5, 0xbf, 0x88, 0xfa, 0x98, 0xd9, 0xb3, 0xf3, 0x54, 0xc4, 0xd6, 0x45, 0xaf, 0xc2, 0x92, 0xe3, + 0xb0, 0x83, 0xc3, 0xc3, 0xa3, 0x28, 0xc5, 0x4c, 0x9e, 0x9d, 0xa7, 0x82, 0xfa, 0x37, 0xbd, 0x01, + 0xcb, 0x8e, 0xc0, 0xc2, 0x71, 0x2e, 0x97, 0x2f, 0x14, 0xa2, 0x7e, 0x26, 0x72, 0x76, 0x9e, 0x0a, + 0x93, 0x26, 0x13, 0x7c, 0xf1, 0x73, 0xd2, 0xb7, 0xf5, 0x66, 0x12, 0x02, 0xfb, 0x6a, 0x8d, 0xae, + 0xc3, 0xec, 0xe8, 0x6b, 0xdf, 0x79, 0xf6, 0xe3, 0x6f, 0x6e, 0x26, 0xe3, 0x11, 0x68, 0xf1, 0x7c, + 0x0a, 0x33, 0x23, 0x0f, 0xe9, 0xfb, 0x1e, 0x5c, 0x14, 0x95, 0x2e, 0x93, 0xf6, 0x86, 0x73, 0x89, + 0xa4, 0xdf, 0x88, 0xbd, 0x44, 0xda, 0x15, 0xea, 0x9e, 0x22, 0xd9, 0x5e, 0x06, 0xb4, 0x06, 0xb4, + 0xc3, 0xab, 0x60, 0xdd, 0x83, 0x17, 0x82, 0x65, 0xb6, 0xbc, 0x63, 0xad, 0xa8, 0x12, 0x44, 0xc7, + 0x2e, 0xcf, 0x6b, 0x57, 0xf8, 0xb1, 0x90, 0xcc, 0x03, 0xaf, 0x48, 0x2b, 0xde, 0x33, 0x88, 0x39, + 0x5e, 0x78, 0xbd, 0x38, 0x32, 0xe7, 0xb9, 0x7d, 0x0d, 0xb0, 0x15, 0xf8, 0x7b, 0x00, 0xdb, 0xad, + 0x90, 0x73, 0x73, 0x31, 0xc0, 0x30, 0xeb, 0x57, 0x63, 0x2c, 0xef, 0x05, 0x08, 0x9b, 0x17, 0x20, + 0xd6, 0x6d, 0x18, 0x01, 0x30, 0xab, 0x57, 0x00, 0xec, 0xda, 0x1b, 0x39, 0x9b, 0xef, 0x5f, 0x31, + 0x94, 0xe0, 0xdc, 0xb5, 0xe7, 0x72, 0x9e, 0xd4, 0x61, 0x76, 0xf4, 0x10, 0x70, 0xcd, 0x72, 0x04, + 0xe8, 0xbe, 0x78, 0x5d, 0x36, 0xc9, 0x6c, 0xe1, 0xed, 0x45, 0x92, 0x7a, 0x77, 0x91, 0xa4, 0xfe, + 0xbe, 0x48, 0x52, 0x2f, 0x2f, 0x93, 0xbe, 0x77, 0x97, 0x49, 0xdf, 0x9f, 0x97, 0x49, 0xdf, 0xc9, + 0xe3, 0x9a, 0xa8, 0x9d, 0xb6, 0x2b, 0x69, 0x41, 0x6e, 0x66, 0x04, 0x59, 0x6d, 0xca, 0x6a, 0x46, + 0xac, 0x08, 0x1b, 0x35, 0x39, 0xd3, 0xd9, 0xce, 0x34, 0xe5, 0x6a, 0xbb, 0x81, 0x54, 0xe3, 0xc7, + 0xe3, 0x83, 0x87, 0x1b, 0xe6, 0xbf, 0x47, 0xad, 0xdb, 0x42, 0x6a, 0x25, 0x84, 0xff, 0x3b, 0x6e, + 0xff, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x51, 0x2e, 0xf7, 0xe5, 0x06, 0x15, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1478,6 +1495,13 @@ func (m *MsgChannelOpenInitResponse) MarshalToSizedBuffer(dAtA []byte) (int, err _ = i var l int _ = l + if len(m.Version) > 0 { + i -= len(m.Version) + copy(dAtA[i:], m.Version) + i = encodeVarintTx(dAtA, i, uint64(len(m.Version))) + i-- + dAtA[i] = 0x12 + } if len(m.ChannelId) > 0 { i -= len(m.ChannelId) copy(dAtA[i:], m.ChannelId) @@ -1586,6 +1610,13 @@ func (m *MsgChannelOpenTryResponse) MarshalToSizedBuffer(dAtA []byte) (int, erro _ = i var l int _ = l + if len(m.Version) > 0 { + i -= len(m.Version) + copy(dAtA[i:], m.Version) + i = encodeVarintTx(dAtA, i, uint64(len(m.Version))) + i-- + dAtA[i] = 0xa + } return len(dAtA) - i, nil } @@ -2326,6 +2357,10 @@ func (m *MsgChannelOpenInitResponse) Size() (n int) { if l > 0 { n += 1 + l + sovTx(uint64(l)) } + l = len(m.Version) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } return n } @@ -2368,6 +2403,10 @@ func (m *MsgChannelOpenTryResponse) Size() (n int) { } var l int _ = l + l = len(m.Version) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } return n } @@ -2877,6 +2916,38 @@ func (m *MsgChannelOpenInitResponse) Unmarshal(dAtA []byte) error { } m.ChannelId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Version", 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.Version = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) @@ -3205,6 +3276,38 @@ func (m *MsgChannelOpenTryResponse) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: MsgChannelOpenTryResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Version", 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.Version = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) diff --git a/modules/core/04-channel/types/version.go b/modules/core/04-channel/types/version.go index 15477635e7f..a2696d291ed 100644 --- a/modules/core/04-channel/types/version.go +++ b/modules/core/04-channel/types/version.go @@ -4,7 +4,7 @@ import "strings" const ChannelVersionDelimiter = ":" -// SplitChannelVersion splits the channel version string +// SplitChannelVersion middleware version will split the channel version string // into the outermost middleware version and the underlying app version. // It will use the default delimiter `:` for middleware versions. // In case there's no delimeter, this function returns an empty string for the middleware version (first return argument), diff --git a/modules/core/05-port/types/module.go b/modules/core/05-port/types/module.go index 9f754fe0a3e..e6ba8f3449b 100644 --- a/modules/core/05-port/types/module.go +++ b/modules/core/05-port/types/module.go @@ -113,6 +113,12 @@ type ICS4Wrapper interface { packet exported.PacketI, ack exported.Acknowledgement, ) error + + GetAppVersion( + ctx sdk.Context, + portID, + channelID string, + ) (string, bool) } // Middleware must implement IBCModule to wrap communication from core IBC to underlying application diff --git a/modules/core/keeper/msg_server.go b/modules/core/keeper/msg_server.go index 4893f82dd66..c7a8e87dffe 100644 --- a/modules/core/keeper/msg_server.go +++ b/modules/core/keeper/msg_server.go @@ -196,6 +196,7 @@ func (k Keeper) ChannelOpenInit(goCtx context.Context, msg *channeltypes.MsgChan return &channeltypes.MsgChannelOpenInitResponse{ ChannelId: channelID, + Version: msg.Channel.Version, }, nil } @@ -234,7 +235,9 @@ func (k Keeper) ChannelOpenTry(goCtx context.Context, msg *channeltypes.MsgChann // Write channel into state k.ChannelKeeper.WriteOpenTryChannel(ctx, msg.PortId, channelID, msg.Channel.Ordering, msg.Channel.ConnectionHops, msg.Channel.Counterparty, version) - return &channeltypes.MsgChannelOpenTryResponse{}, nil + return &channeltypes.MsgChannelOpenTryResponse{ + Version: version, + }, nil } // ChannelOpenAck defines a rpc handler method for MsgChannelOpenAck. diff --git a/proto/ibc/applications/fee/v1/ack.proto b/proto/ibc/applications/fee/v1/ack.proto new file mode 100644 index 00000000000..728c7536c6b --- /dev/null +++ b/proto/ibc/applications/fee/v1/ack.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; + +package ibc.applications.fee.v1; + +option go_package = "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types"; + +import "gogoproto/gogo.proto"; + +// IncentivizedAcknowledgement is the acknowledgement format to be used by applications wrapped in the fee middleware +message IncentivizedAcknowledgement { + // the underlying app acknowledgement result bytes + bytes result = 1; + // the relayer address which submits the recv packet message + string forward_relayer_address = 2 [(gogoproto.moretags) = "yaml:\"forward_relayer_address\""]; + // success flag of the base application callback + bool underlying_app_success = 3 [(gogoproto.moretags) = "yaml:\"underlying_app_successl\""]; +} diff --git a/proto/ibc/applications/fee/v1/fee.proto b/proto/ibc/applications/fee/v1/fee.proto new file mode 100644 index 00000000000..e7a1fa438df --- /dev/null +++ b/proto/ibc/applications/fee/v1/fee.proto @@ -0,0 +1,56 @@ +syntax = "proto3"; + +package ibc.applications.fee.v1; + +option go_package = "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types"; + +import "cosmos/base/v1beta1/coin.proto"; +import "gogoproto/gogo.proto"; +import "ibc/core/channel/v1/channel.proto"; + +// Fee defines the ICS29 receive, acknowledgement and timeout fees +message Fee { + // the packet receive fee + repeated cosmos.base.v1beta1.Coin recv_fee = 1 [ + (gogoproto.moretags) = "yaml:\"recv_fee\"", + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; + // the packet acknowledgement fee + repeated cosmos.base.v1beta1.Coin ack_fee = 2 [ + (gogoproto.moretags) = "yaml:\"ack_fee\"", + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; + // the packet timeout fee + repeated cosmos.base.v1beta1.Coin timeout_fee = 3 [ + (gogoproto.moretags) = "yaml:\"timeout_fee\"", + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; +} + +// PacketFee contains ICS29 relayer fees, refund address and optional list of permitted relayers +message PacketFee { + // fee encapsulates the recv, ack and timeout fees associated with an IBC packet + Fee fee = 1 [(gogoproto.nullable) = false]; + // the refund address for unspent fees + string refund_address = 2 [(gogoproto.moretags) = "yaml:\"refund_address\""]; + // optional list of relayers permitted to receive fees + repeated string relayers = 3; +} + +// PacketFees contains a list of type PacketFee +message PacketFees { + // list of packet fees + repeated PacketFee packet_fees = 1 [(gogoproto.moretags) = "yaml:\"packet_fees\"", (gogoproto.nullable) = false]; +} + +// IdentifiedPacketFees contains a list of type PacketFee and associated PacketId +message IdentifiedPacketFees { + // unique packet identifier comprised of the channel ID, port ID and sequence + ibc.core.channel.v1.PacketId packet_id = 1 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"packet_id\""]; + // list of packet fees + repeated PacketFee packet_fees = 2 [(gogoproto.moretags) = "yaml:\"packet_fees\"", (gogoproto.nullable) = false]; +} diff --git a/proto/ibc/applications/fee/v1/genesis.proto b/proto/ibc/applications/fee/v1/genesis.proto new file mode 100644 index 00000000000..cae132239d6 --- /dev/null +++ b/proto/ibc/applications/fee/v1/genesis.proto @@ -0,0 +1,52 @@ +syntax = "proto3"; + +package ibc.applications.fee.v1; + +option go_package = "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types"; + +import "gogoproto/gogo.proto"; +import "ibc/applications/fee/v1/fee.proto"; +import "ibc/core/channel/v1/channel.proto"; + +// GenesisState defines the ICS29 fee middleware genesis state +message GenesisState { + // list of identified packet fees + repeated IdentifiedPacketFees identified_fees = 1 + [(gogoproto.moretags) = "yaml:\"identified_fees\"", (gogoproto.nullable) = false]; + // list of fee enabled channels + repeated FeeEnabledChannel fee_enabled_channels = 2 + [(gogoproto.moretags) = "yaml:\"fee_enabled_channels\"", (gogoproto.nullable) = false]; + // list of registered relayer addresses + repeated RegisteredRelayerAddress registered_relayers = 3 + [(gogoproto.moretags) = "yaml:\"registered_relayers\"", (gogoproto.nullable) = false]; + // list of forward relayer addresses + repeated ForwardRelayerAddress forward_relayers = 4 + [(gogoproto.moretags) = "yaml:\"forward_relayers\"", (gogoproto.nullable) = false]; +} + +// FeeEnabledChannel contains the PortID & ChannelID for a fee enabled channel +message FeeEnabledChannel { + // unique port identifier + string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""]; + // unique channel identifier + string channel_id = 2 [(gogoproto.moretags) = "yaml:\"channel_id\""]; +} + +// RegisteredRelayerAddress contains the address and counterparty address for a specific relayer (for distributing fees) +message RegisteredRelayerAddress { + // the relayer address + string address = 1; + // the counterparty relayer address + string counterparty_address = 2 [(gogoproto.moretags) = "yaml:\"counterparty_address\""]; + // unique channel identifier + string channel_id = 3 [(gogoproto.moretags) = "yaml:\"channel_id\""]; +} + +// ForwardRelayerAddress contains the forward relayer address and PacketId used for async acknowledgements +message ForwardRelayerAddress { + // the forward relayer address + string address = 1; + // unique packet identifer comprised of the channel ID, port ID and sequence + ibc.core.channel.v1.PacketId packet_id = 2 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"packet_id\""]; +} diff --git a/proto/ibc/applications/fee/v1/metadata.proto b/proto/ibc/applications/fee/v1/metadata.proto new file mode 100644 index 00000000000..0afb3e09b2e --- /dev/null +++ b/proto/ibc/applications/fee/v1/metadata.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package ibc.applications.fee.v1; + +option go_package = "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types"; + +import "gogoproto/gogo.proto"; + +// Metadata defines the ICS29 channel specific metadata encoded into the channel version bytestring +// See ICS004: https://github.com/cosmos/ibc/tree/master/spec/core/ics-004-channel-and-packet-semantics#Versioning +message Metadata { + // fee_version defines the ICS29 fee version + string fee_version = 1 [(gogoproto.moretags) = "yaml:\"fee_version\""]; + // app_version defines the underlying application version, which may or may not be a JSON encoded bytestring + string app_version = 2 [(gogoproto.moretags) = "yaml:\"app_version\""]; +} diff --git a/proto/ibc/applications/fee/v1/query.proto b/proto/ibc/applications/fee/v1/query.proto new file mode 100644 index 00000000000..acab12f5161 --- /dev/null +++ b/proto/ibc/applications/fee/v1/query.proto @@ -0,0 +1,203 @@ +syntax = "proto3"; + +package ibc.applications.fee.v1; + +option go_package = "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types"; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos/base/query/v1beta1/pagination.proto"; +import "ibc/applications/fee/v1/fee.proto"; +import "ibc/applications/fee/v1/genesis.proto"; +import "ibc/core/channel/v1/channel.proto"; + +// Query defines the ICS29 gRPC querier service. +service Query { + // IncentivizedPackets returns all incentivized packets and their associated fees + rpc IncentivizedPackets(QueryIncentivizedPacketsRequest) returns (QueryIncentivizedPacketsResponse) { + option (google.api.http).get = "/ibc/apps/fee/v1/incentivized_packets"; + } + + // IncentivizedPacket returns all packet fees for a packet given its identifier + rpc IncentivizedPacket(QueryIncentivizedPacketRequest) returns (QueryIncentivizedPacketResponse) { + option (google.api.http).get = + "/ibc/apps/fee/v1/incentivized_packet/port/{packet_id.port_id}/channel/{packet_id.channel_id}/sequence/" + "{packet_id.sequence}"; + } + + // Gets all incentivized packets for a specific channel + rpc IncentivizedPacketsForChannel(QueryIncentivizedPacketsForChannelRequest) + returns (QueryIncentivizedPacketsForChannelResponse) { + option (google.api.http).get = "/ibc/apps/fee/v1/incentivized_packets/port/{port_id}/channel/{channel_id}"; + } + + // TotalRecvFees returns the total receive fees for a packet given its identifier + rpc TotalRecvFees(QueryTotalRecvFeesRequest) returns (QueryTotalRecvFeesResponse) { + option (google.api.http).get = "/ibc/apps/fee/v1/total_recv_fees/port/{packet_id.port_id}/channel/" + "{packet_id.channel_id}/sequence/{packet_id.sequence}"; + } + + // TotalAckFees returns the total acknowledgement fees for a packet given its identifier + rpc TotalAckFees(QueryTotalAckFeesRequest) returns (QueryTotalAckFeesResponse) { + option (google.api.http).get = "/ibc/apps/fee/v1/total_ack_fees/port/{packet_id.port_id}/channel/" + "{packet_id.channel_id}/sequence/{packet_id.sequence}"; + } + + // TotalTimeoutFees returns the total timeout fees for a packet given its identifier + rpc TotalTimeoutFees(QueryTotalTimeoutFeesRequest) returns (QueryTotalTimeoutFeesResponse) { + option (google.api.http).get = "/ibc/apps/fee/v1/total_timeout_fees/port/{packet_id.port_id}/channel/" + "{packet_id.channel_id}/sequence/{packet_id.sequence}"; + } + + // CounterpartyAddress returns the registered counterparty address for forward relaying + rpc CounterpartyAddress(QueryCounterpartyAddressRequest) returns (QueryCounterpartyAddressResponse) { + option (google.api.http).get = "/ibc/apps/fee/v1/counterparty_address/{relayer_address}/channel/{channel_id}"; + } + + // FeeEnabledChannels returns a list of all fee enabled channels + rpc FeeEnabledChannels(QueryFeeEnabledChannelsRequest) returns (QueryFeeEnabledChannelsResponse) { + option (google.api.http).get = "/ibc/apps/fee/v1/fee_enabled"; + } + + // FeeEnabledChannel returns true if the provided port and channel identifiers belong to a fee enabled channel + rpc FeeEnabledChannel(QueryFeeEnabledChannelRequest) returns (QueryFeeEnabledChannelResponse) { + option (google.api.http).get = "/ibc/apps/fee/v1/fee_enabled/port/{port_id}/channel/{channel_id}"; + } +} + +// QueryIncentivizedPacketsRequest defines the request type for the IncentivizedPackets rpc +message QueryIncentivizedPacketsRequest { + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 1; + // block height at which to query + uint64 query_height = 2; +} + +// QueryIncentivizedPacketsResponse defines the response type for the IncentivizedPackets rpc +message QueryIncentivizedPacketsResponse { + // list of identified fees for incentivized packets + repeated ibc.applications.fee.v1.IdentifiedPacketFees incentivized_packets = 1 [(gogoproto.nullable) = false]; +} + +// QueryIncentivizedPacketRequest defines the request type for the IncentivizedPacket rpc +message QueryIncentivizedPacketRequest { + // unique packet identifier comprised of channel ID, port ID and sequence + ibc.core.channel.v1.PacketId packet_id = 1 [(gogoproto.nullable) = false]; + // block height at which to query + uint64 query_height = 2; +} + +// QueryIncentivizedPacketsResponse defines the response type for the IncentivizedPacket rpc +message QueryIncentivizedPacketResponse { + // the identified fees for the incentivized packet + ibc.applications.fee.v1.IdentifiedPacketFees incentivized_packet = 1 [(gogoproto.nullable) = false]; +} + +// QueryIncentivizedPacketsForChannelRequest defines the request type for querying for all incentivized packets +// for a specific channel +message QueryIncentivizedPacketsForChannelRequest { + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 1; + string port_id = 2; + string channel_id = 3; + // Height to query at + uint64 query_height = 4; +} + +// QueryIncentivizedPacketsResponse defines the response type for the incentivized packets RPC +message QueryIncentivizedPacketsForChannelResponse { + // Map of all incentivized_packets + repeated ibc.applications.fee.v1.IdentifiedPacketFees incentivized_packets = 1; +} + +// QueryTotalRecvFeesRequest defines the request type for the TotalRecvFees rpc +message QueryTotalRecvFeesRequest { + // the packet identifier for the associated fees + ibc.core.channel.v1.PacketId packet_id = 1 [(gogoproto.nullable) = false]; +} + +// QueryTotalRecvFeesResponse defines the response type for the TotalRecvFees rpc +message QueryTotalRecvFeesResponse { + // the total packet receive fees + repeated cosmos.base.v1beta1.Coin recv_fees = 1 [ + (gogoproto.moretags) = "yaml:\"recv_fees\"", + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; +} + +// QueryTotalAckFeesRequest defines the request type for the TotalAckFees rpc +message QueryTotalAckFeesRequest { + // the packet identifier for the associated fees + ibc.core.channel.v1.PacketId packet_id = 1 [(gogoproto.nullable) = false]; +} + +// QueryTotalAckFeesResponse defines the response type for the TotalAckFees rpc +message QueryTotalAckFeesResponse { + // the total packet acknowledgement fees + repeated cosmos.base.v1beta1.Coin ack_fees = 1 [ + (gogoproto.moretags) = "yaml:\"ack_fees\"", + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; +} + +// QueryTotalTimeoutFeesRequest defines the request type for the TotalTimeoutFees rpc +message QueryTotalTimeoutFeesRequest { + // the packet identifier for the associated fees + ibc.core.channel.v1.PacketId packet_id = 1 [(gogoproto.nullable) = false]; +} + +// QueryTotalTimeoutFeesResponse defines the response type for the TotalTimeoutFees rpc +message QueryTotalTimeoutFeesResponse { + // the total packet timeout fees + repeated cosmos.base.v1beta1.Coin timeout_fees = 1 [ + (gogoproto.moretags) = "yaml:\"timeout_fees\"", + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; +} + +// QueryCounterpartyAddressRequest defines the request type for the CounterpartyAddress rpc +message QueryCounterpartyAddressRequest { + // unique channel identifier + string channel_id = 1 [(gogoproto.moretags) = "yaml:\"channel_id\""]; + // the relayer address to which the counterparty is registered + string relayer_address = 2 [(gogoproto.moretags) = "yaml:\"relayer_address\""]; +} + +// QueryCounterpartyAddressResponse defines the response type for the CounterpartyAddress rpc +message QueryCounterpartyAddressResponse { + // the counterparty address used to compensate forward relaying + string counterparty_address = 1 [(gogoproto.moretags) = "yaml:\"counterparty_address\""]; +} + +// QueryFeeEnabledChannelsRequest defines the request type for the FeeEnabledChannels rpc +message QueryFeeEnabledChannelsRequest { + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 1; + // block height at which to query + uint64 query_height = 2; +} + +// QueryFeeEnabledChannelsResponse defines the response type for the FeeEnabledChannels rpc +message QueryFeeEnabledChannelsResponse { + // list of fee enabled channels + repeated ibc.applications.fee.v1.FeeEnabledChannel fee_enabled_channels = 1 + [(gogoproto.moretags) = "yaml:\"fee_enabled_channels\"", (gogoproto.nullable) = false]; +} + +// QueryFeeEnabledChannelRequest defines the request type for the FeeEnabledChannel rpc +message QueryFeeEnabledChannelRequest { + // unique port identifier + string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""]; + // unique channel identifier + string channel_id = 2 [(gogoproto.moretags) = "yaml:\"channel_id\""]; +} + +// QueryFeeEnabledChannelResponse defines the response type for the FeeEnabledChannel rpc +message QueryFeeEnabledChannelResponse { + // boolean flag representing the fee enabled channel status + bool fee_enabled = 1 [(gogoproto.moretags) = "yaml:\"fee_enabled\""]; +} diff --git a/proto/ibc/applications/fee/v1/tx.proto b/proto/ibc/applications/fee/v1/tx.proto new file mode 100644 index 00000000000..7a0044cd363 --- /dev/null +++ b/proto/ibc/applications/fee/v1/tx.proto @@ -0,0 +1,85 @@ +syntax = "proto3"; + +package ibc.applications.fee.v1; + +option go_package = "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types"; + +import "gogoproto/gogo.proto"; +import "ibc/applications/fee/v1/fee.proto"; +import "ibc/core/channel/v1/channel.proto"; + +// Msg defines the ICS29 Msg service. +service Msg { + // RegisterCounterpartyAddress defines a rpc handler method for MsgRegisterCounterpartyAddress + // RegisterCounterpartyAddress is called by the relayer on each channelEnd and allows them to specify their + // counterparty address before relaying. This ensures they will be properly compensated for forward relaying since + // destination chain must send back relayer's source address (counterparty address) in acknowledgement. This function + // may be called more than once by a relayer, in which case, latest counterparty address is always used. + rpc RegisterCounterpartyAddress(MsgRegisterCounterpartyAddress) returns (MsgRegisterCounterpartyAddressResponse); + + // PayPacketFee defines a rpc handler method for MsgPayPacketFee + // PayPacketFee is an open callback that may be called by any module/user that wishes to escrow funds in order to + // incentivize the relaying of the packet at the next sequence + // NOTE: This method is intended to be used within a multi msg transaction, where the subsequent msg that follows + // initiates the lifecycle of the incentivized packet + rpc PayPacketFee(MsgPayPacketFee) returns (MsgPayPacketFeeResponse); + + // PayPacketFeeAsync defines a rpc handler method for MsgPayPacketFeeAsync + // PayPacketFeeAsync is an open callback that may be called by any module/user that wishes to escrow funds in order to + // incentivize the relaying of a known packet (i.e. at a particular sequence) + rpc PayPacketFeeAsync(MsgPayPacketFeeAsync) returns (MsgPayPacketFeeAsyncResponse); +} + +// MsgRegisterCounterpartyAddress defines the request type for the RegisterCounterpartyAddress rpc +message MsgRegisterCounterpartyAddress { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // the relayer address + string address = 1; + // the counterparty relayer address + string counterparty_address = 2 [(gogoproto.moretags) = "yaml:\"counterparty_address\""]; + // unique channel identifier + string channel_id = 3 [(gogoproto.moretags) = "yaml:\"channel_id\""]; +} + +// MsgRegisterCounterpartyAddressResponse defines the response type for the RegisterCounterpartyAddress rpc +message MsgRegisterCounterpartyAddressResponse {} + +// MsgPayPacketFee defines the request type for the PayPacketFee rpc +// This Msg can be used to pay for a packet at the next sequence send & should be combined with the Msg that will be +// paid for +message MsgPayPacketFee { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // fee encapsulates the recv, ack and timeout fees associated with an IBC packet + ibc.applications.fee.v1.Fee fee = 1 [(gogoproto.nullable) = false]; + // the source port unique identifier + string source_port_id = 2 [(gogoproto.moretags) = "yaml:\"source_port_id\""]; + // the source channel unique identifer + string source_channel_id = 3 [(gogoproto.moretags) = "yaml:\"source_channel_id\""]; + // account address to refund fee if necessary + string signer = 4; + // optional list of relayers permitted to the receive packet fees + repeated string relayers = 5; +} + +// MsgPayPacketFeeResponse defines the response type for the PayPacketFee rpc +message MsgPayPacketFeeResponse {} + +// MsgPayPacketFeeAsync defines the request type for the PayPacketFeeAsync rpc +// This Msg can be used to pay for a packet at a specified sequence (instead of the next sequence send) +message MsgPayPacketFeeAsync { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // unique packet identifier comprised of the channel ID, port ID and sequence + ibc.core.channel.v1.PacketId packet_id = 1 + [(gogoproto.moretags) = "yaml:\"packet_id\"", (gogoproto.nullable) = false]; + // the packet fee associated with a particular IBC packet + PacketFee packet_fee = 2 [(gogoproto.moretags) = "yaml:\"packet_fee\"", (gogoproto.nullable) = false]; +} + +// MsgPayPacketFeeAsyncResponse defines the response type for the PayPacketFeeAsync rpc +message MsgPayPacketFeeAsyncResponse {} diff --git a/proto/ibc/core/channel/v1/channel.proto b/proto/ibc/core/channel/v1/channel.proto index 68c6ec17b19..177f2c85fd1 100644 --- a/proto/ibc/core/channel/v1/channel.proto +++ b/proto/ibc/core/channel/v1/channel.proto @@ -132,6 +132,20 @@ message PacketState { bytes data = 4; } +// PacketId is an identifer for a unique Packet +// Source chains refer to packets by source port/channel +// Destination chains refer to packets by destination port/channel +message PacketId { + option (gogoproto.goproto_getters) = false; + + // channel port identifier + string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""]; + // channel unique identifier + string channel_id = 2 [(gogoproto.moretags) = "yaml:\"channel_id\""]; + // packet sequence + uint64 sequence = 3; +} + // Acknowledgement is the recommended acknowledgement format to be used by // app-specific protocols. // NOTE: The field numbers 21 and 22 were explicitly chosen to avoid accidental diff --git a/proto/ibc/core/channel/v1/tx.proto b/proto/ibc/core/channel/v1/tx.proto index 15714173a1a..d34b00e9124 100644 --- a/proto/ibc/core/channel/v1/tx.proto +++ b/proto/ibc/core/channel/v1/tx.proto @@ -68,6 +68,7 @@ message MsgChannelOpenInit { // MsgChannelOpenInitResponse defines the Msg/ChannelOpenInit response type. message MsgChannelOpenInitResponse { string channel_id = 1 [(gogoproto.moretags) = "yaml:\"channel_id\""]; + string version = 2; } // MsgChannelOpenInit defines a msg sent by a Relayer to try to open a channel @@ -91,7 +92,9 @@ message MsgChannelOpenTry { } // MsgChannelOpenTryResponse defines the Msg/ChannelOpenTry response type. -message MsgChannelOpenTryResponse {} +message MsgChannelOpenTryResponse { + string version = 1; +} // MsgChannelOpenAck defines a msg sent by a Relayer to Chain A to acknowledge // the change of channel state to TRYOPEN on Chain B. diff --git a/scripts/protocgen.sh b/scripts/protocgen.sh index efcfb36b30a..4736b47540b 100755 --- a/scripts/protocgen.sh +++ b/scripts/protocgen.sh @@ -31,7 +31,8 @@ buf protoc \ -I "third_party/proto" \ --doc_out=./docs/ibc \ --doc_opt=./docs/protodoc-markdown.tmpl,proto-docs.md \ - $(find "$(pwd)/proto" -maxdepth 5 -name '*.proto') + $(find "$(pwd)/proto" -maxdepth 7 -name '*.proto') +go mod tidy # move proto files to the right places cp -r github.com/cosmos/ibc-go/v*/modules/* modules/ diff --git a/testing/endpoint.go b/testing/endpoint.go index e466bd781e9..7c272d7847f 100644 --- a/testing/endpoint.go +++ b/testing/endpoint.go @@ -328,7 +328,14 @@ func (endpoint *Endpoint) ChanOpenAck() error { proof, height, endpoint.Chain.SenderAccount.GetAddress().String(), ) - return endpoint.Chain.sendMsgs(msg) + + if err = endpoint.Chain.sendMsgs(msg); err != nil { + return err + } + + endpoint.ChannelConfig.Version = endpoint.GetChannel().Version + + return nil } // ChanOpenConfirm will construct and execute a MsgChannelOpenConfirm on the associated endpoint. diff --git a/testing/simapp/app.go b/testing/simapp/app.go index ee148806852..738e29cdd59 100644 --- a/testing/simapp/app.go +++ b/testing/simapp/app.go @@ -89,6 +89,9 @@ import ( icahostkeeper "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/keeper" icahosttypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/types" icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" + ibcfee "github.com/cosmos/ibc-go/v3/modules/apps/29-fee" + ibcfeekeeper "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/keeper" + ibcfeetypes "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" transfer "github.com/cosmos/ibc-go/v3/modules/apps/transfer" ibctransferkeeper "github.com/cosmos/ibc-go/v3/modules/apps/transfer/keeper" ibctransfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" @@ -111,6 +114,11 @@ import ( const appName = "SimApp" +// IBC application testing ports +const ( + MockFeePort string = ibcmock.ModuleName + ibcfeetypes.ModuleName +) + var ( // DefaultNodeHome default home directories for the application daemon DefaultNodeHome string @@ -142,6 +150,7 @@ var ( ica.AppModuleBasic{}, authzmodule.AppModuleBasic{}, vesting.AppModuleBasic{}, + ibcfee.AppModuleBasic{}, ) // module account permissions @@ -153,6 +162,7 @@ var ( stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking}, govtypes.ModuleName: {authtypes.Burner}, ibctransfertypes.ModuleName: {authtypes.Minter, authtypes.Burner}, + ibcfeetypes.ModuleName: nil, icatypes.ModuleName: nil, } ) @@ -192,6 +202,7 @@ type SimApp struct { ParamsKeeper paramskeeper.Keeper AuthzKeeper authzkeeper.Keeper IBCKeeper *ibckeeper.Keeper // IBC Keeper must be a pointer in the app, so we can SetRouter on it correctly + IBCFeeKeeper ibcfeekeeper.Keeper ICAControllerKeeper icacontrollerkeeper.Keeper ICAHostKeeper icahostkeeper.Keeper EvidenceKeeper evidencekeeper.Keeper @@ -201,6 +212,8 @@ type SimApp struct { // make scoped keepers public for test purposes ScopedIBCKeeper capabilitykeeper.ScopedKeeper ScopedTransferKeeper capabilitykeeper.ScopedKeeper + ScopedIBCFeeKeeper capabilitykeeper.ScopedKeeper + ScopedFeeMockKeeper capabilitykeeper.ScopedKeeper ScopedICAControllerKeeper capabilitykeeper.ScopedKeeper ScopedICAHostKeeper capabilitykeeper.ScopedKeeper ScopedIBCMockKeeper capabilitykeeper.ScopedKeeper @@ -209,6 +222,7 @@ type SimApp struct { // make IBC modules public for test purposes // these modules are never directly routed to by the IBC Router ICAAuthModule ibcmock.IBCModule + FeeMockModule ibcmock.IBCModule // the module manager mm *module.Manager @@ -235,7 +249,6 @@ func NewSimApp( homePath string, invCheckPeriod uint, encodingConfig simappparams.EncodingConfig, appOpts servertypes.AppOptions, baseAppOptions ...func(*baseapp.BaseApp), ) *SimApp { - appCodec := encodingConfig.Marshaler legacyAmino := encodingConfig.Amino interfaceRegistry := encodingConfig.InterfaceRegistry @@ -250,7 +263,7 @@ func NewSimApp( minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey, govtypes.StoreKey, paramstypes.StoreKey, ibchost.StoreKey, upgradetypes.StoreKey, feegrant.StoreKey, evidencetypes.StoreKey, ibctransfertypes.StoreKey, icacontrollertypes.StoreKey, icahosttypes.StoreKey, capabilitytypes.StoreKey, - authzkeeper.StoreKey, + authzkeeper.StoreKey, ibcfeetypes.StoreKey, ) tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey) memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey) @@ -281,6 +294,7 @@ func NewSimApp( // NOTE: the IBC mock keeper and application module is used only for testing core IBC. Do // not replicate if you do not need to test core IBC or light clients. scopedIBCMockKeeper := app.CapabilityKeeper.ScopeToModule(ibcmock.ModuleName) + scopedFeeMockKeeper := app.CapabilityKeeper.ScopeToModule(MockFeePort) scopedICAMockKeeper := app.CapabilityKeeper.ScopeToModule(ibcmock.ModuleName + icacontrollertypes.SubModuleName) // seal capability keeper after scoping modules @@ -339,18 +353,35 @@ func NewSimApp( &stakingKeeper, govRouter, ) - // Create Transfer Keepers + app.IBCFeeKeeper = ibcfeekeeper.NewKeeper(appCodec, keys[ibcfeetypes.StoreKey], app.GetSubspace(ibcfeetypes.ModuleName), + app.IBCKeeper.ChannelKeeper, app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper, app.AccountKeeper, app.BankKeeper, + ) + + // Create Transfer Keeper and pass IBCFeeKeeper as expected Channel and PortKeeper + // since fee middleware will wrap the IBCKeeper for underlying application. app.TransferKeeper = ibctransferkeeper.NewKeeper( appCodec, keys[ibctransfertypes.StoreKey], app.GetSubspace(ibctransfertypes.ModuleName), - app.IBCKeeper.ChannelKeeper, app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper, + app.IBCFeeKeeper, app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper, app.AccountKeeper, app.BankKeeper, scopedTransferKeeper, ) transferModule := transfer.NewAppModule(app.TransferKeeper) transferIBCModule := transfer.NewIBCModule(app.TransferKeeper) + // create fee-wrapped transfer module + feeTransferModule := ibcfee.NewIBCModule(app.IBCFeeKeeper, transferIBCModule) + + feeModule := ibcfee.NewAppModule(app.IBCFeeKeeper) + // NOTE: the IBC mock keeper and application module is used only for testing core IBC. Do // not replicate if you do not need to test core IBC or light clients. mockModule := ibcmock.NewAppModule(&app.IBCKeeper.PortKeeper) + + // create fee wrapped mock module + feeMockModule := ibcmock.NewIBCModule(&mockModule, ibcmock.NewMockIBCApp(MockFeePort, scopedFeeMockKeeper)) + app.FeeMockModule = feeMockModule + + feeWithMockModule := ibcfee.NewIBCModule(app.IBCFeeKeeper, feeMockModule) + mockIBCModule := ibcmock.NewIBCModule(&mockModule, ibcmock.NewMockIBCApp(ibcmock.ModuleName, scopedIBCMockKeeper)) app.ICAControllerKeeper = icacontrollerkeeper.NewKeeper( @@ -376,12 +407,15 @@ func NewSimApp( icaHostIBCModule := icahost.NewIBCModule(app.ICAHostKeeper) // Create static IBC router, add app routes, then set and seal it + // pass in top-level (fully-wrapped) IBCModules to IBC Router ibcRouter := porttypes.NewRouter() ibcRouter.AddRoute(icacontrollertypes.SubModuleName, icaControllerIBCModule). AddRoute(icahosttypes.SubModuleName, icaHostIBCModule). AddRoute(ibcmock.ModuleName+icacontrollertypes.SubModuleName, icaControllerIBCModule). // ica with mock auth module stack route to ica (top level of middleware stack) - AddRoute(ibctransfertypes.ModuleName, transferIBCModule). - AddRoute(ibcmock.ModuleName, mockIBCModule) + AddRoute(ibctransfertypes.ModuleName, feeTransferModule). + AddRoute(ibcmock.ModuleName, mockIBCModule). + AddRoute(MockFeePort, feeWithMockModule) + app.IBCKeeper.SetRouter(ibcRouter) // create evidence keeper with router @@ -421,6 +455,7 @@ func NewSimApp( params.NewAppModule(app.ParamsKeeper), authzmodule.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), transferModule, + feeModule, icaModule, mockModule, ) @@ -434,13 +469,13 @@ func NewSimApp( upgradetypes.ModuleName, capabilitytypes.ModuleName, minttypes.ModuleName, distrtypes.ModuleName, slashingtypes.ModuleName, evidencetypes.ModuleName, stakingtypes.ModuleName, ibchost.ModuleName, ibctransfertypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, govtypes.ModuleName, crisistypes.ModuleName, genutiltypes.ModuleName, authz.ModuleName, feegrant.ModuleName, - paramstypes.ModuleName, vestingtypes.ModuleName, icatypes.ModuleName, ibcmock.ModuleName, + paramstypes.ModuleName, vestingtypes.ModuleName, icatypes.ModuleName, ibcfeetypes.ModuleName, ibcmock.ModuleName, ) app.mm.SetOrderEndBlockers( crisistypes.ModuleName, govtypes.ModuleName, stakingtypes.ModuleName, ibchost.ModuleName, ibctransfertypes.ModuleName, capabilitytypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, distrtypes.ModuleName, slashingtypes.ModuleName, minttypes.ModuleName, genutiltypes.ModuleName, evidencetypes.ModuleName, authz.ModuleName, feegrant.ModuleName, paramstypes.ModuleName, - upgradetypes.ModuleName, vestingtypes.ModuleName, icatypes.ModuleName, ibcmock.ModuleName, + upgradetypes.ModuleName, vestingtypes.ModuleName, icatypes.ModuleName, ibcfeetypes.ModuleName, ibcmock.ModuleName, ) // NOTE: The genutils module must occur after staking so that pools are @@ -452,7 +487,7 @@ func NewSimApp( capabilitytypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, distrtypes.ModuleName, stakingtypes.ModuleName, slashingtypes.ModuleName, govtypes.ModuleName, minttypes.ModuleName, crisistypes.ModuleName, ibchost.ModuleName, genutiltypes.ModuleName, evidencetypes.ModuleName, authz.ModuleName, ibctransfertypes.ModuleName, - icatypes.ModuleName, ibcmock.ModuleName, feegrant.ModuleName, paramstypes.ModuleName, upgradetypes.ModuleName, vestingtypes.ModuleName, + icatypes.ModuleName, ibcfeetypes.ModuleName, ibcmock.ModuleName, feegrant.ModuleName, paramstypes.ModuleName, upgradetypes.ModuleName, vestingtypes.ModuleName, ) app.mm.RegisterInvariants(&app.CrisisKeeper) @@ -529,6 +564,7 @@ func NewSimApp( // note replicate if you do not need to test core IBC or light clients. app.ScopedIBCMockKeeper = scopedIBCMockKeeper app.ScopedICAMockKeeper = scopedICAMockKeeper + app.ScopedFeeMockKeeper = scopedFeeMockKeeper return app } diff --git a/testing/values.go b/testing/values.go index 6bdf782c65a..655a4731a74 100644 --- a/testing/values.go +++ b/testing/values.go @@ -14,6 +14,7 @@ import ( commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" "github.com/cosmos/ibc-go/v3/testing/mock" + "github.com/cosmos/ibc-go/v3/testing/simapp" ) const ( @@ -33,6 +34,7 @@ const ( // Application Ports TransferPort = ibctransfertypes.ModuleName MockPort = mock.ModuleName + MockFeePort = simapp.MockFeePort // used for testing proposals Title = "title" From cf893c2fe06a6820da381a978482c95de7e65182 Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Wed, 27 Apr 2022 15:10:34 +0200 Subject: [PATCH 34/71] chore: remove GetHeight from ClientMessage interface (#1285) * adding new consensus heights return value from UpdateState interface, updating 02-client events emission * removing GetHeight() from ClientMessage interface, updating tests * updating godocs to include returned consensus height data * removing unused event emission func, adding deprecation notice, removing GetHeight from solomachine Header, updating tests * removing error return from UpdateState, returning height on duplicate header update * updating event emissions as per suggestions --- modules/core/02-client/keeper/client.go | 21 +++---- modules/core/02-client/keeper/events.go | 32 +++++----- .../core/02-client/legacy/v100/solomachine.go | 2 +- modules/core/02-client/types/events.go | 11 ++-- modules/core/exported/client.go | 5 +- .../06-solomachine/types/header.go | 7 --- .../06-solomachine/types/update.go | 9 ++- .../06-solomachine/types/update_test.go | 12 ++-- .../07-tendermint/types/update.go | 11 ++-- .../07-tendermint/types/update_test.go | 62 ++++++++++++++----- 10 files changed, 102 insertions(+), 70 deletions(-) diff --git a/modules/core/02-client/keeper/client.go b/modules/core/02-client/keeper/client.go index e702c31fe5c..a0ab6348dcf 100644 --- a/modules/core/02-client/keeper/client.go +++ b/modules/core/02-client/keeper/client.go @@ -70,14 +70,6 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, clientMsg exporte return err } - // Marshal the ClientMessage as an Any and encode the resulting bytes to hex. - // This prevents the event value from containing invalid UTF-8 characters - // which may cause data to be lost when JSON encoding/decoding. - clientMsgStr := hex.EncodeToString(types.MustMarshalClientMessage(k.cdc, clientMsg)) - - // set default consensus height with header height - consensusHeight := clientMsg.GetHeight() - foundMisbehaviour := clientState.CheckForMisbehaviour(ctx, k.cdc, clientStore, clientMsg) if foundMisbehaviour { clientState.UpdateStateOnMisbehaviour(ctx, k.cdc, clientStore, clientMsg) @@ -96,14 +88,14 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, clientMsg exporte ) }() - EmitSubmitMisbehaviourEventOnUpdate(ctx, clientID, clientState.ClientType(), consensusHeight, clientMsgStr) + EmitSubmitMisbehaviourEvent(ctx, clientID, clientState) return nil } - clientState.UpdateState(ctx, k.cdc, clientStore, clientMsg) + consensusHeights := clientState.UpdateState(ctx, k.cdc, clientStore, clientMsg) - k.Logger(ctx).Info("client state updated", "client-id", clientID, "height", consensusHeight.String()) + k.Logger(ctx).Info("client state updated", "client-id", clientID, "heights", consensusHeights) defer func() { telemetry.IncrCounterWithLabels( @@ -117,8 +109,13 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, clientMsg exporte ) }() + // Marshal the ClientMessage as an Any and encode the resulting bytes to hex. + // This prevents the event value from containing invalid UTF-8 characters + // which may cause data to be lost when JSON encoding/decoding. + clientMsgStr := hex.EncodeToString(types.MustMarshalClientMessage(k.cdc, clientMsg)) + // emitting events in the keeper emits for both begin block and handler client updates - EmitUpdateClientEvent(ctx, clientID, clientState.ClientType(), consensusHeight, clientMsgStr) + EmitUpdateClientEvent(ctx, clientID, clientState.ClientType(), consensusHeights, clientMsgStr) return nil } diff --git a/modules/core/02-client/keeper/events.go b/modules/core/02-client/keeper/events.go index 4e2f38941a0..d49d41cb1cb 100644 --- a/modules/core/02-client/keeper/events.go +++ b/modules/core/02-client/keeper/events.go @@ -1,6 +1,8 @@ package keeper import ( + "strings" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" @@ -24,13 +26,26 @@ func EmitCreateClientEvent(ctx sdk.Context, clientID string, clientState exporte } // EmitUpdateClientEvent emits an update client event -func EmitUpdateClientEvent(ctx sdk.Context, clientID string, clientType string, consensusHeight exported.Height, clientMsgStr string) { +func EmitUpdateClientEvent(ctx sdk.Context, clientID string, clientType string, consensusHeights []exported.Height, clientMsgStr string) { + var consensusHeightAttr string + if len(consensusHeights) != 0 { + consensusHeightAttr = consensusHeights[0].String() + } + + var consensusHeightsAttr []string + for _, height := range consensusHeights { + consensusHeightsAttr = append(consensusHeightsAttr, height.String()) + } + ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( types.EventTypeUpdateClient, sdk.NewAttribute(types.AttributeKeyClientID, clientID), sdk.NewAttribute(types.AttributeKeyClientType, clientType), - sdk.NewAttribute(types.AttributeKeyConsensusHeight, consensusHeight.String()), + // Deprecated: AttributeKeyConsensusHeight is deprecated and will be removed in a future release. + // Please use AttributeKeyConsensusHeights instead. + sdk.NewAttribute(types.AttributeKeyConsensusHeight, consensusHeightAttr), + sdk.NewAttribute(types.AttributeKeyConsensusHeights, strings.Join(consensusHeightsAttr, ",")), sdk.NewAttribute(types.AttributeKeyHeader, clientMsgStr), ), sdk.NewEvent( @@ -78,16 +93,3 @@ func EmitSubmitMisbehaviourEvent(ctx sdk.Context, clientID string, clientState e ), ) } - -// EmitSubmitMisbehaviourEventOnUpdate emits a client misbehaviour event on a client update event -func EmitSubmitMisbehaviourEventOnUpdate(ctx sdk.Context, clientID string, clientType string, consensusHeight exported.Height, headerStr string) { - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypeSubmitMisbehaviour, - sdk.NewAttribute(types.AttributeKeyClientID, clientID), - sdk.NewAttribute(types.AttributeKeyClientType, clientType), - sdk.NewAttribute(types.AttributeKeyConsensusHeight, consensusHeight.String()), - sdk.NewAttribute(types.AttributeKeyHeader, headerStr), - ), - ) -} diff --git a/modules/core/02-client/legacy/v100/solomachine.go b/modules/core/02-client/legacy/v100/solomachine.go index 19e5459206c..3080c5c8ce4 100644 --- a/modules/core/02-client/legacy/v100/solomachine.go +++ b/modules/core/02-client/legacy/v100/solomachine.go @@ -108,7 +108,7 @@ func (cs *ClientState) VerifyClientMessage( } // UpdateState panis! -func (cs *ClientState) UpdateState(_ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientMessage) error { +func (cs *ClientState) UpdateState(_ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientMessage) []exported.Height { panic("legacy solo machine is deprecated!") } diff --git a/modules/core/02-client/types/events.go b/modules/core/02-client/types/events.go index 391e1e37080..05708a84859 100644 --- a/modules/core/02-client/types/events.go +++ b/modules/core/02-client/types/events.go @@ -8,11 +8,12 @@ import ( // IBC client events const ( - AttributeKeyClientID = "client_id" - AttributeKeySubjectClientID = "subject_client_id" - AttributeKeyClientType = "client_type" - AttributeKeyConsensusHeight = "consensus_height" - AttributeKeyHeader = "header" + AttributeKeyClientID = "client_id" + AttributeKeySubjectClientID = "subject_client_id" + AttributeKeyClientType = "client_type" + AttributeKeyConsensusHeight = "consensus_height" + AttributeKeyConsensusHeights = "consensus_heights" + AttributeKeyHeader = "header" ) // IBC client events vars diff --git a/modules/core/exported/client.go b/modules/core/exported/client.go index 44fc75a6169..b1ed5ca85b2 100644 --- a/modules/core/exported/client.go +++ b/modules/core/exported/client.go @@ -62,8 +62,8 @@ type ClientState interface { VerifyClientMessage(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg ClientMessage) error // UpdateState updates and stores as necessary any associated information for an IBC client, such as the ClientState and corresponding ConsensusState. - // An error is returned if ClientMessage is of type Misbehaviour - UpdateState(sdk.Context, codec.BinaryCodec, sdk.KVStore, ClientMessage) error + // Upon successful update, a list of consensus heights is returned. It assumes the ClientMessage has already been verified. + UpdateState(sdk.Context, codec.BinaryCodec, sdk.KVStore, ClientMessage) []Height // Update and Misbehaviour functions CheckSubstituteAndUpdateState(ctx sdk.Context, cdc codec.BinaryCodec, subjectClientStore, substituteClientStore sdk.KVStore, substituteClient ClientState) (ClientState, error) @@ -203,7 +203,6 @@ type ConsensusState interface { type ClientMessage interface { proto.Message - GetHeight() Height ClientType() string ValidateBasic() error } diff --git a/modules/light-clients/06-solomachine/types/header.go b/modules/light-clients/06-solomachine/types/header.go index eb2cbe5f8b3..a6639a0f0c9 100644 --- a/modules/light-clients/06-solomachine/types/header.go +++ b/modules/light-clients/06-solomachine/types/header.go @@ -17,13 +17,6 @@ func (Header) ClientType() string { return exported.Solomachine } -// GetHeight returns the current sequence number as the height. -// Return clientexported.Height to satisfy interface -// Revision number is always 0 for a solo-machine -func (h Header) GetHeight() exported.Height { - return clienttypes.NewHeight(0, h.Sequence) -} - // GetPubKey unmarshals the new public key into a cryptotypes.PubKey type. // An error is returned if the new public key is nil or the cached value // is not a PubKey. diff --git a/modules/light-clients/06-solomachine/types/update.go b/modules/light-clients/06-solomachine/types/update.go index 0fc5c76544f..e8c464772f7 100644 --- a/modules/light-clients/06-solomachine/types/update.go +++ b/modules/light-clients/06-solomachine/types/update.go @@ -1,6 +1,8 @@ package types import ( + "fmt" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -81,10 +83,11 @@ func (cs ClientState) verifyMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, } // UpdateState updates the consensus state to the new public key and an incremented sequence. -func (cs ClientState) UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg exported.ClientMessage) error { +// A list containing the updated consensus height is returned. +func (cs ClientState) UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg exported.ClientMessage) []exported.Height { smHeader, ok := clientMsg.(*Header) if !ok { - return sdkerrors.Wrapf(clienttypes.ErrInvalidClientType, "expected %T got %T", Header{}, clientMsg) + panic(fmt.Errorf("unsupported ClientMessage: %T", clientMsg)) } // create new solomachine ConsensusState @@ -99,7 +102,7 @@ func (cs ClientState) UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, client clientStore.Set(host.ClientStateKey(), clienttypes.MustMarshalClientState(cdc, &cs)) - return nil + return []exported.Height{clienttypes.NewHeight(0, cs.Sequence)} } // CheckForMisbehaviour returns true for type Misbehaviour (passed VerifyClientMessage check), otherwise returns false diff --git a/modules/light-clients/06-solomachine/types/update_test.go b/modules/light-clients/06-solomachine/types/update_test.go index 7e3acaf0637..223e468e87f 100644 --- a/modules/light-clients/06-solomachine/types/update_test.go +++ b/modules/light-clients/06-solomachine/types/update_test.go @@ -441,23 +441,27 @@ func (suite *SoloMachineTestSuite) TestUpdateState() { suite.Run(tc.name, func() { tc.setup() // setup test - err := clientState.UpdateState(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, clientMsg) - if tc.expPass { - suite.Require().NoError(err) + consensusHeights := clientState.UpdateState(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, clientMsg) clientStateBz := suite.store.Get(host.ClientStateKey()) suite.Require().NotEmpty(clientStateBz) newClientState := clienttypes.MustUnmarshalClientState(suite.chainA.Codec, clientStateBz) + suite.Require().Len(consensusHeights, 1) + suite.Require().Equal(uint64(0), consensusHeights[0].GetRevisionNumber()) + suite.Require().Equal(newClientState.(*types.ClientState).Sequence, consensusHeights[0].GetRevisionHeight()) + suite.Require().False(newClientState.(*types.ClientState).IsFrozen) suite.Require().Equal(clientMsg.(*types.Header).Sequence+1, newClientState.(*types.ClientState).Sequence) suite.Require().Equal(clientMsg.(*types.Header).NewPublicKey, newClientState.(*types.ClientState).ConsensusState.PublicKey) suite.Require().Equal(clientMsg.(*types.Header).NewDiversifier, newClientState.(*types.ClientState).ConsensusState.Diversifier) suite.Require().Equal(clientMsg.(*types.Header).Timestamp, newClientState.(*types.ClientState).ConsensusState.Timestamp) } else { - suite.Require().Error(err) + suite.Require().Panics(func() { + clientState.UpdateState(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, clientMsg) + }) } }) diff --git a/modules/light-clients/07-tendermint/types/update.go b/modules/light-clients/07-tendermint/types/update.go index 18849096bba..b862e80fd30 100644 --- a/modules/light-clients/07-tendermint/types/update.go +++ b/modules/light-clients/07-tendermint/types/update.go @@ -2,6 +2,7 @@ package types import ( "bytes" + "fmt" "reflect" "github.com/cosmos/cosmos-sdk/codec" @@ -154,19 +155,20 @@ func (cs *ClientState) verifyHeader( // If we are updating to a past height, a consensus state is created for that height to be persisted in client store // If we are updating to a future height, the consensus state is created and the client state is updated to reflect // the new latest height +// A list containing the updated consensus height is returned. // UpdateState must only be used to update within a single revision, thus header revision number and trusted height's revision // number must be the same. To update to a new revision, use a separate upgrade path // UpdateState will prune the oldest consensus state if it is expired. -func (cs ClientState) UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg exported.ClientMessage) error { +func (cs ClientState) UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg exported.ClientMessage) []exported.Height { header, ok := clientMsg.(*Header) if !ok { - return sdkerrors.Wrapf(clienttypes.ErrInvalidClientType, "expected type %T, got %T", &Header{}, header) + panic(fmt.Errorf("expected type %T, got %T", &Header{}, clientMsg)) } // check for duplicate update if consensusState, _ := GetConsensusState(clientStore, cdc, header.GetHeight()); consensusState != nil { // perform no-op - return nil + return []exported.Height{header.GetHeight()} } cs.pruneOldestConsensusState(ctx, cdc, clientStore) @@ -175,6 +177,7 @@ func (cs ClientState) UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, client if height.GT(cs.LatestHeight) { cs.LatestHeight = height } + consensusState := &ConsensusState{ Timestamp: header.GetTime(), Root: commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), @@ -186,7 +189,7 @@ func (cs ClientState) UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, client setConsensusState(clientStore, cdc, consensusState, header.GetHeight()) setConsensusMetadata(ctx, clientStore, header.GetHeight()) - return nil + return []exported.Height{height} } // pruneOldestConsensusState will retrieve the earliest consensus state for this clientID and check if it is expired. If it is, diff --git a/modules/light-clients/07-tendermint/types/update_test.go b/modules/light-clients/07-tendermint/types/update_test.go index 022e6d1ba36..59326645248 100644 --- a/modules/light-clients/07-tendermint/types/update_test.go +++ b/modules/light-clients/07-tendermint/types/update_test.go @@ -305,6 +305,7 @@ func (suite *TendermintTestSuite) TestUpdateState() { path *ibctesting.Path clientMessage exported.ClientMessage clientStore sdk.KVStore + consensusHeights []exported.Height pruneHeight clienttypes.Height prevClientState exported.ClientState prevConsensusState exported.ConsensusState @@ -318,10 +319,17 @@ func (suite *TendermintTestSuite) TestUpdateState() { }{ { "success with height later than latest height", func() { - suite.Require().True(path.EndpointA.GetClientState().GetLatestHeight().LT(clientMessage.GetHeight())) + tmHeader, ok := clientMessage.(*types.Header) + suite.Require().True(ok) + suite.Require().True(path.EndpointA.GetClientState().GetLatestHeight().LT(tmHeader.GetHeight())) }, func() { - suite.Require().True(path.EndpointA.GetClientState().GetLatestHeight().EQ(clientMessage.GetHeight())) // new update, updated client state should have changed + tmHeader, ok := clientMessage.(*types.Header) + suite.Require().True(ok) + + clientState := path.EndpointA.GetClientState() + suite.Require().True(clientState.GetLatestHeight().EQ(tmHeader.GetHeight())) // new update, updated client state should have changed + suite.Require().True(clientState.GetLatestHeight().EQ(consensusHeights[0])) }, true, }, { @@ -332,12 +340,16 @@ func (suite *TendermintTestSuite) TestUpdateState() { err := path.EndpointA.UpdateClient() suite.Require().NoError(err) - suite.Require().True(path.EndpointA.GetClientState().GetLatestHeight().GT(clientMessage.GetHeight())) + tmHeader, ok := clientMessage.(*types.Header) + suite.Require().True(ok) + suite.Require().True(path.EndpointA.GetClientState().GetLatestHeight().GT(tmHeader.GetHeight())) prevClientState = path.EndpointA.GetClientState() }, func() { - suite.Require().Equal(path.EndpointA.GetClientState(), prevClientState) // fill in height, no change to client state + clientState := path.EndpointA.GetClientState() + suite.Require().Equal(clientState, prevClientState) // fill in height, no change to client state + suite.Require().True(clientState.GetLatestHeight().GT(consensusHeights[0])) }, true, }, { @@ -349,14 +361,22 @@ func (suite *TendermintTestSuite) TestUpdateState() { // use the same header which just updated the client clientMessage, err = path.EndpointA.Chain.ConstructUpdateTMClientHeader(path.EndpointA.Counterparty.Chain, path.EndpointA.ClientID) suite.Require().NoError(err) - suite.Require().Equal(path.EndpointA.GetClientState().GetLatestHeight(), clientMessage.GetHeight()) + + tmHeader, ok := clientMessage.(*types.Header) + suite.Require().True(ok) + suite.Require().Equal(path.EndpointA.GetClientState().GetLatestHeight(), tmHeader.GetHeight()) prevClientState = path.EndpointA.GetClientState() - prevConsensusState = path.EndpointA.GetConsensusState(clientMessage.GetHeight()) + prevConsensusState = path.EndpointA.GetConsensusState(tmHeader.GetHeight()) }, func() { - suite.Require().Equal(path.EndpointA.GetClientState(), prevClientState) - suite.Require().Equal(path.EndpointA.GetConsensusState(clientMessage.GetHeight()), prevConsensusState) + clientState := path.EndpointA.GetClientState() + suite.Require().Equal(clientState, prevClientState) + suite.Require().True(clientState.GetLatestHeight().EQ(consensusHeights[0])) + + tmHeader, ok := clientMessage.(*types.Header) + suite.Require().True(ok) + suite.Require().Equal(path.EndpointA.GetConsensusState(tmHeader.GetHeight()), prevConsensusState) }, true, }, { @@ -385,7 +405,12 @@ func (suite *TendermintTestSuite) TestUpdateState() { suite.Require().NoError(err) }, func() { - suite.Require().True(path.EndpointA.GetClientState().GetLatestHeight().EQ(clientMessage.GetHeight())) // new update, updated client state should have changed + tmHeader, ok := clientMessage.(*types.Header) + suite.Require().True(ok) + + clientState := path.EndpointA.GetClientState() + suite.Require().True(clientState.GetLatestHeight().EQ(tmHeader.GetHeight())) // new update, updated client state should have changed + suite.Require().True(clientState.GetLatestHeight().EQ(consensusHeights[0])) // ensure consensus state was pruned _, found := path.EndpointA.Chain.GetConsensusState(path.EndpointA.ClientID, pruneHeight) @@ -396,7 +421,8 @@ func (suite *TendermintTestSuite) TestUpdateState() { "invalid ClientMessage type", func() { clientMessage = &types.Misbehaviour{} }, - func() {}, false, + func() {}, + false, }, } for _, tc := range testCases { @@ -417,12 +443,10 @@ func (suite *TendermintTestSuite) TestUpdateState() { tc.malleate() clientState := path.EndpointA.GetClientState() - clientStore = suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - err = clientState.UpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, clientMessage) if tc.expPass { - suite.Require().NoError(err) + consensusHeights = clientState.UpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, clientMessage) header := clientMessage.(*types.Header) expConsensusState := &types.ConsensusState{ @@ -437,7 +461,9 @@ func (suite *TendermintTestSuite) TestUpdateState() { suite.Require().Equal(expConsensusState, updatedConsensusState) } else { - suite.Require().Error(err) + suite.Require().Panics(func() { + clientState.UpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, clientMessage) + }) } // perform custom checks @@ -556,7 +582,9 @@ func (suite *TendermintTestSuite) TestCheckForMisbehaviour() { NextValidatorsHash: header.Header.NextValidatorsHash, } - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), path.EndpointA.ClientID, clientMessage.GetHeight(), consensusState) + tmHeader, ok := clientMessage.(*types.Header) + suite.Require().True(ok) + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), path.EndpointA.ClientID, tmHeader.GetHeight(), consensusState) }, false, }, @@ -572,7 +600,9 @@ func (suite *TendermintTestSuite) TestCheckForMisbehaviour() { NextValidatorsHash: header.Header.NextValidatorsHash, } - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), path.EndpointA.ClientID, clientMessage.GetHeight(), consensusState) + tmHeader, ok := clientMessage.(*types.Header) + suite.Require().True(ok) + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), path.EndpointA.ClientID, tmHeader.GetHeight(), consensusState) }, true, }, From 55b115a1223f021021dccfc2a93cbaa9a1baa7a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?colin=20axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Wed, 27 Apr 2022 16:17:24 +0200 Subject: [PATCH 35/71] update godoc for VerifyClientMessage (#1281) * update godoc * Update modules/core/exported/client.go Co-authored-by: Carlos Rodriguez * update UpdateState godoc * reorganize interface functions logically Co-authored-by: Carlos Rodriguez --- modules/core/exported/client.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/modules/core/exported/client.go b/modules/core/exported/client.go index b1ed5ca85b2..b4ff463e5fc 100644 --- a/modules/core/exported/client.go +++ b/modules/core/exported/client.go @@ -49,18 +49,22 @@ type ClientState interface { // Clients must return their status. Only Active clients are allowed to process packets. Status(ctx sdk.Context, clientStore sdk.KVStore, cdc codec.BinaryCodec) Status - // Checks for evidence of a misbehaviour in Header or Misbehaviour type - CheckForMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, msg ClientMessage) bool - // Genesis function ExportMetadata(sdk.KVStore) []GenesisMetadata + // VerifyClientMessage must verify a ClientMessage. A ClientMessage could be a Header, Misbehaviour, or batch update. + // It must handle each type of ClientMessage appropriately. Calls to CheckForMisbehaviour, UpdateState, and UpdateStateOnMisbehaviour + // will assume that the content of the ClientMessage has been verified and can be trusted. An error should be returned + // if the ClientMessage fails to verify. + VerifyClientMessage(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg ClientMessage) error + + // Checks for evidence of a misbehaviour in Header or Misbehaviour type. It assumes the ClientMessage + // has already been verified. + CheckForMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, msg ClientMessage) bool + // UpdateStateOnMisbehaviour should perform appropriate state changes on a client state given that misbehaviour has been detected and verified UpdateStateOnMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg ClientMessage) - // VerifyClientMessage verifies a ClientMessage. A ClientMessage could be a Header, Misbehaviour, or batch update. - VerifyClientMessage(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg ClientMessage) error - // UpdateState updates and stores as necessary any associated information for an IBC client, such as the ClientState and corresponding ConsensusState. // Upon successful update, a list of consensus heights is returned. It assumes the ClientMessage has already been verified. UpdateState(sdk.Context, codec.BinaryCodec, sdk.KVStore, ClientMessage) []Height From 48882a917aec2e239c27c5fed9616420ef305c6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?colin=20axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Thu, 28 Apr 2022 16:14:40 +0200 Subject: [PATCH 36/71] Add VerifyMembership to 07-tendermint (#1297) * add VerifyMembership function and initial tests * revert unnecessary code changes * add rest of successful verify test cases * increase code coverage with additional tests * chore: remove field naming from test cases * add test case using VerifyMembership on non IBC store value --- .../07-tendermint/types/client_state.go | 110 +++++-- .../07-tendermint/types/client_state_test.go | 279 +++++++++++++++++- testing/chain.go | 13 +- 3 files changed, 376 insertions(+), 26 deletions(-) diff --git a/modules/light-clients/07-tendermint/types/client_state.go b/modules/light-clients/07-tendermint/types/client_state.go index f0d0e79972f..404ed692fe5 100644 --- a/modules/light-clients/07-tendermint/types/client_state.go +++ b/modules/light-clients/07-tendermint/types/client_state.go @@ -192,6 +192,7 @@ func (cs ClientState) Initialize(ctx sdk.Context, _ codec.BinaryCodec, clientSto // VerifyClientState verifies a proof of the client state of the running chain // stored on the target machine +// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294 func (cs ClientState) VerifyClientState( store sdk.KVStore, cdc codec.BinaryCodec, @@ -231,6 +232,7 @@ func (cs ClientState) VerifyClientState( // VerifyClientConsensusState verifies a proof of the consensus state of the // Tendermint client stored on the target machine. +// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294 func (cs ClientState) VerifyClientConsensusState( store sdk.KVStore, cdc codec.BinaryCodec, @@ -275,6 +277,7 @@ func (cs ClientState) VerifyClientConsensusState( // VerifyConnectionState verifies a proof of the connection state of the // specified connection end stored on the target machine. +// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294 func (cs ClientState) VerifyConnectionState( store sdk.KVStore, cdc codec.BinaryCodec, @@ -314,6 +317,7 @@ func (cs ClientState) VerifyConnectionState( // VerifyChannelState verifies a proof of the channel state of the specified // channel end, under the specified port, stored on the target machine. +// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294 func (cs ClientState) VerifyChannelState( store sdk.KVStore, cdc codec.BinaryCodec, @@ -354,6 +358,7 @@ func (cs ClientState) VerifyChannelState( // VerifyPacketCommitment verifies a proof of an outgoing packet commitment at // the specified port, specified channel, and specified sequence. +// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294 func (cs ClientState) VerifyPacketCommitment( ctx sdk.Context, store sdk.KVStore, @@ -393,6 +398,7 @@ func (cs ClientState) VerifyPacketCommitment( // VerifyPacketAcknowledgement verifies a proof of an incoming packet // acknowledgement at the specified port, specified channel, and specified sequence. +// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294 func (cs ClientState) VerifyPacketAcknowledgement( ctx sdk.Context, store sdk.KVStore, @@ -433,6 +439,7 @@ func (cs ClientState) VerifyPacketAcknowledgement( // VerifyPacketReceiptAbsence verifies a proof of the absence of an // incoming packet receipt at the specified port, specified channel, and // specified sequence. +// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294 func (cs ClientState) VerifyPacketReceiptAbsence( ctx sdk.Context, store sdk.KVStore, @@ -471,6 +478,7 @@ func (cs ClientState) VerifyPacketReceiptAbsence( // VerifyNextSequenceRecv verifies a proof of the next sequence number to be // received of the specified channel at the specified port. +// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294 func (cs ClientState) VerifyNextSequenceRecv( ctx sdk.Context, store sdk.KVStore, @@ -509,39 +517,97 @@ func (cs ClientState) VerifyNextSequenceRecv( return nil } +// VerifyMembership is a generic proof verification method which verifies a proof of the existence of a value at a given CommitmentPath at the specified height. +// The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). +func (cs ClientState) VerifyMembership( + ctx sdk.Context, + clientStore sdk.KVStore, + cdc codec.BinaryCodec, + height exported.Height, + delayTimePeriod uint64, + delayBlockPeriod uint64, + proof []byte, + path []byte, + value []byte, +) error { + if cs.GetLatestHeight().LT(height) { + return sdkerrors.Wrapf( + sdkerrors.ErrInvalidHeight, + "client state height < proof height (%d < %d), please ensure the client has been updated", cs.GetLatestHeight(), height, + ) + } + + if err := verifyDelayPeriodPassed(ctx, clientStore, height, delayTimePeriod, delayBlockPeriod); err != nil { + return err + } + + var merkleProof commitmenttypes.MerkleProof + if err := cdc.Unmarshal(proof, &merkleProof); err != nil { + return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "failed to unmarshal proof into ICS 23 commitment merkle proof") + } + + var merklePath commitmenttypes.MerklePath + if err := cdc.Unmarshal(path, &merklePath); err != nil { + return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "failed to unmarshal path into ICS 23 commitment merkle path") + } + + consensusState, found := GetConsensusState(clientStore, cdc, height) + if !found { + return sdkerrors.Wrap(clienttypes.ErrConsensusStateNotFound, "please ensure the proof was constructed against a height that exists on the client") + } + + if err := merkleProof.VerifyMembership(cs.ProofSpecs, consensusState.GetRoot(), merklePath, value); err != nil { + return err + } + + return nil +} + // verifyDelayPeriodPassed will ensure that at least delayTimePeriod amount of time and delayBlockPeriod number of blocks have passed // since consensus state was submitted before allowing verification to continue. func verifyDelayPeriodPassed(ctx sdk.Context, store sdk.KVStore, proofHeight exported.Height, delayTimePeriod, delayBlockPeriod uint64) error { - // check that executing chain's timestamp has passed consensusState's processed time + delay time period - processedTime, ok := GetProcessedTime(store, proofHeight) - if !ok { - return sdkerrors.Wrapf(ErrProcessedTimeNotFound, "processed time not found for height: %s", proofHeight) - } - currentTimestamp := uint64(ctx.BlockTime().UnixNano()) - validTime := processedTime + delayTimePeriod - // NOTE: delay time period is inclusive, so if currentTimestamp is validTime, then we return no error - if currentTimestamp < validTime { - return sdkerrors.Wrapf(ErrDelayPeriodNotPassed, "cannot verify packet until time: %d, current time: %d", - validTime, currentTimestamp) - } - // check that executing chain's height has passed consensusState's processed height + delay block period - processedHeight, ok := GetProcessedHeight(store, proofHeight) - if !ok { - return sdkerrors.Wrapf(ErrProcessedHeightNotFound, "processed height not found for height: %s", proofHeight) + if delayTimePeriod != 0 { + // check that executing chain's timestamp has passed consensusState's processed time + delay time period + processedTime, ok := GetProcessedTime(store, proofHeight) + if !ok { + return sdkerrors.Wrapf(ErrProcessedTimeNotFound, "processed time not found for height: %s", proofHeight) + } + + currentTimestamp := uint64(ctx.BlockTime().UnixNano()) + validTime := processedTime + delayTimePeriod + + // NOTE: delay time period is inclusive, so if currentTimestamp is validTime, then we return no error + if currentTimestamp < validTime { + return sdkerrors.Wrapf(ErrDelayPeriodNotPassed, "cannot verify packet until time: %d, current time: %d", + validTime, currentTimestamp) + } + } - currentHeight := clienttypes.GetSelfHeight(ctx) - validHeight := clienttypes.NewHeight(processedHeight.GetRevisionNumber(), processedHeight.GetRevisionHeight()+delayBlockPeriod) - // NOTE: delay block period is inclusive, so if currentHeight is validHeight, then we return no error - if currentHeight.LT(validHeight) { - return sdkerrors.Wrapf(ErrDelayPeriodNotPassed, "cannot verify packet until height: %s, current height: %s", - validHeight, currentHeight) + + if delayBlockPeriod != 0 { + // check that executing chain's height has passed consensusState's processed height + delay block period + processedHeight, ok := GetProcessedHeight(store, proofHeight) + if !ok { + return sdkerrors.Wrapf(ErrProcessedHeightNotFound, "processed height not found for height: %s", proofHeight) + } + + currentHeight := clienttypes.GetSelfHeight(ctx) + validHeight := clienttypes.NewHeight(processedHeight.GetRevisionNumber(), processedHeight.GetRevisionHeight()+delayBlockPeriod) + + // NOTE: delay block period is inclusive, so if currentHeight is validHeight, then we return no error + if currentHeight.LT(validHeight) { + return sdkerrors.Wrapf(ErrDelayPeriodNotPassed, "cannot verify packet until height: %s, current height: %s", + validHeight, currentHeight) + } } + return nil } // produceVerificationArgs perfoms the basic checks on the arguments that are // shared between the verification functions and returns the unmarshalled // merkle proof, the consensus state and an error if one occurred. +// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294 func produceVerificationArgs( store sdk.KVStore, cdc codec.BinaryCodec, diff --git a/modules/light-clients/07-tendermint/types/client_state_test.go b/modules/light-clients/07-tendermint/types/client_state_test.go index cf52d2996b5..58416f29832 100644 --- a/modules/light-clients/07-tendermint/types/client_state_test.go +++ b/modules/light-clients/07-tendermint/types/client_state_test.go @@ -4,7 +4,9 @@ import ( "time" ics23 "github.com/confio/ics23/go" + sdk "github.com/cosmos/cosmos-sdk/types" + transfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" @@ -201,6 +203,277 @@ func (suite *TendermintTestSuite) TestInitialize() { } } +func (suite *TendermintTestSuite) TestVerifyMembership() { + var ( + testingpath *ibctesting.Path + delayTimePeriod uint64 + delayBlockPeriod uint64 + proofHeight exported.Height + proof []byte + path []byte + value []byte + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "successful ClientState verification", + func() { + // default proof construction uses ClientState + }, + true, + }, + { + "successful ConsensusState verification", func() { + key := host.FullConsensusStateKey(testingpath.EndpointB.ClientID, testingpath.EndpointB.GetClientState().GetLatestHeight()) + merklePath := commitmenttypes.NewMerklePath(string(key)) + merklePath, err := commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + path, err = suite.chainB.Codec.Marshal(&merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + + consensusState := testingpath.EndpointB.GetConsensusState(testingpath.EndpointB.GetClientState().GetLatestHeight()).(*types.ConsensusState) + value, err = suite.chainB.Codec.MarshalInterface(consensusState) + suite.Require().NoError(err) + }, + true, + }, + { + "successful Connection verification", func() { + key := host.ConnectionKey(testingpath.EndpointB.ConnectionID) + merklePath := commitmenttypes.NewMerklePath(string(key)) + merklePath, err := commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + path, err = suite.chainB.Codec.Marshal(&merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + + connection := testingpath.EndpointB.GetConnection() + value, err = suite.chainB.Codec.Marshal(&connection) + suite.Require().NoError(err) + }, + true, + }, + { + "successful Channel verification", func() { + key := host.ChannelKey(testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID) + merklePath := commitmenttypes.NewMerklePath(string(key)) + merklePath, err := commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + path, err = suite.chainB.Codec.Marshal(&merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + + channel := testingpath.EndpointB.GetChannel() + value, err = suite.chainB.Codec.Marshal(&channel) + suite.Require().NoError(err) + }, + true, + }, + { + "successful PacketCommitment verification", func() { + // send from chainB to chainA since we are proving chainB sent a packet + packet := channeltypes.NewPacket(ibctesting.MockPacketData, 1, testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID, testingpath.EndpointA.ChannelConfig.PortID, testingpath.EndpointA.ChannelID, clienttypes.NewHeight(0, 100), 0) + err := testingpath.EndpointB.SendPacket(packet) + suite.Require().NoError(err) + + // make packet commitment proof + key := host.PacketCommitmentKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + merklePath := commitmenttypes.NewMerklePath(string(key)) + merklePath, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + path, err = suite.chainB.Codec.Marshal(&merklePath) + suite.Require().NoError(err) + + proof, proofHeight = testingpath.EndpointB.QueryProof(key) + + value = channeltypes.CommitPacket(suite.chainA.App.GetIBCKeeper().Codec(), packet) + }, true, + }, + { + "successful Acknowledgement verification", func() { + // send from chainA to chainB since we are proving chainB wrote an acknowledgement + packet := channeltypes.NewPacket(ibctesting.MockPacketData, 1, testingpath.EndpointA.ChannelConfig.PortID, testingpath.EndpointA.ChannelID, testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID, clienttypes.NewHeight(0, 100), 0) + err := testingpath.EndpointA.SendPacket(packet) + suite.Require().NoError(err) + + // write receipt and ack + err = testingpath.EndpointB.RecvPacket(packet) + suite.Require().NoError(err) + + key := host.PacketAcknowledgementKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + merklePath := commitmenttypes.NewMerklePath(string(key)) + merklePath, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + path, err = suite.chainB.Codec.Marshal(&merklePath) + suite.Require().NoError(err) + + proof, proofHeight = testingpath.EndpointB.QueryProof(key) + + value = channeltypes.CommitAcknowledgement(ibcmock.MockAcknowledgement.Acknowledgement()) + }, + true, + }, + { + "successful NextSequenceRecv verification", func() { + // send from chainA to chainB since we are proving chainB incremented the sequence recv + packet := channeltypes.NewPacket(ibctesting.MockPacketData, 1, testingpath.EndpointA.ChannelConfig.PortID, testingpath.EndpointA.ChannelID, testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID, clienttypes.NewHeight(0, 100), 0) + + // send packet + err := testingpath.EndpointA.SendPacket(packet) + suite.Require().NoError(err) + + // next seq recv incremented + err = testingpath.EndpointB.RecvPacket(packet) + suite.Require().NoError(err) + + key := host.NextSequenceRecvKey(packet.GetSourcePort(), packet.GetSourceChannel()) + merklePath := commitmenttypes.NewMerklePath(string(key)) + merklePath, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + path, err = suite.chainB.Codec.Marshal(&merklePath) + suite.Require().NoError(err) + + proof, proofHeight = testingpath.EndpointB.QueryProof(key) + + value = sdk.Uint64ToBigEndian(packet.GetSequence() + 1) + }, + true, + }, + { + "successful verification outside IBC store", func() { + key := transfertypes.PortKey + merklePath := commitmenttypes.NewMerklePath(string(key)) + merklePath, err := commitmenttypes.ApplyPrefix(commitmenttypes.NewMerklePrefix([]byte(transfertypes.StoreKey)), merklePath) + suite.Require().NoError(err) + + path, err = suite.chainB.Codec.Marshal(&merklePath) + suite.Require().NoError(err) + + clientState := testingpath.EndpointA.GetClientState() + proof, proofHeight = suite.chainB.QueryProofForStore(transfertypes.StoreKey, key, int64(clientState.GetLatestHeight().GetRevisionHeight())) + + value = []byte(suite.chainB.GetSimApp().TransferKeeper.GetPort(suite.chainB.GetContext())) + suite.Require().NoError(err) + }, + true, + }, + { + "delay time period has passed", func() { + delayTimePeriod = uint64(time.Second.Nanoseconds()) + }, + true, + }, + { + "delay time period has not passed", func() { + delayTimePeriod = uint64(time.Hour.Nanoseconds()) + }, + false, + }, + { + "delay block period has passed", func() { + delayBlockPeriod = 1 + }, + true, + }, + { + "delay block period has not passed", func() { + delayBlockPeriod = 1000 + }, + false, + }, + { + "latest client height < height", func() { + proofHeight = testingpath.EndpointA.GetClientState().GetLatestHeight().Increment() + }, false, + }, + { + "failed to unmarshal merkle path", func() { + path = []byte("invalid merkle path") + }, false, + }, + { + "failed to unmarshal merkle proof", func() { + proof = invalidProof + }, false, + }, + { + "consensus state not found", func() { + proofHeight = clienttypes.ZeroHeight() + }, false, + }, + { + "proof verification failed", func() { + // change the value being prooved + value = []byte("invalid value") + }, false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() // reset + testingpath = ibctesting.NewPath(suite.chainA, suite.chainB) + testingpath.SetChannelOrdered() + suite.coordinator.Setup(testingpath) + + // reset time and block delays to 0, malleate may change to a specific non-zero value. + delayTimePeriod = 0 + delayBlockPeriod = 0 + + // create default proof, merklePath, and value which passes + // may be overwritten by malleate() + key := host.FullClientStateKey(testingpath.EndpointB.ClientID) + merklePath := commitmenttypes.NewMerklePath(string(key)) + merklePath, err := commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + path, err = suite.chainA.Codec.Marshal(&merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + + clientState := testingpath.EndpointB.GetClientState().(*types.ClientState) + value, err = suite.chainB.Codec.MarshalInterface(clientState) + suite.Require().NoError(err) + + tc.malleate() // make changes as necessary + + clientState = testingpath.EndpointA.GetClientState().(*types.ClientState) + + ctx := suite.chainA.GetContext() + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, testingpath.EndpointA.ClientID) + + err = clientState.VerifyMembership( + ctx, store, suite.chainA.Codec, proofHeight, delayTimePeriod, delayBlockPeriod, + proof, path, value, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294 func (suite *TendermintTestSuite) TestVerifyClientConsensusState() { testCases := []struct { name string @@ -268,6 +541,7 @@ func (suite *TendermintTestSuite) TestVerifyClientConsensusState() { // test verification of the connection on chainB being represented in the // light client on chainA +// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294 func (suite *TendermintTestSuite) TestVerifyConnectionState() { var ( clientState *types.ClientState @@ -342,6 +616,7 @@ func (suite *TendermintTestSuite) TestVerifyConnectionState() { // test verification of the channel on chainB being represented in the light // client on chainA +// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294 func (suite *TendermintTestSuite) TestVerifyChannelState() { var ( clientState *types.ClientState @@ -417,6 +692,7 @@ func (suite *TendermintTestSuite) TestVerifyChannelState() { // test verification of the packet commitment on chainB being represented // in the light client on chainA. A send from chainB to chainA is simulated. +// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294 func (suite *TendermintTestSuite) TestVerifyPacketCommitment() { var ( clientState *types.ClientState @@ -531,6 +807,7 @@ func (suite *TendermintTestSuite) TestVerifyPacketCommitment() { // test verification of the acknowledgement on chainB being represented // in the light client on chainA. A send and ack from chainA to chainB // is simulated. +// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294 func (suite *TendermintTestSuite) TestVerifyPacketAcknowledgement() { var ( clientState *types.ClientState @@ -577,7 +854,6 @@ func (suite *TendermintTestSuite) TestVerifyPacketAcknowledgement() { }, expPass: false, }, - { "ApplyPrefix failed", func() { prefix = commitmenttypes.MerklePrefix{} @@ -765,6 +1041,7 @@ func (suite *TendermintTestSuite) TestVerifyPacketReceiptAbsence() { // test verification of the next receive sequence on chainB being represented // in the light client on chainA. A send and receive from chainB to chainA is // simulated. +// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294 func (suite *TendermintTestSuite) TestVerifyNextSeqRecv() { var ( clientState *types.ClientState diff --git a/testing/chain.go b/testing/chain.go index 9e248312bc8..769acd8f7d1 100644 --- a/testing/chain.go +++ b/testing/chain.go @@ -200,11 +200,18 @@ func (chain *TestChain) QueryProof(key []byte) ([]byte, clienttypes.Height) { return chain.QueryProofAtHeight(key, chain.App.LastBlockHeight()) } -// QueryProof performs an abci query with the given key and returns the proto encoded merkle proof -// for the query and the height at which the proof will succeed on a tendermint verifier. +// QueryProofAtHeight performs an abci query with the given key and returns the proto encoded merkle proof +// for the query and the height at which the proof will succeed on a tendermint verifier. Only the IBC +// store is supported func (chain *TestChain) QueryProofAtHeight(key []byte, height int64) ([]byte, clienttypes.Height) { + return chain.QueryProofForStore(host.StoreKey, key, height) +} + +// QueryProofForStore performs an abci query with the given key and returns the proto encoded merkle proof +// for the query and the height at which the proof will succeed on a tendermint verifier. +func (chain *TestChain) QueryProofForStore(storeKey string, key []byte, height int64) ([]byte, clienttypes.Height) { res := chain.App.Query(abci.RequestQuery{ - Path: fmt.Sprintf("store/%s/key", host.StoreKey), + Path: fmt.Sprintf("store/%s/key", storeKey), Height: height - 1, Data: key, Prove: true, From 8f468213fa7adca76d775fb232fb98d2c3295eb4 Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Mon, 9 May 2022 17:19:57 +0200 Subject: [PATCH 37/71] chore: MsgUpdateClient rename header to client_message (#1316) * updating MsgUpdateClient to use client_message field in favour of header * updating clis, field naming, and adding deprecation notices --- docs/ibc/proto-docs.md | 4 +- modules/core/02-client/client/cli/cli.go | 2 +- modules/core/02-client/client/cli/tx.go | 22 ++--- modules/core/02-client/types/msgs.go | 18 ++-- modules/core/02-client/types/msgs_test.go | 2 +- modules/core/02-client/types/tx.pb.go | 100 +++++++++++----------- modules/core/keeper/msg_server.go | 4 +- proto/ibc/core/client/v1/tx.proto | 6 +- 8 files changed, 80 insertions(+), 78 deletions(-) diff --git a/docs/ibc/proto-docs.md b/docs/ibc/proto-docs.md index 30722206dc7..523958ffe83 100644 --- a/docs/ibc/proto-docs.md +++ b/docs/ibc/proto-docs.md @@ -3418,13 +3418,13 @@ type. ### MsgUpdateClient MsgUpdateClient defines an sdk.Msg to update a IBC client state using -the given header. +the given client message. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `client_id` | [string](#string) | | client unique identifier | -| `header` | [google.protobuf.Any](#google.protobuf.Any) | | header to update the light client | +| `client_message` | [google.protobuf.Any](#google.protobuf.Any) | | client message to update the light client | | `signer` | [string](#string) | | signer address | diff --git a/modules/core/02-client/client/cli/cli.go b/modules/core/02-client/client/cli/cli.go index be33557d7ed..2a2546f1ab1 100644 --- a/modules/core/02-client/client/cli/cli.go +++ b/modules/core/02-client/client/cli/cli.go @@ -44,7 +44,7 @@ func NewTxCmd() *cobra.Command { txCmd.AddCommand( NewCreateClientCmd(), NewUpdateClientCmd(), - NewSubmitMisbehaviourCmd(), + NewSubmitMisbehaviourCmd(), // Deprecated NewUpgradeClientCmd(), ) diff --git a/modules/core/02-client/client/cli/tx.go b/modules/core/02-client/client/cli/tx.go index 097baaa5f82..00cee6e5d2a 100644 --- a/modules/core/02-client/client/cli/tx.go +++ b/modules/core/02-client/client/cli/tx.go @@ -86,10 +86,10 @@ func NewCreateClientCmd() *cobra.Command { // NewUpdateClientCmd defines the command to update an IBC client. func NewUpdateClientCmd() *cobra.Command { return &cobra.Command{ - Use: "update [client-id] [path/to/header.json]", - Short: "update existing client with a header", - Long: "update existing client with a header", - Example: fmt.Sprintf("%s tx ibc %s update [client-id] [path/to/header.json] --from node0 --home ../node0/cli --chain-id $CID", version.AppName, types.SubModuleName), + Use: "update [client-id] [path/to/client_msg.json]", + Short: "update existing client with a client message", + Long: "update existing client with a client message, for example a header, misbehaviour or batch update", + Example: fmt.Sprintf("%s tx ibc %s update [client-id] [path/to/client_msg.json] --from node0 --home ../node0/cli --chain-id $CID", version.AppName, types.SubModuleName), Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientTxContext(cmd) @@ -100,22 +100,22 @@ func NewUpdateClientCmd() *cobra.Command { cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) - var header exported.ClientMessage - headerContentOrFileName := args[1] - if err := cdc.UnmarshalInterfaceJSON([]byte(headerContentOrFileName), &header); err != nil { + var clientMsg exported.ClientMessage + clientMsgContentOrFileName := args[1] + if err := cdc.UnmarshalInterfaceJSON([]byte(clientMsgContentOrFileName), &clientMsg); err != nil { // check for file path if JSON input is not provided - contents, err := ioutil.ReadFile(headerContentOrFileName) + contents, err := ioutil.ReadFile(clientMsgContentOrFileName) if err != nil { return fmt.Errorf("neither JSON input nor path to .json file for header were provided: %w", err) } - if err := cdc.UnmarshalInterfaceJSON(contents, &header); err != nil { + if err := cdc.UnmarshalInterfaceJSON(contents, &clientMsg); err != nil { return fmt.Errorf("error unmarshalling header file: %w", err) } } - msg, err := types.NewMsgUpdateClient(clientID, header, clientCtx.GetFromAddress().String()) + msg, err := types.NewMsgUpdateClient(clientID, clientMsg, clientCtx.GetFromAddress().String()) if err != nil { return err } @@ -127,6 +127,8 @@ func NewUpdateClientCmd() *cobra.Command { // NewSubmitMisbehaviourCmd defines the command to submit a misbehaviour to prevent // future updates. +// Deprecated: NewSubmitMisbehaviourCmd is deprecated and will be removed in a future release. +// Please use NewUpdateClientCmd instead. func NewSubmitMisbehaviourCmd() *cobra.Command { return &cobra.Command{ Use: "misbehaviour [clientID] [path/to/misbehaviour.json]", diff --git a/modules/core/02-client/types/msgs.go b/modules/core/02-client/types/msgs.go index 9d2d0f06d60..43d98a93493 100644 --- a/modules/core/02-client/types/msgs.go +++ b/modules/core/02-client/types/msgs.go @@ -101,16 +101,16 @@ func (msg MsgCreateClient) UnpackInterfaces(unpacker codectypes.AnyUnpacker) err // NewMsgUpdateClient creates a new MsgUpdateClient instance //nolint:interfacer -func NewMsgUpdateClient(id string, header exported.ClientMessage, signer string) (*MsgUpdateClient, error) { - anyHeader, err := PackClientMessage(header) +func NewMsgUpdateClient(id string, clientMsg exported.ClientMessage, signer string) (*MsgUpdateClient, error) { + anyClientMsg, err := PackClientMessage(clientMsg) if err != nil { return nil, err } return &MsgUpdateClient{ - ClientId: id, - Header: anyHeader, - Signer: signer, + ClientId: id, + ClientMessage: anyClientMsg, + Signer: signer, }, nil } @@ -120,11 +120,11 @@ func (msg MsgUpdateClient) ValidateBasic() error { if err != nil { return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) } - header, err := UnpackClientMessage(msg.Header) + clientMsg, err := UnpackClientMessage(msg.ClientMessage) if err != nil { return err } - if err := header.ValidateBasic(); err != nil { + if err := clientMsg.ValidateBasic(); err != nil { return err } return host.ClientIdentifierValidator(msg.ClientId) @@ -141,8 +141,8 @@ func (msg MsgUpdateClient) GetSigners() []sdk.AccAddress { // UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces func (msg MsgUpdateClient) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { - var header exported.ClientMessage - return unpacker.UnpackAny(msg.Header, &header) + var clientMsg exported.ClientMessage + return unpacker.UnpackAny(msg.ClientMessage, &clientMsg) } // NewMsgUpgradeClient creates a new MsgUpgradeClient instance diff --git a/modules/core/02-client/types/msgs_test.go b/modules/core/02-client/types/msgs_test.go index 65dcb900ba0..ee5bed3cfb8 100644 --- a/modules/core/02-client/types/msgs_test.go +++ b/modules/core/02-client/types/msgs_test.go @@ -278,7 +278,7 @@ func (suite *TypesTestSuite) TestMsgUpdateClient_ValidateBasic() { { "failed to unpack header", func() { - msg.Header = nil + msg.ClientMessage = nil }, false, }, diff --git a/modules/core/02-client/types/tx.pb.go b/modules/core/02-client/types/tx.pb.go index 97bf12ae73f..1b017c4dfde 100644 --- a/modules/core/02-client/types/tx.pb.go +++ b/modules/core/02-client/types/tx.pb.go @@ -111,12 +111,12 @@ func (m *MsgCreateClientResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgCreateClientResponse proto.InternalMessageInfo // MsgUpdateClient defines an sdk.Msg to update a IBC client state using -// the given header. +// the given client message. type MsgUpdateClient struct { // client unique identifier ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` - // header to update the light client - Header *types.Any `protobuf:"bytes,2,opt,name=header,proto3" json:"header,omitempty"` + // client message to update the light client + ClientMessage *types.Any `protobuf:"bytes,2,opt,name=client_message,json=clientMessage,proto3" json:"client_message,omitempty"` // signer address Signer string `protobuf:"bytes,3,opt,name=signer,proto3" json:"signer,omitempty"` } @@ -376,46 +376,46 @@ func init() { func init() { proto.RegisterFile("ibc/core/client/v1/tx.proto", fileDescriptor_cb5dc4651eb49a04) } var fileDescriptor_cb5dc4651eb49a04 = []byte{ - // 617 bytes of a gzipped FileDescriptorProto + // 621 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0xbf, 0x6e, 0xd3, 0x40, - 0x1c, 0x8e, 0x1b, 0x88, 0xda, 0x6b, 0xa0, 0x95, 0x09, 0xa9, 0xeb, 0xaa, 0x76, 0x64, 0x3a, 0x04, - 0xd1, 0xda, 0x24, 0x59, 0x50, 0xc5, 0x42, 0x3a, 0x31, 0x44, 0x02, 0x57, 0x0c, 0xb0, 0x04, 0xff, - 0xb9, 0x5e, 0x4e, 0xc4, 0xbe, 0xc8, 0x67, 0x47, 0xe4, 0x0d, 0x18, 0x19, 0x78, 0x80, 0x8a, 0x27, - 0xe0, 0x31, 0x18, 0x3b, 0x30, 0x30, 0x45, 0x55, 0xb2, 0x30, 0xe7, 0x09, 0x50, 0x7c, 0x4e, 0xb0, - 0x1d, 0x3b, 0x8a, 0x04, 0x6c, 0x3e, 0xff, 0xbe, 0xfb, 0xbe, 0xdf, 0xe7, 0xef, 0x77, 0x3e, 0x70, - 0x84, 0x4d, 0x4b, 0xb3, 0x88, 0x07, 0x35, 0xab, 0x8f, 0xa1, 0xeb, 0x6b, 0xc3, 0x86, 0xe6, 0x7f, - 0x54, 0x07, 0x1e, 0xf1, 0x09, 0xcf, 0x63, 0xd3, 0x52, 0xe7, 0x45, 0x95, 0x15, 0xd5, 0x61, 0x43, - 0xac, 0x20, 0x82, 0x48, 0x58, 0xd6, 0xe6, 0x4f, 0x0c, 0x29, 0x1e, 0x22, 0x42, 0x50, 0x1f, 0x6a, - 0xe1, 0xca, 0x0c, 0xae, 0x34, 0xc3, 0x1d, 0xb1, 0x92, 0x72, 0xcb, 0x81, 0xbd, 0x0e, 0x45, 0x17, - 0x1e, 0x34, 0x7c, 0x78, 0x11, 0xf2, 0xf0, 0xaf, 0x40, 0x99, 0x31, 0x76, 0xa9, 0x6f, 0xf8, 0x50, - 0xe0, 0x6a, 0x5c, 0x7d, 0xb7, 0x59, 0x51, 0x19, 0x8b, 0xba, 0x60, 0x51, 0x5f, 0xb8, 0xa3, 0xf6, - 0xc1, 0x6c, 0x2c, 0x3f, 0x18, 0x19, 0x4e, 0xff, 0x5c, 0x89, 0xef, 0x51, 0xf4, 0x5d, 0xb6, 0xbc, - 0x9c, 0xaf, 0xf8, 0xb7, 0x60, 0xcf, 0x22, 0x2e, 0x85, 0x2e, 0x0d, 0x68, 0x44, 0xba, 0xb5, 0x86, - 0x54, 0x9c, 0x8d, 0xe5, 0x6a, 0x44, 0x9a, 0xdc, 0xa6, 0xe8, 0xf7, 0x97, 0x6f, 0x18, 0x75, 0x15, - 0x94, 0x28, 0x46, 0x2e, 0xf4, 0x84, 0x62, 0x8d, 0xab, 0xef, 0xe8, 0xd1, 0xea, 0x7c, 0xfb, 0xd3, - 0xb5, 0x5c, 0xf8, 0x75, 0x2d, 0x17, 0x94, 0x43, 0x70, 0x90, 0x72, 0xa8, 0x43, 0x3a, 0x98, 0xb3, - 0x28, 0x5f, 0x98, 0xfb, 0x37, 0x03, 0xfb, 0x8f, 0xfb, 0x06, 0xd8, 0x89, 0x9c, 0x60, 0x3b, 0xb4, - 0xbe, 0xd3, 0xae, 0xcc, 0xc6, 0xf2, 0x7e, 0xc2, 0x24, 0xb6, 0x15, 0x7d, 0x9b, 0x3d, 0xbf, 0xb4, - 0xf9, 0x53, 0x50, 0xea, 0x41, 0xc3, 0x86, 0xde, 0x3a, 0x57, 0x7a, 0x84, 0xd9, 0xb8, 0xe3, 0x78, - 0x57, 0xcb, 0x8e, 0x7f, 0x14, 0xc1, 0x7e, 0x58, 0x43, 0x9e, 0x61, 0xff, 0x45, 0xcb, 0xe9, 0x8c, - 0xb7, 0xfe, 0x47, 0xc6, 0xc5, 0x7f, 0x94, 0xf1, 0x6b, 0x50, 0x19, 0x78, 0x84, 0x5c, 0x75, 0x03, - 0x66, 0xbb, 0xcb, 0x74, 0x85, 0x3b, 0x35, 0xae, 0x5e, 0x6e, 0xcb, 0xb3, 0xb1, 0x7c, 0xc4, 0x98, - 0xb2, 0x50, 0x8a, 0xce, 0x87, 0xaf, 0x93, 0x9f, 0xec, 0x03, 0x38, 0x4e, 0x81, 0x53, 0xbd, 0xdf, - 0x0d, 0xb9, 0xeb, 0xb3, 0xb1, 0x7c, 0x92, 0xc9, 0x9d, 0xee, 0x59, 0x4c, 0x88, 0xe4, 0xcd, 0x68, - 0x29, 0x27, 0x71, 0x11, 0x08, 0xe9, 0x54, 0x97, 0x91, 0x7f, 0xe3, 0xc0, 0xc3, 0x0e, 0x45, 0x97, - 0x81, 0xe9, 0x60, 0xbf, 0x83, 0xa9, 0x09, 0x7b, 0xc6, 0x10, 0x93, 0xc0, 0xe3, 0x5b, 0xab, 0xb9, - 0x57, 0xb3, 0x72, 0x17, 0xb8, 0x58, 0xf2, 0xcf, 0x41, 0xd9, 0x89, 0x91, 0xac, 0x4d, 0x7e, 0x4b, - 0xe0, 0xf4, 0x04, 0x9a, 0x17, 0x93, 0xc3, 0x1b, 0x22, 0x56, 0xed, 0xc8, 0xe0, 0x38, 0xb3, 0xe3, - 0x85, 0xa7, 0xe6, 0xd7, 0x22, 0x28, 0x76, 0x28, 0xe2, 0xdf, 0x83, 0x72, 0xe2, 0xd7, 0xf3, 0x48, - 0x5d, 0xfd, 0xa9, 0xa9, 0xa9, 0xd3, 0x2b, 0x3e, 0xd9, 0x00, 0xb4, 0x50, 0x9a, 0x2b, 0x24, 0x8e, - 0x77, 0x9e, 0x42, 0x1c, 0x94, 0xab, 0x90, 0x75, 0x24, 0x79, 0x0b, 0xdc, 0x4b, 0xce, 0xd6, 0x49, - 0xee, 0xee, 0x18, 0x4a, 0x3c, 0xdd, 0x04, 0xb5, 0x14, 0xf1, 0x00, 0x9f, 0x31, 0x00, 0x8f, 0x73, - 0x38, 0x56, 0xa1, 0x62, 0x63, 0x63, 0xe8, 0x42, 0xb3, 0xad, 0x7f, 0x9f, 0x48, 0xdc, 0xcd, 0x44, - 0xe2, 0x6e, 0x27, 0x12, 0xf7, 0x79, 0x2a, 0x15, 0x6e, 0xa6, 0x52, 0xe1, 0xe7, 0x54, 0x2a, 0xbc, - 0x7b, 0x86, 0xb0, 0xdf, 0x0b, 0x4c, 0xd5, 0x22, 0x8e, 0x66, 0x11, 0xea, 0x10, 0xaa, 0x61, 0xd3, - 0x3a, 0x43, 0x44, 0x1b, 0xb6, 0x34, 0x87, 0xd8, 0x41, 0x1f, 0x52, 0x76, 0x6f, 0x3d, 0x6d, 0x9e, - 0x45, 0x57, 0x97, 0x3f, 0x1a, 0x40, 0x6a, 0x96, 0xc2, 0xf9, 0x6a, 0xfd, 0x0e, 0x00, 0x00, 0xff, - 0xff, 0x47, 0x75, 0x66, 0xe0, 0xda, 0x06, 0x00, 0x00, + 0x1c, 0x8e, 0x1b, 0xa8, 0x9a, 0x6b, 0xfa, 0x47, 0x26, 0xa4, 0xae, 0xab, 0xda, 0x91, 0xe9, 0x10, + 0x04, 0xb5, 0x49, 0xb2, 0xa0, 0xc2, 0x42, 0x3a, 0x31, 0x44, 0x02, 0x57, 0x0c, 0xb0, 0x04, 0xff, + 0xb9, 0x5e, 0x4f, 0xc4, 0xbe, 0xc8, 0x67, 0x47, 0xe4, 0x0d, 0x18, 0x79, 0x84, 0x0a, 0x5e, 0x80, + 0xc7, 0x60, 0xec, 0xc0, 0xc0, 0x14, 0x55, 0xc9, 0xc2, 0x9c, 0x27, 0x40, 0xf1, 0x39, 0xc1, 0x76, + 0xec, 0x28, 0x12, 0xb0, 0xf9, 0xee, 0xf7, 0xdd, 0xf7, 0xfd, 0x3e, 0x7f, 0xbf, 0xb3, 0xc1, 0x11, + 0x36, 0x2d, 0xcd, 0x22, 0x1e, 0xd4, 0xac, 0x1e, 0x86, 0xae, 0xaf, 0x0d, 0x1a, 0x9a, 0xff, 0x51, + 0xed, 0x7b, 0xc4, 0x27, 0x3c, 0x8f, 0x4d, 0x4b, 0x9d, 0x15, 0x55, 0x56, 0x54, 0x07, 0x0d, 0xb1, + 0x82, 0x08, 0x22, 0x61, 0x59, 0x9b, 0x3d, 0x31, 0xa4, 0x78, 0x88, 0x08, 0x41, 0x3d, 0xa8, 0x85, + 0x2b, 0x33, 0xb8, 0xd4, 0x0c, 0x77, 0xc8, 0x4a, 0xca, 0x2d, 0x07, 0xf6, 0x3a, 0x14, 0x9d, 0x7b, + 0xd0, 0xf0, 0xe1, 0x79, 0xc8, 0xc3, 0xbf, 0x02, 0x65, 0xc6, 0xd8, 0xa5, 0xbe, 0xe1, 0x43, 0x81, + 0xab, 0x71, 0xf5, 0xed, 0x66, 0x45, 0x65, 0x2c, 0xea, 0x9c, 0x45, 0x7d, 0xe1, 0x0e, 0xdb, 0x07, + 0xd3, 0x91, 0x7c, 0x6f, 0x68, 0x38, 0xbd, 0x33, 0x25, 0x7e, 0x46, 0xd1, 0xb7, 0xd9, 0xf2, 0x62, + 0xb6, 0xe2, 0xdf, 0x82, 0x3d, 0x8b, 0xb8, 0x14, 0xba, 0x34, 0xa0, 0x11, 0xe9, 0xc6, 0x0a, 0x52, + 0x71, 0x3a, 0x92, 0xab, 0x11, 0x69, 0xf2, 0x98, 0xa2, 0xef, 0x2e, 0x76, 0x18, 0x75, 0x15, 0x6c, + 0x52, 0x8c, 0x5c, 0xe8, 0x09, 0xc5, 0x1a, 0x57, 0x2f, 0xe9, 0xd1, 0xea, 0x6c, 0xeb, 0xd3, 0xb5, + 0x5c, 0xf8, 0x75, 0x2d, 0x17, 0x94, 0x43, 0x70, 0x90, 0x72, 0xa8, 0x43, 0xda, 0x9f, 0xb1, 0x28, + 0x5f, 0x99, 0xfb, 0x37, 0x7d, 0xfb, 0x8f, 0xfb, 0x06, 0x28, 0x45, 0x4e, 0xb0, 0x1d, 0x5a, 0x2f, + 0xb5, 0x2b, 0xd3, 0x91, 0xbc, 0x9f, 0x30, 0x89, 0x6d, 0x45, 0xdf, 0x62, 0xcf, 0x2f, 0x6d, 0xfe, + 0x19, 0xd8, 0x8d, 0xf6, 0x1d, 0x48, 0xa9, 0x81, 0x56, 0xba, 0xd3, 0x77, 0x18, 0xb6, 0xc3, 0xa0, + 0x6b, 0x1b, 0x88, 0x37, 0xb9, 0x30, 0xf0, 0xa3, 0x08, 0xf6, 0xc3, 0x1a, 0xf2, 0x0c, 0xfb, 0x2f, + 0x1c, 0xa4, 0x23, 0xdf, 0xf8, 0x1f, 0x91, 0x17, 0xff, 0x51, 0xe4, 0xaf, 0x41, 0xa5, 0xef, 0x11, + 0x72, 0xd9, 0x0d, 0x98, 0xed, 0x2e, 0xd3, 0x15, 0xee, 0xd4, 0xb8, 0x7a, 0xb9, 0x2d, 0x4f, 0x47, + 0xf2, 0x11, 0x63, 0xca, 0x42, 0x29, 0x3a, 0x1f, 0x6e, 0x27, 0x5f, 0xd9, 0x07, 0x70, 0x9c, 0x02, + 0xa7, 0x7a, 0xbf, 0x1b, 0x72, 0xd7, 0xa7, 0x23, 0xf9, 0x24, 0x93, 0x3b, 0xdd, 0xb3, 0x98, 0x10, + 0xc9, 0x1b, 0xd9, 0xcd, 0x9c, 0xc4, 0x45, 0x20, 0xa4, 0x53, 0x5d, 0x44, 0xfe, 0x8d, 0x03, 0xf7, + 0x3b, 0x14, 0x5d, 0x04, 0xa6, 0x83, 0xfd, 0x0e, 0xa6, 0x26, 0xbc, 0x32, 0x06, 0x98, 0x04, 0x1e, + 0xdf, 0x5a, 0xce, 0xbd, 0x9a, 0x95, 0xbb, 0xc0, 0xc5, 0x92, 0x7f, 0x0e, 0xca, 0x4e, 0x8c, 0x64, + 0x65, 0xf2, 0x1b, 0x02, 0xa7, 0x27, 0xd0, 0xbc, 0x98, 0x1c, 0xde, 0x10, 0xb1, 0x6c, 0x47, 0x06, + 0xc7, 0x99, 0x1d, 0xcf, 0x3d, 0x35, 0xbf, 0x14, 0x41, 0xb1, 0x43, 0x11, 0xff, 0x1e, 0x94, 0x13, + 0x5f, 0xa2, 0x07, 0xea, 0xf2, 0x37, 0x4e, 0x4d, 0x5d, 0x66, 0xf1, 0xd1, 0x1a, 0xa0, 0xb9, 0xd2, + 0x4c, 0x21, 0x71, 0xdb, 0xf3, 0x14, 0xe2, 0xa0, 0x5c, 0x85, 0xac, 0x2b, 0xc9, 0x5b, 0x60, 0x27, + 0x39, 0x5b, 0x27, 0xb9, 0xa7, 0x63, 0x28, 0xf1, 0xf1, 0x3a, 0xa8, 0x85, 0x88, 0x07, 0xf8, 0x8c, + 0x01, 0x78, 0x98, 0xc3, 0xb1, 0x0c, 0x15, 0x1b, 0x6b, 0x43, 0xe7, 0x9a, 0x6d, 0xfd, 0xfb, 0x58, + 0xe2, 0x6e, 0xc6, 0x12, 0x77, 0x3b, 0x96, 0xb8, 0xcf, 0x13, 0xa9, 0x70, 0x33, 0x91, 0x0a, 0x3f, + 0x27, 0x52, 0xe1, 0xdd, 0x53, 0x84, 0xfd, 0xab, 0xc0, 0x54, 0x2d, 0xe2, 0x68, 0x16, 0xa1, 0x0e, + 0xa1, 0x1a, 0x36, 0xad, 0x53, 0x44, 0xb4, 0x41, 0x4b, 0x73, 0x88, 0x1d, 0xf4, 0x20, 0x65, 0xbf, + 0xb1, 0x27, 0xcd, 0xd3, 0xe8, 0x4f, 0xe6, 0x0f, 0xfb, 0x90, 0x9a, 0x9b, 0xe1, 0x7c, 0xb5, 0x7e, + 0x07, 0x00, 0x00, 0xff, 0xff, 0x46, 0x72, 0x93, 0xc2, 0xe9, 0x06, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -718,9 +718,9 @@ func (m *MsgUpdateClient) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x1a } - if m.Header != nil { + if m.ClientMessage != nil { { - size, err := m.Header.MarshalToSizedBuffer(dAtA[:i]) + size, err := m.ClientMessage.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -984,8 +984,8 @@ func (m *MsgUpdateClient) Size() (n int) { if l > 0 { n += 1 + l + sovTx(uint64(l)) } - if m.Header != nil { - l = m.Header.Size() + if m.ClientMessage != nil { + l = m.ClientMessage.Size() n += 1 + l + sovTx(uint64(l)) } l = len(m.Signer) @@ -1349,7 +1349,7 @@ func (m *MsgUpdateClient) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ClientMessage", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -1376,10 +1376,10 @@ func (m *MsgUpdateClient) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.Header == nil { - m.Header = &types.Any{} + if m.ClientMessage == nil { + m.ClientMessage = &types.Any{} } - if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.ClientMessage.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex diff --git a/modules/core/keeper/msg_server.go b/modules/core/keeper/msg_server.go index c7a8e87dffe..fd7ccbdff64 100644 --- a/modules/core/keeper/msg_server.go +++ b/modules/core/keeper/msg_server.go @@ -45,12 +45,12 @@ func (k Keeper) CreateClient(goCtx context.Context, msg *clienttypes.MsgCreateCl func (k Keeper) UpdateClient(goCtx context.Context, msg *clienttypes.MsgUpdateClient) (*clienttypes.MsgUpdateClientResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - header, err := clienttypes.UnpackClientMessage(msg.Header) + clientMsg, err := clienttypes.UnpackClientMessage(msg.ClientMessage) if err != nil { return nil, err } - if err = k.ClientKeeper.UpdateClient(ctx, msg.ClientId, header); err != nil { + if err = k.ClientKeeper.UpdateClient(ctx, msg.ClientId, clientMsg); err != nil { return nil, err } diff --git a/proto/ibc/core/client/v1/tx.proto b/proto/ibc/core/client/v1/tx.proto index 51f03780c75..386bf9a51ee 100644 --- a/proto/ibc/core/client/v1/tx.proto +++ b/proto/ibc/core/client/v1/tx.proto @@ -40,15 +40,15 @@ message MsgCreateClient { message MsgCreateClientResponse {} // MsgUpdateClient defines an sdk.Msg to update a IBC client state using -// the given header. +// the given client message. message MsgUpdateClient { option (gogoproto.equal) = false; option (gogoproto.goproto_getters) = false; // client unique identifier string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; - // header to update the light client - google.protobuf.Any header = 2; + // client message to update the light client + google.protobuf.Any client_message = 2; // signer address string signer = 3; } From e1f2103cec0f4ef288fdcbffaec4b8c9e798c637 Mon Sep 17 00:00:00 2001 From: khanh <50263489+catShaark@users.noreply.github.com> Date: Tue, 10 May 2022 16:41:52 +0700 Subject: [PATCH 38/71] Add migration docs for 02-client refactor (#1287) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add changes on : ClientState, Header, Misbehaviour, ConsensState, light client impl to migration doc * Update docs/migrations/v3-to-v4.md Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * Update docs/migrations/v3-to-v4.md Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * Update docs/migrations/v3-to-v4.md Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * Update docs/migrations/v3-to-v4.md Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * Update docs/migrations/v3-to-v4.md Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * Update docs/migrations/v3-to-v4.md Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * add more entries and more description * Update docs/migrations/v3-to-v4.md Co-authored-by: Carlos Rodriguez * Update docs/migrations/v3-to-v4.md Co-authored-by: Carlos Rodriguez * Update docs/migrations/v3-to-v4.md Co-authored-by: Carlos Rodriguez * Update docs/migrations/v3-to-v4.md Co-authored-by: Carlos Rodriguez * Update docs/migrations/v3-to-v4.md Co-authored-by: Carlos Rodriguez * Update docs/migrations/v3-to-v4.md Co-authored-by: Carlos Rodriguez * Update docs/migrations/v3-to-v4.md Co-authored-by: Carlos Rodriguez * Update docs/migrations/v3-to-v4.md Co-authored-by: Carlos Rodriguez * Update docs/migrations/v3-to-v4.md Co-authored-by: Sean King * Update docs/migrations/v3-to-v4.md Co-authored-by: Carlos Rodriguez * update migration doc * update migration doc * update migration doc Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> Co-authored-by: Carlos Rodriguez Co-authored-by: Sean King --- docs/migrations/v3-to-v4.md | 50 +++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/docs/migrations/v3-to-v4.md b/docs/migrations/v3-to-v4.md index f933a7380f7..f558e25ab60 100644 --- a/docs/migrations/v3-to-v4.md +++ b/docs/migrations/v3-to-v4.md @@ -18,13 +18,53 @@ No genesis or in-place migrations required when upgrading from v1 or v2 of ibc-g ## Chains -### IS04 - Channel +### IS04 - Channel -The `WriteAcknowledgement` API now takes the `exported.Acknowledgement` type instead of passing in the acknowledgement byte array directly. -This is an API breaking change and as such IBC application developers will have to update any calls to `WriteAcknowledgement`. +The `WriteAcknowledgement` API now takes the `exported.Acknowledgement` type instead of passing in the acknowledgement byte array directly. +This is an API breaking change and as such IBC application developers will have to update any calls to `WriteAcknowledgement`. ## IBC Light Clients -The `VerifyUpgradeAndUpdateState` function has been modified. The client state and consensus state return value has been removed. +### `ClientState` interface changes -Light clients **must** set the updated client state and consensus state in the client store after verifying a valid client upgrade. +The `VerifyUpgradeAndUpdateState` function has been modified. The client state and consensus state return values have been removed. + +Light clients **must** handle all management of client and consensus states including the setting of updated client state and consensus state in the client store. + +The `CheckHeaderAndUpdateState` function has been split into 4 new functions: + +- `VerifyClientMessage` verifies a `ClientMessage`. A `ClientMessage` could be a `Header`, `Misbehaviour`, or batch update. Calls to `CheckForMisbehaviour`, `UpdateState`, and `UpdateStateOnMisbehaviour` will assume that the content of the `ClientMessage` has been verified and can be trusted. An error should be returned if the `ClientMessage` fails to verify. + +- `CheckForMisbehaviour` checks for evidence of a misbehaviour in `Header` or `Misbehaviour` types. + +- `UpdateStateOnMisbehaviour` performs appropriate state changes on a `ClientState` given that misbehaviour has been detected and verified. + +- `UpdateState` updates and stores as necessary any associated information for an IBC client, such as the `ClientState` and corresponding `ConsensusState`. An error is returned if `ClientMessage` is of type `Misbehaviour`. Upon successful update, a list containing the updated consensus state height is returned. + +The `CheckMisbehaviourAndUpdateState` function has been removed from `ClientState` interface. This functionality is now encapsulated by the usage of `VerifyClientMessage`, `CheckForMisbehaviour`, `UpdateStateOnMisbehaviour`, `UpdateState`. + +The function `GetTimestampAtHeight` has been added to the `ClientState` interface. It should return the timestamp for a consensus state associated with the provided height. + +### `Header` and `Misbehaviour` + +`exported.Header` and `exported.Misbehaviour` interface types have been merged and renamed to `ClientMessage` interface. + +`GetHeight` function has been removed from `exported.Header` and thus is not included in the `ClientMessage` interface + +### `ConsensusState` + +The `GetRoot` function has been removed from consensus state interface since it was not used by core IBC. + +### Light client implementations + +09-localhost light client implementation has been removed because it is currently non-functional. + +### Client Keeper + +Keeper function `CheckMisbehaviourAndUpdateState` has been removed since function `UpdateClient` can now handle updating `ClientState` on `ClientMessage` type which can be any `Misbehaviour` implementations. + +### SDK Message + +`MsgSubmitMisbehaviour` is deprecated since `MsgUpdateClient` can now submit a `ClientMessage` type which can be any `Misbehaviour` implementations. + +The field `header` in `MsgUpdateClient` has been renamed to `client message`. From 31b6ead70a68c2b263d0db44c5aeb6d50149585d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?colin=20axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Tue, 10 May 2022 12:40:14 +0200 Subject: [PATCH 39/71] Add revision number tests for 07-tendermint (#1302) * add revision tests for VerifyClientMessage adds UpgradeChain which manually upgrades the chainID to the next revision adds test cases for VerifyClientMessage in relation to a changing revision number * fix revision tests * update revision tests, use revision format as default in testing * remove hard coding from upgrade tests * fix misbehaviour handle tests * add changelog entry * fix client state tests * disable updates to previous revisions * fix hard coded tests * add test case for unsuccessful update to previous revision * Update modules/light-clients/07-tendermint/types/update_test.go Co-authored-by: Aditya * add strings trim space * add tests for non revision chainIDs Co-authored-by: Aditya --- CHANGELOG.md | 1 + .../host/keeper/relay_test.go | 4 +- modules/apps/29-fee/transfer_test.go | 2 +- .../apps/transfer/keeper/mbt_relay_test.go | 4 +- modules/apps/transfer/keeper/relay_test.go | 16 +- modules/apps/transfer/transfer_test.go | 2 +- modules/core/02-client/keeper/client_test.go | 40 +- modules/core/02-client/keeper/keeper_test.go | 6 +- modules/core/02-client/types/codec_test.go | 2 +- modules/core/02-client/types/genesis_test.go | 39 +- .../core/03-connection/keeper/verify_test.go | 6 +- modules/core/04-channel/keeper/packet_test.go | 2 +- modules/core/ante/ante_test.go | 8 +- modules/core/genesis_test.go | 2 +- modules/core/keeper/msg_server_test.go | 25 +- .../07-tendermint/types/client_state_test.go | 14 +- .../types/misbehaviour_handle.go | 2 + .../types/misbehaviour_handle_test.go | 429 ++++++++++++++++-- .../07-tendermint/types/update.go | 14 +- .../07-tendermint/types/update_test.go | 123 ++--- .../07-tendermint/types/upgrade_test.go | 27 +- testing/coordinator.go | 6 +- testing/endpoint.go | 54 +++ 23 files changed, 617 insertions(+), 211 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d243bcb0286..9a43567ede3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,6 +64,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (modules/core/02-client) [\#1195](https://github.com/cosmos/ibc-go/pull/1210) Removing `CheckHeaderAndUpdateState` from `ClientState` interface & associated light client implementations. * (modules/core/02-client) [\#1189](https://github.com/cosmos/ibc-go/pull/1212) Removing `CheckMisbehaviourAndUpdateState` from `ClientState` interface & associated light client implementations. * (modules/core/exported) [\#1206](https://github.com/cosmos/ibc-go/pull/1206) Adding new method `UpdateState` to `ClientState` interface. +* (testing) [\#1302](https://github.com/cosmos/ibc-go/pull/1302) Change testchain default behaviour to use a chainID in the revision format. Set `ChainIDSuffix` to an empty string to disable this functionality. ### Features diff --git a/modules/apps/27-interchain-accounts/host/keeper/relay_test.go b/modules/apps/27-interchain-accounts/host/keeper/relay_test.go index fda31c34ef0..0b4e8161317 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/relay_test.go +++ b/modules/apps/27-interchain-accounts/host/keeper/relay_test.go @@ -264,7 +264,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { Token: sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)), Sender: interchainAccountAddr, Receiver: suite.chainA.SenderAccount.GetAddress().String(), - TimeoutHeight: clienttypes.NewHeight(0, 100), + TimeoutHeight: clienttypes.NewHeight(1, 100), TimeoutTimestamp: uint64(0), } @@ -420,7 +420,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, - clienttypes.NewHeight(0, 100), + clienttypes.NewHeight(1, 100), 0, ) diff --git a/modules/apps/29-fee/transfer_test.go b/modules/apps/29-fee/transfer_test.go index 9d7557fd6c4..f26183797ed 100644 --- a/modules/apps/29-fee/transfer_test.go +++ b/modules/apps/29-fee/transfer_test.go @@ -30,7 +30,7 @@ func (suite *FeeTestSuite) TestFeeTransfer() { msgs := []sdk.Msg{ types.NewMsgPayPacketFee(fee, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, suite.chainA.SenderAccount.GetAddress().String(), nil), - transfertypes.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, coin, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), clienttypes.NewHeight(0, 100), 0), + transfertypes.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, coin, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), clienttypes.NewHeight(1, 100), 0), } res, err := suite.chainA.SendMsgs(msgs...) suite.Require().NoError(err) // message committed diff --git a/modules/apps/transfer/keeper/mbt_relay_test.go b/modules/apps/transfer/keeper/mbt_relay_test.go index d4d338bf73a..3caef9aa5f9 100644 --- a/modules/apps/transfer/keeper/mbt_relay_test.go +++ b/modules/apps/transfer/keeper/mbt_relay_test.go @@ -312,7 +312,7 @@ func (suite *KeeperTestSuite) TestModelBasedRelay() { description := file_info.Name() + " # " + strconv.Itoa(i+1) suite.Run(fmt.Sprintf("Case %s", description), func() { seq := uint64(1) - packet := channeltypes.NewPacket(tc.packet.Data.GetBytes(), seq, tc.packet.SourcePort, tc.packet.SourceChannel, tc.packet.DestPort, tc.packet.DestChannel, clienttypes.NewHeight(0, 100), 0) + packet := channeltypes.NewPacket(tc.packet.Data.GetBytes(), seq, tc.packet.SourcePort, tc.packet.SourceChannel, tc.packet.DestPort, tc.packet.DestChannel, clienttypes.NewHeight(1, 100), 0) bankBefore := BankFromBalances(tc.bankBefore) realBankBefore := BankOfChain(suite.chainB) // First validate the packet itself (mimics what happens when the packet is being sent and/or received) @@ -344,7 +344,7 @@ func (suite *KeeperTestSuite) TestModelBasedRelay() { sdk.NewCoin(denom, amount), sender, tc.packet.Data.Receiver, - clienttypes.NewHeight(0, 110), + clienttypes.NewHeight(1, 110), 0) } case "OnRecvPacket": diff --git a/modules/apps/transfer/keeper/relay_test.go b/modules/apps/transfer/keeper/relay_test.go index ce34f316669..e76c1c1fb29 100644 --- a/modules/apps/transfer/keeper/relay_test.go +++ b/modules/apps/transfer/keeper/relay_test.go @@ -97,13 +97,13 @@ func (suite *KeeperTestSuite) TestSendTransfer() { if !tc.sendFromSource { // send coin from chainB to chainA coinFromBToA := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) - transferMsg := types.NewMsgTransfer(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, coinFromBToA, suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), clienttypes.NewHeight(0, 110), 0) + transferMsg := types.NewMsgTransfer(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, coinFromBToA, suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), clienttypes.NewHeight(1, 110), 0) _, err = suite.chainB.SendMsgs(transferMsg) suite.Require().NoError(err) // message committed // receive coin on chainA from chainB fungibleTokenPacket := types.NewFungibleTokenPacketData(coinFromBToA.Denom, coinFromBToA.Amount.String(), suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String()) - packet := channeltypes.NewPacket(fungibleTokenPacket.GetBytes(), 1, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, clienttypes.NewHeight(0, 110), 0) + packet := channeltypes.NewPacket(fungibleTokenPacket.GetBytes(), 1, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, clienttypes.NewHeight(1, 110), 0) // get proof of packet commitment from chainB err = path.EndpointA.UpdateClient() @@ -118,7 +118,7 @@ func (suite *KeeperTestSuite) TestSendTransfer() { err = suite.chainA.GetSimApp().TransferKeeper.SendTransfer( suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, amount, - suite.chainA.SenderAccount.GetAddress(), suite.chainB.SenderAccount.GetAddress().String(), clienttypes.NewHeight(0, 110), 0, + suite.chainA.SenderAccount.GetAddress(), suite.chainB.SenderAccount.GetAddress().String(), clienttypes.NewHeight(1, 110), 0, ) if tc.expPass { @@ -195,7 +195,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { if tc.recvIsSource { // send coin from chainB to chainA, receive them, acknowledge them, and send back to chainB coinFromBToA := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) - transferMsg := types.NewMsgTransfer(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, coinFromBToA, suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), clienttypes.NewHeight(0, 110), 0) + transferMsg := types.NewMsgTransfer(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, coinFromBToA, suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), clienttypes.NewHeight(1, 110), 0) res, err := suite.chainB.SendMsgs(transferMsg) suite.Require().NoError(err) // message committed @@ -214,14 +214,14 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { } // send coin from chainA to chainB - transferMsg := types.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, sdk.NewCoin(trace.IBCDenom(), amount), suite.chainA.SenderAccount.GetAddress().String(), receiver, clienttypes.NewHeight(0, 110), 0) + transferMsg := types.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, sdk.NewCoin(trace.IBCDenom(), amount), suite.chainA.SenderAccount.GetAddress().String(), receiver, clienttypes.NewHeight(1, 110), 0) _, err := suite.chainA.SendMsgs(transferMsg) suite.Require().NoError(err) // message committed tc.malleate() data := types.NewFungibleTokenPacketData(trace.GetFullDenomPath(), amount.String(), suite.chainA.SenderAccount.GetAddress().String(), receiver) - packet := channeltypes.NewPacket(data.GetBytes(), seq, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(0, 100), 0) + packet := channeltypes.NewPacket(data.GetBytes(), seq, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) err = suite.chainB.GetSimApp().TransferKeeper.OnRecvPacket(suite.chainB.GetContext(), packet, data) @@ -290,7 +290,7 @@ func (suite *KeeperTestSuite) TestOnAcknowledgementPacket() { tc.malleate() data := types.NewFungibleTokenPacketData(trace.GetFullDenomPath(), amount.String(), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String()) - packet := channeltypes.NewPacket(data.GetBytes(), 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(0, 100), 0) + packet := channeltypes.NewPacket(data.GetBytes(), 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) preCoin := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), trace.IBCDenom()) @@ -376,7 +376,7 @@ func (suite *KeeperTestSuite) TestOnTimeoutPacket() { tc.malleate() data := types.NewFungibleTokenPacketData(trace.GetFullDenomPath(), amount.String(), sender, suite.chainB.SenderAccount.GetAddress().String()) - packet := channeltypes.NewPacket(data.GetBytes(), 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(0, 100), 0) + packet := channeltypes.NewPacket(data.GetBytes(), 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) preCoin := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), trace.IBCDenom()) diff --git a/modules/apps/transfer/transfer_test.go b/modules/apps/transfer/transfer_test.go index 5190cdc8d29..5402f0573bb 100644 --- a/modules/apps/transfer/transfer_test.go +++ b/modules/apps/transfer/transfer_test.go @@ -47,7 +47,7 @@ func (suite *TransferTestSuite) TestHandleMsgTransfer() { suite.coordinator.Setup(path) // originalBalance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), sdk.DefaultBondDenom) - timeoutHeight := clienttypes.NewHeight(0, 110) + timeoutHeight := clienttypes.NewHeight(1, 110) amount, ok := sdk.NewIntFromString("9223372036854775808") // 2^63 (one above int64) suite.Require().True(ok) diff --git a/modules/core/02-client/keeper/client_test.go b/modules/core/02-client/keeper/client_test.go index 2061bb84a4a..5f84c62b255 100644 --- a/modules/core/02-client/keeper/client_test.go +++ b/modules/core/02-client/keeper/client_test.go @@ -98,7 +98,7 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { {"valid duplicate update", func() { clientID := path.EndpointA.ClientID - height1 := types.NewHeight(0, 1) + height1 := types.NewHeight(1, 1) // store previous consensus state prevConsState := &ibctmtypes.ConsensusState{ @@ -107,7 +107,7 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { } suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), clientID, height1, prevConsState) - height5 := types.NewHeight(0, 5) + height5 := types.NewHeight(1, 5) // store next consensus state to check that trustedHeight does not need to be hightest consensus state before header height nextConsState := &ibctmtypes.ConsensusState{ Timestamp: suite.past.Add(time.Minute), @@ -115,7 +115,7 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { } suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), clientID, height5, nextConsState) - height3 := types.NewHeight(0, 3) + height3 := types.NewHeight(1, 3) // updateHeader will fill in consensus state between prevConsState and suite.consState // clientState should not be updated updateHeader = createPastUpdateFn(height3, height1) @@ -125,7 +125,7 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { {"misbehaviour detection: conflicting header", func() { clientID := path.EndpointA.ClientID - height1 := types.NewHeight(0, 1) + height1 := types.NewHeight(1, 1) // store previous consensus state prevConsState := &ibctmtypes.ConsensusState{ Timestamp: suite.past, @@ -133,7 +133,7 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { } suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), clientID, height1, prevConsState) - height5 := types.NewHeight(0, 5) + height5 := types.NewHeight(1, 5) // store next consensus state to check that trustedHeight does not need to be hightest consensus state before header height nextConsState := &ibctmtypes.ConsensusState{ Timestamp: suite.past.Add(time.Minute), @@ -141,7 +141,7 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { } suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), clientID, height5, nextConsState) - height3 := types.NewHeight(0, 3) + height3 := types.NewHeight(1, 3) // updateHeader will fill in consensus state between prevConsState and suite.consState // clientState should not be updated updateHeader = createPastUpdateFn(height3, height1) @@ -188,7 +188,7 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { }, false, false}, {"client is not active", func() { clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) - clientState.FrozenHeight = types.NewHeight(0, 1) + clientState.FrozenHeight = types.NewHeight(1, 1) suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID, clientState) updateHeader = createFutureUpdateFn(clientState.GetLatestHeight().(types.Height)) }, false, false}, @@ -258,7 +258,6 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { lastHeight exported.Height proofUpgradedClient, proofUpgradedConsState []byte upgradedClientBz, upgradedConsStateBz []byte - err error ) testCases := []struct { @@ -270,14 +269,13 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { name: "successful upgrade", setup: func() { // last Height is at next block - lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) + lastHeight = clienttypes.NewHeight(1, uint64(suite.chainB.GetContext().BlockHeight()+1)) // zero custom fields and store in upgrade store suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) // commit upgrade store changes and update clients - suite.coordinator.CommitBlock(suite.chainB) err := path.EndpointA.UpdateClient() suite.Require().NoError(err) @@ -294,7 +292,7 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { name: "client state not found", setup: func() { // last Height is at next block - lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) + lastHeight = clienttypes.NewHeight(1, uint64(suite.chainB.GetContext().BlockHeight()+1)) // zero custom fields and store in upgrade store suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) @@ -322,7 +320,7 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { // client is frozen // last Height is at next block - lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) + lastHeight = clienttypes.NewHeight(1, uint64(suite.chainB.GetContext().BlockHeight()+1)) // zero custom fields and store in upgrade store suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) @@ -343,7 +341,7 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { // set frozen client in store tmClient, ok := cs.(*ibctmtypes.ClientState) suite.Require().True(ok) - tmClient.FrozenHeight = types.NewHeight(0, 1) + tmClient.FrozenHeight = types.NewHeight(1, 1) suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID, tmClient) }, expPass: false, @@ -352,14 +350,16 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { name: "tendermint client VerifyUpgrade fails", setup: func() { // last Height is at next block - lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) + lastHeight = clienttypes.NewHeight(1, uint64(suite.chainB.GetContext().BlockHeight()+1)) // zero custom fields and store in upgrade store suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) // change upgradedClient client-specified parameters - upgradedClient = ibctmtypes.NewClientState("wrongchainID", ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, true, true) + tmClient := upgradedClient.(*ibctmtypes.ClientState) + tmClient.ChainId = "wrongchainID" + upgradedClient = tmClient suite.coordinator.CommitBlock(suite.chainB) err := path.EndpointA.UpdateClient() @@ -379,7 +379,14 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { tc := tc path = ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupClients(path) - upgradedClient = ibctmtypes.NewClientState("newChainId-1", ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + + clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) + revisionNumber := clienttypes.ParseChainID(clientState.ChainId) + + newChainID, err := clienttypes.SetRevisionNumber(clientState.ChainId, revisionNumber+1) + suite.Require().NoError(err) + + upgradedClient = ibctmtypes.NewClientState(newChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, clienttypes.NewHeight(revisionNumber+1, clientState.GetLatestHeight().GetRevisionHeight()+1), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) upgradedClient = upgradedClient.ZeroCustomFields() upgradedClientBz, err = types.MarshalClientState(suite.chainA.App.AppCodec(), upgradedClient) suite.Require().NoError(err) @@ -409,6 +416,7 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { func (suite *KeeperTestSuite) TestUpdateClientEventEmission() { path := ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupClients(path) + header, err := suite.chainA.ConstructUpdateTMClientHeader(suite.chainB, path.EndpointA.ClientID) suite.Require().NoError(err) diff --git a/modules/core/02-client/keeper/keeper_test.go b/modules/core/02-client/keeper/keeper_test.go index f56607e4dcf..83e017dd8a4 100644 --- a/modules/core/02-client/keeper/keeper_test.go +++ b/modules/core/02-client/keeper/keeper_test.go @@ -44,7 +44,6 @@ const ( var ( testClientHeight = types.NewHeight(0, 5) testClientHeightRevision1 = types.NewHeight(1, 5) - newClientHeight = types.NewHeight(1, 1) ) type KeeperTestSuite struct { @@ -156,7 +155,8 @@ func (suite *KeeperTestSuite) TestSetClientConsensusState() { } func (suite *KeeperTestSuite) TestValidateSelfClient() { - testClientHeight := types.NewHeight(0, uint64(suite.chainA.GetContext().BlockHeight()-1)) + testClientHeight := types.GetSelfHeight(suite.chainA.GetContext()) + testClientHeight.RevisionHeight-- testCases := []struct { name string @@ -185,7 +185,7 @@ func (suite *KeeperTestSuite) TestValidateSelfClient() { }, { "invalid client height", - ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.NewHeight(0, uint64(suite.chainA.GetContext().BlockHeight())), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.GetSelfHeight(suite.chainA.GetContext()).Increment().(types.Height), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), false, }, { diff --git a/modules/core/02-client/types/codec_test.go b/modules/core/02-client/types/codec_test.go index 197114a7be0..7ff2455f507 100644 --- a/modules/core/02-client/types/codec_test.go +++ b/modules/core/02-client/types/codec_test.go @@ -30,7 +30,7 @@ func (suite *TypesTestSuite) TestPackClientState() { }, { "tendermint client", - ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), true, }, { diff --git a/modules/core/02-client/types/genesis_test.go b/modules/core/02-client/types/genesis_test.go index 1734fd482d2..8dac1291172 100644 --- a/modules/core/02-client/types/genesis_test.go +++ b/modules/core/02-client/types/genesis_test.go @@ -16,7 +16,6 @@ import ( ) const ( - chainID = "chainID" tmClientID0 = "07-tendermint-0" tmClientID1 = "07-tendermint-1" invalidClientID = "myclient-0" @@ -26,7 +25,7 @@ const ( height = 10 ) -var clientHeight = types.NewHeight(0, 10) +var clientHeight = types.NewHeight(1, 10) func (suite *TypesTestSuite) TestMarshalGenesisState() { cdc := suite.chainA.App.AppCodec() @@ -59,8 +58,8 @@ func (suite *TypesTestSuite) TestValidateGenesis() { signers := make(map[string]tmtypes.PrivValidator) signers[val.Address.String()] = privVal - heightMinus1 := types.NewHeight(0, height-1) - header := suite.chainA.CreateTMClientHeader(chainID, int64(clientHeight.RevisionHeight), heightMinus1, now, valSet, valSet, valSet, signers) + heightMinus1 := types.NewHeight(1, height-1) + header := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, int64(clientHeight.RevisionHeight), heightMinus1, now, valSet, valSet, valSet, signers) testCases := []struct { name string @@ -77,7 +76,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - tmClientID0, ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + tmClientID0, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -113,7 +112,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - soloMachineClientID, ibctmtypes.NewClientState(chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + soloMachineClientID, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), types.NewIdentifiedClientState(tmClientID0, solomachinetypes.NewClientState(0, &solomachinetypes.ConsensusState{suite.solomachine.ConsensusState().PublicKey, suite.solomachine.Diversifier, suite.solomachine.Time}, false)), }, @@ -130,7 +129,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - invalidClientID, ibctmtypes.NewClientState(chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + invalidClientID, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -158,7 +157,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - tmClientID0, ibctmtypes.NewClientState(chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + tmClientID0, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -166,7 +165,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { tmClientID1, []types.ConsensusStateWithHeight{ types.NewConsensusStateWithHeight( - types.NewHeight(0, 1), + types.NewHeight(1, 1), ibctmtypes.NewConsensusState( header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, ), @@ -186,7 +185,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - tmClientID0, ibctmtypes.NewClientState(chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + tmClientID0, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -214,7 +213,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - tmClientID0, ibctmtypes.NewClientState(chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + tmClientID0, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -222,7 +221,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { tmClientID0, []types.ConsensusStateWithHeight{ types.NewConsensusStateWithHeight( - types.NewHeight(0, 1), + types.NewHeight(1, 1), ibctmtypes.NewConsensusState( time.Time{}, commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, ), @@ -242,7 +241,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - tmClientID0, ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + tmClientID0, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -270,7 +269,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - clientID, ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + clientID, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -306,7 +305,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - clientID, ibctmtypes.NewClientState(chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + clientID, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -341,7 +340,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - tmClientID0, ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + tmClientID0, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -369,7 +368,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - tmClientID0, ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + tmClientID0, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -397,10 +396,10 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - tmClientID0, ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + tmClientID0, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), types.NewIdentifiedClientState( - tmClientID1, ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + tmClientID1, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -428,7 +427,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - "my-client", ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + "my-client", ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ diff --git a/modules/core/03-connection/keeper/verify_test.go b/modules/core/03-connection/keeper/verify_test.go index df9e6a5d69d..520dfed1397 100644 --- a/modules/core/03-connection/keeper/verify_test.go +++ b/modules/core/03-connection/keeper/verify_test.go @@ -14,7 +14,7 @@ import ( ibcmock "github.com/cosmos/ibc-go/v3/testing/mock" ) -var defaultTimeoutHeight = clienttypes.NewHeight(0, 100000) +var defaultTimeoutHeight = clienttypes.NewHeight(1, 100000) // TestVerifyClientState verifies a client state of chainA // stored on path.EndpointB (which is on chainB) @@ -62,7 +62,7 @@ func (suite *KeeperTestSuite) TestVerifyClientState() { tc.malleate() counterpartyClient, clientProof := path.EndpointB.QueryClientStateProof() - proofHeight := clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()-1)) + proofHeight := clienttypes.NewHeight(1, uint64(suite.chainB.GetContext().BlockHeight()-1)) connection := path.EndpointA.GetConnection() @@ -138,7 +138,7 @@ func (suite *KeeperTestSuite) TestVerifyClientConsensusState() { connection := path.EndpointA.GetConnection() proof, consensusHeight := suite.chainB.QueryConsensusStateProof(path.EndpointB.ClientID) - proofHeight := clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()-1)) + proofHeight := clienttypes.NewHeight(1, uint64(suite.chainB.GetContext().BlockHeight()-1)) consensusState, err := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetSelfConsensusState(suite.chainA.GetContext(), consensusHeight) suite.Require().NoError(err) diff --git a/modules/core/04-channel/keeper/packet_test.go b/modules/core/04-channel/keeper/packet_test.go index db6cce545c9..1f7e5546c70 100644 --- a/modules/core/04-channel/keeper/packet_test.go +++ b/modules/core/04-channel/keeper/packet_test.go @@ -20,7 +20,7 @@ import ( var ( disabledTimeoutTimestamp = uint64(0) disabledTimeoutHeight = clienttypes.ZeroHeight() - timeoutHeight = clienttypes.NewHeight(0, 100) + timeoutHeight = clienttypes.NewHeight(1, 100) // for when the testing package cannot be used clientIDA = "clientA" diff --git a/modules/core/ante/ante_test.go b/modules/core/ante/ante_test.go index 737b5f97b7b..43c17d9f382 100644 --- a/modules/core/ante/ante_test.go +++ b/modules/core/ante/ante_test.go @@ -49,7 +49,7 @@ func (suite *AnteTestSuite) createRecvPacketMessage(sequenceNumber uint64, isRed packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequenceNumber, suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, - clienttypes.NewHeight(1, 0), 0) + clienttypes.NewHeight(2, 0), 0) err := suite.path.EndpointA.SendPacket(packet) suite.Require().NoError(err) @@ -73,7 +73,7 @@ func (suite *AnteTestSuite) createAcknowledgementMessage(sequenceNumber uint64, packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequenceNumber, suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, - clienttypes.NewHeight(1, 0), 0) + clienttypes.NewHeight(2, 0), 0) err := suite.path.EndpointB.SendPacket(packet) suite.Require().NoError(err) @@ -415,11 +415,11 @@ func (suite *AnteTestSuite) TestAnteDecorator() { packet := channeltypes.NewPacket(ibctesting.MockPacketData, 2, suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID, - clienttypes.NewHeight(1, 0), 0) + clienttypes.NewHeight(2, 0), 0) return []sdk.Msg{ suite.createRecvPacketMessage(uint64(1), false), - channeltypes.NewMsgRecvPacket(packet, []byte("proof"), clienttypes.NewHeight(0, 1), "signer"), + channeltypes.NewMsgRecvPacket(packet, []byte("proof"), clienttypes.NewHeight(1, 1), "signer"), } }, false, diff --git a/modules/core/genesis_test.go b/modules/core/genesis_test.go index 8ea981ff5fe..f45d70a8eca 100644 --- a/modules/core/genesis_test.go +++ b/modules/core/genesis_test.go @@ -33,7 +33,7 @@ const ( channel2 = "channel-1" ) -var clientHeight = clienttypes.NewHeight(0, 10) +var clientHeight = clienttypes.NewHeight(1, 10) type IBCTestSuite struct { suite.Suite diff --git a/modules/core/keeper/msg_server_test.go b/modules/core/keeper/msg_server_test.go index 1e1933ee724..f61e2efd5a8 100644 --- a/modules/core/keeper/msg_server_test.go +++ b/modules/core/keeper/msg_server_test.go @@ -21,7 +21,7 @@ import ( const height = 10 var ( - timeoutHeight = clienttypes.NewHeight(0, 10000) + timeoutHeight = clienttypes.NewHeight(1, 10000) maxSequence = uint64(10) ) @@ -660,15 +660,13 @@ func (suite *KeeperTestSuite) TestHandleTimeoutOnClosePacket() { func (suite *KeeperTestSuite) TestUpgradeClient() { var ( path *ibctesting.Path + newChainID string + newClientHeight clienttypes.Height upgradedClient exported.ClientState upgradedConsState exported.ConsensusState lastHeight exported.Height msg *clienttypes.MsgUpgradeClient ) - - newClientHeight := clienttypes.NewHeight(1, 1) - newChainId := "newChainId-1" - cases := []struct { name string setup func() @@ -677,8 +675,7 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { { name: "successful upgrade", setup: func() { - - upgradedClient = ibctmtypes.NewClientState(newChainId, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod+ibctesting.TrustingPeriod, ibctesting.MaxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + upgradedClient = ibctmtypes.NewClientState(newChainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod+ibctesting.TrustingPeriod, ibctesting.MaxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) // Call ZeroCustomFields on upgraded clients to clear any client-chosen parameters in test-case upgradedClient upgradedClient = upgradedClient.ZeroCustomFields() @@ -718,8 +715,7 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { { name: "VerifyUpgrade fails", setup: func() { - - upgradedClient = ibctmtypes.NewClientState(newChainId, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod+ibctesting.TrustingPeriod, ibctesting.MaxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + upgradedClient = ibctmtypes.NewClientState(newChainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod+ibctesting.TrustingPeriod, ibctesting.MaxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) // Call ZeroCustomFields on upgraded clients to clear any client-chosen parameters in test-case upgradedClient upgradedClient = upgradedClient.ZeroCustomFields() @@ -756,9 +752,18 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { path = ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupClients(path) + var err error + clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) + revisionNumber := clienttypes.ParseChainID(clientState.ChainId) + + newChainID, err = clienttypes.SetRevisionNumber(clientState.ChainId, revisionNumber+1) + suite.Require().NoError(err) + + newClientHeight = clienttypes.NewHeight(revisionNumber+1, clientState.GetLatestHeight().GetRevisionHeight()+1) + tc.setup() - _, err := keeper.Keeper.UpgradeClient(*suite.chainA.App.GetIBCKeeper(), sdk.WrapSDKContext(suite.chainA.GetContext()), msg) + _, err = keeper.Keeper.UpgradeClient(*suite.chainA.App.GetIBCKeeper(), sdk.WrapSDKContext(suite.chainA.GetContext()), msg) if tc.expPass { suite.Require().NoError(err, "upgrade handler failed on valid case: %s", tc.name) diff --git a/modules/light-clients/07-tendermint/types/client_state_test.go b/modules/light-clients/07-tendermint/types/client_state_test.go index 58416f29832..dc138544d25 100644 --- a/modules/light-clients/07-tendermint/types/client_state_test.go +++ b/modules/light-clients/07-tendermint/types/client_state_test.go @@ -283,7 +283,7 @@ func (suite *TendermintTestSuite) TestVerifyMembership() { { "successful PacketCommitment verification", func() { // send from chainB to chainA since we are proving chainB sent a packet - packet := channeltypes.NewPacket(ibctesting.MockPacketData, 1, testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID, testingpath.EndpointA.ChannelConfig.PortID, testingpath.EndpointA.ChannelID, clienttypes.NewHeight(0, 100), 0) + packet := channeltypes.NewPacket(ibctesting.MockPacketData, 1, testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID, testingpath.EndpointA.ChannelConfig.PortID, testingpath.EndpointA.ChannelID, clienttypes.NewHeight(1, 100), 0) err := testingpath.EndpointB.SendPacket(packet) suite.Require().NoError(err) @@ -304,7 +304,7 @@ func (suite *TendermintTestSuite) TestVerifyMembership() { { "successful Acknowledgement verification", func() { // send from chainA to chainB since we are proving chainB wrote an acknowledgement - packet := channeltypes.NewPacket(ibctesting.MockPacketData, 1, testingpath.EndpointA.ChannelConfig.PortID, testingpath.EndpointA.ChannelID, testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID, clienttypes.NewHeight(0, 100), 0) + packet := channeltypes.NewPacket(ibctesting.MockPacketData, 1, testingpath.EndpointA.ChannelConfig.PortID, testingpath.EndpointA.ChannelID, testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) err := testingpath.EndpointA.SendPacket(packet) suite.Require().NoError(err) @@ -329,7 +329,7 @@ func (suite *TendermintTestSuite) TestVerifyMembership() { { "successful NextSequenceRecv verification", func() { // send from chainA to chainB since we are proving chainB incremented the sequence recv - packet := channeltypes.NewPacket(ibctesting.MockPacketData, 1, testingpath.EndpointA.ChannelConfig.PortID, testingpath.EndpointA.ChannelID, testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID, clienttypes.NewHeight(0, 100), 0) + packet := channeltypes.NewPacket(ibctesting.MockPacketData, 1, testingpath.EndpointA.ChannelConfig.PortID, testingpath.EndpointA.ChannelID, testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) // send packet err := testingpath.EndpointA.SendPacket(packet) @@ -766,7 +766,7 @@ func (suite *TendermintTestSuite) TestVerifyPacketCommitment() { // setup testing conditions path := ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.Setup(path) - packet := channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, clienttypes.NewHeight(0, 100), 0) + packet := channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, clienttypes.NewHeight(1, 100), 0) err := path.EndpointB.SendPacket(packet) suite.Require().NoError(err) @@ -880,7 +880,7 @@ func (suite *TendermintTestSuite) TestVerifyPacketAcknowledgement() { // setup testing conditions path := ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.Setup(path) - packet := channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(0, 100), 0) + packet := channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) // send packet err := path.EndpointA.SendPacket(packet) @@ -999,7 +999,7 @@ func (suite *TendermintTestSuite) TestVerifyPacketReceiptAbsence() { // setup testing conditions path := ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.Setup(path) - packet := channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(0, 100), 0) + packet := channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) // send packet, but no recv err := path.EndpointA.SendPacket(packet) @@ -1116,7 +1116,7 @@ func (suite *TendermintTestSuite) TestVerifyNextSeqRecv() { path := ibctesting.NewPath(suite.chainA, suite.chainB) path.SetChannelOrdered() suite.coordinator.Setup(path) - packet := channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(0, 100), 0) + packet := channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) // send packet err := path.EndpointA.SendPacket(packet) diff --git a/modules/light-clients/07-tendermint/types/misbehaviour_handle.go b/modules/light-clients/07-tendermint/types/misbehaviour_handle.go index a8a01c456d1..ea2ab19cc47 100644 --- a/modules/light-clients/07-tendermint/types/misbehaviour_handle.go +++ b/modules/light-clients/07-tendermint/types/misbehaviour_handle.go @@ -112,6 +112,8 @@ func checkMisbehaviourHeader( chainID := clientState.GetChainID() // If chainID is in revision format, then set revision number of chainID with the revision number // of the misbehaviour header + // NOTE: misbehaviour verification is not supported for chains which upgrade to a new chainID without + // strictly following the chainID revision format if clienttypes.IsRevisionFormat(chainID) { chainID, _ = clienttypes.SetRevisionNumber(chainID, header.GetHeight().GetRevisionNumber()) } diff --git a/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go b/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go index 2fde6ae2920..dc8c07c1bcd 100644 --- a/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go +++ b/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go @@ -1,6 +1,8 @@ package types_test import ( + "fmt" + "strings" "time" tmtypes "github.com/tendermint/tendermint/types" @@ -119,54 +121,68 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { }, true, }, - /* - { - - "valid misbehaviour at a previous revision", - types.NewClientState(chainIDRevision1, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(1, 1), commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - heightMinus1, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), - heightMinus3, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainIDRevision0, int64(height.RevisionHeight+1), heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainIDRevision0, int64(height.RevisionHeight+1), heightMinus3, suite.now.Add(time.Minute), bothValSet, bothValSet, suite.valSet, bothSigners), - ClientId: chainID, - }, - suite.now, - true, + { + + "valid misbehaviour at a previous revision", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + misbehaviour = &types.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + + // increment revision number + err = path.EndpointB.UpgradeChain() + suite.Require().NoError(err) }, - { - "valid misbehaviour at a future revision", - types.NewClientState(chainIDRevision0, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - heightMinus1, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), - heightMinus3, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainIDRevision0, 3, heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainIDRevision0, 3, heightMinus3, suite.now.Add(time.Minute), bothValSet, bothValSet, suite.valSet, bothSigners), - ClientId: chainID, - }, - suite.now, - true, + true, + }, + { + "valid misbehaviour at a future revision", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + futureRevision := fmt.Sprintf("%s-%d", strings.TrimSuffix(suite.chainB.ChainID, fmt.Sprintf("-%d", clienttypes.ParseChainID(suite.chainB.ChainID))), height.GetRevisionNumber()+1) + + misbehaviour = &types.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(futureRevision, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(futureRevision, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } }, - { - "valid misbehaviour with trusted heights at a previous revision", - types.NewClientState(chainIDRevision1, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(1, 1), commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), - heightMinus1, - types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), - heightMinus3, - &types.Misbehaviour{ - Header1: suite.chainA.CreateTMClientHeader(chainIDRevision1, 1, heightMinus1, suite.now, bothValSet, bothValSet, bothValSet, bothSigners), - Header2: suite.chainA.CreateTMClientHeader(chainIDRevision1, 1, heightMinus3, suite.now.Add(time.Minute), bothValSet, bothValSet, suite.valSet, bothSigners), - ClientId: chainID, - }, - suite.now, - true, + true, + }, + { + "valid misbehaviour with trusted heights at a previous revision", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + // increment revision of chainID + err = path.EndpointB.UpgradeChain() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + misbehaviour = &types.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } }, - */ + true, + }, { "consensus state's valset hash different from misbehaviour should still pass", func() { trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) @@ -360,10 +376,330 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { err := path.EndpointA.CreateClient() suite.Require().NoError(err) + tc.malleate() + clientState := path.EndpointA.GetClientState() + clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + + err = clientState.VerifyClientMessage(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, misbehaviour) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +// test both fork and time misbehaviour for chainIDs not in the revision format +// this function is separate as it must use a global variable in the testing package +// to initialize chains not in the revision format +func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { + // NOTE: chains set to non revision format + ibctesting.ChainIDSuffix = "" + + // Setup different validators and signers for testing different types of updates + altPrivVal := ibctestingmock.NewPV() + altPubKey, err := altPrivVal.GetPubKey() + suite.Require().NoError(err) + + // create modified heights to use for test-cases + altVal := tmtypes.NewValidator(altPubKey, 100) + + // Create alternative validator set with only altVal, invalid update (too much change in valSet) + altValSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{altVal}) + altSigners := getAltSigners(altVal, altPrivVal) + + var ( + path *ibctesting.Path + misbehaviour exported.ClientMessage + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "valid fork misbehaviour", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + misbehaviour = &types.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, + true, + }, + { + "valid time misbehaviour", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + misbehaviour = &types.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+3, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, + true, + }, + { + "valid time misbehaviour, header 1 time stricly less than header 2 time", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + misbehaviour = &types.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+3, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Hour), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, + true, + }, + { + "valid misbehavior at height greater than last consensusState", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + misbehaviour = &types.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+1, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+1, trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, true, + }, + { + "valid misbehaviour with different trusted heights", func() { + trustedHeight1 := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals1, found := suite.chainB.GetValsAtHeight(int64(trustedHeight1.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + trustedHeight2 := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals2, found := suite.chainB.GetValsAtHeight(int64(trustedHeight2.RevisionHeight) + 1) + suite.Require().True(found) + + misbehaviour = &types.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight1, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals1, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight2, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals2, suite.chainB.Signers), + } + + }, + true, + }, + { + "consensus state's valset hash different from misbehaviour should still pass", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + // Create bothValSet with both suite validator and altVal + bothValSet := tmtypes.NewValidatorSet(append(suite.chainB.Vals.Validators, altValSet.Proposer)) + bothSigners := suite.chainB.Signers + bothSigners[altValSet.Proposer.Address.String()] = altPrivVal + + misbehaviour = &types.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), bothValSet, suite.chainB.NextVals, trustedVals, bothSigners), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, bothValSet, suite.chainB.NextVals, trustedVals, bothSigners), + } + }, true, + }, + { + "invalid fork misbehaviour: identical headers", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + misbehaviourHeader := suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers) + misbehaviour = &types.Misbehaviour{ + Header1: misbehaviourHeader, + Header2: misbehaviourHeader, + } + }, false, + }, + { + "invalid time misbehaviour: monotonically increasing time", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + misbehaviour = &types.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+3, trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, false, + }, + { + "invalid misbehaviour: misbehaviour from different chain", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + misbehaviour = &types.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader("evmos", int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader("evmos", int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + + }, false, + }, + { + "misbehaviour trusted validators does not match validator hash in trusted consensus state", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + misbehaviour = &types.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, altValSet, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, altValSet, suite.chainB.Signers), + } + }, false, + }, + { + "trusted consensus state does not exist", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + misbehaviour = &types.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight.Increment().(clienttypes.Height), suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, false, + }, + { + "invalid tendermint misbehaviour", func() { + misbehaviour = &smtypes.Misbehaviour{} + }, false, + }, + { + "trusting period expired", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + suite.chainA.ExpireClient(path.EndpointA.ClientConfig.(*ibctesting.TendermintConfig).TrustingPeriod) + + misbehaviour = &types.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, false, + }, + { + "header 1 valset has too much change", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + misbehaviour = &types.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), altValSet, suite.chainB.NextVals, trustedVals, altSigners), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + } + }, false, + }, + { + "header 2 valset has too much change", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + misbehaviour = &types.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, altValSet, suite.chainB.NextVals, trustedVals, altSigners), + } + }, false, + }, + { + "both header 1 and header 2 valsets have too much change", func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + misbehaviour = &types.Misbehaviour{ + Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), altValSet, suite.chainB.NextVals, trustedVals, altSigners), + Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, altValSet, suite.chainB.NextVals, trustedVals, altSigners), + } + }, false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + path = ibctesting.NewPath(suite.chainA, suite.chainB) + + err := path.EndpointA.CreateClient() + suite.Require().NoError(err) tc.malleate() + clientState := path.EndpointA.GetClientState() clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) err = clientState.VerifyClientMessage(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, misbehaviour) @@ -375,4 +711,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { } }) } + + // NOTE: reset chain creation to revision format + ibctesting.ChainIDSuffix = "-1" } diff --git a/modules/light-clients/07-tendermint/types/update.go b/modules/light-clients/07-tendermint/types/update.go index b862e80fd30..827d598d0bd 100644 --- a/modules/light-clients/07-tendermint/types/update.go +++ b/modules/light-clients/07-tendermint/types/update.go @@ -109,21 +109,11 @@ func (cs *ClientState) verifyHeader( ) } - chainID := cs.GetChainID() - // If chainID is in revision format, then set revision number of chainID with the revision number - // of the header we are verifying - // This is useful if the update is at a previous revision rather than an update to the latest revision - // of the client. - // The chainID must be set correctly for the previous revision before attempting verification. - // Updates for previous revisions are not supported if the chainID is not in revision format. - if clienttypes.IsRevisionFormat(chainID) { - chainID, _ = clienttypes.SetRevisionNumber(chainID, header.GetHeight().GetRevisionNumber()) - } - // Construct a trusted header using the fields in consensus state // Only Height, Time, and NextValidatorsHash are necessary for verification + // NOTE: updates must be within the same revision trustedHeader := tmtypes.Header{ - ChainID: chainID, + ChainID: cs.GetChainID(), Height: int64(header.TrustedHeight.RevisionHeight), Time: consState.Timestamp, NextValidatorsHash: consState.NextValidatorsHash, diff --git a/modules/light-clients/07-tendermint/types/update_test.go b/modules/light-clients/07-tendermint/types/update_test.go index 59326645248..abe9cfb9382 100644 --- a/modules/light-clients/07-tendermint/types/update_test.go +++ b/modules/light-clients/07-tendermint/types/update_test.go @@ -208,67 +208,70 @@ func (suite *TendermintTestSuite) TestVerifyHeader() { }, expPass: false, }, - // TODO: add revision tests after helper function to upgrade chain/client - /* - { - name: "successful update for a previous revision", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainIDRevision1, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - consStateHeight = heightMinus3 - newHeader = suite.chainA.CreateTMClientHeader(chainIDRevision0, int64(height.RevisionHeight), heightMinus3, suite.headerTime, bothValSet, bothValSet, suite.valSet, bothSigners) - currentTime = suite.now - }, - expPass: true, - }, - { - name: "successful update with identical header to a previous update", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, heightPlus1, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) - currentTime = suite.now - ctx := suite.chainA.GetContext().WithBlockTime(currentTime) - // Store the header's consensus state in client store before UpdateClient call - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(ctx, clientID, heightPlus1, newHeader.ConsensusState()) - }, - expFrozen: false, - expPass: true, + { + name: "unsuccessful update for a previous revision", + malleate: func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + // passing the CurrentHeader.Height as the block height as it will become an update to previous revision once we upgrade the client + header = suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers) + + // increment the revision of the chain + err = path.EndpointB.UpgradeChain() + suite.Require().NoError(err) }, - { - name: "unsuccessful update to a future revision", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainIDRevision0, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainIDRevision1, 1, height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) - currentTime = suite.now - }, - expPass: false, + expPass: false, + }, + { + name: "successful update with identical header to a previous update", + malleate: func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + // passing the CurrentHeader.Height as the block height as it will become a previous height once we commit N blocks + header = suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers) + + // update client so the header constructed becomes a duplicate + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) }, - { - name: "unsuccessful update: header height revision and trusted height revision mismatch", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainIDRevision1, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(1, 1), commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainIDRevision1, 3, height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) - currentTime = suite.now - }, - expFrozen: false, - expPass: false, + expPass: true, + }, + + { + name: "unsuccessful update to a future revision", + malleate: func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + header = suite.chainB.CreateTMClientHeader(suite.chainB.ChainID+"-1", suite.chainB.CurrentHeader.Height+5, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers) }, - { - name: "unsuccessful update: trusting period has passed since last client timestamp", - setup: func(suite *TendermintTestSuite) { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) - consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, suite.valSet, suite.signers) - // make current time pass trusting period from last timestamp on clientstate - currentTime = suite.now.Add(trustingPeriod) - }, - expFrozen: false, - expPass: false, + expPass: false, + }, + + { + name: "unsuccessful update: header height revision and trusted height revision mismatch", + malleate: func() { + trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + // increment the revision of the chain + err = path.EndpointB.UpgradeChain() + suite.Require().NoError(err) + + header = suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers) }, - */ + expPass: false, + }, } for _, tc := range testCases { @@ -284,16 +287,16 @@ func (suite *TendermintTestSuite) TestVerifyHeader() { header, err = path.EndpointA.Chain.ConstructUpdateTMClientHeader(path.EndpointA.Counterparty.Chain, path.EndpointA.ClientID) suite.Require().NoError(err) + tc.malleate() + clientState := path.EndpointA.GetClientState() clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - tc.malleate() - err = clientState.VerifyClientMessage(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, header) if tc.expPass { - suite.Require().NoError(err) + suite.Require().NoError(err, tc.name) } else { suite.Require().Error(err) } diff --git a/modules/light-clients/07-tendermint/types/upgrade_test.go b/modules/light-clients/07-tendermint/types/upgrade_test.go index 112d3366cda..218991fc4e9 100644 --- a/modules/light-clients/07-tendermint/types/upgrade_test.go +++ b/modules/light-clients/07-tendermint/types/upgrade_test.go @@ -10,12 +10,9 @@ import ( ibctesting "github.com/cosmos/ibc-go/v3/testing" ) -var ( - newChainId = "newChainId-1" -) - func (suite *TendermintTestSuite) TestVerifyUpgrade() { var ( + newChainID string upgradedClient exported.ClientState upgradedConsState exported.ConsensusState lastHeight clienttypes.Height @@ -57,9 +54,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { { name: "successful upgrade to same revision", setup: func() { - upgradedHeight := clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+2)) - // don't use -1 suffix in chain id - upgradedClient = types.NewClientState("newChainId", types.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, upgradedHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + upgradedClient = types.NewClientState(suite.chainB.ChainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, clienttypes.NewHeight(clienttypes.ParseChainID(suite.chainB.ChainID), upgradedClient.GetLatestHeight().GetRevisionHeight()+10), commitmenttypes.GetSDKSpecs(), upgradePath, false, false) upgradedClient = upgradedClient.ZeroCustomFields() upgradedClientBz, err = clienttypes.MarshalClientState(suite.chainA.App.AppCodec(), upgradedClient) suite.Require().NoError(err) @@ -114,7 +109,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { name: "unsuccessful upgrade: committed client does not have zeroed custom fields", setup: func() { // non-zeroed upgrade client - upgradedClient = types.NewClientState(newChainId, types.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + upgradedClient = types.NewClientState(newChainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) upgradedClientBz, err = clienttypes.MarshalClientState(suite.chainA.App.AppCodec(), upgradedClient) suite.Require().NoError(err) @@ -172,7 +167,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) // change upgradedClient client-specified parameters - upgradedClient = types.NewClientState(newChainId, types.DefaultTrustLevel, ubdPeriod, ubdPeriod+trustingPeriod, maxClockDrift+5, lastHeight, commitmenttypes.GetSDKSpecs(), upgradePath, true, false) + upgradedClient = types.NewClientState(newChainID, types.DefaultTrustLevel, ubdPeriod, ubdPeriod+trustingPeriod, maxClockDrift+5, lastHeight, commitmenttypes.GetSDKSpecs(), upgradePath, true, false) suite.coordinator.CommitBlock(suite.chainB) err := path.EndpointA.UpdateClient() @@ -403,7 +398,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { name: "unsuccessful upgrade: final client is not valid", setup: func() { // new client has smaller unbonding period such that old trusting period is no longer valid - upgradedClient = types.NewClientState(newChainId, types.DefaultTrustLevel, trustingPeriod, trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + upgradedClient = types.NewClientState(newChainID, types.DefaultTrustLevel, trustingPeriod, trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) upgradedClientBz, err = clienttypes.MarshalClientState(suite.chainA.App.AppCodec(), upgradedClient) suite.Require().NoError(err) @@ -438,7 +433,15 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { path = ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupClients(path) - upgradedClient = types.NewClientState(newChainId, types.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + + clientState := path.EndpointA.GetClientState().(*types.ClientState) + revisionNumber := clienttypes.ParseChainID(clientState.ChainId) + + var err error + newChainID, err = clienttypes.SetRevisionNumber(clientState.ChainId, revisionNumber+1) + suite.Require().NoError(err) + + upgradedClient = types.NewClientState(newChainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, clienttypes.NewHeight(revisionNumber+1, clientState.GetLatestHeight().GetRevisionHeight()+1), commitmenttypes.GetSDKSpecs(), upgradePath, false, false) upgradedClient = upgradedClient.ZeroCustomFields() upgradedClientBz, err = clienttypes.MarshalClientState(suite.chainA.App.AppCodec(), upgradedClient) suite.Require().NoError(err) @@ -457,7 +460,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { // Call ZeroCustomFields on upgraded clients to clear any client-chosen parameters in test-case upgradedClient upgradedClient = upgradedClient.ZeroCustomFields() - err := cs.VerifyUpgradeAndUpdateState( + err = cs.VerifyUpgradeAndUpdateState( suite.chainA.GetContext(), suite.cdc, clientStore, diff --git a/testing/coordinator.go b/testing/coordinator.go index 4c0ebc78463..42743621c60 100644 --- a/testing/coordinator.go +++ b/testing/coordinator.go @@ -11,7 +11,9 @@ import ( ) var ( - ChainIDPrefix = "testchain" + ChainIDPrefix = "testchain" + // to disable revision format, set ChainIDSuffix to "" + ChainIDSuffix = "-1" globalStartTime = time.Date(2020, 1, 2, 0, 0, 0, 0, time.UTC) TimeIncrement = time.Second * 5 ) @@ -173,7 +175,7 @@ func (coord *Coordinator) GetChain(chainID string) *TestChain { // GetChainID returns the chainID used for the provided index. func GetChainID(index int) string { - return ChainIDPrefix + strconv.Itoa(index) + return ChainIDPrefix + strconv.Itoa(index) + ChainIDSuffix } // CommitBlock commits a block on the provided indexes and then increments the global time. diff --git a/testing/endpoint.go b/testing/endpoint.go index 7c272d7847f..093f657e5b6 100644 --- a/testing/endpoint.go +++ b/testing/endpoint.go @@ -2,6 +2,7 @@ package ibctesting import ( "fmt" + "strings" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" @@ -154,6 +155,59 @@ func (endpoint *Endpoint) UpdateClient() (err error) { return endpoint.Chain.sendMsgs(msg) } +// UpgradeChain will upgrade a chain's chainID to the next revision number. +// It will also update the counterparty client. +// TODO: implement actual upgrade chain functionality via scheduling an upgrade +// and upgrading the client via MsgUpgradeClient +// see reference https://github.com/cosmos/ibc-go/pull/1169 +func (endpoint *Endpoint) UpgradeChain() error { + if strings.TrimSpace(endpoint.Counterparty.ClientID) == "" { + return fmt.Errorf("cannot upgrade chain if there is no counterparty client") + } + + clientState := endpoint.Counterparty.GetClientState().(*ibctmtypes.ClientState) + + // increment revision number in chainID + + oldChainID := clientState.ChainId + if !clienttypes.IsRevisionFormat(oldChainID) { + return fmt.Errorf("cannot upgrade chain which is not of revision format: %s", oldChainID) + } + + revisionNumber := clienttypes.ParseChainID(oldChainID) + newChainID, err := clienttypes.SetRevisionNumber(oldChainID, revisionNumber+1) + if err != nil { + return err + } + + // update chain + endpoint.Chain.ChainID = newChainID + endpoint.Chain.CurrentHeader.ChainID = newChainID + endpoint.Chain.NextBlock() // commit changes + + // update counterparty client manually + clientState.ChainId = newChainID + clientState.LatestHeight = clienttypes.NewHeight(revisionNumber+1, clientState.LatestHeight.GetRevisionHeight()+1) + endpoint.Counterparty.SetClientState(clientState) + + consensusState := &ibctmtypes.ConsensusState{ + Timestamp: endpoint.Chain.LastHeader.GetTime(), + Root: commitmenttypes.NewMerkleRoot(endpoint.Chain.LastHeader.Header.GetAppHash()), + NextValidatorsHash: endpoint.Chain.LastHeader.Header.NextValidatorsHash, + } + endpoint.Counterparty.SetConsensusState(consensusState, clientState.GetLatestHeight()) + + // ensure the next update isn't identical to the one set in state + endpoint.Chain.Coordinator.IncrementTime() + endpoint.Chain.NextBlock() + + if err = endpoint.Counterparty.UpdateClient(); err != nil { + return err + } + + return nil +} + // ConnOpenInit will construct and execute a MsgConnectionOpenInit on the associated endpoint. func (endpoint *Endpoint) ConnOpenInit() error { msg := connectiontypes.NewMsgConnectionOpenInit( From d12004466cbee84c895d53fda7daaa9e2c880665 Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Tue, 10 May 2022 14:03:19 +0200 Subject: [PATCH 40/71] ADR 005: update client consensus height events (#1315) * adding context section and draft notes for decision section * updating adr with decisions and consequences sections * adding references to adr 005 * removing decision templating * Apply suggestions from code review Co-authored-by: Carlos Rodriguez * adding versioned links * reorder decision points, add additional info on header cross-checking * typo Co-authored-by: Carlos Rodriguez --- .../adr-005-consensus-height-events.md | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 docs/architecture/adr-005-consensus-height-events.md diff --git a/docs/architecture/adr-005-consensus-height-events.md b/docs/architecture/adr-005-consensus-height-events.md new file mode 100644 index 00000000000..430d7064a1e --- /dev/null +++ b/docs/architecture/adr-005-consensus-height-events.md @@ -0,0 +1,88 @@ +# ADR 005: UpdateClient Events - ClientState Consensus Heights + +## Changelog +* 25/04/2022: initial draft + +## Status + +Accepted + +## Context + +The `ibc-go` implementation leverages the [Cosmos-SDK's EventManager](https://github.com/cosmos/cosmos-sdk/blob/v0.45.4/docs/core/events.md#EventManager) to provide subscribers a method of reacting to application specific events. +Some IBC relayers depend on the [`consensus_height`](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/core/02-client/keeper/events.go#L33) attribute emitted as part of `UpdateClient` events in order to run `07-tendermint` misbehaviour detection by cross-checking the details of the *Header* emitted at a given consensus height against those of the *Header* from the originating chain. This includes such details as: + +- The `SignedHeader` containing the commitment root. +- The `ValidatorSet` that signed the *Header*. +- The `TrustedHeight` seen by the client at less than or equal to the height of *Header*. +- The last `TrustedValidatorSet` at the trusted height. + +Following the refactor of the `02-client` submodule and associated `ClientState` interfaces, it will now be possible for +light client implementations to perform such actions as batch updates, inserting `N` number of `ConsensusState`s into the application state tree with a single `UpdateClient` message. This flexibility is provided in `ibc-go` by the usage of the [Protobuf `Any`](https://developers.google.com/protocol-buffers/docs/proto3#any) field contained within the [`UpdateClient`](https://github.com/cosmos/ibc-go/blob/v3.0.0/proto/ibc/core/client/v1/tx.proto#L44) message. +For example, a batched client update message serialized as a Protobuf `Any` type for the `07-tendermint` lightclient implementation could be defined as follows: + +```protobuf +message BatchedHeaders { + repeated Header headers = 1; +} +``` + +To complement this flexibility, the `UpdateClient` handler will now support the submission of [client misbehaviour](https://github.com/cosmos/ibc/tree/master/spec/core/ics-002-client-semantics#misbehaviour) by consolidating the `Header` and `Misbehaviour` interfaces into a single `ClientMessage` interface type: + +```go +// ClientMessage is an interface used to update an IBC client. +// The update may be done by a single header, a batch of headers, misbehaviour, or any type which when verified produces +// a change to state of the IBC client +type ClientMessage interface { + proto.Message + + ClientType() string + ValidateBasic() error +} +``` + +To support this functionality the `GetHeight()` method has been omitted from the new `ClientMessage` interface. +Emission of standardised events from the `02-client` submodule now becomes problematic and is two-fold: + +1. The `02-client` submodule previously depended upon the `GetHeight()` method of `Header` types in order to [retrieve the updated consensus height](https://github.com/cosmos/ibc-go/blob/v3.0.0/modules/core/02-client/keeper/client.go#L90). +2. Emitting a single `consensus_height` event attribute is not sufficient in the case of a batched client update containing multiple *Headers*. + +## Decision + +The following decisions have been made in order to provide flexibility to consumers of `UpdateClient` events in a non-breaking fashion: + +1. Return a list of updated consensus heights `[]exported.Height` from the new `UpdateState` method of the `ClientState` interface. + +```go +// UpdateState updates and stores as necessary any associated information for an IBC client, such as the ClientState and corresponding ConsensusState. +// Upon successful update, a list of consensus heights is returned. It assumes the ClientMessage has already been verified. +UpdateState(sdk.Context, codec.BinaryCodec, sdk.KVStore, ClientMessage) []Height +``` + +2. Maintain the `consensus_height` event attribute emitted from the `02-client` update handler, but mark as deprecated for future removal. For example, with tendermint lightclients this will simply be `consensusHeights[0]` following a successful update using a single *Header*. + +3. Add an additional `consensus_heights` event attribute, containing a comma separated list of updated heights. This provides flexibility for emitting a single consensus height or multiple consensus heights in the example use-case of batched header updates. + +## Consequences + +### Positive + +- Subscribers of IBC core events can act upon `UpdateClient` events containing one or more consensus heights. +- Deprecation of the existing `consensus_height` attribute allows consumers to continue to process `UpdateClient` events as normal, with a path to upgrade to using the `consensus_heights` attribute moving forward. + +### Negative + +- Consumers of IBC core `UpdateClient` events are forced to make future code changes. + +### Neutral + +## References + +Discussions: +- [#1208](https://github.com/cosmos/ibc-go/pull/1208#discussion_r839691927) + +Issues: +- [#594](https://github.com/cosmos/ibc-go/issues/594) + +PRs: +- [#1285](https://github.com/cosmos/ibc-go/pull/1285) From e2bdd1fdba21d2b59c8622d5444027cf96a1f015 Mon Sep 17 00:00:00 2001 From: Sean King Date: Thu, 30 Jun 2022 11:52:22 +0200 Subject: [PATCH 41/71] feat: VerifyNonMembership 07-tendermint implementation (#1611) --- .../07-tendermint/types/client_state.go | 45 ++++ .../07-tendermint/types/client_state_test.go | 232 ++++++++++++++++++ 2 files changed, 277 insertions(+) diff --git a/modules/light-clients/07-tendermint/types/client_state.go b/modules/light-clients/07-tendermint/types/client_state.go index 404ed692fe5..c78d54b3168 100644 --- a/modules/light-clients/07-tendermint/types/client_state.go +++ b/modules/light-clients/07-tendermint/types/client_state.go @@ -563,6 +563,51 @@ func (cs ClientState) VerifyMembership( return nil } +// VerifyNonMembership is a generic proof verification method which verifies the absense of a given CommitmentPath at a specified height. +// The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). +func (cs ClientState) VerifyNonMembership( + ctx sdk.Context, + clientStore sdk.KVStore, + cdc codec.BinaryCodec, + height exported.Height, + delayTimePeriod uint64, + delayBlockPeriod uint64, + proof []byte, + path []byte, +) error { + if cs.GetLatestHeight().LT(height) { + return sdkerrors.Wrapf( + sdkerrors.ErrInvalidHeight, + "client state height < proof height (%d < %d), please ensure the client has been updated", cs.GetLatestHeight(), height, + ) + } + + if err := verifyDelayPeriodPassed(ctx, clientStore, height, delayTimePeriod, delayBlockPeriod); err != nil { + return err + } + + var merkleProof commitmenttypes.MerkleProof + if err := cdc.Unmarshal(proof, &merkleProof); err != nil { + return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "failed to unmarshal proof into ICS 23 commitment merkle proof") + } + + var merklePath commitmenttypes.MerklePath + if err := cdc.Unmarshal(path, &merklePath); err != nil { + return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "failed to unmarshal path into ICS 23 commitment merkle path") + } + + consensusState, found := GetConsensusState(clientStore, cdc, height) + if !found { + return sdkerrors.Wrap(clienttypes.ErrConsensusStateNotFound, "please ensure the proof was constructed against a height that exists on the client") + } + + if err := merkleProof.VerifyNonMembership(cs.ProofSpecs, consensusState.GetRoot(), merklePath); err != nil { + return err + } + + return nil +} + // verifyDelayPeriodPassed will ensure that at least delayTimePeriod amount of time and delayBlockPeriod number of blocks have passed // since consensus state was submitted before allowing verification to continue. func verifyDelayPeriodPassed(ctx sdk.Context, store sdk.KVStore, proofHeight exported.Height, delayTimePeriod, delayBlockPeriod uint64) error { diff --git a/modules/light-clients/07-tendermint/types/client_state_test.go b/modules/light-clients/07-tendermint/types/client_state_test.go index dc138544d25..b9d7c7f7016 100644 --- a/modules/light-clients/07-tendermint/types/client_state_test.go +++ b/modules/light-clients/07-tendermint/types/client_state_test.go @@ -473,6 +473,238 @@ func (suite *TendermintTestSuite) TestVerifyMembership() { } } +func (suite *TendermintTestSuite) TestVerifyNonMembership() { + var ( + testingpath *ibctesting.Path + delayTimePeriod uint64 + delayBlockPeriod uint64 + proofHeight exported.Height + proof []byte + path []byte + invalidClientID = "09-tendermint" + invalidConnectionID = "connection-100" + invalidChannelID = "channel-800" + invalidPortID = "invalid-port" + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "successful ClientState verification of non membership", + func() { + // default proof construction uses ClientState + }, + true, + }, + { + "successful ConsensusState verification of non membership", func() { + key := host.FullConsensusStateKey(invalidClientID, testingpath.EndpointB.GetClientState().GetLatestHeight()) + merklePath := commitmenttypes.NewMerklePath(string(key)) + merklePath, err := commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + path, err = suite.chainB.Codec.Marshal(&merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + }, + true, + }, + { + "successful Connection verification of non membership", func() { + key := host.ConnectionKey(invalidConnectionID) + merklePath := commitmenttypes.NewMerklePath(string(key)) + merklePath, err := commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + path, err = suite.chainB.Codec.Marshal(&merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + }, + true, + }, + { + "successful Channel verification of non membership", func() { + key := host.ChannelKey(testingpath.EndpointB.ChannelConfig.PortID, invalidChannelID) + merklePath := commitmenttypes.NewMerklePath(string(key)) + merklePath, err := commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + path, err = suite.chainB.Codec.Marshal(&merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + }, + true, + }, + { + "successful PacketCommitment verification of non membership", func() { + // make packet commitment proof + key := host.PacketCommitmentKey(invalidPortID, invalidChannelID, 1) + merklePath := commitmenttypes.NewMerklePath(string(key)) + merklePath, err := commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + path, err = suite.chainB.Codec.Marshal(&merklePath) + suite.Require().NoError(err) + + proof, proofHeight = testingpath.EndpointB.QueryProof(key) + }, true, + }, + { + "successful Acknowledgement verification of non membership", func() { + key := host.PacketAcknowledgementKey(invalidPortID, invalidChannelID, 1) + merklePath := commitmenttypes.NewMerklePath(string(key)) + merklePath, err := commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + path, err = suite.chainB.Codec.Marshal(&merklePath) + suite.Require().NoError(err) + + proof, proofHeight = testingpath.EndpointB.QueryProof(key) + }, + true, + }, + { + "successful NextSequenceRecv verification of non membership", func() { + key := host.NextSequenceRecvKey(invalidPortID, invalidChannelID) + merklePath := commitmenttypes.NewMerklePath(string(key)) + merklePath, err := commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + path, err = suite.chainB.Codec.Marshal(&merklePath) + suite.Require().NoError(err) + + proof, proofHeight = testingpath.EndpointB.QueryProof(key) + }, + true, + }, + { + "successful verification of non membership outside IBC store", func() { + key := []byte{0x08} + merklePath := commitmenttypes.NewMerklePath(string(key)) + merklePath, err := commitmenttypes.ApplyPrefix(commitmenttypes.NewMerklePrefix([]byte(transfertypes.StoreKey)), merklePath) + suite.Require().NoError(err) + + path, err = suite.chainB.Codec.Marshal(&merklePath) + suite.Require().NoError(err) + + clientState := testingpath.EndpointA.GetClientState() + proof, proofHeight = suite.chainB.QueryProofForStore(transfertypes.StoreKey, key, int64(clientState.GetLatestHeight().GetRevisionHeight())) + }, + true, + }, + { + "delay time period has passed", func() { + delayTimePeriod = uint64(time.Second.Nanoseconds()) + }, + true, + }, + { + "delay time period has not passed", func() { + delayTimePeriod = uint64(time.Hour.Nanoseconds()) + }, + false, + }, + { + "delay block period has passed", func() { + delayBlockPeriod = 1 + }, + true, + }, + { + "delay block period has not passed", func() { + delayBlockPeriod = 1000 + }, + false, + }, + { + "latest client height < height", func() { + proofHeight = testingpath.EndpointA.GetClientState().GetLatestHeight().Increment() + }, false, + }, + { + "failed to unmarshal merkle path", func() { + path = []byte("invalid merkle path") + }, false, + }, + { + "failed to unmarshal merkle proof", func() { + proof = invalidProof + }, false, + }, + { + "consensus state not found", func() { + proofHeight = clienttypes.ZeroHeight() + }, false, + }, + { + "verify non membership fails as path exists", func() { + // change the value being prooved + key := host.FullClientStateKey(testingpath.EndpointB.ClientID) + merklePath := commitmenttypes.NewMerklePath(string(key)) + merklePath, err := commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + path, err = suite.chainA.Codec.Marshal(&merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + }, false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() // reset + testingpath = ibctesting.NewPath(suite.chainA, suite.chainB) + testingpath.SetChannelOrdered() + suite.coordinator.Setup(testingpath) + + // reset time and block delays to 0, malleate may change to a specific non-zero value. + delayTimePeriod = 0 + delayBlockPeriod = 0 + + // create default proof, merklePath, and value which passes + // may be overwritten by malleate() + key := host.FullClientStateKey("invalid-client-id") + + merklePath := commitmenttypes.NewMerklePath(string(key)) + merklePath, err := commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + path, err = suite.chainA.Codec.Marshal(&merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + + tc.malleate() // make changes as necessary + + clientState := testingpath.EndpointA.GetClientState().(*types.ClientState) + + ctx := suite.chainA.GetContext() + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, testingpath.EndpointA.ClientID) + + err = clientState.VerifyNonMembership( + ctx, store, suite.chainA.Codec, proofHeight, delayTimePeriod, delayBlockPeriod, + proof, path, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + // TODO: remove, https://github.com/cosmos/ibc-go/issues/1294 func (suite *TendermintTestSuite) TestVerifyClientConsensusState() { testCases := []struct { From aab9ca2080b1cf5f52386ae5e1732a5d16902a06 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Thu, 30 Jun 2022 15:32:31 +0100 Subject: [PATCH 42/71] chore: making changes based on nits from 02-client refactor audit (#1585) --- modules/core/02-client/keeper/client.go | 81 ++++++++----------- modules/core/02-client/keeper/events.go | 10 ++- .../07-tendermint/types/update.go | 53 ++++++------ 3 files changed, 66 insertions(+), 78 deletions(-) diff --git a/modules/core/02-client/keeper/client.go b/modules/core/02-client/keeper/client.go index a0ab6348dcf..a1646da9c6b 100644 --- a/modules/core/02-client/keeper/client.go +++ b/modules/core/02-client/keeper/client.go @@ -1,8 +1,6 @@ package keeper import ( - "encoding/hex" - "github.com/armon/go-metrics" "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" @@ -27,7 +25,6 @@ func (k Keeper) CreateClient( clientID := k.GenerateClientIdentifier(ctx, clientState.ClientType()) - k.SetClientState(ctx, clientID, clientState) k.Logger(ctx).Info("client created at height", "client-id", clientID, "height", clientState.GetLatestHeight().String()) // verifies initial consensus state against client state and initializes client store with any client-specific metadata @@ -36,17 +33,16 @@ func (k Keeper) CreateClient( return "", err } + k.SetClientState(ctx, clientID, clientState) k.SetClientConsensusState(ctx, clientID, clientState.GetLatestHeight(), consensusState) k.Logger(ctx).Info("client created at height", "client-id", clientID, "height", clientState.GetLatestHeight().String()) - defer func() { - telemetry.IncrCounterWithLabels( - []string{"ibc", "client", "create"}, - 1, - []metrics.Label{telemetry.NewLabel(types.LabelClientType, clientState.ClientType())}, - ) - }() + defer telemetry.IncrCounterWithLabels( + []string{"ibc", "client", "create"}, + 1, + []metrics.Label{telemetry.NewLabel(types.LabelClientType, clientState.ClientType())}, + ) EmitCreateClientEvent(ctx, clientID, clientState) @@ -76,17 +72,15 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, clientMsg exporte k.Logger(ctx).Info("client frozen due to misbehaviour", "client-id", clientID) - defer func() { - telemetry.IncrCounterWithLabels( - []string{"ibc", "client", "misbehaviour"}, - 1, - []metrics.Label{ - telemetry.NewLabel(types.LabelClientType, clientState.ClientType()), - telemetry.NewLabel(types.LabelClientID, clientID), - telemetry.NewLabel(types.LabelMsgType, "update"), - }, - ) - }() + defer telemetry.IncrCounterWithLabels( + []string{"ibc", "client", "misbehaviour"}, + 1, + []metrics.Label{ + telemetry.NewLabel(types.LabelClientType, clientState.ClientType()), + telemetry.NewLabel(types.LabelClientID, clientID), + telemetry.NewLabel(types.LabelMsgType, "update"), + }, + ) EmitSubmitMisbehaviourEvent(ctx, clientID, clientState) @@ -97,25 +91,18 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, clientMsg exporte k.Logger(ctx).Info("client state updated", "client-id", clientID, "heights", consensusHeights) - defer func() { - telemetry.IncrCounterWithLabels( - []string{"ibc", "client", "update"}, - 1, - []metrics.Label{ - telemetry.NewLabel(types.LabelClientType, clientState.ClientType()), - telemetry.NewLabel(types.LabelClientID, clientID), - telemetry.NewLabel(types.LabelUpdateType, "msg"), - }, - ) - }() - - // Marshal the ClientMessage as an Any and encode the resulting bytes to hex. - // This prevents the event value from containing invalid UTF-8 characters - // which may cause data to be lost when JSON encoding/decoding. - clientMsgStr := hex.EncodeToString(types.MustMarshalClientMessage(k.cdc, clientMsg)) + defer telemetry.IncrCounterWithLabels( + []string{"ibc", "client", "update"}, + 1, + []metrics.Label{ + telemetry.NewLabel(types.LabelClientType, clientState.ClientType()), + telemetry.NewLabel(types.LabelClientID, clientID), + telemetry.NewLabel(types.LabelUpdateType, "msg"), + }, + ) // emitting events in the keeper emits for both begin block and handler client updates - EmitUpdateClientEvent(ctx, clientID, clientState.ClientType(), consensusHeights, clientMsgStr) + EmitUpdateClientEvent(ctx, clientID, clientState.ClientType(), consensusHeights, k.cdc, clientMsg) return nil } @@ -143,16 +130,14 @@ func (k Keeper) UpgradeClient(ctx sdk.Context, clientID string, upgradedClient e k.Logger(ctx).Info("client state upgraded", "client-id", clientID, "height", upgradedClient.GetLatestHeight().String()) - defer func() { - telemetry.IncrCounterWithLabels( - []string{"ibc", "client", "upgrade"}, - 1, - []metrics.Label{ - telemetry.NewLabel(types.LabelClientType, upgradedClient.ClientType()), - telemetry.NewLabel(types.LabelClientID, clientID), - }, - ) - }() + defer telemetry.IncrCounterWithLabels( + []string{"ibc", "client", "upgrade"}, + 1, + []metrics.Label{ + telemetry.NewLabel(types.LabelClientType, upgradedClient.ClientType()), + telemetry.NewLabel(types.LabelClientID, clientID), + }, + ) EmitUpgradeClientEvent(ctx, clientID, upgradedClient) diff --git a/modules/core/02-client/keeper/events.go b/modules/core/02-client/keeper/events.go index d49d41cb1cb..ec15ca41f4e 100644 --- a/modules/core/02-client/keeper/events.go +++ b/modules/core/02-client/keeper/events.go @@ -1,8 +1,10 @@ package keeper import ( + "encoding/hex" "strings" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" @@ -26,7 +28,13 @@ func EmitCreateClientEvent(ctx sdk.Context, clientID string, clientState exporte } // EmitUpdateClientEvent emits an update client event -func EmitUpdateClientEvent(ctx sdk.Context, clientID string, clientType string, consensusHeights []exported.Height, clientMsgStr string) { +func EmitUpdateClientEvent(ctx sdk.Context, clientID string, clientType string, consensusHeights []exported.Height, cdc codec.BinaryCodec, clientMsg exported.ClientMessage) { + + // Marshal the ClientMessage as an Any and encode the resulting bytes to hex. + // This prevents the event value from containing invalid UTF-8 characters + // which may cause data to be lost when JSON encoding/decoding. + clientMsgStr := hex.EncodeToString(types.MustMarshalClientMessage(cdc, clientMsg)) + var consensusHeightAttr string if len(consensusHeights) != 0 { consensusHeightAttr = consensusHeights[0].String() diff --git a/modules/light-clients/07-tendermint/types/update.go b/modules/light-clients/07-tendermint/types/update.go index 827d598d0bd..31d16799118 100644 --- a/modules/light-clients/07-tendermint/types/update.go +++ b/modules/light-clients/07-tendermint/types/update.go @@ -17,26 +17,6 @@ import ( "github.com/cosmos/ibc-go/v3/modules/core/exported" ) -// checkTrustedHeader checks that consensus state matches trusted fields of Header -func checkTrustedHeader(header *Header, consState *ConsensusState) error { - tmTrustedValidators, err := tmtypes.ValidatorSetFromProto(header.TrustedValidators) - if err != nil { - return sdkerrors.Wrap(err, "trusted validator set in not tendermint validator set type") - } - - // assert that trustedVals is NextValidators of last trusted header - // to do this, we check that trustedVals.Hash() == consState.NextValidatorsHash - tvalHash := tmTrustedValidators.Hash() - if !bytes.Equal(consState.NextValidatorsHash, tvalHash) { - return sdkerrors.Wrapf( - ErrInvalidValidatorSet, - "trusted validators %s, does not hash to latest trusted validators. Expected: %X, got: %X", - header.TrustedValidators, consState.NextValidatorsHash, tvalHash, - ) - } - return nil -} - // VerifyClientMessage checks if the clientMessage is of type Header or Misbehaviour and verifies the message func (cs *ClientState) VerifyClientMessage( ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, @@ -190,15 +170,13 @@ func (cs ClientState) pruneOldestConsensusState(ctx sdk.Context, cdc codec.Binar // so that we can delete consensus state and all associated metadata. var ( pruneHeight exported.Height - pruneError error ) pruneCb := func(height exported.Height) bool { consState, found := GetConsensusState(clientStore, cdc, height) // this error should never occur if !found { - pruneError = sdkerrors.Wrapf(clienttypes.ErrConsensusStateNotFound, "failed to retrieve consensus state at height: %s", height) - return true + panic(sdkerrors.Wrapf(clienttypes.ErrConsensusStateNotFound, "failed to retrieve consensus state at height: %s", height)) } if cs.IsExpired(consState.Timestamp, ctx.BlockTime()) { @@ -209,9 +187,6 @@ func (cs ClientState) pruneOldestConsensusState(ctx sdk.Context, cdc codec.Binar } IterateConsensusStateAscending(clientStore, pruneCb) - if pruneError != nil { - panic(pruneError) - } // if pruneHeight is set, delete consensus state and metadata if pruneHeight != nil { @@ -230,11 +205,11 @@ func (cs ClientState) CheckForMisbehaviour(ctx sdk.Context, cdc codec.BinaryCode // Check if the Client store already has a consensus state for the header's height // If the consensus state exists, and it matches the header then we return early // since header has already been submitted in a previous UpdateClient. - prevConsState, _ := GetConsensusState(clientStore, cdc, tmHeader.GetHeight()) - if prevConsState != nil { + existingConsState, _ := GetConsensusState(clientStore, cdc, tmHeader.GetHeight()) + if existingConsState != nil { // This header has already been submitted and the necessary state is already stored // in client store, thus we can return early without further validation. - if reflect.DeepEqual(prevConsState, tmHeader.ConsensusState()) { + if reflect.DeepEqual(existingConsState, tmHeader.ConsensusState()) { return false } @@ -272,3 +247,23 @@ func (cs ClientState) UpdateStateOnMisbehaviour(ctx sdk.Context, cdc codec.Binar clientStore.Set(host.ClientStateKey(), clienttypes.MustMarshalClientState(cdc, &cs)) } + +// checkTrustedHeader checks that consensus state matches trusted fields of Header +func checkTrustedHeader(header *Header, consState *ConsensusState) error { + tmTrustedValidators, err := tmtypes.ValidatorSetFromProto(header.TrustedValidators) + if err != nil { + return sdkerrors.Wrap(err, "trusted validator set in not tendermint validator set type") + } + + // assert that trustedVals is NextValidators of last trusted header + // to do this, we check that trustedVals.Hash() == consState.NextValidatorsHash + tvalHash := tmTrustedValidators.Hash() + if !bytes.Equal(consState.NextValidatorsHash, tvalHash) { + return sdkerrors.Wrapf( + ErrInvalidValidatorSet, + "trusted validators %s, does not hash to latest trusted validators. Expected: %X, got: %X", + header.TrustedValidators, consState.NextValidatorsHash, tvalHash, + ) + } + return nil +} From 4def1963fba92f99dff228747ce8ed082e7ea22d Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Mon, 4 Jul 2022 12:38:43 +0200 Subject: [PATCH 43/71] chore: add generic proof verification methods to ClientState interface (#1645) * adding generic proof verification methods to ClientState interface * adding function boilerplate to solomachine and legacy v100 solomachine * adding comments for todos --- .../core/02-client/legacy/v100/solomachine.go | 29 ++++++++++++++++ modules/core/exported/client.go | 27 +++++++++++++++ .../06-solomachine/types/client_state.go | 33 +++++++++++++++++++ 3 files changed, 89 insertions(+) diff --git a/modules/core/02-client/legacy/v100/solomachine.go b/modules/core/02-client/legacy/v100/solomachine.go index 3080c5c8ce4..e264b354ca1 100644 --- a/modules/core/02-client/legacy/v100/solomachine.go +++ b/modules/core/02-client/legacy/v100/solomachine.go @@ -211,6 +211,35 @@ func (cs ClientState) VerifyNextSequenceRecv( panic("legacy solo machine is deprecated!") } +// VerifyMembership panics! +func (cs *ClientState) VerifyMembership( + ctx sdk.Context, + clientStore sdk.KVStore, + cdc codec.BinaryCodec, + height exported.Height, + delayTimePeriod uint64, + delayBlockPeriod uint64, + proof []byte, + path []byte, + value []byte, +) error { + panic("legacy solo machine is deprecated!") +} + +// VerifyNonMembership panics! +func (cs *ClientState) VerifyNonMembership( + ctx sdk.Context, + clientStore sdk.KVStore, + cdc codec.BinaryCodec, + height exported.Height, + delayTimePeriod uint64, + delayBlockPeriod uint64, + proof []byte, + path []byte, +) error { + panic("legacy solo machine is deprecated") +} + // ClientType panics! func (ConsensusState) ClientType() string { panic("legacy solo machine is deprecated!") diff --git a/modules/core/exported/client.go b/modules/core/exported/client.go index b4ff463e5fc..c319b7bd139 100644 --- a/modules/core/exported/client.go +++ b/modules/core/exported/client.go @@ -95,6 +95,33 @@ type ClientState interface { // State verification functions + // VerifyMembership is a generic proof verification method which verifies a proof of the existence of a value at a given CommitmentPath at the specified height. + // The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). + VerifyMembership( + ctx sdk.Context, + clientStore sdk.KVStore, + cdc codec.BinaryCodec, + height Height, + delayTimePeriod uint64, + delayBlockPeriod uint64, + proof []byte, + path []byte, + value []byte, + ) error + + // VerifyNonMembership is a generic proof verification method which verifies the absense of a given CommitmentPath at a specified height. + // The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). + VerifyNonMembership( + ctx sdk.Context, + clientStore sdk.KVStore, + cdc codec.BinaryCodec, + height Height, + delayTimePeriod uint64, + delayBlockPeriod uint64, + proof []byte, + path []byte, + ) error + VerifyClientState( store sdk.KVStore, cdc codec.BinaryCodec, diff --git a/modules/light-clients/06-solomachine/types/client_state.go b/modules/light-clients/06-solomachine/types/client_state.go index ef3088b314f..9cd172925a1 100644 --- a/modules/light-clients/06-solomachine/types/client_state.go +++ b/modules/light-clients/06-solomachine/types/client_state.go @@ -415,6 +415,39 @@ func (cs *ClientState) VerifyNextSequenceRecv( return nil } +// VerifyMembership is a generic proof verification method which verifies a proof of the existence of a value at a given CommitmentPath at the specified height. +// The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). +func (cs *ClientState) VerifyMembership( + ctx sdk.Context, + clientStore sdk.KVStore, + cdc codec.BinaryCodec, + height exported.Height, + delayTimePeriod uint64, + delayBlockPeriod uint64, + proof []byte, + path []byte, + value []byte, +) error { + // TODO: Implement 06-solomachine VerifyMembership + return nil +} + +// VerifyNonMembership is a generic proof verification method which verifies the absense of a given CommitmentPath at a specified height. +// The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). +func (cs *ClientState) VerifyNonMembership( + ctx sdk.Context, + clientStore sdk.KVStore, + cdc codec.BinaryCodec, + height exported.Height, + delayTimePeriod uint64, + delayBlockPeriod uint64, + proof []byte, + path []byte, +) error { + // TODO: Implement 06-solomachine VerifyNonMembership + return nil +} + // produceVerificationArgs perfoms the basic checks on the arguments that are // shared between the verification functions and returns the public key of the // consensus state, the unmarshalled proof representing the signature and timestamp From 32c4827a8521c0d1965d3a5152c1cc6c608bb2ea Mon Sep 17 00:00:00 2001 From: Charly Date: Wed, 6 Jul 2022 14:31:27 +0200 Subject: [PATCH 44/71] chore: add GetTimestampAtHeight to client state #888 (#1659) Co-authored-by: Bo Du --- CHANGELOG.md | 1 + .../core/02-client/legacy/v100/solomachine.go | 7 +++ modules/core/exported/client.go | 6 +++ .../06-solomachine/types/client_state.go | 13 +++++ .../06-solomachine/types/client_state_test.go | 50 ++++++++++++++++++- .../07-tendermint/types/client_state.go | 15 ++++++ .../07-tendermint/types/client_state_test.go | 47 +++++++++++++++++ 7 files changed, 138 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a43567ede3..c021f77df94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -121,6 +121,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Improvements +* (client) [\#888](https://github.com/cosmos/ibc-go/pull/888) Add `GetTimestampAtHeight` to `ClientState` * (interchain-accounts) [\#1037](https://github.com/cosmos/ibc-go/pull/1037) Add a function `InitModule` to the interchain accounts `AppModule`. This function should be called within the upgrade handler when adding the interchain accounts module to a chain. It should be called in place of InitGenesis (set the consensus version in the version map). * (testing) [\#1003](https://github.com/cosmos/ibc-go/pull/1003) Testing chain's `Signer` fields has changed from `[]tmtypes.PrivValidator` to `map[string]tmtypes.PrivValidator` to accomodate valset updates changing the order of the ValidatorSet. * (testing) [\#1003](https://github.com/cosmos/ibc-go/pull/1003) `SignAndDeliver` will now just deliver the transaction without creating and committing a block. Thus, it requires that `BeginBlock` MUST be called before `SignAndDeliver` diff --git a/modules/core/02-client/legacy/v100/solomachine.go b/modules/core/02-client/legacy/v100/solomachine.go index e264b354ca1..d41d6c31dbc 100644 --- a/modules/core/02-client/legacy/v100/solomachine.go +++ b/modules/core/02-client/legacy/v100/solomachine.go @@ -211,6 +211,13 @@ func (cs ClientState) VerifyNextSequenceRecv( panic("legacy solo machine is deprecated!") } +// GetTimestampAtHeight panics! +func (cs ClientState) GetTimestampAtHeight( + sdk.Context, sdk.KVStore, codec.BinaryCodec, exported.Height, +) (uint64, error) { + panic("legacy solo machine is deprecated!") +} + // VerifyMembership panics! func (cs *ClientState) VerifyMembership( ctx sdk.Context, diff --git a/modules/core/exported/client.go b/modules/core/exported/client.go index c319b7bd139..f71715aa6c5 100644 --- a/modules/core/exported/client.go +++ b/modules/core/exported/client.go @@ -214,6 +214,12 @@ type ClientState interface { channelID string, nextSequenceRecv uint64, ) error + GetTimestampAtHeight( + ctx sdk.Context, + clientStore sdk.KVStore, + cdc codec.BinaryCodec, + height Height, + ) (uint64, error) } // ConsensusState is the state of the consensus process diff --git a/modules/light-clients/06-solomachine/types/client_state.go b/modules/light-clients/06-solomachine/types/client_state.go index 9cd172925a1..06dbccb6e64 100644 --- a/modules/light-clients/06-solomachine/types/client_state.go +++ b/modules/light-clients/06-solomachine/types/client_state.go @@ -39,6 +39,19 @@ func (cs ClientState) GetLatestHeight() exported.Height { return clienttypes.NewHeight(0, cs.Sequence) } +// GetTimestampAtHeight returns the timestamp in nanoseconds of the consensus state at the given height. +func (cs ClientState) GetTimestampAtHeight( + _ sdk.Context, + clientStore sdk.KVStore, + cdc codec.BinaryCodec, + height exported.Height, +) (uint64, error) { + if !cs.GetLatestHeight().EQ(height) { + return 0, sdkerrors.Wrapf(ErrInvalidSequence, "not latest height (%s)", height) + } + return cs.ConsensusState.Timestamp, nil +} + // Status returns the status of the solo machine client. // The client may be: // - Active: if frozen sequence is 0 diff --git a/modules/light-clients/06-solomachine/types/client_state_test.go b/modules/light-clients/06-solomachine/types/client_state_test.go index 09ea9693119..3e0c4904b7b 100644 --- a/modules/light-clients/06-solomachine/types/client_state_test.go +++ b/modules/light-clients/06-solomachine/types/client_state_test.go @@ -27,7 +27,7 @@ var ( func (suite *SoloMachineTestSuite) TestStatus() { clientState := suite.solomachine.ClientState() - // solo machine discards arguements + // solo machine discards arguments status := clientState.Status(suite.chainA.GetContext(), nil, nil) suite.Require().Equal(exported.Active, status) @@ -845,3 +845,51 @@ func (suite *SoloMachineTestSuite) TestVerifyNextSeqRecv() { } } } + +func (suite *SoloMachineTestSuite) TestGetTimestampAtHeight() { + tmPath := ibctesting.NewPath(suite.chainA, suite.chainB) + suite.coordinator.SetupClients(tmPath) + // Single setup for all test cases. + suite.SetupTest() + + testCases := []struct { + name string + clientState *types.ClientState + height exported.Height + expValue uint64 + expPass bool + }{ + { + name: "get timestamp at height exists", + clientState: suite.solomachine.ClientState(), + height: suite.solomachine.ClientState().GetLatestHeight(), + expValue: suite.solomachine.ClientState().ConsensusState.Timestamp, + expPass: true, + }, + { + name: "get timestamp at height not exists", + clientState: suite.solomachine.ClientState(), + height: suite.solomachine.ClientState().GetLatestHeight().Increment(), + }, + } + + for i, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + ctx := suite.chainA.GetContext() + + ts, err := tc.clientState.GetTimestampAtHeight( + ctx, suite.store, suite.chainA.Codec, tc.height, + ) + + suite.Require().Equal(tc.expValue, ts) + + if tc.expPass { + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) + } else { + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) + } + }) + } +} diff --git a/modules/light-clients/07-tendermint/types/client_state.go b/modules/light-clients/07-tendermint/types/client_state.go index c78d54b3168..029f8da0932 100644 --- a/modules/light-clients/07-tendermint/types/client_state.go +++ b/modules/light-clients/07-tendermint/types/client_state.go @@ -58,6 +58,21 @@ func (cs ClientState) GetLatestHeight() exported.Height { return cs.LatestHeight } +// GetTimestampAtHeight returns the timestamp in nanoseconds of the consensus state at the given height. +func (cs ClientState) GetTimestampAtHeight( + ctx sdk.Context, + clientStore sdk.KVStore, + cdc codec.BinaryCodec, + height exported.Height, +) (uint64, error) { + // get consensus state at height from clientStore to check for expiry + consState, found := GetConsensusState(clientStore, cdc, height) + if !found { + return 0, sdkerrors.Wrapf(clienttypes.ErrConsensusStateNotFound, "height (%s)", height) + } + return consState.GetTimestamp(), nil +} + // Status returns the status of the tendermint client. // The client may be: // - Active: FrozenHeight is zero and client is not expired diff --git a/modules/light-clients/07-tendermint/types/client_state_test.go b/modules/light-clients/07-tendermint/types/client_state_test.go index b9d7c7f7016..0a46d6dd7f0 100644 --- a/modules/light-clients/07-tendermint/types/client_state_test.go +++ b/modules/light-clients/07-tendermint/types/client_state_test.go @@ -1390,3 +1390,50 @@ func (suite *TendermintTestSuite) TestVerifyNextSeqRecv() { }) } } + +func (suite *TendermintTestSuite) TestGetTimestampAtHeight() { + suite.SetupTest() + + path := ibctesting.NewPath(suite.chainA, suite.chainB) + path.SetChannelOrdered() + suite.coordinator.Setup(path) + + ctx := suite.chainA.GetContext() + clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) + clientState := path.EndpointA.GetClientState() + + testCases := []struct { + name string + height exported.Height + expValue uint64 + expPass bool + }{ + { + name: "get timestamp at height exists", + height: clientState.GetLatestHeight(), + expValue: path.EndpointA.GetConsensusState(clientState.GetLatestHeight()).GetTimestamp(), + expPass: true, + }, + { + name: "get timestamp at height not exists", + height: clientState.GetLatestHeight().Increment(), + }, + } + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + ts, err := clientState.GetTimestampAtHeight( + ctx, clientStore, suite.chainA.Codec, tc.height, + ) + + suite.Require().Equal(tc.expValue, ts) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} From 1617de7126b403b0cb545064ec86598ecc576a5c Mon Sep 17 00:00:00 2001 From: Charly Date: Thu, 7 Jul 2022 15:48:18 +0200 Subject: [PATCH 45/71] chore: modify connection keeper GetTimestampAtHeight to use the client state interface function (#1666) * updated connection keeper to use clientstate to get timestamp, updated tests --- modules/core/02-client/keeper/client.go | 4 ++-- modules/core/02-client/keeper/proposal.go | 2 +- modules/core/02-client/types/errors.go | 2 +- modules/core/03-connection/keeper/keeper.go | 15 ++++++------ .../core/03-connection/keeper/keeper_test.go | 16 +++++++++---- modules/core/03-connection/keeper/verify.go | 16 ++++++------- modules/core/04-channel/keeper/packet.go | 23 ++++++------------- modules/core/04-channel/keeper/packet_test.go | 17 ++++++++++++++ 8 files changed, 56 insertions(+), 39 deletions(-) diff --git a/modules/core/02-client/keeper/client.go b/modules/core/02-client/keeper/client.go index a1646da9c6b..9fc10fe3d1b 100644 --- a/modules/core/02-client/keeper/client.go +++ b/modules/core/02-client/keeper/client.go @@ -59,7 +59,7 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, clientMsg exporte clientStore := k.ClientStore(ctx, clientID) if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(types.ErrClientNotActive, "cannot update client (%s) with status %s", clientID, status) + return sdkerrors.Wrapf(types.ErrClientStateNotFound, "cannot update client (%s) with status %s", clientID, status) } if err := clientState.VerifyClientMessage(ctx, k.cdc, clientStore, clientMsg); err != nil { @@ -119,7 +119,7 @@ func (k Keeper) UpgradeClient(ctx sdk.Context, clientID string, upgradedClient e clientStore := k.ClientStore(ctx, clientID) if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(types.ErrClientNotActive, "cannot upgrade client (%s) with status %s", clientID, status) + return sdkerrors.Wrapf(types.ErrClientStateNotFound, "cannot upgrade client (%s) with status %s", clientID, status) } if err := clientState.VerifyUpgradeAndUpdateState(ctx, k.cdc, clientStore, diff --git a/modules/core/02-client/keeper/proposal.go b/modules/core/02-client/keeper/proposal.go index ffe29f688cb..115707777e5 100644 --- a/modules/core/02-client/keeper/proposal.go +++ b/modules/core/02-client/keeper/proposal.go @@ -41,7 +41,7 @@ func (k Keeper) ClientUpdateProposal(ctx sdk.Context, p *types.ClientUpdatePropo substituteClientStore := k.ClientStore(ctx, p.SubstituteClientId) if status := substituteClientState.Status(ctx, substituteClientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(types.ErrClientNotActive, "substitute client is not Active, status is %s", status) + return sdkerrors.Wrapf(types.ErrClientStateNotFound, "substitute client is not Active, status is %s", status) } clientState, err := subjectClientState.CheckSubstituteAndUpdateState(ctx, k.cdc, subjectClientStore, substituteClientStore, substituteClientState) diff --git a/modules/core/02-client/types/errors.go b/modules/core/02-client/types/errors.go index c40bae878cc..f97ab0014cb 100644 --- a/modules/core/02-client/types/errors.go +++ b/modules/core/02-client/types/errors.go @@ -33,5 +33,5 @@ var ( ErrInvalidHeight = sdkerrors.Register(SubModuleName, 26, "invalid height") ErrInvalidSubstitute = sdkerrors.Register(SubModuleName, 27, "invalid client state substitute") ErrInvalidUpgradeProposal = sdkerrors.Register(SubModuleName, 28, "invalid upgrade proposal") - ErrClientNotActive = sdkerrors.Register(SubModuleName, 29, "client is not active") + ErrClientStateNotFound = sdkerrors.Register(SubModuleName, 29, "client state not found") ) diff --git a/modules/core/03-connection/keeper/keeper.go b/modules/core/03-connection/keeper/keeper.go index 22954ca7943..8221dd597bc 100644 --- a/modules/core/03-connection/keeper/keeper.go +++ b/modules/core/03-connection/keeper/keeper.go @@ -86,18 +86,19 @@ func (k Keeper) SetConnection(ctx sdk.Context, connectionID string, connection t // GetTimestampAtHeight returns the timestamp in nanoseconds of the consensus state at the // given height. func (k Keeper) GetTimestampAtHeight(ctx sdk.Context, connection types.ConnectionEnd, height exported.Height) (uint64, error) { - consensusState, found := k.clientKeeper.GetClientConsensusState( - ctx, connection.GetClientID(), height, - ) - + clientState, found := k.clientKeeper.GetClientState(ctx, connection.GetClientID()) if !found { return 0, sdkerrors.Wrapf( - clienttypes.ErrConsensusStateNotFound, - "clientID (%s), height (%s)", connection.GetClientID(), height, + clienttypes.ErrClientStateNotFound, "clientID (%s)", connection.GetClientID(), ) } - return consensusState.GetTimestamp(), nil + timestamp, err := clientState.GetTimestampAtHeight(ctx, k.clientKeeper.ClientStore(ctx, connection.GetClientID()), k.cdc, height) + if err != nil { + return 0, err + } + + return timestamp, nil } // GetClientConnectionPaths returns all the connection paths stored under a diff --git a/modules/core/03-connection/keeper/keeper_test.go b/modules/core/03-connection/keeper/keeper_test.go index b4dfa839028..0c25d51c452 100644 --- a/modules/core/03-connection/keeper/keeper_test.go +++ b/modules/core/03-connection/keeper/keeper_test.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/suite" "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v3/modules/core/exported" ibctesting "github.com/cosmos/ibc-go/v3/testing" ) @@ -112,7 +113,10 @@ func (suite KeeperTestSuite) TestGetAllClientConnectionPaths() { // TestGetTimestampAtHeight verifies if the clients on each chain return the // correct timestamp for the other chain. func (suite *KeeperTestSuite) TestGetTimestampAtHeight() { - var connection types.ConnectionEnd + var ( + connection types.ConnectionEnd + height exported.Height + ) cases := []struct { msg string @@ -123,10 +127,14 @@ func (suite *KeeperTestSuite) TestGetTimestampAtHeight() { path := ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupConnections(path) connection = path.EndpointA.GetConnection() + height = suite.chainB.LastHeader.GetHeight() }, true}, + {"client state not found", func() {}, false}, {"consensus state not found", func() { - // any non-nil value of connection is valid - suite.Require().NotNil(connection) + path := ibctesting.NewPath(suite.chainA, suite.chainB) + suite.coordinator.SetupConnections(path) + connection = path.EndpointA.GetConnection() + height = suite.chainB.LastHeader.GetHeight().Increment() }, false}, } @@ -137,7 +145,7 @@ func (suite *KeeperTestSuite) TestGetTimestampAtHeight() { tc.malleate() actualTimestamp, err := suite.chainA.App.GetIBCKeeper().ConnectionKeeper.GetTimestampAtHeight( - suite.chainA.GetContext(), connection, suite.chainB.LastHeader.GetHeight(), + suite.chainA.GetContext(), connection, height, ) if tc.expPass { diff --git a/modules/core/03-connection/keeper/verify.go b/modules/core/03-connection/keeper/verify.go index 1059cf3444e..abdad3fd002 100644 --- a/modules/core/03-connection/keeper/verify.go +++ b/modules/core/03-connection/keeper/verify.go @@ -28,7 +28,7 @@ func (k Keeper) VerifyClientState( } if status := targetClient.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) + return sdkerrors.Wrapf(clienttypes.ErrClientStateNotFound, "client (%s) status is %s", clientID, status) } if err := targetClient.VerifyClientState( @@ -59,7 +59,7 @@ func (k Keeper) VerifyClientConsensusState( } if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) + return sdkerrors.Wrapf(clienttypes.ErrClientStateNotFound, "client (%s) status is %s", clientID, status) } if err := clientState.VerifyClientConsensusState( @@ -91,7 +91,7 @@ func (k Keeper) VerifyConnectionState( } if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) + return sdkerrors.Wrapf(clienttypes.ErrClientStateNotFound, "client (%s) status is %s", clientID, status) } if err := clientState.VerifyConnectionState( @@ -124,7 +124,7 @@ func (k Keeper) VerifyChannelState( } if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) + return sdkerrors.Wrapf(clienttypes.ErrClientStateNotFound, "client (%s) status is %s", clientID, status) } if err := clientState.VerifyChannelState( @@ -159,7 +159,7 @@ func (k Keeper) VerifyPacketCommitment( } if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) + return sdkerrors.Wrapf(clienttypes.ErrClientStateNotFound, "client (%s) status is %s", clientID, status) } // get time and block delays @@ -199,7 +199,7 @@ func (k Keeper) VerifyPacketAcknowledgement( } if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) + return sdkerrors.Wrapf(clienttypes.ErrClientStateNotFound, "client (%s) status is %s", clientID, status) } // get time and block delays @@ -239,7 +239,7 @@ func (k Keeper) VerifyPacketReceiptAbsence( } if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) + return sdkerrors.Wrapf(clienttypes.ErrClientStateNotFound, "client (%s) status is %s", clientID, status) } // get time and block delays @@ -278,7 +278,7 @@ func (k Keeper) VerifyNextSequenceRecv( } if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) + return sdkerrors.Wrapf(clienttypes.ErrClientStateNotFound, "client (%s) status is %s", clientID, status) } // get time and block delays diff --git a/modules/core/04-channel/keeper/packet.go b/modules/core/04-channel/keeper/packet.go index c3e8e45b89c..05be506077a 100644 --- a/modules/core/04-channel/keeper/packet.go +++ b/modules/core/04-channel/keeper/packet.go @@ -71,7 +71,7 @@ func (k Keeper) SendPacket( // prevent accidental sends with clients that cannot be updated clientStore := k.clientKeeper.ClientStore(ctx, connectionEnd.GetClientID()) if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(clienttypes.ErrClientNotActive, "cannot send packet using client (%s) with status %s", connectionEnd.GetClientID(), status) + return sdkerrors.Wrapf(clienttypes.ErrClientStateNotFound, "cannot send packet using client (%s) with status %s", connectionEnd.GetClientID(), status) } // check if packet is timed out on the receiving chain @@ -84,25 +84,16 @@ func (k Keeper) SendPacket( ) } - clientType, _, err := clienttypes.ParseClientIdentifier(connectionEnd.GetClientID()) + latestTimestamp, err := k.connectionKeeper.GetTimestampAtHeight(ctx, connectionEnd, latestHeight) if err != nil { return err } - // NOTE: this is a temporary fix. Solo machine does not support usage of 'GetTimestampAtHeight' - // A future change should move this function to be a ClientState callback. - if clientType != exported.Solomachine { - latestTimestamp, err := k.connectionKeeper.GetTimestampAtHeight(ctx, connectionEnd, latestHeight) - if err != nil { - return err - } - - if packet.GetTimeoutTimestamp() != 0 && latestTimestamp >= packet.GetTimeoutTimestamp() { - return sdkerrors.Wrapf( - types.ErrPacketTimeout, - "receiving chain block timestamp >= packet timeout timestamp (%s >= %s)", time.Unix(0, int64(latestTimestamp)), time.Unix(0, int64(packet.GetTimeoutTimestamp())), - ) - } + if packet.GetTimeoutTimestamp() != 0 && latestTimestamp >= packet.GetTimeoutTimestamp() { + return sdkerrors.Wrapf( + types.ErrPacketTimeout, + "receiving chain block timestamp >= packet timeout timestamp (%s >= %s)", time.Unix(0, int64(latestTimestamp)), time.Unix(0, int64(packet.GetTimeoutTimestamp())), + ) } nextSequenceSend, found := k.GetNextSequenceSend(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) diff --git a/modules/core/04-channel/keeper/packet_test.go b/modules/core/04-channel/keeper/packet_test.go index 1f7e5546c70..66bc6846450 100644 --- a/modules/core/04-channel/keeper/packet_test.go +++ b/modules/core/04-channel/keeper/packet_test.go @@ -177,6 +177,23 @@ func (suite *KeeperTestSuite) TestSendPacket() { packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, disabledTimeoutHeight, timestamp) channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) }, false}, + {"timeout timestamp passed with solomachine", func() { + suite.coordinator.Setup(path) + // swap client with solomachine + solomachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachinesingle", "testing", 1) + path.EndpointA.ClientID = clienttypes.FormatClientIdentifier(exported.Solomachine, 10) + path.EndpointA.SetClientState(solomachine.ClientState()) + connection := path.EndpointA.GetConnection() + connection.ClientId = path.EndpointA.ClientID + path.EndpointA.SetConnection(connection) + + clientState := path.EndpointA.GetClientState() + timestamp, err := suite.chainA.App.GetIBCKeeper().ConnectionKeeper.GetTimestampAtHeight(suite.chainA.GetContext(), connection, clientState.GetLatestHeight()) + suite.Require().NoError(err) + + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, disabledTimeoutHeight, timestamp) + channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) + }, false}, {"next sequence send not found", func() { path := ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupConnections(path) From 3987a5bf20c9cde5c5ab23216f1c4476a80bbf65 Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Thu, 7 Jul 2022 15:55:21 +0100 Subject: [PATCH 46/71] refactor: replace usage of verification funcs in 03-connection (#1647) * replacing 03-connection verifications with generic membership methods * adding inline comments for skipping delay period checks * chore: removing legacy verify funcs from `ClientState` interface (#1649) * removing legacy verify funcs from clientstate interface * chore: remove legacy verify funcs tm client (#1651) * removing legacy verification methods from tendermint client * removing commented out tests * fixing interface definition --- modules/core/03-connection/keeper/verify.go | 174 ++++- modules/core/exported/client.go | 92 --- .../07-tendermint/types/client_state.go | 374 --------- .../07-tendermint/types/client_state_test.go | 739 ------------------ 4 files changed, 148 insertions(+), 1231 deletions(-) diff --git a/modules/core/03-connection/keeper/verify.go b/modules/core/03-connection/keeper/verify.go index abdad3fd002..d3807b1f104 100644 --- a/modules/core/03-connection/keeper/verify.go +++ b/modules/core/03-connection/keeper/verify.go @@ -7,6 +7,10 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/exported" ) @@ -31,9 +35,27 @@ func (k Keeper) VerifyClientState( return sdkerrors.Wrapf(clienttypes.ErrClientStateNotFound, "client (%s) status is %s", clientID, status) } - if err := targetClient.VerifyClientState( - clientStore, k.cdc, height, - connection.GetCounterparty().GetPrefix(), connection.GetCounterparty().GetClientID(), proof, clientState); err != nil { + merklePath := commitmenttypes.NewMerklePath(host.FullClientStatePath(connection.GetCounterparty().GetClientID())) + merklePath, err := commitmenttypes.ApplyPrefix(connection.GetCounterparty().GetPrefix(), merklePath) + if err != nil { + return err + } + + path, err := k.cdc.Marshal(&merklePath) + if err != nil { + return err + } + + bz, err := k.cdc.MarshalInterface(clientState) + if err != nil { + return err + } + + if err := targetClient.VerifyMembership( + ctx, clientStore, k.cdc, height, + 0, 0, // skip delay period checks for non-packet processing verification + proof, path, bz, + ); err != nil { return sdkerrors.Wrapf(err, "failed client state verification for target client: %s", clientID) } @@ -62,9 +84,26 @@ func (k Keeper) VerifyClientConsensusState( return sdkerrors.Wrapf(clienttypes.ErrClientStateNotFound, "client (%s) status is %s", clientID, status) } - if err := clientState.VerifyClientConsensusState( - clientStore, k.cdc, height, - connection.GetCounterparty().GetClientID(), consensusHeight, connection.GetCounterparty().GetPrefix(), proof, consensusState, + merklePath := commitmenttypes.NewMerklePath(host.FullConsensusStatePath(connection.GetCounterparty().GetClientID(), consensusHeight)) + merklePath, err := commitmenttypes.ApplyPrefix(connection.GetCounterparty().GetPrefix(), merklePath) + if err != nil { + return err + } + + path, err := k.cdc.Marshal(&merklePath) + if err != nil { + return err + } + + bz, err := k.cdc.MarshalInterface(consensusState) + if err != nil { + return err + } + + if err := clientState.VerifyMembership( + ctx, clientStore, k.cdc, height, + 0, 0, // skip delay period checks for non-packet processing verification + proof, path, bz, ); err != nil { return sdkerrors.Wrapf(err, "failed consensus state verification for client (%s)", clientID) } @@ -80,7 +119,7 @@ func (k Keeper) VerifyConnectionState( height exported.Height, proof []byte, connectionID string, - connectionEnd exported.ConnectionI, // opposite connection + counterpartyConnection exported.ConnectionI, // opposite connection ) error { clientID := connection.GetClientID() clientStore := k.clientKeeper.ClientStore(ctx, clientID) @@ -94,9 +133,31 @@ func (k Keeper) VerifyConnectionState( return sdkerrors.Wrapf(clienttypes.ErrClientStateNotFound, "client (%s) status is %s", clientID, status) } - if err := clientState.VerifyConnectionState( - clientStore, k.cdc, height, - connection.GetCounterparty().GetPrefix(), proof, connectionID, connectionEnd, + merklePath := commitmenttypes.NewMerklePath(host.ConnectionPath(connectionID)) + merklePath, err := commitmenttypes.ApplyPrefix(connection.GetCounterparty().GetPrefix(), merklePath) + if err != nil { + return err + } + + path, err := k.cdc.Marshal(&merklePath) + if err != nil { + return err + } + + connectionEnd, ok := counterpartyConnection.(connectiontypes.ConnectionEnd) + if !ok { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "invalid connection type %T", counterpartyConnection) + } + + bz, err := k.cdc.Marshal(&connectionEnd) + if err != nil { + return err + } + + if err := clientState.VerifyMembership( + ctx, clientStore, k.cdc, height, + 0, 0, // skip delay period checks for non-packet processing verification + proof, path, bz, ); err != nil { return sdkerrors.Wrapf(err, "failed connection state verification for client (%s)", clientID) } @@ -127,10 +188,31 @@ func (k Keeper) VerifyChannelState( return sdkerrors.Wrapf(clienttypes.ErrClientStateNotFound, "client (%s) status is %s", clientID, status) } - if err := clientState.VerifyChannelState( - clientStore, k.cdc, height, - connection.GetCounterparty().GetPrefix(), proof, - portID, channelID, channel, + merklePath := commitmenttypes.NewMerklePath(host.ChannelPath(portID, channelID)) + merklePath, err := commitmenttypes.ApplyPrefix(connection.GetCounterparty().GetPrefix(), merklePath) + if err != nil { + return err + } + + path, err := k.cdc.Marshal(&merklePath) + if err != nil { + return err + } + + channelEnd, ok := channel.(channeltypes.Channel) + if !ok { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "invalid channel type %T", channel) + } + + bz, err := k.cdc.Marshal(&channelEnd) + if err != nil { + return err + } + + if err := clientState.VerifyMembership( + ctx, clientStore, k.cdc, height, + 0, 0, // skip delay period checks for non-packet processing verification + proof, path, bz, ); err != nil { return sdkerrors.Wrapf(err, "failed channel state verification for client (%s)", clientID) } @@ -166,11 +248,21 @@ func (k Keeper) VerifyPacketCommitment( timeDelay := connection.GetDelayPeriod() blockDelay := k.getBlockDelay(ctx, connection) - if err := clientState.VerifyPacketCommitment( + merklePath := commitmenttypes.NewMerklePath(host.PacketCommitmentPath(portID, channelID, sequence)) + merklePath, err := commitmenttypes.ApplyPrefix(connection.GetCounterparty().GetPrefix(), merklePath) + if err != nil { + return err + } + + path, err := k.cdc.Marshal(&merklePath) + if err != nil { + return err + } + + if err := clientState.VerifyMembership( ctx, clientStore, k.cdc, height, timeDelay, blockDelay, - connection.GetCounterparty().GetPrefix(), proof, portID, channelID, - sequence, commitmentBytes, + proof, path, commitmentBytes, ); err != nil { return sdkerrors.Wrapf(err, "failed packet commitment verification for client (%s)", clientID) } @@ -206,11 +298,21 @@ func (k Keeper) VerifyPacketAcknowledgement( timeDelay := connection.GetDelayPeriod() blockDelay := k.getBlockDelay(ctx, connection) - if err := clientState.VerifyPacketAcknowledgement( + merklePath := commitmenttypes.NewMerklePath(host.PacketAcknowledgementPath(portID, channelID, sequence)) + merklePath, err := commitmenttypes.ApplyPrefix(connection.GetCounterparty().GetPrefix(), merklePath) + if err != nil { + return err + } + + path, err := k.cdc.Marshal(&merklePath) + if err != nil { + return err + } + + if err := clientState.VerifyMembership( ctx, clientStore, k.cdc, height, timeDelay, blockDelay, - connection.GetCounterparty().GetPrefix(), proof, portID, channelID, - sequence, acknowledgement, + proof, path, channeltypes.CommitAcknowledgement(acknowledgement), ); err != nil { return sdkerrors.Wrapf(err, "failed packet acknowledgement verification for client (%s)", clientID) } @@ -246,11 +348,21 @@ func (k Keeper) VerifyPacketReceiptAbsence( timeDelay := connection.GetDelayPeriod() blockDelay := k.getBlockDelay(ctx, connection) - if err := clientState.VerifyPacketReceiptAbsence( + merklePath := commitmenttypes.NewMerklePath(host.PacketReceiptPath(portID, channelID, sequence)) + merklePath, err := commitmenttypes.ApplyPrefix(connection.GetCounterparty().GetPrefix(), merklePath) + if err != nil { + return err + } + + path, err := k.cdc.Marshal(&merklePath) + if err != nil { + return err + } + + if err := clientState.VerifyNonMembership( ctx, clientStore, k.cdc, height, timeDelay, blockDelay, - connection.GetCounterparty().GetPrefix(), proof, portID, channelID, - sequence, + proof, path, ); err != nil { return sdkerrors.Wrapf(err, "failed packet receipt absence verification for client (%s)", clientID) } @@ -285,11 +397,21 @@ func (k Keeper) VerifyNextSequenceRecv( timeDelay := connection.GetDelayPeriod() blockDelay := k.getBlockDelay(ctx, connection) - if err := clientState.VerifyNextSequenceRecv( + merklePath := commitmenttypes.NewMerklePath(host.NextSequenceRecvPath(portID, channelID)) + merklePath, err := commitmenttypes.ApplyPrefix(connection.GetCounterparty().GetPrefix(), merklePath) + if err != nil { + return err + } + + path, err := k.cdc.Marshal(&merklePath) + if err != nil { + return err + } + + if err := clientState.VerifyMembership( ctx, clientStore, k.cdc, height, timeDelay, blockDelay, - connection.GetCounterparty().GetPrefix(), proof, portID, channelID, - nextSequenceRecv, + proof, path, sdk.Uint64ToBigEndian(nextSequenceRecv), ); err != nil { return sdkerrors.Wrapf(err, "failed next sequence receive verification for client (%s)", clientID) } diff --git a/modules/core/exported/client.go b/modules/core/exported/client.go index f71715aa6c5..9d54a66c71b 100644 --- a/modules/core/exported/client.go +++ b/modules/core/exported/client.go @@ -122,98 +122,6 @@ type ClientState interface { path []byte, ) error - VerifyClientState( - store sdk.KVStore, - cdc codec.BinaryCodec, - height Height, - prefix Prefix, - counterpartyClientIdentifier string, - proof []byte, - clientState ClientState, - ) error - VerifyClientConsensusState( - store sdk.KVStore, - cdc codec.BinaryCodec, - height Height, - counterpartyClientIdentifier string, - consensusHeight Height, - prefix Prefix, - proof []byte, - consensusState ConsensusState, - ) error - VerifyConnectionState( - store sdk.KVStore, - cdc codec.BinaryCodec, - height Height, - prefix Prefix, - proof []byte, - connectionID string, - connectionEnd ConnectionI, - ) error - VerifyChannelState( - store sdk.KVStore, - cdc codec.BinaryCodec, - height Height, - prefix Prefix, - proof []byte, - portID, - channelID string, - channel ChannelI, - ) error - VerifyPacketCommitment( - ctx sdk.Context, - store sdk.KVStore, - cdc codec.BinaryCodec, - height Height, - delayTimePeriod uint64, - delayBlockPeriod uint64, - prefix Prefix, - proof []byte, - portID, - channelID string, - sequence uint64, - commitmentBytes []byte, - ) error - VerifyPacketAcknowledgement( - ctx sdk.Context, - store sdk.KVStore, - cdc codec.BinaryCodec, - height Height, - delayTimePeriod uint64, - delayBlockPeriod uint64, - prefix Prefix, - proof []byte, - portID, - channelID string, - sequence uint64, - acknowledgement []byte, - ) error - VerifyPacketReceiptAbsence( - ctx sdk.Context, - store sdk.KVStore, - cdc codec.BinaryCodec, - height Height, - delayTimePeriod uint64, - delayBlockPeriod uint64, - prefix Prefix, - proof []byte, - portID, - channelID string, - sequence uint64, - ) error - VerifyNextSequenceRecv( - ctx sdk.Context, - store sdk.KVStore, - cdc codec.BinaryCodec, - height Height, - delayTimePeriod uint64, - delayBlockPeriod uint64, - prefix Prefix, - proof []byte, - portID, - channelID string, - nextSequenceRecv uint64, - ) error GetTimestampAtHeight( ctx sdk.Context, clientStore sdk.KVStore, diff --git a/modules/light-clients/07-tendermint/types/client_state.go b/modules/light-clients/07-tendermint/types/client_state.go index 029f8da0932..1593f76c959 100644 --- a/modules/light-clients/07-tendermint/types/client_state.go +++ b/modules/light-clients/07-tendermint/types/client_state.go @@ -12,10 +12,7 @@ import ( tmtypes "github.com/tendermint/tendermint/types" clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/exported" ) @@ -205,333 +202,6 @@ func (cs ClientState) Initialize(ctx sdk.Context, _ codec.BinaryCodec, clientSto return nil } -// VerifyClientState verifies a proof of the client state of the running chain -// stored on the target machine -// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294 -func (cs ClientState) VerifyClientState( - store sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - prefix exported.Prefix, - counterpartyClientIdentifier string, - proof []byte, - clientState exported.ClientState, -) error { - merkleProof, provingConsensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) - if err != nil { - return err - } - - clientPrefixedPath := commitmenttypes.NewMerklePath(host.FullClientStatePath(counterpartyClientIdentifier)) - path, err := commitmenttypes.ApplyPrefix(prefix, clientPrefixedPath) - if err != nil { - return err - } - - if clientState == nil { - return sdkerrors.Wrap(clienttypes.ErrInvalidClient, "client state cannot be empty") - } - - _, ok := clientState.(*ClientState) - if !ok { - return sdkerrors.Wrapf(clienttypes.ErrInvalidClient, "invalid client type %T, expected %T", clientState, &ClientState{}) - } - - bz, err := cdc.MarshalInterface(clientState) - if err != nil { - return err - } - - return merkleProof.VerifyMembership(cs.ProofSpecs, provingConsensusState.GetRoot(), path, bz) -} - -// VerifyClientConsensusState verifies a proof of the consensus state of the -// Tendermint client stored on the target machine. -// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294 -func (cs ClientState) VerifyClientConsensusState( - store sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - counterpartyClientIdentifier string, - consensusHeight exported.Height, - prefix exported.Prefix, - proof []byte, - consensusState exported.ConsensusState, -) error { - merkleProof, provingConsensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) - if err != nil { - return err - } - - clientPrefixedPath := commitmenttypes.NewMerklePath(host.FullConsensusStatePath(counterpartyClientIdentifier, consensusHeight)) - path, err := commitmenttypes.ApplyPrefix(prefix, clientPrefixedPath) - if err != nil { - return err - } - - if consensusState == nil { - return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "consensus state cannot be empty") - } - - _, ok := consensusState.(*ConsensusState) - if !ok { - return sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "invalid consensus type %T, expected %T", consensusState, &ConsensusState{}) - } - - bz, err := cdc.MarshalInterface(consensusState) - if err != nil { - return err - } - - if err := merkleProof.VerifyMembership(cs.ProofSpecs, provingConsensusState.GetRoot(), path, bz); err != nil { - return err - } - - return nil -} - -// VerifyConnectionState verifies a proof of the connection state of the -// specified connection end stored on the target machine. -// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294 -func (cs ClientState) VerifyConnectionState( - store sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - prefix exported.Prefix, - proof []byte, - connectionID string, - connectionEnd exported.ConnectionI, -) error { - merkleProof, consensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) - if err != nil { - return err - } - - connectionPath := commitmenttypes.NewMerklePath(host.ConnectionPath(connectionID)) - path, err := commitmenttypes.ApplyPrefix(prefix, connectionPath) - if err != nil { - return err - } - - connection, ok := connectionEnd.(connectiontypes.ConnectionEnd) - if !ok { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "invalid connection type %T", connectionEnd) - } - - bz, err := cdc.Marshal(&connection) - if err != nil { - return err - } - - if err := merkleProof.VerifyMembership(cs.ProofSpecs, consensusState.GetRoot(), path, bz); err != nil { - return err - } - - return nil -} - -// VerifyChannelState verifies a proof of the channel state of the specified -// channel end, under the specified port, stored on the target machine. -// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294 -func (cs ClientState) VerifyChannelState( - store sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - prefix exported.Prefix, - proof []byte, - portID, - channelID string, - channel exported.ChannelI, -) error { - merkleProof, consensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) - if err != nil { - return err - } - - channelPath := commitmenttypes.NewMerklePath(host.ChannelPath(portID, channelID)) - path, err := commitmenttypes.ApplyPrefix(prefix, channelPath) - if err != nil { - return err - } - - channelEnd, ok := channel.(channeltypes.Channel) - if !ok { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "invalid channel type %T", channel) - } - - bz, err := cdc.Marshal(&channelEnd) - if err != nil { - return err - } - - if err := merkleProof.VerifyMembership(cs.ProofSpecs, consensusState.GetRoot(), path, bz); err != nil { - return err - } - - return nil -} - -// VerifyPacketCommitment verifies a proof of an outgoing packet commitment at -// the specified port, specified channel, and specified sequence. -// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294 -func (cs ClientState) VerifyPacketCommitment( - ctx sdk.Context, - store sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - delayTimePeriod uint64, - delayBlockPeriod uint64, - prefix exported.Prefix, - proof []byte, - portID, - channelID string, - sequence uint64, - commitmentBytes []byte, -) error { - merkleProof, consensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) - if err != nil { - return err - } - - // check delay period has passed - if err := verifyDelayPeriodPassed(ctx, store, height, delayTimePeriod, delayBlockPeriod); err != nil { - return err - } - - commitmentPath := commitmenttypes.NewMerklePath(host.PacketCommitmentPath(portID, channelID, sequence)) - path, err := commitmenttypes.ApplyPrefix(prefix, commitmentPath) - if err != nil { - return err - } - - if err := merkleProof.VerifyMembership(cs.ProofSpecs, consensusState.GetRoot(), path, commitmentBytes); err != nil { - return err - } - - return nil -} - -// VerifyPacketAcknowledgement verifies a proof of an incoming packet -// acknowledgement at the specified port, specified channel, and specified sequence. -// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294 -func (cs ClientState) VerifyPacketAcknowledgement( - ctx sdk.Context, - store sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - delayTimePeriod uint64, - delayBlockPeriod uint64, - prefix exported.Prefix, - proof []byte, - portID, - channelID string, - sequence uint64, - acknowledgement []byte, -) error { - merkleProof, consensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) - if err != nil { - return err - } - - // check delay period has passed - if err := verifyDelayPeriodPassed(ctx, store, height, delayTimePeriod, delayBlockPeriod); err != nil { - return err - } - - ackPath := commitmenttypes.NewMerklePath(host.PacketAcknowledgementPath(portID, channelID, sequence)) - path, err := commitmenttypes.ApplyPrefix(prefix, ackPath) - if err != nil { - return err - } - - if err := merkleProof.VerifyMembership(cs.ProofSpecs, consensusState.GetRoot(), path, channeltypes.CommitAcknowledgement(acknowledgement)); err != nil { - return err - } - - return nil -} - -// VerifyPacketReceiptAbsence verifies a proof of the absence of an -// incoming packet receipt at the specified port, specified channel, and -// specified sequence. -// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294 -func (cs ClientState) VerifyPacketReceiptAbsence( - ctx sdk.Context, - store sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - delayTimePeriod uint64, - delayBlockPeriod uint64, - prefix exported.Prefix, - proof []byte, - portID, - channelID string, - sequence uint64, -) error { - merkleProof, consensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) - if err != nil { - return err - } - - // check delay period has passed - if err := verifyDelayPeriodPassed(ctx, store, height, delayTimePeriod, delayBlockPeriod); err != nil { - return err - } - - receiptPath := commitmenttypes.NewMerklePath(host.PacketReceiptPath(portID, channelID, sequence)) - path, err := commitmenttypes.ApplyPrefix(prefix, receiptPath) - if err != nil { - return err - } - - if err := merkleProof.VerifyNonMembership(cs.ProofSpecs, consensusState.GetRoot(), path); err != nil { - return err - } - - return nil -} - -// VerifyNextSequenceRecv verifies a proof of the next sequence number to be -// received of the specified channel at the specified port. -// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294 -func (cs ClientState) VerifyNextSequenceRecv( - ctx sdk.Context, - store sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - delayTimePeriod uint64, - delayBlockPeriod uint64, - prefix exported.Prefix, - proof []byte, - portID, - channelID string, - nextSequenceRecv uint64, -) error { - merkleProof, consensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) - if err != nil { - return err - } - - // check delay period has passed - if err := verifyDelayPeriodPassed(ctx, store, height, delayTimePeriod, delayBlockPeriod); err != nil { - return err - } - - nextSequenceRecvPath := commitmenttypes.NewMerklePath(host.NextSequenceRecvPath(portID, channelID)) - path, err := commitmenttypes.ApplyPrefix(prefix, nextSequenceRecvPath) - if err != nil { - return err - } - - bz := sdk.Uint64ToBigEndian(nextSequenceRecv) - - if err := merkleProof.VerifyMembership(cs.ProofSpecs, consensusState.GetRoot(), path, bz); err != nil { - return err - } - - return nil -} - // VerifyMembership is a generic proof verification method which verifies a proof of the existence of a value at a given CommitmentPath at the specified height. // The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). func (cs ClientState) VerifyMembership( @@ -663,47 +333,3 @@ func verifyDelayPeriodPassed(ctx sdk.Context, store sdk.KVStore, proofHeight exp return nil } - -// produceVerificationArgs perfoms the basic checks on the arguments that are -// shared between the verification functions and returns the unmarshalled -// merkle proof, the consensus state and an error if one occurred. -// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294 -func produceVerificationArgs( - store sdk.KVStore, - cdc codec.BinaryCodec, - cs ClientState, - height exported.Height, - prefix exported.Prefix, - proof []byte, -) (merkleProof commitmenttypes.MerkleProof, consensusState *ConsensusState, err error) { - if cs.GetLatestHeight().LT(height) { - return commitmenttypes.MerkleProof{}, nil, sdkerrors.Wrapf( - sdkerrors.ErrInvalidHeight, - "client state height < proof height (%d < %d), please ensure the client has been updated", cs.GetLatestHeight(), height, - ) - } - - if prefix == nil { - return commitmenttypes.MerkleProof{}, nil, sdkerrors.Wrap(commitmenttypes.ErrInvalidPrefix, "prefix cannot be empty") - } - - _, ok := prefix.(*commitmenttypes.MerklePrefix) - if !ok { - return commitmenttypes.MerkleProof{}, nil, sdkerrors.Wrapf(commitmenttypes.ErrInvalidPrefix, "invalid prefix type %T, expected *MerklePrefix", prefix) - } - - if proof == nil { - return commitmenttypes.MerkleProof{}, nil, sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "proof cannot be empty") - } - - if err = cdc.Unmarshal(proof, &merkleProof); err != nil { - return commitmenttypes.MerkleProof{}, nil, sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "failed to unmarshal proof into commitment merkle proof") - } - - consensusState, found := GetConsensusState(store, cdc, height) - if !found { - return commitmenttypes.MerkleProof{}, nil, sdkerrors.Wrap(clienttypes.ErrConsensusStateNotFound, "please ensure the proof was constructed against a height that exists on the client") - } - - return merkleProof, consensusState, nil -} diff --git a/modules/light-clients/07-tendermint/types/client_state_test.go b/modules/light-clients/07-tendermint/types/client_state_test.go index 0a46d6dd7f0..56bbf0909b3 100644 --- a/modules/light-clients/07-tendermint/types/client_state_test.go +++ b/modules/light-clients/07-tendermint/types/client_state_test.go @@ -18,12 +18,6 @@ import ( ) const ( - testClientID = "clientidone" - testConnectionID = "connectionid" - testPortID = "testportid" - testChannelID = "testchannelid" - testSequence = 1 - // Do not change the length of these variables fiftyCharChainID = "12345678901234567890123456789012345678901234567890" fiftyOneCharChainID = "123456789012345678901234567890123456789012345678901" @@ -704,736 +698,3 @@ func (suite *TendermintTestSuite) TestVerifyNonMembership() { }) } } - -// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294 -func (suite *TendermintTestSuite) TestVerifyClientConsensusState() { - testCases := []struct { - name string - clientState *types.ClientState - consensusState *types.ConsensusState - prefix commitmenttypes.MerklePrefix - proof []byte - expPass bool - }{ - // FIXME: uncomment - // { - // name: "successful verification", - // clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), - // consensusState: types.ConsensusState{ - // Root: commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), - // }, - // prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), - // expPass: true, - // }, - { - name: "ApplyPrefix failed", - clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - consensusState: &types.ConsensusState{ - Root: commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), - }, - prefix: commitmenttypes.MerklePrefix{}, - expPass: false, - }, - { - name: "latest client height < height", - clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - consensusState: &types.ConsensusState{ - Root: commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), - }, - prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), - expPass: false, - }, - { - name: "proof verification failed", - clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - consensusState: &types.ConsensusState{ - Root: commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), - NextValidatorsHash: suite.valsHash, - }, - prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), - proof: []byte{}, - expPass: false, - }, - } - - for i, tc := range testCases { - tc := tc - - err := tc.clientState.VerifyClientConsensusState( - nil, suite.cdc, height, "chainA", tc.clientState.LatestHeight, tc.prefix, tc.proof, tc.consensusState, - ) - - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - } - } -} - -// test verification of the connection on chainB being represented in the -// light client on chainA -// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294 -func (suite *TendermintTestSuite) TestVerifyConnectionState() { - var ( - clientState *types.ClientState - proof []byte - proofHeight exported.Height - prefix commitmenttypes.MerklePrefix - ) - - testCases := []struct { - name string - malleate func() - expPass bool - }{ - { - "successful verification", func() {}, true, - }, - { - "ApplyPrefix failed", func() { - prefix = commitmenttypes.MerklePrefix{} - }, false, - }, - { - "latest client height < height", func() { - proofHeight = clientState.LatestHeight.Increment() - }, false, - }, - { - "proof verification failed", func() { - proof = invalidProof - }, false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() // reset - - // setup testing conditions - path := ibctesting.NewPath(suite.chainA, suite.chainB) - suite.coordinator.Setup(path) - connection := path.EndpointB.GetConnection() - - var ok bool - clientStateI := suite.chainA.GetClientState(path.EndpointA.ClientID) - clientState, ok = clientStateI.(*types.ClientState) - suite.Require().True(ok) - - prefix = suite.chainB.GetPrefix() - - // make connection proof - connectionKey := host.ConnectionKey(path.EndpointB.ConnectionID) - proof, proofHeight = suite.chainB.QueryProof(connectionKey) - - tc.malleate() // make changes as necessary - - store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - - err := clientState.VerifyConnectionState( - store, suite.chainA.Codec, proofHeight, &prefix, proof, path.EndpointB.ConnectionID, connection, - ) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} - -// test verification of the channel on chainB being represented in the light -// client on chainA -// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294 -func (suite *TendermintTestSuite) TestVerifyChannelState() { - var ( - clientState *types.ClientState - proof []byte - proofHeight exported.Height - prefix commitmenttypes.MerklePrefix - ) - - testCases := []struct { - name string - malleate func() - expPass bool - }{ - { - "successful verification", func() {}, true, - }, - { - "ApplyPrefix failed", func() { - prefix = commitmenttypes.MerklePrefix{} - }, false, - }, - { - "latest client height < height", func() { - proofHeight = clientState.LatestHeight.Increment() - }, false, - }, - { - "proof verification failed", func() { - proof = invalidProof - }, false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() // reset - - // setup testing conditions - path := ibctesting.NewPath(suite.chainA, suite.chainB) - suite.coordinator.Setup(path) - channel := path.EndpointB.GetChannel() - - var ok bool - clientStateI := suite.chainA.GetClientState(path.EndpointA.ClientID) - clientState, ok = clientStateI.(*types.ClientState) - suite.Require().True(ok) - - prefix = suite.chainB.GetPrefix() - - // make channel proof - channelKey := host.ChannelKey(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) - proof, proofHeight = suite.chainB.QueryProof(channelKey) - - tc.malleate() // make changes as necessary - - store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - - err := clientState.VerifyChannelState( - store, suite.chainA.Codec, proofHeight, &prefix, proof, - path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, channel, - ) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} - -// test verification of the packet commitment on chainB being represented -// in the light client on chainA. A send from chainB to chainA is simulated. -// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294 -func (suite *TendermintTestSuite) TestVerifyPacketCommitment() { - var ( - clientState *types.ClientState - proof []byte - delayTimePeriod uint64 - delayBlockPeriod uint64 - proofHeight exported.Height - prefix commitmenttypes.MerklePrefix - ) - - testCases := []struct { - name string - malleate func() - expPass bool - }{ - { - "successful verification", func() {}, true, - }, - { - name: "delay time period has passed", - malleate: func() { - delayTimePeriod = uint64(time.Second.Nanoseconds()) - }, - expPass: true, - }, - { - name: "delay time period has not passed", - malleate: func() { - delayTimePeriod = uint64(time.Hour.Nanoseconds()) - }, - expPass: false, - }, - { - name: "delay block period has passed", - malleate: func() { - delayBlockPeriod = 1 - }, - expPass: true, - }, - { - name: "delay block period has not passed", - malleate: func() { - delayBlockPeriod = 1000 - }, - expPass: false, - }, - - { - "ApplyPrefix failed", func() { - prefix = commitmenttypes.MerklePrefix{} - }, false, - }, - { - "latest client height < height", func() { - proofHeight = clientState.LatestHeight.Increment() - }, false, - }, - { - "proof verification failed", func() { - proof = invalidProof - }, false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() // reset - - // setup testing conditions - path := ibctesting.NewPath(suite.chainA, suite.chainB) - suite.coordinator.Setup(path) - packet := channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, clienttypes.NewHeight(1, 100), 0) - err := path.EndpointB.SendPacket(packet) - suite.Require().NoError(err) - - var ok bool - clientStateI := suite.chainA.GetClientState(path.EndpointA.ClientID) - clientState, ok = clientStateI.(*types.ClientState) - suite.Require().True(ok) - - prefix = suite.chainB.GetPrefix() - - // make packet commitment proof - packetKey := host.PacketCommitmentKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) - proof, proofHeight = path.EndpointB.QueryProof(packetKey) - - // reset time and block delays to 0, malleate may change to a specific non-zero value. - delayTimePeriod = 0 - delayBlockPeriod = 0 - tc.malleate() // make changes as necessary - - ctx := suite.chainA.GetContext() - store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) - - commitment := channeltypes.CommitPacket(suite.chainA.App.GetIBCKeeper().Codec(), packet) - err = clientState.VerifyPacketCommitment( - ctx, store, suite.chainA.Codec, proofHeight, delayTimePeriod, delayBlockPeriod, &prefix, proof, - packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence(), commitment, - ) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} - -// test verification of the acknowledgement on chainB being represented -// in the light client on chainA. A send and ack from chainA to chainB -// is simulated. -// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294 -func (suite *TendermintTestSuite) TestVerifyPacketAcknowledgement() { - var ( - clientState *types.ClientState - proof []byte - delayTimePeriod uint64 - delayBlockPeriod uint64 - proofHeight exported.Height - prefix commitmenttypes.MerklePrefix - ) - - testCases := []struct { - name string - malleate func() - expPass bool - }{ - { - "successful verification", func() {}, true, - }, - { - name: "delay time period has passed", - malleate: func() { - delayTimePeriod = uint64(time.Second.Nanoseconds()) - }, - expPass: true, - }, - { - name: "delay time period has not passed", - malleate: func() { - delayTimePeriod = uint64(time.Hour.Nanoseconds()) - }, - expPass: false, - }, - { - name: "delay block period has passed", - malleate: func() { - delayBlockPeriod = 1 - }, - expPass: true, - }, - { - name: "delay block period has not passed", - malleate: func() { - delayBlockPeriod = 10 - }, - expPass: false, - }, - { - "ApplyPrefix failed", func() { - prefix = commitmenttypes.MerklePrefix{} - }, false, - }, - { - "latest client height < height", func() { - proofHeight = clientState.LatestHeight.Increment() - }, false, - }, - { - "proof verification failed", func() { - proof = invalidProof - }, false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() // reset - - // setup testing conditions - path := ibctesting.NewPath(suite.chainA, suite.chainB) - suite.coordinator.Setup(path) - packet := channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) - - // send packet - err := path.EndpointA.SendPacket(packet) - suite.Require().NoError(err) - - // write receipt and ack - err = path.EndpointB.RecvPacket(packet) - suite.Require().NoError(err) - - var ok bool - clientStateI := suite.chainA.GetClientState(path.EndpointA.ClientID) - clientState, ok = clientStateI.(*types.ClientState) - suite.Require().True(ok) - - prefix = suite.chainB.GetPrefix() - - // make packet acknowledgement proof - acknowledgementKey := host.PacketAcknowledgementKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) - proof, proofHeight = suite.chainB.QueryProof(acknowledgementKey) - - // reset time and block delays to 0, malleate may change to a specific non-zero value. - delayTimePeriod = 0 - delayBlockPeriod = 0 - tc.malleate() // make changes as necessary - - ctx := suite.chainA.GetContext() - store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) - - err = clientState.VerifyPacketAcknowledgement( - ctx, store, suite.chainA.Codec, proofHeight, delayTimePeriod, delayBlockPeriod, &prefix, proof, - packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(), ibcmock.MockAcknowledgement.Acknowledgement(), - ) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} - -// test verification of the absent acknowledgement on chainB being represented -// in the light client on chainA. A send from chainB to chainA is simulated, but -// no receive. -func (suite *TendermintTestSuite) TestVerifyPacketReceiptAbsence() { - var ( - clientState *types.ClientState - proof []byte - delayTimePeriod uint64 - delayBlockPeriod uint64 - proofHeight exported.Height - prefix commitmenttypes.MerklePrefix - ) - - testCases := []struct { - name string - malleate func() - expPass bool - }{ - { - "successful verification", func() {}, true, - }, - { - name: "delay time period has passed", - malleate: func() { - delayTimePeriod = uint64(time.Second.Nanoseconds()) - }, - expPass: true, - }, - { - name: "delay time period has not passed", - malleate: func() { - delayTimePeriod = uint64(time.Hour.Nanoseconds()) - }, - expPass: false, - }, - { - name: "delay block period has passed", - malleate: func() { - delayBlockPeriod = 1 - }, - expPass: true, - }, - { - name: "delay block period has not passed", - malleate: func() { - delayBlockPeriod = 10 - }, - expPass: false, - }, - - { - "ApplyPrefix failed", func() { - prefix = commitmenttypes.MerklePrefix{} - }, false, - }, - { - "latest client height < height", func() { - proofHeight = clientState.LatestHeight.Increment() - }, false, - }, - { - "proof verification failed", func() { - proof = invalidProof - }, false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() // reset - - // setup testing conditions - path := ibctesting.NewPath(suite.chainA, suite.chainB) - suite.coordinator.Setup(path) - packet := channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) - - // send packet, but no recv - err := path.EndpointA.SendPacket(packet) - suite.Require().NoError(err) - - var ok bool - clientStateI := suite.chainA.GetClientState(path.EndpointA.ClientID) - clientState, ok = clientStateI.(*types.ClientState) - suite.Require().True(ok) - - prefix = suite.chainB.GetPrefix() - - // make packet receipt absence proof - receiptKey := host.PacketReceiptKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) - proof, proofHeight = path.EndpointB.QueryProof(receiptKey) - - // reset time and block delays to 0, malleate may change to a specific non-zero value. - delayTimePeriod = 0 - delayBlockPeriod = 0 - tc.malleate() // make changes as necessary - - ctx := suite.chainA.GetContext() - store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) - - err = clientState.VerifyPacketReceiptAbsence( - ctx, store, suite.chainA.Codec, proofHeight, delayTimePeriod, delayBlockPeriod, &prefix, proof, - packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(), - ) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} - -// test verification of the next receive sequence on chainB being represented -// in the light client on chainA. A send and receive from chainB to chainA is -// simulated. -// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294 -func (suite *TendermintTestSuite) TestVerifyNextSeqRecv() { - var ( - clientState *types.ClientState - proof []byte - delayTimePeriod uint64 - delayBlockPeriod uint64 - proofHeight exported.Height - prefix commitmenttypes.MerklePrefix - ) - - testCases := []struct { - name string - malleate func() - expPass bool - }{ - { - "successful verification", func() {}, true, - }, - { - name: "delay time period has passed", - malleate: func() { - delayTimePeriod = uint64(time.Second.Nanoseconds()) - }, - expPass: true, - }, - { - name: "delay time period has not passed", - malleate: func() { - delayTimePeriod = uint64(time.Hour.Nanoseconds()) - }, - expPass: false, - }, - { - name: "delay block period has passed", - malleate: func() { - delayBlockPeriod = 1 - }, - expPass: true, - }, - { - name: "delay block period has not passed", - malleate: func() { - delayBlockPeriod = 10 - }, - expPass: false, - }, - - { - "ApplyPrefix failed", func() { - prefix = commitmenttypes.MerklePrefix{} - }, false, - }, - { - "latest client height < height", func() { - proofHeight = clientState.LatestHeight.Increment() - }, false, - }, - { - "proof verification failed", func() { - proof = invalidProof - }, false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() // reset - - // setup testing conditions - path := ibctesting.NewPath(suite.chainA, suite.chainB) - path.SetChannelOrdered() - suite.coordinator.Setup(path) - packet := channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) - - // send packet - err := path.EndpointA.SendPacket(packet) - suite.Require().NoError(err) - - // next seq recv incremented - err = path.EndpointB.RecvPacket(packet) - suite.Require().NoError(err) - - var ok bool - clientStateI := suite.chainA.GetClientState(path.EndpointA.ClientID) - clientState, ok = clientStateI.(*types.ClientState) - suite.Require().True(ok) - - prefix = suite.chainB.GetPrefix() - - // make next seq recv proof - nextSeqRecvKey := host.NextSequenceRecvKey(packet.GetDestPort(), packet.GetDestChannel()) - proof, proofHeight = suite.chainB.QueryProof(nextSeqRecvKey) - - // reset time and block delays to 0, malleate may change to a specific non-zero value. - delayTimePeriod = 0 - delayBlockPeriod = 0 - tc.malleate() // make changes as necessary - - ctx := suite.chainA.GetContext() - store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) - - err = clientState.VerifyNextSequenceRecv( - ctx, store, suite.chainA.Codec, proofHeight, delayTimePeriod, delayBlockPeriod, &prefix, proof, - packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()+1, - ) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} - -func (suite *TendermintTestSuite) TestGetTimestampAtHeight() { - suite.SetupTest() - - path := ibctesting.NewPath(suite.chainA, suite.chainB) - path.SetChannelOrdered() - suite.coordinator.Setup(path) - - ctx := suite.chainA.GetContext() - clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) - clientState := path.EndpointA.GetClientState() - - testCases := []struct { - name string - height exported.Height - expValue uint64 - expPass bool - }{ - { - name: "get timestamp at height exists", - height: clientState.GetLatestHeight(), - expValue: path.EndpointA.GetConsensusState(clientState.GetLatestHeight()).GetTimestamp(), - expPass: true, - }, - { - name: "get timestamp at height not exists", - height: clientState.GetLatestHeight().Increment(), - }, - } - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - ts, err := clientState.GetTimestampAtHeight( - ctx, clientStore, suite.chainA.Codec, tc.height, - ) - - suite.Require().Equal(tc.expValue, ts) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} From 81a7cae633ab36a04ff3d0ec613ea8a641afbc31 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Mon, 11 Jul 2022 15:31:04 +0100 Subject: [PATCH 47/71] Check that client state is zeroed out for ibc client upgrade proposals (#1676) --- CHANGELOG.md | 1 + modules/core/02-client/types/proposal.go | 7 ++++++- modules/core/02-client/types/proposal_test.go | 17 ++++++++++++++--- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c021f77df94..1459234ad64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -78,6 +78,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (modules/core/04-channel) [\#1130](https://github.com/cosmos/ibc-go/pull/1130) Call `packet.GetSequence()` rather than passing func in `WriteAcknowledgement` log output * (apps/29-fee) [\#1278](https://github.com/cosmos/ibc-go/pull/1278) The URI path for the query to get all incentivized packets for a specifc channel did not follow the same format as the rest of queries. +* (modules/core/02-client)[\#1676](https://github.com/cosmos/ibc-go/pull/1676) ClientState must be zeroed out for `UpgradeProposals` to pass validation. This prevents a proposal containing information governance is not actually voting on. ## [v3.0.0](https://github.com/cosmos/ibc-go/releases/tag/v3.0.0) - 2022-03-15 diff --git a/modules/core/02-client/types/proposal.go b/modules/core/02-client/types/proposal.go index 75c9778e8c9..02dc3c517ee 100644 --- a/modules/core/02-client/types/proposal.go +++ b/modules/core/02-client/types/proposal.go @@ -2,6 +2,7 @@ package types import ( "fmt" + "reflect" codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -111,11 +112,15 @@ func (up *UpgradeProposal) ValidateBasic() error { return sdkerrors.Wrap(ErrInvalidUpgradeProposal, "upgraded client state cannot be nil") } - _, err := UnpackClientState(up.UpgradedClientState) + clientState, err := UnpackClientState(up.UpgradedClientState) if err != nil { return sdkerrors.Wrap(err, "failed to unpack upgraded client state") } + if !reflect.DeepEqual(clientState, clientState.ZeroCustomFields()) { + return sdkerrors.Wrap(ErrInvalidUpgradeProposal, "upgraded client state is not zeroed out") + } + return nil } diff --git a/modules/core/02-client/types/proposal_test.go b/modules/core/02-client/types/proposal_test.go index a32dcdac4e8..ad5be469961 100644 --- a/modules/core/02-client/types/proposal_test.go +++ b/modules/core/02-client/types/proposal_test.go @@ -109,13 +109,24 @@ func (suite *TypesTestSuite) TestUpgradeProposalValidateBasic() { }{ { "success", func() { - proposal, err = types.NewUpgradeProposal(ibctesting.Title, ibctesting.Description, plan, cs) + proposal, err = types.NewUpgradeProposal(ibctesting.Title, ibctesting.Description, plan, cs.ZeroCustomFields()) suite.Require().NoError(err) }, true, }, { "fails validate abstract - empty title", func() { - proposal, err = types.NewUpgradeProposal("", ibctesting.Description, plan, cs) + proposal, err = types.NewUpgradeProposal("", ibctesting.Description, plan, cs.ZeroCustomFields()) + suite.Require().NoError(err) + + }, false, + }, + { + "non zeroed fields", func() { + proposal, err = types.NewUpgradeProposal(ibctesting.Title, ibctesting.Description, plan, &ibctmtypes.ClientState{ + FrozenHeight: types.Height{ + RevisionHeight: 10, + }, + }) suite.Require().NoError(err) }, false, @@ -123,7 +134,7 @@ func (suite *TypesTestSuite) TestUpgradeProposalValidateBasic() { { "plan height is zero", func() { invalidPlan := upgradetypes.Plan{Name: "ibc upgrade", Height: 0} - proposal, err = types.NewUpgradeProposal(ibctesting.Title, ibctesting.Description, invalidPlan, cs) + proposal, err = types.NewUpgradeProposal(ibctesting.Title, ibctesting.Description, invalidPlan, cs.ZeroCustomFields()) suite.Require().NoError(err) }, false, }, From 60e530596adc99cb97b6d1ed5b96859fcf24b5b6 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Mon, 11 Jul 2022 16:07:00 +0100 Subject: [PATCH 48/71] Zero out client state before upgrading client proof (#1674) --- CHANGELOG.md | 1 + modules/core/02-client/keeper/client_test.go | 3 --- modules/light-clients/07-tendermint/types/upgrade.go | 6 +++--- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1459234ad64..57ffd0946a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -78,6 +78,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (modules/core/04-channel) [\#1130](https://github.com/cosmos/ibc-go/pull/1130) Call `packet.GetSequence()` rather than passing func in `WriteAcknowledgement` log output * (apps/29-fee) [\#1278](https://github.com/cosmos/ibc-go/pull/1278) The URI path for the query to get all incentivized packets for a specifc channel did not follow the same format as the rest of queries. +* (07-tendermint) [\#1674](https://github.com/cosmos/ibc-go/pull/1674) Submitted ClientState is zeroed out before checking the proof in order to prevent the proposal from containing information governance is not actually voting on. * (modules/core/02-client)[\#1676](https://github.com/cosmos/ibc-go/pull/1676) ClientState must be zeroed out for `UpgradeProposals` to pass validation. This prevents a proposal containing information governance is not actually voting on. ## [v3.0.0](https://github.com/cosmos/ibc-go/releases/tag/v3.0.0) - 2022-03-15 diff --git a/modules/core/02-client/keeper/client_test.go b/modules/core/02-client/keeper/client_test.go index 5f84c62b255..3f52359c62c 100644 --- a/modules/core/02-client/keeper/client_test.go +++ b/modules/core/02-client/keeper/client_test.go @@ -399,9 +399,6 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { tc.setup() - // Call ZeroCustomFields on upgraded clients to clear any client-chosen parameters in test-case upgradedClient - upgradedClient = upgradedClient.ZeroCustomFields() - err = suite.chainA.App.GetIBCKeeper().ClientKeeper.UpgradeClient(suite.chainA.GetContext(), path.EndpointA.ClientID, upgradedClient, upgradedConsState, proofUpgradedClient, proofUpgradedConsState) if tc.expPass { diff --git a/modules/light-clients/07-tendermint/types/upgrade.go b/modules/light-clients/07-tendermint/types/upgrade.go index c3910aa8816..e295f8e4ba3 100644 --- a/modules/light-clients/07-tendermint/types/upgrade.go +++ b/modules/light-clients/07-tendermint/types/upgrade.go @@ -14,11 +14,11 @@ import ( ) // VerifyUpgradeAndUpdateState checks if the upgraded client has been committed by the current client -// It will zero out all client-specific fields (e.g. TrustingPeriod and verify all data +// It will zero out all client-specific fields (e.g. TrustingPeriod) and verify all data // in client state that must be the same across all valid Tendermint clients for the new chain. // VerifyUpgrade will return an error if: // - the upgradedClient is not a Tendermint ClientState -// - the lastest height of the client state does not have the same revision number or has a greater +// - the latest height of the client state does not have the same revision number or has a greater // height than the committed client. // - the height of upgraded client is not greater than that of current client // - the latest height of the new client does not match or is greater than the height in committed client @@ -73,7 +73,7 @@ func (cs ClientState) VerifyUpgradeAndUpdateState( } // Verify client proof - bz, err := cdc.MarshalInterface(upgradedClient) + bz, err := cdc.MarshalInterface(upgradedClient.ZeroCustomFields()) if err != nil { return sdkerrors.Wrapf(clienttypes.ErrInvalidClient, "could not marshal client state: %v", err) } From 549c18152c14171bff403a05878ef32da48cf665 Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Tue, 12 Jul 2022 14:54:00 +0100 Subject: [PATCH 49/71] chore: restructuring 07-tendermint lightclient directory layout (#1677) * restructuring 07-tendermint lightclient directory tree * adding changelog entry * chore: restructuring 06-solomachine lightclient directory layout (#1679) * restructing 06-solomachine folder layout * tidy go modules * fixing dead link * adding changelog entry --- CHANGELOG.md | 2 + go.mod | 2 +- go.sum | 4 +- modules/core/02-client/abci.go | 2 +- modules/core/02-client/abci_test.go | 2 +- modules/core/02-client/client/utils/utils.go | 2 +- modules/core/02-client/keeper/client_test.go | 4 +- .../core/02-client/keeper/grpc_query_test.go | 2 +- modules/core/02-client/keeper/keeper.go | 2 +- modules/core/02-client/keeper/keeper_test.go | 4 +- .../core/02-client/keeper/proposal_test.go | 2 +- modules/core/02-client/legacy/v100/genesis.go | 2 +- .../02-client/legacy/v100/genesis_test.go | 2 +- modules/core/02-client/legacy/v100/store.go | 4 +- .../core/02-client/legacy/v100/store_test.go | 4 +- .../core/02-client/proposal_handler_test.go | 2 +- .../core/02-client/simulation/decoder_test.go | 2 +- modules/core/02-client/types/codec_test.go | 2 +- modules/core/02-client/types/encoding_test.go | 2 +- modules/core/02-client/types/genesis_test.go | 4 +- modules/core/02-client/types/msgs_test.go | 4 +- modules/core/02-client/types/proposal_test.go | 2 +- .../03-connection/keeper/handshake_test.go | 2 +- .../core/03-connection/keeper/verify_test.go | 2 +- modules/core/03-connection/types/msgs_test.go | 2 +- modules/core/04-channel/keeper/packet_test.go | 2 +- modules/core/genesis_test.go | 2 +- modules/core/keeper/msg_server_test.go | 2 +- modules/core/simulation/decoder_test.go | 2 +- modules/core/types/codec.go | 4 +- .../{types => }/client_state.go | 2 +- .../{types => }/client_state_test.go | 254 +++++++++--------- .../06-solomachine/{types => }/codec.go | 2 +- .../06-solomachine/codec_test.go | 190 +++++++++++++ .../{types => }/consensus_state.go | 2 +- .../{types => }/consensus_state_test.go | 28 +- .../06-solomachine/{types => }/errors.go | 2 +- .../06-solomachine/{types => }/header.go | 2 +- .../06-solomachine/{types => }/header_test.go | 20 +- .../{types => }/misbehaviour.go | 2 +- .../{types => }/misbehaviour_handle.go | 2 +- .../{types => }/misbehaviour_test.go | 40 +-- .../light-clients/06-solomachine/module.go | 6 +- .../06-solomachine/{types => }/proof.go | 2 +- .../06-solomachine/{types => }/proof_test.go | 27 +- .../{types => }/proposal_handle.go | 2 +- .../{types => }/proposal_handle_test.go | 24 +- .../06-solomachine/{types => }/solomachine.go | 2 +- .../{types => }/solomachine.pb.go | 36 +-- .../{types => }/solomachine_test.go | 19 +- .../06-solomachine/spec/01_concepts.md | 6 +- .../06-solomachine/types/codec_test.go | 190 ------------- .../06-solomachine/{types => }/update.go | 2 +- .../06-solomachine/{types => }/update_test.go | 180 ++++++------- .../07-tendermint/{types => }/client_state.go | 2 +- .../{types => }/client_state_test.go | 48 ++-- .../07-tendermint/{types => }/codec.go | 2 +- .../{types => }/consensus_state.go | 2 +- .../{types => }/consensus_state_test.go | 20 +- .../07-tendermint/{types => }/errors.go | 2 +- .../07-tendermint/{types => }/fraction.go | 2 +- .../07-tendermint/{types => }/genesis.go | 2 +- .../07-tendermint/{types => }/genesis_test.go | 34 +-- .../07-tendermint/{types => }/header.go | 2 +- .../07-tendermint/{types => }/header_test.go | 6 +- .../07-tendermint/{types => }/misbehaviour.go | 2 +- .../{types => }/misbehaviour_handle.go | 2 +- .../{types => }/misbehaviour_handle_test.go | 72 ++--- .../{types => }/misbehaviour_test.go | 70 ++--- modules/light-clients/07-tendermint/module.go | 6 +- .../{types => }/proposal_handle.go | 8 +- .../{types => }/proposal_handle_test.go | 40 +-- .../07-tendermint/{types => }/store.go | 2 +- .../07-tendermint/{types => }/store_test.go | 66 ++--- .../{types => }/tendermint.pb.go | 140 +++++----- .../{types => }/tendermint_test.go | 6 +- .../07-tendermint/{types => }/update.go | 2 +- .../07-tendermint/{types => }/update_test.go | 65 +++-- .../07-tendermint/{types => }/upgrade.go | 2 +- .../07-tendermint/{types => }/upgrade_test.go | 24 +- .../solomachine/v2/solomachine.proto | 2 +- .../tendermint/v1/tendermint.proto | 2 +- testing/chain.go | 2 +- testing/config.go | 2 +- testing/endpoint.go | 2 +- testing/solomachine.go | 2 +- testing/values.go | 2 +- 87 files changed, 876 insertions(+), 885 deletions(-) rename modules/light-clients/06-solomachine/{types => }/client_state.go (99%) rename modules/light-clients/06-solomachine/{types => }/client_state_test.go (69%) rename modules/light-clients/06-solomachine/{types => }/codec.go (99%) create mode 100644 modules/light-clients/06-solomachine/codec_test.go rename modules/light-clients/06-solomachine/{types => }/consensus_state.go (98%) rename modules/light-clients/06-solomachine/{types => }/consensus_state_test.go (64%) rename modules/light-clients/06-solomachine/{types => }/errors.go (97%) rename modules/light-clients/06-solomachine/{types => }/header.go (98%) rename modules/light-clients/06-solomachine/{types => }/header_test.go (83%) rename modules/light-clients/06-solomachine/{types => }/misbehaviour.go (99%) rename modules/light-clients/06-solomachine/{types => }/misbehaviour_handle.go (98%) rename modules/light-clients/06-solomachine/{types => }/misbehaviour_test.go (65%) rename modules/light-clients/06-solomachine/{types => }/proof.go (99%) rename modules/light-clients/06-solomachine/{types => }/proof_test.go (57%) rename modules/light-clients/06-solomachine/{types => }/proposal_handle.go (99%) rename modules/light-clients/06-solomachine/{types => }/proposal_handle_test.go (71%) rename modules/light-clients/06-solomachine/{types => }/solomachine.go (98%) rename modules/light-clients/06-solomachine/{types => }/solomachine.pb.go (98%) rename modules/light-clients/06-solomachine/{types => }/solomachine_test.go (85%) delete mode 100644 modules/light-clients/06-solomachine/types/codec_test.go rename modules/light-clients/06-solomachine/{types => }/update.go (99%) rename modules/light-clients/06-solomachine/{types => }/update_test.go (66%) rename modules/light-clients/07-tendermint/{types => }/client_state.go (99%) rename modules/light-clients/07-tendermint/{types => }/client_state_test.go (86%) rename modules/light-clients/07-tendermint/{types => }/codec.go (97%) rename modules/light-clients/07-tendermint/{types => }/consensus_state.go (99%) rename modules/light-clients/07-tendermint/{types => }/consensus_state_test.go (81%) rename modules/light-clients/07-tendermint/{types => }/errors.go (98%) rename modules/light-clients/07-tendermint/{types => }/fraction.go (97%) rename modules/light-clients/07-tendermint/{types => }/genesis.go (97%) rename modules/light-clients/07-tendermint/{types => }/genesis_test.go (67%) rename modules/light-clients/07-tendermint/{types => }/header.go (99%) rename modules/light-clients/07-tendermint/{types => }/header_test.go (94%) rename modules/light-clients/07-tendermint/{types => }/misbehaviour.go (99%) rename modules/light-clients/07-tendermint/{types => }/misbehaviour_handle.go (99%) rename modules/light-clients/07-tendermint/{types => }/misbehaviour_handle_test.go (95%) rename modules/light-clients/07-tendermint/{types => }/misbehaviour_test.go (81%) rename modules/light-clients/07-tendermint/{types => }/proposal_handle.go (97%) rename modules/light-clients/07-tendermint/{types => }/proposal_handle_test.go (86%) rename modules/light-clients/07-tendermint/{types => }/store.go (99%) rename modules/light-clients/07-tendermint/{types => }/store_test.go (64%) rename modules/light-clients/07-tendermint/{types => }/tendermint.pb.go (87%) rename modules/light-clients/07-tendermint/{types => }/tendermint_test.go (96%) rename modules/light-clients/07-tendermint/{types => }/update.go (99%) rename modules/light-clients/07-tendermint/{types => }/update_test.go (93%) rename modules/light-clients/07-tendermint/{types => }/upgrade.go (99%) rename modules/light-clients/07-tendermint/{types => }/upgrade_test.go (93%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57ffd0946a1..e9c0c464864 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,8 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### API Breaking * (transfer) [\#1250](https://github.com/cosmos/ibc-go/pull/1250) Deprecate `GetTransferAccount` since the `transfer` module account is never used. +* (06-solomachine) [\#1679](https://github.com/cosmos/ibc-go/pull/1679) Remove `types` sub-package from `06-solomachine` lightclient directory. +* (07-tendermint) [\#1677](https://github.com/cosmos/ibc-go/pull/1677) Remove `types` sub-package from `07-tendermint` lightclient directory. ### State Machine Breaking diff --git a/go.mod b/go.mod index 4f56c03dd9e..225f608d60f 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alp require ( github.com/armon/go-metrics v0.3.10 github.com/confio/ics23/go v0.7.0 - github.com/cosmos/cosmos-sdk v0.45.3 + github.com/cosmos/cosmos-sdk v0.45.4 github.com/gogo/protobuf v1.3.3 github.com/golang/protobuf v1.5.2 github.com/gorilla/mux v1.8.0 diff --git a/go.sum b/go.sum index a8f6d150085..5a51d87749c 100644 --- a/go.sum +++ b/go.sum @@ -229,8 +229,8 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cosmos/btcutil v1.0.4 h1:n7C2ngKXo7UC9gNyMNLbzqz7Asuf+7Qv4gnX/rOdQ44= github.com/cosmos/btcutil v1.0.4/go.mod h1:Ffqc8Hn6TJUdDgHBwIZLtrLQC1KdJ9jGJl/TvgUaxbU= -github.com/cosmos/cosmos-sdk v0.45.3 h1:PiVSU3IkNEDPhoxOZHk2lPnhwBBJgEYAtAR0jGXRN4g= -github.com/cosmos/cosmos-sdk v0.45.3/go.mod h1:qYm5JEr0ZlbnmoP/Q3b+dYMOliHf4ddHirpILiwZzqg= +github.com/cosmos/cosmos-sdk v0.45.4 h1:eStDAhJdMY8n5arbBRe+OwpNeBSunxSBHp1g55ulfdA= +github.com/cosmos/cosmos-sdk v0.45.4/go.mod h1:WOqtDxN3eCCmnYLVla10xG7lEXkFjpTaqm2a2WasgCc= github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= diff --git a/modules/core/02-client/abci.go b/modules/core/02-client/abci.go index ec209b4fdd0..f32fd129309 100644 --- a/modules/core/02-client/abci.go +++ b/modules/core/02-client/abci.go @@ -4,7 +4,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/ibc-go/v3/modules/core/02-client/keeper" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ) // BeginBlocker is used to perform IBC client upgrades diff --git a/modules/core/02-client/abci_test.go b/modules/core/02-client/abci_test.go index 5d220f53a42..16aee80088a 100644 --- a/modules/core/02-client/abci_test.go +++ b/modules/core/02-client/abci_test.go @@ -10,7 +10,7 @@ import ( upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" client "github.com/cosmos/ibc-go/v3/modules/core/02-client" "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v3/testing" ) diff --git a/modules/core/02-client/client/utils/utils.go b/modules/core/02-client/client/utils/utils.go index a596ff61410..0ea44d7523b 100644 --- a/modules/core/02-client/client/utils/utils.go +++ b/modules/core/02-client/client/utils/utils.go @@ -13,7 +13,7 @@ import ( host "github.com/cosmos/ibc-go/v3/modules/core/24-host" ibcclient "github.com/cosmos/ibc-go/v3/modules/core/client" "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ) // QueryClientState returns a client state. If prove is true, it performs an ABCI store query diff --git a/modules/core/02-client/keeper/client_test.go b/modules/core/02-client/keeper/client_test.go index 3f52359c62c..504eca3f726 100644 --- a/modules/core/02-client/keeper/client_test.go +++ b/modules/core/02-client/keeper/client_test.go @@ -11,8 +11,8 @@ import ( clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v3/modules/core/exported" - solomachinetypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + solomachinetypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v3/testing" ) diff --git a/modules/core/02-client/keeper/grpc_query_test.go b/modules/core/02-client/keeper/grpc_query_test.go index 60a5211733a..af8183bc5de 100644 --- a/modules/core/02-client/keeper/grpc_query_test.go +++ b/modules/core/02-client/keeper/grpc_query_test.go @@ -13,7 +13,7 @@ import ( "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v3/testing" ) diff --git a/modules/core/02-client/keeper/keeper.go b/modules/core/02-client/keeper/keeper.go index 74a043e9dea..1d32549654a 100644 --- a/modules/core/02-client/keeper/keeper.go +++ b/modules/core/02-client/keeper/keeper.go @@ -18,7 +18,7 @@ import ( commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ) // Keeper represents a type that grants read and write permissions to any client diff --git a/modules/core/02-client/keeper/keeper_test.go b/modules/core/02-client/keeper/keeper_test.go index 83e017dd8a4..00bcfda178e 100644 --- a/modules/core/02-client/keeper/keeper_test.go +++ b/modules/core/02-client/keeper/keeper_test.go @@ -19,8 +19,8 @@ import ( "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v3/modules/core/exported" - solomachinetypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + solomachinetypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v3/testing" ibctestingmock "github.com/cosmos/ibc-go/v3/testing/mock" "github.com/cosmos/ibc-go/v3/testing/simapp" diff --git a/modules/core/02-client/keeper/proposal_test.go b/modules/core/02-client/keeper/proposal_test.go index 0fbef636305..0dcd123f06e 100644 --- a/modules/core/02-client/keeper/proposal_test.go +++ b/modules/core/02-client/keeper/proposal_test.go @@ -6,7 +6,7 @@ import ( "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v3/testing" ) diff --git a/modules/core/02-client/legacy/v100/genesis.go b/modules/core/02-client/legacy/v100/genesis.go index fa052ade3f8..507443382a5 100644 --- a/modules/core/02-client/legacy/v100/genesis.go +++ b/modules/core/02-client/legacy/v100/genesis.go @@ -10,7 +10,7 @@ import ( "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ) // MigrateGenesis accepts exported v1.0.0 IBC client genesis file and migrates it to: diff --git a/modules/core/02-client/legacy/v100/genesis_test.go b/modules/core/02-client/legacy/v100/genesis_test.go index 4835ce98213..0c64722bc59 100644 --- a/modules/core/02-client/legacy/v100/genesis_test.go +++ b/modules/core/02-client/legacy/v100/genesis_test.go @@ -14,7 +14,7 @@ import ( "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v3/testing" "github.com/cosmos/ibc-go/v3/testing/simapp" ) diff --git a/modules/core/02-client/legacy/v100/store.go b/modules/core/02-client/legacy/v100/store.go index f92aa224676..08910ea419a 100644 --- a/modules/core/02-client/legacy/v100/store.go +++ b/modules/core/02-client/legacy/v100/store.go @@ -14,8 +14,8 @@ import ( clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/exported" - smtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + smtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ) // MigrateStore performs in-place store migrations from SDK v0.40 of the IBC module to v1.0.0 of ibc-go. diff --git a/modules/core/02-client/legacy/v100/store_test.go b/modules/core/02-client/legacy/v100/store_test.go index a1c1be3034a..db06d7d4cbd 100644 --- a/modules/core/02-client/legacy/v100/store_test.go +++ b/modules/core/02-client/legacy/v100/store_test.go @@ -6,11 +6,11 @@ import ( "github.com/stretchr/testify/suite" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/legacy/v100" + v100 "github.com/cosmos/ibc-go/v3/modules/core/02-client/legacy/v100" "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v3/testing" ) diff --git a/modules/core/02-client/proposal_handler_test.go b/modules/core/02-client/proposal_handler_test.go index 4ef2798d633..2e510f8777e 100644 --- a/modules/core/02-client/proposal_handler_test.go +++ b/modules/core/02-client/proposal_handler_test.go @@ -7,7 +7,7 @@ import ( client "github.com/cosmos/ibc-go/v3/modules/core/02-client" clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v3/testing" ) diff --git a/modules/core/02-client/simulation/decoder_test.go b/modules/core/02-client/simulation/decoder_test.go index 0e106614f64..962435a7e25 100644 --- a/modules/core/02-client/simulation/decoder_test.go +++ b/modules/core/02-client/simulation/decoder_test.go @@ -11,7 +11,7 @@ import ( "github.com/cosmos/ibc-go/v3/modules/core/02-client/simulation" "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" "github.com/cosmos/ibc-go/v3/testing/simapp" ) diff --git a/modules/core/02-client/types/codec_test.go b/modules/core/02-client/types/codec_test.go index 7ff2455f507..2db68dbb244 100644 --- a/modules/core/02-client/types/codec_test.go +++ b/modules/core/02-client/types/codec_test.go @@ -6,7 +6,7 @@ import ( "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v3/testing" ) diff --git a/modules/core/02-client/types/encoding_test.go b/modules/core/02-client/types/encoding_test.go index efc4f85aad3..e1846c39f25 100644 --- a/modules/core/02-client/types/encoding_test.go +++ b/modules/core/02-client/types/encoding_test.go @@ -2,7 +2,7 @@ package types_test import ( "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ) func (suite *TypesTestSuite) TestMarshalHeader() { diff --git a/modules/core/02-client/types/genesis_test.go b/modules/core/02-client/types/genesis_test.go index 8dac1291172..3774ff48800 100644 --- a/modules/core/02-client/types/genesis_test.go +++ b/modules/core/02-client/types/genesis_test.go @@ -9,8 +9,8 @@ import ( "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v3/modules/core/exported" - solomachinetypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + solomachinetypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v3/testing" ibctestingmock "github.com/cosmos/ibc-go/v3/testing/mock" ) diff --git a/modules/core/02-client/types/msgs_test.go b/modules/core/02-client/types/msgs_test.go index ee5bed3cfb8..c51ebfce957 100644 --- a/modules/core/02-client/types/msgs_test.go +++ b/modules/core/02-client/types/msgs_test.go @@ -9,8 +9,8 @@ import ( "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - solomachinetypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + solomachinetypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v3/testing" ) diff --git a/modules/core/02-client/types/proposal_test.go b/modules/core/02-client/types/proposal_test.go index ad5be469961..8ef103ee18a 100644 --- a/modules/core/02-client/types/proposal_test.go +++ b/modules/core/02-client/types/proposal_test.go @@ -9,7 +9,7 @@ import ( upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v3/testing" ) diff --git a/modules/core/03-connection/keeper/handshake_test.go b/modules/core/03-connection/keeper/handshake_test.go index 6f5dfbce1d9..316c82023ee 100644 --- a/modules/core/03-connection/keeper/handshake_test.go +++ b/modules/core/03-connection/keeper/handshake_test.go @@ -7,7 +7,7 @@ import ( "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v3/testing" ) diff --git a/modules/core/03-connection/keeper/verify_test.go b/modules/core/03-connection/keeper/verify_test.go index 520dfed1397..f6fc1f92c19 100644 --- a/modules/core/03-connection/keeper/verify_test.go +++ b/modules/core/03-connection/keeper/verify_test.go @@ -9,7 +9,7 @@ import ( channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v3/testing" ibcmock "github.com/cosmos/ibc-go/v3/testing/mock" ) diff --git a/modules/core/03-connection/types/msgs_test.go b/modules/core/03-connection/types/msgs_test.go index 9cf741bb2c0..871da3c2b9c 100644 --- a/modules/core/03-connection/types/msgs_test.go +++ b/modules/core/03-connection/types/msgs_test.go @@ -15,7 +15,7 @@ import ( clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v3/testing" "github.com/cosmos/ibc-go/v3/testing/simapp" ) diff --git a/modules/core/04-channel/keeper/packet_test.go b/modules/core/04-channel/keeper/packet_test.go index 66bc6846450..949b45a2e2f 100644 --- a/modules/core/04-channel/keeper/packet_test.go +++ b/modules/core/04-channel/keeper/packet_test.go @@ -12,7 +12,7 @@ import ( "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v3/testing" ibcmock "github.com/cosmos/ibc-go/v3/testing/mock" ) diff --git a/modules/core/genesis_test.go b/modules/core/genesis_test.go index f45d70a8eca..e27f44cf5bd 100644 --- a/modules/core/genesis_test.go +++ b/modules/core/genesis_test.go @@ -15,7 +15,7 @@ import ( commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v3/modules/core/exported" "github.com/cosmos/ibc-go/v3/modules/core/types" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v3/testing" "github.com/cosmos/ibc-go/v3/testing/simapp" ) diff --git a/modules/core/keeper/msg_server_test.go b/modules/core/keeper/msg_server_test.go index f61e2efd5a8..f4d0a05282f 100644 --- a/modules/core/keeper/msg_server_test.go +++ b/modules/core/keeper/msg_server_test.go @@ -13,7 +13,7 @@ import ( host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/exported" "github.com/cosmos/ibc-go/v3/modules/core/keeper" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v3/testing" ibcmock "github.com/cosmos/ibc-go/v3/testing/mock" ) diff --git a/modules/core/simulation/decoder_test.go b/modules/core/simulation/decoder_test.go index 6639c96b8e4..621375a024f 100644 --- a/modules/core/simulation/decoder_test.go +++ b/modules/core/simulation/decoder_test.go @@ -12,7 +12,7 @@ import ( channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/simulation" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" "github.com/cosmos/ibc-go/v3/testing/simapp" ) diff --git a/modules/core/types/codec.go b/modules/core/types/codec.go index 8069c76715e..079fa341d25 100644 --- a/modules/core/types/codec.go +++ b/modules/core/types/codec.go @@ -7,8 +7,8 @@ import ( connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - solomachinetypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + solomachinetypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ) // RegisterInterfaces registers x/ibc interfaces into protobuf Any. diff --git a/modules/light-clients/06-solomachine/types/client_state.go b/modules/light-clients/06-solomachine/client_state.go similarity index 99% rename from modules/light-clients/06-solomachine/types/client_state.go rename to modules/light-clients/06-solomachine/client_state.go index 06dbccb6e64..b985bbcf3be 100644 --- a/modules/light-clients/06-solomachine/types/client_state.go +++ b/modules/light-clients/06-solomachine/client_state.go @@ -1,4 +1,4 @@ -package types +package solomachine import ( "reflect" diff --git a/modules/light-clients/06-solomachine/types/client_state_test.go b/modules/light-clients/06-solomachine/client_state_test.go similarity index 69% rename from modules/light-clients/06-solomachine/types/client_state_test.go rename to modules/light-clients/06-solomachine/client_state_test.go index 3e0c4904b7b..ab1d7c66a08 100644 --- a/modules/light-clients/06-solomachine/types/client_state_test.go +++ b/modules/light-clients/06-solomachine/client_state_test.go @@ -1,4 +1,4 @@ -package types_test +package solomachine_test import ( clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" @@ -6,8 +6,8 @@ import ( channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v3/modules/core/exported" - "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + solomachine "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v3/testing" ) @@ -39,41 +39,41 @@ func (suite *SoloMachineTestSuite) TestStatus() { func (suite *SoloMachineTestSuite) TestClientStateValidateBasic() { // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { testCases := []struct { name string - clientState *types.ClientState + clientState *solomachine.ClientState expPass bool }{ { "valid client state", - solomachine.ClientState(), + sm.ClientState(), true, }, { "empty ClientState", - &types.ClientState{}, + &solomachine.ClientState{}, false, }, { "sequence is zero", - types.NewClientState(0, &types.ConsensusState{solomachine.ConsensusState().PublicKey, solomachine.Diversifier, solomachine.Time}, false), + solomachine.NewClientState(0, &solomachine.ConsensusState{sm.ConsensusState().PublicKey, sm.Diversifier, sm.Time}, false), false, }, { "timestamp is zero", - types.NewClientState(1, &types.ConsensusState{solomachine.ConsensusState().PublicKey, solomachine.Diversifier, 0}, false), + solomachine.NewClientState(1, &solomachine.ConsensusState{sm.ConsensusState().PublicKey, sm.Diversifier, 0}, false), false, }, { "diversifier is blank", - types.NewClientState(1, &types.ConsensusState{solomachine.ConsensusState().PublicKey, " ", 1}, false), + solomachine.NewClientState(1, &solomachine.ConsensusState{sm.ConsensusState().PublicKey, " ", 1}, false), false, }, { "pubkey is empty", - types.NewClientState(1, &types.ConsensusState{nil, solomachine.Diversifier, solomachine.Time}, false), + solomachine.NewClientState(1, &solomachine.ConsensusState{nil, sm.Diversifier, sm.Time}, false), false, }, } @@ -97,8 +97,8 @@ func (suite *SoloMachineTestSuite) TestClientStateValidateBasic() { func (suite *SoloMachineTestSuite) TestInitialize() { // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - malleatedConsensus := solomachine.ClientState().ConsensusState + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + malleatedConsensus := sm.ClientState().ConsensusState malleatedConsensus.Timestamp = malleatedConsensus.Timestamp + 10 testCases := []struct { @@ -108,7 +108,7 @@ func (suite *SoloMachineTestSuite) TestInitialize() { }{ { "valid consensus state", - solomachine.ConsensusState(), + sm.ConsensusState(), true, }, { @@ -129,7 +129,7 @@ func (suite *SoloMachineTestSuite) TestInitialize() { } for _, tc := range testCases { - err := solomachine.ClientState().Initialize( + err := sm.ClientState().Initialize( suite.chainA.GetContext(), suite.chainA.Codec, suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "solomachine"), tc.consState, @@ -152,16 +152,16 @@ func (suite *SoloMachineTestSuite) TestVerifyClientState() { path := suite.solomachine.GetClientStatePath(counterpartyClientIdentifier) // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - value, err := types.ClientStateSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, clientState) + value, err := solomachine.ClientStateSignBytes(suite.chainA.Codec, sm.Sequence, sm.Time, sm.Diversifier, path, clientState) suite.Require().NoError(err) - sig := solomachine.GenerateSignature(value) + sig := sm.GenerateSignature(value) - signatureDoc := &types.TimestampedSignatureData{ + signatureDoc := &solomachine.TimestampedSignatureData{ SignatureData: sig, - Timestamp: solomachine.Time, + Timestamp: sm.Time, } proof, err := suite.chainA.Codec.Marshal(signatureDoc) @@ -169,38 +169,38 @@ func (suite *SoloMachineTestSuite) TestVerifyClientState() { testCases := []struct { name string - clientState *types.ClientState + clientState *solomachine.ClientState prefix exported.Prefix proof []byte expPass bool }{ { "successful verification", - solomachine.ClientState(), + sm.ClientState(), prefix, proof, true, }, { "ApplyPrefix failed", - solomachine.ClientState(), + sm.ClientState(), nil, proof, false, }, { "consensus state in client state is nil", - types.NewClientState(1, nil, false), + solomachine.NewClientState(1, nil, false), prefix, proof, false, }, { "client state latest height is less than sequence", - types.NewClientState(solomachine.Sequence-1, - &types.ConsensusState{ - Timestamp: solomachine.Time, - PublicKey: solomachine.ConsensusState().PublicKey, + solomachine.NewClientState(sm.Sequence-1, + &solomachine.ConsensusState{ + Timestamp: sm.Time, + PublicKey: sm.ConsensusState().PublicKey, }, false), prefix, proof, @@ -208,10 +208,10 @@ func (suite *SoloMachineTestSuite) TestVerifyClientState() { }, { "consensus state timestamp is greater than signature", - types.NewClientState(solomachine.Sequence, - &types.ConsensusState{ - Timestamp: solomachine.Time + 1, - PublicKey: solomachine.ConsensusState().PublicKey, + solomachine.NewClientState(sm.Sequence, + &solomachine.ConsensusState{ + Timestamp: sm.Time + 1, + PublicKey: sm.ConsensusState().PublicKey, }, false), prefix, proof, @@ -220,14 +220,14 @@ func (suite *SoloMachineTestSuite) TestVerifyClientState() { { "proof is nil", - solomachine.ClientState(), + sm.ClientState(), prefix, nil, false, }, { "proof verification failed", - solomachine.ClientState(), + sm.ClientState(), prefix, suite.GetInvalidProof(), false, @@ -245,7 +245,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientState() { } // NOTE: to replicate the ordering of connection handshake, we must decrement proof height by 1 - height := clienttypes.NewHeight(solomachine.GetHeight().GetRevisionNumber(), solomachine.GetHeight().GetRevisionHeight()-1) + height := clienttypes.NewHeight(sm.GetHeight().GetRevisionNumber(), sm.GetHeight().GetRevisionHeight()-1) err := tc.clientState.VerifyClientState( suite.store, suite.chainA.Codec, height, tc.prefix, counterpartyClientIdentifier, tc.proof, clientState, @@ -274,15 +274,15 @@ func (suite *SoloMachineTestSuite) TestVerifyClientConsensusState() { path := suite.solomachine.GetConsensusStatePath(counterpartyClientIdentifier, consensusHeight) // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - value, err := types.ConsensusStateSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, consensusState) + value, err := solomachine.ConsensusStateSignBytes(suite.chainA.Codec, sm.Sequence, sm.Time, sm.Diversifier, path, consensusState) suite.Require().NoError(err) - sig := solomachine.GenerateSignature(value) - signatureDoc := &types.TimestampedSignatureData{ + sig := sm.GenerateSignature(value) + signatureDoc := &solomachine.TimestampedSignatureData{ SignatureData: sig, - Timestamp: solomachine.Time, + Timestamp: sm.Time, } proof, err := suite.chainA.Codec.Marshal(signatureDoc) @@ -290,38 +290,38 @@ func (suite *SoloMachineTestSuite) TestVerifyClientConsensusState() { testCases := []struct { name string - clientState *types.ClientState + clientState *solomachine.ClientState prefix exported.Prefix proof []byte expPass bool }{ { "successful verification", - solomachine.ClientState(), + sm.ClientState(), prefix, proof, true, }, { "ApplyPrefix failed", - solomachine.ClientState(), + sm.ClientState(), nil, proof, false, }, { "consensus state in client state is nil", - types.NewClientState(1, nil, false), + solomachine.NewClientState(1, nil, false), prefix, proof, false, }, { "client state latest height is less than sequence", - types.NewClientState(solomachine.Sequence-1, - &types.ConsensusState{ - Timestamp: solomachine.Time, - PublicKey: solomachine.ConsensusState().PublicKey, + solomachine.NewClientState(sm.Sequence-1, + &solomachine.ConsensusState{ + Timestamp: sm.Time, + PublicKey: sm.ConsensusState().PublicKey, }, false), prefix, proof, @@ -329,10 +329,10 @@ func (suite *SoloMachineTestSuite) TestVerifyClientConsensusState() { }, { "consensus state timestamp is greater than signature", - types.NewClientState(solomachine.Sequence, - &types.ConsensusState{ - Timestamp: solomachine.Time + 1, - PublicKey: solomachine.ConsensusState().PublicKey, + solomachine.NewClientState(sm.Sequence, + &solomachine.ConsensusState{ + Timestamp: sm.Time + 1, + PublicKey: sm.ConsensusState().PublicKey, }, false), prefix, proof, @@ -341,14 +341,14 @@ func (suite *SoloMachineTestSuite) TestVerifyClientConsensusState() { { "proof is nil", - solomachine.ClientState(), + sm.ClientState(), prefix, nil, false, }, { "proof verification failed", - solomachine.ClientState(), + sm.ClientState(), prefix, suite.GetInvalidProof(), false, @@ -366,7 +366,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientConsensusState() { } // NOTE: to replicate the ordering of connection handshake, we must decrement proof height by 1 - height := clienttypes.NewHeight(solomachine.GetHeight().GetRevisionNumber(), solomachine.GetHeight().GetRevisionHeight()-2) + height := clienttypes.NewHeight(sm.GetHeight().GetRevisionNumber(), sm.GetHeight().GetRevisionHeight()-2) err := tc.clientState.VerifyClientConsensusState( suite.store, suite.chainA.Codec, height, counterpartyClientIdentifier, consensusHeight, tc.prefix, tc.proof, consensusState, @@ -391,15 +391,15 @@ func (suite *SoloMachineTestSuite) TestVerifyConnectionState() { path := suite.solomachine.GetConnectionStatePath(testConnectionID) // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - value, err := types.ConnectionStateSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, conn) + value, err := solomachine.ConnectionStateSignBytes(suite.chainA.Codec, sm.Sequence, sm.Time, sm.Diversifier, path, conn) suite.Require().NoError(err) - sig := solomachine.GenerateSignature(value) - signatureDoc := &types.TimestampedSignatureData{ + sig := sm.GenerateSignature(value) + signatureDoc := &solomachine.TimestampedSignatureData{ SignatureData: sig, - Timestamp: solomachine.Time, + Timestamp: sm.Time, } proof, err := suite.chainA.Codec.Marshal(signatureDoc) @@ -407,35 +407,35 @@ func (suite *SoloMachineTestSuite) TestVerifyConnectionState() { testCases := []struct { name string - clientState *types.ClientState + clientState *solomachine.ClientState prefix exported.Prefix proof []byte expPass bool }{ { "successful verification", - solomachine.ClientState(), + sm.ClientState(), prefix, proof, true, }, { "ApplyPrefix failed", - solomachine.ClientState(), + sm.ClientState(), commitmenttypes.NewMerklePrefix([]byte{}), proof, false, }, { "proof is nil", - solomachine.ClientState(), + sm.ClientState(), prefix, nil, false, }, { "proof verification failed", - solomachine.ClientState(), + sm.ClientState(), prefix, suite.GetInvalidProof(), false, @@ -448,7 +448,7 @@ func (suite *SoloMachineTestSuite) TestVerifyConnectionState() { expSeq := tc.clientState.Sequence + 1 err := tc.clientState.VerifyConnectionState( - suite.store, suite.chainA.Codec, solomachine.GetHeight(), tc.prefix, tc.proof, testConnectionID, conn, + suite.store, suite.chainA.Codec, sm.GetHeight(), tc.prefix, tc.proof, testConnectionID, conn, ) if tc.expPass { @@ -469,15 +469,15 @@ func (suite *SoloMachineTestSuite) TestVerifyChannelState() { path := suite.solomachine.GetChannelStatePath(testPortID, testChannelID) // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - value, err := types.ChannelStateSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, ch) + value, err := solomachine.ChannelStateSignBytes(suite.chainA.Codec, sm.Sequence, sm.Time, sm.Diversifier, path, ch) suite.Require().NoError(err) - sig := solomachine.GenerateSignature(value) - signatureDoc := &types.TimestampedSignatureData{ + sig := sm.GenerateSignature(value) + signatureDoc := &solomachine.TimestampedSignatureData{ SignatureData: sig, - Timestamp: solomachine.Time, + Timestamp: sm.Time, } proof, err := suite.chainA.Codec.Marshal(signatureDoc) @@ -485,35 +485,35 @@ func (suite *SoloMachineTestSuite) TestVerifyChannelState() { testCases := []struct { name string - clientState *types.ClientState + clientState *solomachine.ClientState prefix exported.Prefix proof []byte expPass bool }{ { "successful verification", - solomachine.ClientState(), + sm.ClientState(), prefix, proof, true, }, { "ApplyPrefix failed", - solomachine.ClientState(), + sm.ClientState(), nil, proof, false, }, { "proof is nil", - solomachine.ClientState(), + sm.ClientState(), prefix, nil, false, }, { "proof verification failed", - solomachine.ClientState(), + sm.ClientState(), prefix, suite.GetInvalidProof(), false, @@ -526,7 +526,7 @@ func (suite *SoloMachineTestSuite) TestVerifyChannelState() { expSeq := tc.clientState.Sequence + 1 err := tc.clientState.VerifyChannelState( - suite.store, suite.chainA.Codec, solomachine.GetHeight(), tc.prefix, tc.proof, testPortID, testChannelID, ch, + suite.store, suite.chainA.Codec, sm.GetHeight(), tc.prefix, tc.proof, testPortID, testChannelID, ch, ) if tc.expPass { @@ -544,17 +544,17 @@ func (suite *SoloMachineTestSuite) TestVerifyPacketCommitment() { commitmentBytes := []byte("COMMITMENT BYTES") // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - path := solomachine.GetPacketCommitmentPath(testPortID, testChannelID) + path := sm.GetPacketCommitmentPath(testPortID, testChannelID) - value, err := types.PacketCommitmentSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, commitmentBytes) + value, err := solomachine.PacketCommitmentSignBytes(suite.chainA.Codec, sm.Sequence, sm.Time, sm.Diversifier, path, commitmentBytes) suite.Require().NoError(err) - sig := solomachine.GenerateSignature(value) - signatureDoc := &types.TimestampedSignatureData{ + sig := sm.GenerateSignature(value) + signatureDoc := &solomachine.TimestampedSignatureData{ SignatureData: sig, - Timestamp: solomachine.Time, + Timestamp: sm.Time, } proof, err := suite.chainA.Codec.Marshal(signatureDoc) @@ -562,35 +562,35 @@ func (suite *SoloMachineTestSuite) TestVerifyPacketCommitment() { testCases := []struct { name string - clientState *types.ClientState + clientState *solomachine.ClientState prefix exported.Prefix proof []byte expPass bool }{ { "successful verification", - solomachine.ClientState(), + sm.ClientState(), prefix, proof, true, }, { "ApplyPrefix failed", - solomachine.ClientState(), + sm.ClientState(), commitmenttypes.NewMerklePrefix([]byte{}), proof, false, }, { "proof is nil", - solomachine.ClientState(), + sm.ClientState(), prefix, nil, false, }, { "proof verification failed", - solomachine.ClientState(), + sm.ClientState(), prefix, suite.GetInvalidProof(), false, @@ -604,7 +604,7 @@ func (suite *SoloMachineTestSuite) TestVerifyPacketCommitment() { ctx := suite.chainA.GetContext() err := tc.clientState.VerifyPacketCommitment( - ctx, suite.store, suite.chainA.Codec, solomachine.GetHeight(), 0, 0, tc.prefix, tc.proof, testPortID, testChannelID, solomachine.Sequence, commitmentBytes, + ctx, suite.store, suite.chainA.Codec, sm.GetHeight(), 0, 0, tc.prefix, tc.proof, testPortID, testChannelID, sm.Sequence, commitmentBytes, ) if tc.expPass { @@ -620,17 +620,17 @@ func (suite *SoloMachineTestSuite) TestVerifyPacketCommitment() { func (suite *SoloMachineTestSuite) TestVerifyPacketAcknowledgement() { ack := []byte("ACK") // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - path := solomachine.GetPacketAcknowledgementPath(testPortID, testChannelID) + path := sm.GetPacketAcknowledgementPath(testPortID, testChannelID) - value, err := types.PacketAcknowledgementSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, ack) + value, err := solomachine.PacketAcknowledgementSignBytes(suite.chainA.Codec, sm.Sequence, sm.Time, sm.Diversifier, path, ack) suite.Require().NoError(err) - sig := solomachine.GenerateSignature(value) - signatureDoc := &types.TimestampedSignatureData{ + sig := sm.GenerateSignature(value) + signatureDoc := &solomachine.TimestampedSignatureData{ SignatureData: sig, - Timestamp: solomachine.Time, + Timestamp: sm.Time, } proof, err := suite.chainA.Codec.Marshal(signatureDoc) @@ -638,35 +638,35 @@ func (suite *SoloMachineTestSuite) TestVerifyPacketAcknowledgement() { testCases := []struct { name string - clientState *types.ClientState + clientState *solomachine.ClientState prefix exported.Prefix proof []byte expPass bool }{ { "successful verification", - solomachine.ClientState(), + sm.ClientState(), prefix, proof, true, }, { "ApplyPrefix failed", - solomachine.ClientState(), + sm.ClientState(), commitmenttypes.NewMerklePrefix([]byte{}), proof, false, }, { "proof is nil", - solomachine.ClientState(), + sm.ClientState(), prefix, nil, false, }, { "proof verification failed", - solomachine.ClientState(), + sm.ClientState(), prefix, suite.GetInvalidProof(), false, @@ -680,7 +680,7 @@ func (suite *SoloMachineTestSuite) TestVerifyPacketAcknowledgement() { ctx := suite.chainA.GetContext() err := tc.clientState.VerifyPacketAcknowledgement( - ctx, suite.store, suite.chainA.Codec, solomachine.GetHeight(), 0, 0, tc.prefix, tc.proof, testPortID, testChannelID, solomachine.Sequence, ack, + ctx, suite.store, suite.chainA.Codec, sm.GetHeight(), 0, 0, tc.prefix, tc.proof, testPortID, testChannelID, sm.Sequence, ack, ) if tc.expPass { @@ -695,18 +695,18 @@ func (suite *SoloMachineTestSuite) TestVerifyPacketAcknowledgement() { func (suite *SoloMachineTestSuite) TestVerifyPacketReceiptAbsence() { // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { // absence uses receipt path as well - path := solomachine.GetPacketReceiptPath(testPortID, testChannelID) + path := sm.GetPacketReceiptPath(testPortID, testChannelID) - value, err := types.PacketReceiptAbsenceSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path) + value, err := solomachine.PacketReceiptAbsenceSignBytes(suite.chainA.Codec, sm.Sequence, sm.Time, sm.Diversifier, path) suite.Require().NoError(err) - sig := solomachine.GenerateSignature(value) - signatureDoc := &types.TimestampedSignatureData{ + sig := sm.GenerateSignature(value) + signatureDoc := &solomachine.TimestampedSignatureData{ SignatureData: sig, - Timestamp: solomachine.Time, + Timestamp: sm.Time, } proof, err := suite.chainA.Codec.Marshal(signatureDoc) @@ -714,35 +714,35 @@ func (suite *SoloMachineTestSuite) TestVerifyPacketReceiptAbsence() { testCases := []struct { name string - clientState *types.ClientState + clientState *solomachine.ClientState prefix exported.Prefix proof []byte expPass bool }{ { "successful verification", - solomachine.ClientState(), + sm.ClientState(), prefix, proof, true, }, { "ApplyPrefix failed", - solomachine.ClientState(), + sm.ClientState(), commitmenttypes.NewMerklePrefix([]byte{}), proof, false, }, { "proof is nil", - solomachine.ClientState(), + sm.ClientState(), prefix, nil, false, }, { "proof verification failed", - solomachine.ClientState(), + sm.ClientState(), prefix, suite.GetInvalidProof(), false, @@ -756,7 +756,7 @@ func (suite *SoloMachineTestSuite) TestVerifyPacketReceiptAbsence() { ctx := suite.chainA.GetContext() err := tc.clientState.VerifyPacketReceiptAbsence( - ctx, suite.store, suite.chainA.Codec, solomachine.GetHeight(), 0, 0, tc.prefix, tc.proof, testPortID, testChannelID, solomachine.Sequence, + ctx, suite.store, suite.chainA.Codec, sm.GetHeight(), 0, 0, tc.prefix, tc.proof, testPortID, testChannelID, sm.Sequence, ) if tc.expPass { @@ -771,18 +771,18 @@ func (suite *SoloMachineTestSuite) TestVerifyPacketReceiptAbsence() { func (suite *SoloMachineTestSuite) TestVerifyNextSeqRecv() { // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - nextSeqRecv := solomachine.Sequence + 1 - path := solomachine.GetNextSequenceRecvPath(testPortID, testChannelID) + nextSeqRecv := sm.Sequence + 1 + path := sm.GetNextSequenceRecvPath(testPortID, testChannelID) - value, err := types.NextSequenceRecvSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, nextSeqRecv) + value, err := solomachine.NextSequenceRecvSignBytes(suite.chainA.Codec, sm.Sequence, sm.Time, sm.Diversifier, path, nextSeqRecv) suite.Require().NoError(err) - sig := solomachine.GenerateSignature(value) - signatureDoc := &types.TimestampedSignatureData{ + sig := sm.GenerateSignature(value) + signatureDoc := &solomachine.TimestampedSignatureData{ SignatureData: sig, - Timestamp: solomachine.Time, + Timestamp: sm.Time, } proof, err := suite.chainA.Codec.Marshal(signatureDoc) @@ -790,35 +790,35 @@ func (suite *SoloMachineTestSuite) TestVerifyNextSeqRecv() { testCases := []struct { name string - clientState *types.ClientState + clientState *solomachine.ClientState prefix exported.Prefix proof []byte expPass bool }{ { "successful verification", - solomachine.ClientState(), + sm.ClientState(), prefix, proof, true, }, { "ApplyPrefix failed", - solomachine.ClientState(), + sm.ClientState(), commitmenttypes.NewMerklePrefix([]byte{}), proof, false, }, { "proof is nil", - solomachine.ClientState(), + sm.ClientState(), prefix, nil, false, }, { "proof verification failed", - solomachine.ClientState(), + sm.ClientState(), prefix, suite.GetInvalidProof(), false, @@ -832,7 +832,7 @@ func (suite *SoloMachineTestSuite) TestVerifyNextSeqRecv() { ctx := suite.chainA.GetContext() err := tc.clientState.VerifyNextSequenceRecv( - ctx, suite.store, suite.chainA.Codec, solomachine.GetHeight(), 0, 0, tc.prefix, tc.proof, testPortID, testChannelID, nextSeqRecv, + ctx, suite.store, suite.chainA.Codec, sm.GetHeight(), 0, 0, tc.prefix, tc.proof, testPortID, testChannelID, nextSeqRecv, ) if tc.expPass { @@ -854,7 +854,7 @@ func (suite *SoloMachineTestSuite) TestGetTimestampAtHeight() { testCases := []struct { name string - clientState *types.ClientState + clientState *solomachine.ClientState height exported.Height expValue uint64 expPass bool diff --git a/modules/light-clients/06-solomachine/types/codec.go b/modules/light-clients/06-solomachine/codec.go similarity index 99% rename from modules/light-clients/06-solomachine/types/codec.go rename to modules/light-clients/06-solomachine/codec.go index 9ceccaef3cb..a857433ed56 100644 --- a/modules/light-clients/06-solomachine/types/codec.go +++ b/modules/light-clients/06-solomachine/codec.go @@ -1,4 +1,4 @@ -package types +package solomachine import ( "github.com/cosmos/cosmos-sdk/codec" diff --git a/modules/light-clients/06-solomachine/codec_test.go b/modules/light-clients/06-solomachine/codec_test.go new file mode 100644 index 00000000000..ff7d5084043 --- /dev/null +++ b/modules/light-clients/06-solomachine/codec_test.go @@ -0,0 +1,190 @@ +package solomachine_test + +import ( + clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + solomachine "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" + ibctesting "github.com/cosmos/ibc-go/v3/testing" +) + +func (suite SoloMachineTestSuite) TestUnmarshalDataByType() { + var ( + data []byte + err error + ) + + // test singlesig and multisig public keys + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + cdc := suite.chainA.App.AppCodec() + cases := []struct { + name string + dataType solomachine.DataType + malleate func() + expPass bool + }{ + { + "empty data", solomachine.CLIENT, func() { + data = []byte{} + }, false, + }, + { + "unspecified", solomachine.UNSPECIFIED, func() { + path := sm.GetClientStatePath(counterpartyClientIdentifier) + data, err = solomachine.ClientStateDataBytes(cdc, path, sm.ClientState()) + suite.Require().NoError(err) + }, false, + }, + { + "client", solomachine.CLIENT, func() { + path := sm.GetClientStatePath(counterpartyClientIdentifier) + data, err = solomachine.ClientStateDataBytes(cdc, path, sm.ClientState()) + suite.Require().NoError(err) + }, true, + }, + { + "bad client (provides consensus state data)", solomachine.CLIENT, func() { + path := sm.GetConsensusStatePath(counterpartyClientIdentifier, clienttypes.NewHeight(0, 5)) + data, err = solomachine.ConsensusStateDataBytes(cdc, path, sm.ConsensusState()) + suite.Require().NoError(err) + }, false, + }, + { + "consensus", solomachine.CONSENSUS, func() { + path := sm.GetConsensusStatePath(counterpartyClientIdentifier, clienttypes.NewHeight(0, 5)) + data, err = solomachine.ConsensusStateDataBytes(cdc, path, sm.ConsensusState()) + suite.Require().NoError(err) + + }, true, + }, + { + "bad consensus (provides client state data)", solomachine.CONSENSUS, func() { + path := sm.GetClientStatePath(counterpartyClientIdentifier) + data, err = solomachine.ClientStateDataBytes(cdc, path, sm.ClientState()) + suite.Require().NoError(err) + }, false, + }, + { + "connection", solomachine.CONNECTION, func() { + counterparty := connectiontypes.NewCounterparty("clientB", testConnectionID, *prefix) + conn := connectiontypes.NewConnectionEnd(connectiontypes.OPEN, "clientA", counterparty, connectiontypes.ExportedVersionsToProto(connectiontypes.GetCompatibleVersions()), 0) + path := sm.GetConnectionStatePath("connectionID") + + data, err = solomachine.ConnectionStateDataBytes(cdc, path, conn) + suite.Require().NoError(err) + + }, true, + }, + { + "bad connection (uses channel data)", solomachine.CONNECTION, func() { + counterparty := channeltypes.NewCounterparty(testPortID, testChannelID) + ch := channeltypes.NewChannel(channeltypes.OPEN, channeltypes.ORDERED, counterparty, []string{testConnectionID}, "1.0.0") + path := sm.GetChannelStatePath("portID", "channelID") + + data, err = solomachine.ChannelStateDataBytes(cdc, path, ch) + suite.Require().NoError(err) + }, false, + }, + { + "channel", solomachine.CHANNEL, func() { + counterparty := channeltypes.NewCounterparty(testPortID, testChannelID) + ch := channeltypes.NewChannel(channeltypes.OPEN, channeltypes.ORDERED, counterparty, []string{testConnectionID}, "1.0.0") + path := sm.GetChannelStatePath("portID", "channelID") + + data, err = solomachine.ChannelStateDataBytes(cdc, path, ch) + suite.Require().NoError(err) + }, true, + }, + { + "bad channel (uses connection data)", solomachine.CHANNEL, func() { + counterparty := connectiontypes.NewCounterparty("clientB", testConnectionID, *prefix) + conn := connectiontypes.NewConnectionEnd(connectiontypes.OPEN, "clientA", counterparty, connectiontypes.ExportedVersionsToProto(connectiontypes.GetCompatibleVersions()), 0) + path := sm.GetConnectionStatePath("connectionID") + + data, err = solomachine.ConnectionStateDataBytes(cdc, path, conn) + suite.Require().NoError(err) + + }, false, + }, + { + "packet commitment", solomachine.PACKETCOMMITMENT, func() { + commitment := []byte("packet commitment") + path := sm.GetPacketCommitmentPath("portID", "channelID") + + data, err = solomachine.PacketCommitmentDataBytes(cdc, path, commitment) + suite.Require().NoError(err) + }, true, + }, + { + "bad packet commitment (uses next seq recv)", solomachine.PACKETCOMMITMENT, func() { + path := sm.GetNextSequenceRecvPath("portID", "channelID") + + data, err = solomachine.NextSequenceRecvDataBytes(cdc, path, 10) + suite.Require().NoError(err) + }, false, + }, + { + "packet acknowledgement", solomachine.PACKETACKNOWLEDGEMENT, func() { + commitment := []byte("packet acknowledgement") + path := sm.GetPacketAcknowledgementPath("portID", "channelID") + + data, err = solomachine.PacketAcknowledgementDataBytes(cdc, path, commitment) + suite.Require().NoError(err) + }, true, + }, + { + "bad packet acknowledgement (uses next sequence recv)", solomachine.PACKETACKNOWLEDGEMENT, func() { + path := sm.GetNextSequenceRecvPath("portID", "channelID") + + data, err = solomachine.NextSequenceRecvDataBytes(cdc, path, 10) + suite.Require().NoError(err) + }, false, + }, + { + "packet acknowledgement absence", solomachine.PACKETRECEIPTABSENCE, func() { + path := sm.GetPacketReceiptPath("portID", "channelID") + + data, err = solomachine.PacketReceiptAbsenceDataBytes(cdc, path) + suite.Require().NoError(err) + }, true, + }, + { + "next sequence recv", solomachine.NEXTSEQUENCERECV, func() { + path := sm.GetNextSequenceRecvPath("portID", "channelID") + + data, err = solomachine.NextSequenceRecvDataBytes(cdc, path, 10) + suite.Require().NoError(err) + }, true, + }, + { + "bad next sequence recv (uses packet commitment)", solomachine.NEXTSEQUENCERECV, func() { + commitment := []byte("packet commitment") + path := sm.GetPacketCommitmentPath("portID", "channelID") + + data, err = solomachine.PacketCommitmentDataBytes(cdc, path, commitment) + suite.Require().NoError(err) + }, false, + }, + } + + for _, tc := range cases { + tc := tc + + suite.Run(tc.name, func() { + tc.malleate() + + data, err := solomachine.UnmarshalDataByType(cdc, tc.dataType, data) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(data) + } else { + suite.Require().Error(err) + suite.Require().Nil(data) + } + }) + } + } + +} diff --git a/modules/light-clients/06-solomachine/types/consensus_state.go b/modules/light-clients/06-solomachine/consensus_state.go similarity index 98% rename from modules/light-clients/06-solomachine/types/consensus_state.go rename to modules/light-clients/06-solomachine/consensus_state.go index 61b15b65882..35e11fa6064 100644 --- a/modules/light-clients/06-solomachine/types/consensus_state.go +++ b/modules/light-clients/06-solomachine/consensus_state.go @@ -1,4 +1,4 @@ -package types +package solomachine import ( "strings" diff --git a/modules/light-clients/06-solomachine/types/consensus_state_test.go b/modules/light-clients/06-solomachine/consensus_state_test.go similarity index 64% rename from modules/light-clients/06-solomachine/types/consensus_state_test.go rename to modules/light-clients/06-solomachine/consensus_state_test.go index 5b2b29cadcd..6b601a95cb6 100644 --- a/modules/light-clients/06-solomachine/types/consensus_state_test.go +++ b/modules/light-clients/06-solomachine/consensus_state_test.go @@ -1,8 +1,8 @@ -package types_test +package solomachine_test import ( "github.com/cosmos/ibc-go/v3/modules/core/exported" - "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types" + solomachine "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" ibctesting "github.com/cosmos/ibc-go/v3/testing" ) @@ -15,41 +15,41 @@ func (suite *SoloMachineTestSuite) TestConsensusState() { func (suite *SoloMachineTestSuite) TestConsensusStateValidateBasic() { // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { testCases := []struct { name string - consensusState *types.ConsensusState + consensusState *solomachine.ConsensusState expPass bool }{ { "valid consensus state", - solomachine.ConsensusState(), + sm.ConsensusState(), true, }, { "timestamp is zero", - &types.ConsensusState{ - PublicKey: solomachine.ConsensusState().PublicKey, + &solomachine.ConsensusState{ + PublicKey: sm.ConsensusState().PublicKey, Timestamp: 0, - Diversifier: solomachine.Diversifier, + Diversifier: sm.Diversifier, }, false, }, { "diversifier is blank", - &types.ConsensusState{ - PublicKey: solomachine.ConsensusState().PublicKey, - Timestamp: solomachine.Time, + &solomachine.ConsensusState{ + PublicKey: sm.ConsensusState().PublicKey, + Timestamp: sm.Time, Diversifier: " ", }, false, }, { "pubkey is nil", - &types.ConsensusState{ - Timestamp: solomachine.Time, - Diversifier: solomachine.Diversifier, + &solomachine.ConsensusState{ + Timestamp: sm.Time, + Diversifier: sm.Diversifier, PublicKey: nil, }, false, diff --git a/modules/light-clients/06-solomachine/types/errors.go b/modules/light-clients/06-solomachine/errors.go similarity index 97% rename from modules/light-clients/06-solomachine/types/errors.go rename to modules/light-clients/06-solomachine/errors.go index 3e27f60732a..73ca5999dbc 100644 --- a/modules/light-clients/06-solomachine/types/errors.go +++ b/modules/light-clients/06-solomachine/errors.go @@ -1,4 +1,4 @@ -package types +package solomachine import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" diff --git a/modules/light-clients/06-solomachine/types/header.go b/modules/light-clients/06-solomachine/header.go similarity index 98% rename from modules/light-clients/06-solomachine/types/header.go rename to modules/light-clients/06-solomachine/header.go index a6639a0f0c9..f2a63ef4ddb 100644 --- a/modules/light-clients/06-solomachine/types/header.go +++ b/modules/light-clients/06-solomachine/header.go @@ -1,4 +1,4 @@ -package types +package solomachine import ( "strings" diff --git a/modules/light-clients/06-solomachine/types/header_test.go b/modules/light-clients/06-solomachine/header_test.go similarity index 83% rename from modules/light-clients/06-solomachine/types/header_test.go rename to modules/light-clients/06-solomachine/header_test.go index 48ce858c76a..5b02b41c555 100644 --- a/modules/light-clients/06-solomachine/types/header_test.go +++ b/modules/light-clients/06-solomachine/header_test.go @@ -1,20 +1,20 @@ -package types_test +package solomachine_test import ( "github.com/cosmos/ibc-go/v3/modules/core/exported" - "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types" + solomachine "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" ibctesting "github.com/cosmos/ibc-go/v3/testing" ) func (suite *SoloMachineTestSuite) TestHeaderValidateBasic() { // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - header := solomachine.CreateHeader() + header := sm.CreateHeader() cases := []struct { name string - header *types.Header + header *solomachine.Header expPass bool }{ { @@ -24,7 +24,7 @@ func (suite *SoloMachineTestSuite) TestHeaderValidateBasic() { }, { "sequence is zero", - &types.Header{ + &solomachine.Header{ Sequence: 0, Timestamp: header.Timestamp, Signature: header.Signature, @@ -35,7 +35,7 @@ func (suite *SoloMachineTestSuite) TestHeaderValidateBasic() { }, { "timestamp is zero", - &types.Header{ + &solomachine.Header{ Sequence: header.Sequence, Timestamp: 0, Signature: header.Signature, @@ -46,7 +46,7 @@ func (suite *SoloMachineTestSuite) TestHeaderValidateBasic() { }, { "signature is empty", - &types.Header{ + &solomachine.Header{ Sequence: header.Sequence, Timestamp: header.Timestamp, Signature: []byte{}, @@ -57,7 +57,7 @@ func (suite *SoloMachineTestSuite) TestHeaderValidateBasic() { }, { "diversifier contains only spaces", - &types.Header{ + &solomachine.Header{ Sequence: header.Sequence, Timestamp: header.Timestamp, Signature: header.Signature, @@ -68,7 +68,7 @@ func (suite *SoloMachineTestSuite) TestHeaderValidateBasic() { }, { "public key is nil", - &types.Header{ + &solomachine.Header{ Sequence: header.Sequence, Timestamp: header.Timestamp, Signature: header.Signature, diff --git a/modules/light-clients/06-solomachine/types/misbehaviour.go b/modules/light-clients/06-solomachine/misbehaviour.go similarity index 99% rename from modules/light-clients/06-solomachine/types/misbehaviour.go rename to modules/light-clients/06-solomachine/misbehaviour.go index 0b4c2f3bcd0..145841e4b0e 100644 --- a/modules/light-clients/06-solomachine/types/misbehaviour.go +++ b/modules/light-clients/06-solomachine/misbehaviour.go @@ -1,4 +1,4 @@ -package types +package solomachine import ( "bytes" diff --git a/modules/light-clients/06-solomachine/types/misbehaviour_handle.go b/modules/light-clients/06-solomachine/misbehaviour_handle.go similarity index 98% rename from modules/light-clients/06-solomachine/types/misbehaviour_handle.go rename to modules/light-clients/06-solomachine/misbehaviour_handle.go index becf8a654a5..c5f53239fd2 100644 --- a/modules/light-clients/06-solomachine/types/misbehaviour_handle.go +++ b/modules/light-clients/06-solomachine/misbehaviour_handle.go @@ -1,4 +1,4 @@ -package types +package solomachine import ( "github.com/cosmos/cosmos-sdk/codec" diff --git a/modules/light-clients/06-solomachine/types/misbehaviour_test.go b/modules/light-clients/06-solomachine/misbehaviour_test.go similarity index 65% rename from modules/light-clients/06-solomachine/types/misbehaviour_test.go rename to modules/light-clients/06-solomachine/misbehaviour_test.go index d7c41996e19..0bb533a7660 100644 --- a/modules/light-clients/06-solomachine/types/misbehaviour_test.go +++ b/modules/light-clients/06-solomachine/misbehaviour_test.go @@ -1,8 +1,8 @@ -package types_test +package solomachine_test import ( "github.com/cosmos/ibc-go/v3/modules/core/exported" - "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types" + solomachine "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" ibctesting "github.com/cosmos/ibc-go/v3/testing" ) @@ -14,95 +14,95 @@ func (suite *SoloMachineTestSuite) TestMisbehaviour() { func (suite *SoloMachineTestSuite) TestMisbehaviourValidateBasic() { // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { testCases := []struct { name string - malleateMisbehaviour func(misbehaviour *types.Misbehaviour) + malleateMisbehaviour func(misbehaviour *solomachine.Misbehaviour) expPass bool }{ { "valid misbehaviour", - func(*types.Misbehaviour) {}, + func(*solomachine.Misbehaviour) {}, true, }, { "invalid client ID", - func(misbehaviour *types.Misbehaviour) { + func(misbehaviour *solomachine.Misbehaviour) { misbehaviour.ClientId = "(badclientid)" }, false, }, { "sequence is zero", - func(misbehaviour *types.Misbehaviour) { + func(misbehaviour *solomachine.Misbehaviour) { misbehaviour.Sequence = 0 }, false, }, { "signature one sig is empty", - func(misbehaviour *types.Misbehaviour) { + func(misbehaviour *solomachine.Misbehaviour) { misbehaviour.SignatureOne.Signature = []byte{} }, false, }, { "signature two sig is empty", - func(misbehaviour *types.Misbehaviour) { + func(misbehaviour *solomachine.Misbehaviour) { misbehaviour.SignatureTwo.Signature = []byte{} }, false, }, { "signature one data is empty", - func(misbehaviour *types.Misbehaviour) { + func(misbehaviour *solomachine.Misbehaviour) { misbehaviour.SignatureOne.Data = nil }, false, }, { "signature two data is empty", - func(misbehaviour *types.Misbehaviour) { + func(misbehaviour *solomachine.Misbehaviour) { misbehaviour.SignatureTwo.Data = []byte{} }, false, }, { "signatures are identical", - func(misbehaviour *types.Misbehaviour) { + func(misbehaviour *solomachine.Misbehaviour) { misbehaviour.SignatureTwo.Signature = misbehaviour.SignatureOne.Signature }, false, }, { "data signed is identical", - func(misbehaviour *types.Misbehaviour) { + func(misbehaviour *solomachine.Misbehaviour) { misbehaviour.SignatureTwo.Data = misbehaviour.SignatureOne.Data }, false, }, { "data type for SignatureOne is unspecified", - func(misbehaviour *types.Misbehaviour) { - misbehaviour.SignatureOne.DataType = types.UNSPECIFIED + func(misbehaviour *solomachine.Misbehaviour) { + misbehaviour.SignatureOne.DataType = solomachine.UNSPECIFIED }, false, }, { "data type for SignatureTwo is unspecified", - func(misbehaviour *types.Misbehaviour) { - misbehaviour.SignatureTwo.DataType = types.UNSPECIFIED + func(misbehaviour *solomachine.Misbehaviour) { + misbehaviour.SignatureTwo.DataType = solomachine.UNSPECIFIED }, false, }, { "timestamp for SignatureOne is zero", - func(misbehaviour *types.Misbehaviour) { + func(misbehaviour *solomachine.Misbehaviour) { misbehaviour.SignatureOne.Timestamp = 0 }, false, }, { "timestamp for SignatureTwo is zero", - func(misbehaviour *types.Misbehaviour) { + func(misbehaviour *solomachine.Misbehaviour) { misbehaviour.SignatureTwo.Timestamp = 0 }, false, }, @@ -113,7 +113,7 @@ func (suite *SoloMachineTestSuite) TestMisbehaviourValidateBasic() { suite.Run(tc.name, func() { - misbehaviour := solomachine.CreateMisbehaviour() + misbehaviour := sm.CreateMisbehaviour() tc.malleateMisbehaviour(misbehaviour) err := misbehaviour.ValidateBasic() diff --git a/modules/light-clients/06-solomachine/module.go b/modules/light-clients/06-solomachine/module.go index d607282725a..d66b482541c 100644 --- a/modules/light-clients/06-solomachine/module.go +++ b/modules/light-clients/06-solomachine/module.go @@ -1,10 +1,6 @@ package solomachine -import ( - "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types" -) - // Name returns the solo machine client name. func Name() string { - return types.SubModuleName + return SubModuleName } diff --git a/modules/light-clients/06-solomachine/types/proof.go b/modules/light-clients/06-solomachine/proof.go similarity index 99% rename from modules/light-clients/06-solomachine/types/proof.go rename to modules/light-clients/06-solomachine/proof.go index f884a26d745..cdbd8f19983 100644 --- a/modules/light-clients/06-solomachine/types/proof.go +++ b/modules/light-clients/06-solomachine/proof.go @@ -1,4 +1,4 @@ -package types +package solomachine import ( "github.com/cosmos/cosmos-sdk/codec" diff --git a/modules/light-clients/06-solomachine/types/proof_test.go b/modules/light-clients/06-solomachine/proof_test.go similarity index 57% rename from modules/light-clients/06-solomachine/types/proof_test.go rename to modules/light-clients/06-solomachine/proof_test.go index 65e88aa3bde..dd7bb52ec1e 100644 --- a/modules/light-clients/06-solomachine/types/proof_test.go +++ b/modules/light-clients/06-solomachine/proof_test.go @@ -1,11 +1,10 @@ -package types_test +package solomachine_test import ( cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/types/tx/signing" - "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types" - solomachinetypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types" + solomachine "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" ibctesting "github.com/cosmos/ibc-go/v3/testing" ) @@ -14,11 +13,11 @@ func (suite *SoloMachineTestSuite) TestVerifySignature() { signBytes := []byte("sign bytes") singleSignature := suite.solomachine.GenerateSignature(signBytes) - singleSigData, err := solomachinetypes.UnmarshalSignatureData(cdc, singleSignature) + singleSigData, err := solomachine.UnmarshalSignatureData(cdc, singleSignature) suite.Require().NoError(err) multiSignature := suite.solomachineMulti.GenerateSignature(signBytes) - multiSigData, err := solomachinetypes.UnmarshalSignatureData(cdc, multiSignature) + multiSigData, err := solomachine.UnmarshalSignatureData(cdc, multiSignature) suite.Require().NoError(err) testCases := []struct { @@ -57,7 +56,7 @@ func (suite *SoloMachineTestSuite) TestVerifySignature() { tc := tc suite.Run(tc.name, func() { - err := solomachinetypes.VerifySignature(tc.publicKey, signBytes, tc.sigData) + err := solomachine.VerifySignature(tc.publicKey, signBytes, tc.sigData) if tc.expPass { suite.Require().NoError(err) @@ -71,15 +70,15 @@ func (suite *SoloMachineTestSuite) TestVerifySignature() { func (suite *SoloMachineTestSuite) TestClientStateSignBytes() { cdc := suite.chainA.App.AppCodec() - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { // success - path := solomachine.GetClientStatePath(counterpartyClientIdentifier) - bz, err := types.ClientStateSignBytes(cdc, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, solomachine.ClientState()) + path := sm.GetClientStatePath(counterpartyClientIdentifier) + bz, err := solomachine.ClientStateSignBytes(cdc, sm.Sequence, sm.Time, sm.Diversifier, path, sm.ClientState()) suite.Require().NoError(err) suite.Require().NotNil(bz) // nil client state - bz, err = types.ClientStateSignBytes(cdc, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, nil) + bz, err = solomachine.ClientStateSignBytes(cdc, sm.Sequence, sm.Time, sm.Diversifier, path, nil) suite.Require().Error(err) suite.Require().Nil(bz) } @@ -88,15 +87,15 @@ func (suite *SoloMachineTestSuite) TestClientStateSignBytes() { func (suite *SoloMachineTestSuite) TestConsensusStateSignBytes() { cdc := suite.chainA.App.AppCodec() - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { // success - path := solomachine.GetConsensusStatePath(counterpartyClientIdentifier, consensusHeight) - bz, err := types.ConsensusStateSignBytes(cdc, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, solomachine.ConsensusState()) + path := sm.GetConsensusStatePath(counterpartyClientIdentifier, consensusHeight) + bz, err := solomachine.ConsensusStateSignBytes(cdc, sm.Sequence, sm.Time, sm.Diversifier, path, sm.ConsensusState()) suite.Require().NoError(err) suite.Require().NotNil(bz) // nil consensus state - bz, err = types.ConsensusStateSignBytes(cdc, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, nil) + bz, err = solomachine.ConsensusStateSignBytes(cdc, sm.Sequence, sm.Time, sm.Diversifier, path, nil) suite.Require().Error(err) suite.Require().Nil(bz) } diff --git a/modules/light-clients/06-solomachine/types/proposal_handle.go b/modules/light-clients/06-solomachine/proposal_handle.go similarity index 99% rename from modules/light-clients/06-solomachine/types/proposal_handle.go rename to modules/light-clients/06-solomachine/proposal_handle.go index 456129537f4..4a32e2af2e1 100644 --- a/modules/light-clients/06-solomachine/types/proposal_handle.go +++ b/modules/light-clients/06-solomachine/proposal_handle.go @@ -1,4 +1,4 @@ -package types +package solomachine import ( "reflect" diff --git a/modules/light-clients/06-solomachine/types/proposal_handle_test.go b/modules/light-clients/06-solomachine/proposal_handle_test.go similarity index 71% rename from modules/light-clients/06-solomachine/types/proposal_handle_test.go rename to modules/light-clients/06-solomachine/proposal_handle_test.go index fbb814c0791..26f04b93326 100644 --- a/modules/light-clients/06-solomachine/types/proposal_handle_test.go +++ b/modules/light-clients/06-solomachine/proposal_handle_test.go @@ -1,22 +1,22 @@ -package types_test +package solomachine_test import ( clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/exported" - "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + solomachine "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v3/testing" ) func (suite *SoloMachineTestSuite) TestCheckSubstituteAndUpdateState() { var ( - subjectClientState *types.ClientState + subjectClientState *solomachine.ClientState substituteClientState exported.ClientState ) // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { testCases := []struct { name string @@ -46,12 +46,12 @@ func (suite *SoloMachineTestSuite) TestCheckSubstituteAndUpdateState() { { "substitute public key is nil", func() { - substituteClientState.(*types.ClientState).ConsensusState.PublicKey = nil + substituteClientState.(*solomachine.ClientState).ConsensusState.PublicKey = nil }, false, }, { "subject and substitute use the same public key", func() { - substituteClientState.(*types.ClientState).ConsensusState.PublicKey = subjectClientState.ConsensusState.PublicKey + substituteClientState.(*solomachine.ClientState).ConsensusState.PublicKey = subjectClientState.ConsensusState.PublicKey }, false, }, } @@ -62,14 +62,14 @@ func (suite *SoloMachineTestSuite) TestCheckSubstituteAndUpdateState() { suite.Run(tc.name, func() { suite.SetupTest() - subjectClientState = solomachine.ClientState() + subjectClientState = sm.ClientState() subjectClientState.AllowUpdateAfterProposal = true substitute := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "substitute", "testing", 5) substituteClientState = substitute.ClientState() tc.malleate() - subjectClientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), solomachine.ClientID) + subjectClientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), sm.ClientID) substituteClientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), substitute.ClientID) updatedClient, err := subjectClientState.CheckSubstituteAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), subjectClientStore, substituteClientStore, substituteClientState) @@ -77,9 +77,9 @@ func (suite *SoloMachineTestSuite) TestCheckSubstituteAndUpdateState() { if tc.expPass { suite.Require().NoError(err) - suite.Require().Equal(substituteClientState.(*types.ClientState).ConsensusState, updatedClient.(*types.ClientState).ConsensusState) - suite.Require().Equal(substituteClientState.(*types.ClientState).Sequence, updatedClient.(*types.ClientState).Sequence) - suite.Require().Equal(false, updatedClient.(*types.ClientState).IsFrozen) + suite.Require().Equal(substituteClientState.(*solomachine.ClientState).ConsensusState, updatedClient.(*solomachine.ClientState).ConsensusState) + suite.Require().Equal(substituteClientState.(*solomachine.ClientState).Sequence, updatedClient.(*solomachine.ClientState).Sequence) + suite.Require().Equal(false, updatedClient.(*solomachine.ClientState).IsFrozen) // ensure updated client state is set in store bz := subjectClientStore.Get(host.ClientStateKey()) diff --git a/modules/light-clients/06-solomachine/types/solomachine.go b/modules/light-clients/06-solomachine/solomachine.go similarity index 98% rename from modules/light-clients/06-solomachine/types/solomachine.go rename to modules/light-clients/06-solomachine/solomachine.go index 90eff77d200..e3d58781ea8 100644 --- a/modules/light-clients/06-solomachine/types/solomachine.go +++ b/modules/light-clients/06-solomachine/solomachine.go @@ -1,4 +1,4 @@ -package types +package solomachine import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" diff --git a/modules/light-clients/06-solomachine/types/solomachine.pb.go b/modules/light-clients/06-solomachine/solomachine.pb.go similarity index 98% rename from modules/light-clients/06-solomachine/types/solomachine.pb.go rename to modules/light-clients/06-solomachine/solomachine.pb.go index 6eafde55d0c..845195afbdd 100644 --- a/modules/light-clients/06-solomachine/types/solomachine.pb.go +++ b/modules/light-clients/06-solomachine/solomachine.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: ibc/lightclients/solomachine/v2/solomachine.proto -package types +package solomachine import ( fmt "fmt" @@ -824,11 +824,11 @@ func init() { } var fileDescriptor_141333b361aae010 = []byte{ - // 1373 bytes of a gzipped FileDescriptorProto + // 1372 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0x5f, 0x6f, 0xdb, 0x54, 0x14, 0xaf, 0xb3, 0xac, 0x6b, 0x4e, 0xba, 0x36, 0x78, 0xd9, 0x96, 0x7a, 0x53, 0x62, 0x8c, 0x18, - 0x05, 0xb1, 0x84, 0x76, 0x62, 0x42, 0x13, 0x02, 0x1c, 0xc7, 0x63, 0xd9, 0x5a, 0x37, 0x38, 0x2e, - 0xb0, 0x09, 0xc9, 0x38, 0xce, 0x6d, 0x62, 0x2d, 0xf1, 0xcd, 0x62, 0x27, 0x59, 0x90, 0x90, 0x10, + 0x05, 0xb1, 0x84, 0x76, 0x62, 0x42, 0x03, 0x01, 0x8e, 0xe3, 0xb1, 0x6c, 0xad, 0x1b, 0x1c, 0x17, + 0xd8, 0x84, 0x64, 0x39, 0xce, 0x6d, 0x62, 0x2d, 0xf1, 0xcd, 0x62, 0x27, 0x59, 0x90, 0x90, 0x10, 0x4f, 0x23, 0xe2, 0x81, 0x2f, 0x10, 0x09, 0x81, 0xf8, 0x1c, 0xbc, 0x21, 0x78, 0xdb, 0x23, 0x4f, 0x01, 0x6d, 0xdf, 0x20, 0x9f, 0x00, 0xd9, 0xf7, 0x26, 0xb6, 0xd3, 0x35, 0x15, 0xff, 0xde, 0xee, 0x3d, 0xbf, 0x73, 0x7e, 0xe7, 0xcf, 0x3d, 0x3e, 0xf7, 0x1a, 0x76, 0xac, 0x9a, 0x59, 0x68, 0x59, @@ -864,11 +864,11 @@ var fileDescriptor_141333b361aae010 = []byte{ 0xe8, 0xd8, 0x9e, 0x35, 0xfb, 0xce, 0xa9, 0xcd, 0x5e, 0x9d, 0x59, 0x89, 0x76, 0xbd, 0x64, 0xb8, 0x46, 0x31, 0x33, 0x9d, 0xe4, 0xd2, 0x24, 0x8e, 0x08, 0xa3, 0xa0, 0xae, 0xcf, 0xf7, 0x07, 0xf6, 0x82, 0x47, 0x77, 0x80, 0x69, 0xd1, 0xff, 0x2b, 0x8f, 0xee, 0x00, 0x87, 0x3d, 0x6a, 0x03, 0x4c, - 0x6b, 0xf9, 0x0b, 0x03, 0xa9, 0x45, 0x8a, 0x68, 0x83, 0x30, 0x8b, 0x0d, 0xf2, 0x19, 0x24, 0xea, + 0x6b, 0xf9, 0x0b, 0x03, 0xa9, 0x45, 0x8a, 0x68, 0x83, 0x30, 0x8b, 0x0d, 0xf2, 0x39, 0x24, 0xea, 0x86, 0x6b, 0xe8, 0xee, 0xb0, 0x43, 0x2a, 0xb7, 0xb1, 0xfb, 0xfa, 0xa9, 0x61, 0x7a, 0xbc, 0xda, 0xb0, 0x83, 0xc2, 0x93, 0x66, 0xce, 0x22, 0xa8, 0x6b, 0x75, 0x8a, 0xb3, 0x2c, 0xc4, 0xbd, 0x35, 0xed, 0x4b, 0x7f, 0x1d, 0x6d, 0xe7, 0xf8, 0x8b, 0xbf, 0x8c, 0xaf, 0x18, 0xc8, 0x68, 0x33, 0x19, - 0xaa, 0xcf, 0x73, 0xf2, 0x13, 0xfa, 0x00, 0x36, 0x82, 0x5a, 0xf8, 0xf4, 0x7e, 0x56, 0xe1, 0xee, + 0xaa, 0xcf, 0x73, 0xf2, 0x13, 0xfa, 0x10, 0x36, 0x82, 0x5a, 0xf8, 0xf4, 0x7e, 0x56, 0xe1, 0xee, 0x8d, 0xe2, 0x82, 0x1a, 0x1c, 0x47, 0xe9, 0x58, 0x08, 0xb1, 0x17, 0x87, 0xf0, 0x07, 0x03, 0x09, 0xcf, 0x6f, 0x71, 0xe8, 0x22, 0xe7, 0x5f, 0x7c, 0x9f, 0x0b, 0xa3, 0xe2, 0xcc, 0xf1, 0x51, 0x11, 0x39, 0x82, 0xf8, 0xff, 0x75, 0x04, 0x67, 0x83, 0x23, 0xa0, 0x19, 0xfe, 0xc4, 0x00, 0x90, 0xf1, @@ -886,7 +886,7 @@ var fileDescriptor_141333b361aae010 = []byte{ 0xdf, 0xd9, 0xba, 0x1a, 0x92, 0x08, 0xf7, 0x61, 0x8b, 0x70, 0x89, 0xe6, 0x43, 0x1b, 0x0f, 0x5a, 0xa8, 0xde, 0x40, 0x4b, 0x09, 0xb7, 0x61, 0xd3, 0x88, 0xaa, 0x52, 0xd6, 0x45, 0xb1, 0x90, 0x87, 0x0c, 0xa1, 0x56, 0x91, 0x89, 0xac, 0x8e, 0x2b, 0xd6, 0x1c, 0x6f, 0x0e, 0x9c, 0xc4, 0x2c, 0x34, - 0x21, 0xad, 0xa0, 0xc7, 0x6e, 0x95, 0xce, 0x0b, 0x15, 0x99, 0xfd, 0x13, 0xa3, 0x78, 0x17, 0xce, + 0x21, 0xad, 0xa0, 0xc7, 0x6e, 0x95, 0xce, 0x0b, 0x15, 0x99, 0xfd, 0x13, 0xa3, 0x78, 0x0f, 0xce, 0xdb, 0xe8, 0xb1, 0xab, 0x3b, 0xe8, 0x91, 0xde, 0x45, 0x66, 0x9f, 0xcc, 0x93, 0xf0, 0x35, 0x10, 0x81, 0x05, 0x35, 0x69, 0x13, 0x6a, 0x8f, 0xf5, 0x8d, 0x6f, 0xe3, 0xb0, 0x36, 0x1b, 0x0c, 0xec, 0x3b, 0xf0, 0x4a, 0x49, 0xd4, 0x44, 0x5d, 0xbb, 0x5f, 0x91, 0xf5, 0x43, 0xa5, 0xac, 0x94, 0xb5, @@ -898,19 +898,19 @@ var fileDescriptor_141333b361aae010 = []byte{ 0xaa, 0x7e, 0x86, 0xdb, 0x18, 0x8d, 0x79, 0x08, 0xe4, 0xec, 0x36, 0x5c, 0x0e, 0xe9, 0xdf, 0x11, 0x15, 0x45, 0xde, 0xa3, 0xca, 0x71, 0x2e, 0x39, 0x1a, 0xf3, 0xe7, 0xa8, 0x90, 0x7d, 0x1b, 0xae, 0x04, 0x9a, 0x15, 0x51, 0xba, 0x27, 0x6b, 0xba, 0x74, 0xb0, 0xbf, 0x5f, 0xd6, 0xf6, 0x65, 0x45, - 0x4b, 0x9d, 0xe5, 0xd2, 0xa3, 0x31, 0x9f, 0x22, 0x40, 0x20, 0x67, 0xdf, 0x07, 0xfe, 0x98, 0x99, - 0x28, 0xdd, 0x53, 0x0e, 0x3e, 0xd9, 0x93, 0x4b, 0x1f, 0xca, 0xbe, 0xed, 0x2a, 0xb7, 0x35, 0x1a, - 0xf3, 0x17, 0x09, 0xba, 0x00, 0xb2, 0xef, 0xbd, 0x80, 0x40, 0x95, 0x25, 0xb9, 0x5c, 0xd1, 0x74, + 0x4b, 0x9d, 0xe5, 0xd2, 0xa3, 0x31, 0x9f, 0x22, 0x40, 0x20, 0x67, 0x3f, 0x00, 0xfe, 0x98, 0x99, + 0x28, 0xdd, 0x53, 0x0e, 0x3e, 0xdd, 0x93, 0x4b, 0x1f, 0xc9, 0xbe, 0xed, 0x2a, 0xb7, 0x35, 0x1a, + 0xf3, 0x17, 0x09, 0xba, 0x00, 0xb2, 0xef, 0xbf, 0x80, 0x40, 0x95, 0x25, 0xb9, 0x5c, 0xd1, 0x74, 0xb1, 0x58, 0x95, 0x15, 0x49, 0x4e, 0x9d, 0xe3, 0x32, 0xa3, 0x31, 0x9f, 0x26, 0x28, 0x05, 0x29, - 0xc6, 0xde, 0x84, 0xab, 0x81, 0xbd, 0x22, 0x7f, 0xaa, 0xe9, 0x55, 0xf9, 0xa3, 0x43, 0x0f, 0xf2, - 0x68, 0x3e, 0x4e, 0xad, 0x91, 0xc0, 0x3d, 0x64, 0x06, 0x78, 0x72, 0x96, 0x87, 0x54, 0x60, 0x77, + 0xc6, 0xde, 0x84, 0xab, 0x81, 0xbd, 0x22, 0x7f, 0xa6, 0xe9, 0x55, 0xf9, 0xe3, 0x43, 0x0f, 0xf2, + 0x68, 0x3e, 0x49, 0xad, 0x91, 0xc0, 0x3d, 0x64, 0x06, 0x78, 0x72, 0x96, 0x87, 0x54, 0x60, 0x77, 0x47, 0x16, 0x4b, 0xb2, 0x9a, 0x4a, 0x90, 0x93, 0x21, 0x3b, 0x2e, 0xfe, 0xe4, 0xc7, 0xec, 0x4a, - 0xf1, 0xf3, 0x5f, 0x9f, 0x65, 0x99, 0xa7, 0xcf, 0xb2, 0xcc, 0x9f, 0xcf, 0xb2, 0xcc, 0x77, 0xcf, - 0xb3, 0x2b, 0x4f, 0x9f, 0x67, 0x57, 0x7e, 0x7f, 0x9e, 0x5d, 0x79, 0x70, 0xbb, 0x61, 0xb9, 0xcd, - 0x5e, 0x2d, 0x6f, 0xe2, 0x76, 0xc1, 0xc4, 0x4e, 0x1b, 0x3b, 0x05, 0xab, 0x66, 0x5e, 0x6f, 0xe0, - 0x42, 0xff, 0x46, 0xa1, 0x8d, 0xeb, 0xbd, 0x16, 0x72, 0xc8, 0x1f, 0xd5, 0xf5, 0xd9, 0x2f, 0xd5, - 0x5b, 0x37, 0xaf, 0x87, 0xff, 0xaa, 0xbc, 0x6b, 0xc6, 0xa9, 0xad, 0xfa, 0xf3, 0xec, 0xc6, 0x5f, - 0x01, 0x00, 0x00, 0xff, 0xff, 0xe7, 0x08, 0x62, 0xbe, 0x82, 0x0d, 0x00, 0x00, + 0xf1, 0xe8, 0xd7, 0x67, 0x59, 0xe6, 0xe9, 0xb3, 0x2c, 0xf3, 0xe7, 0xb3, 0x2c, 0xf3, 0xdd, 0xf3, + 0xec, 0xca, 0xd3, 0xe7, 0xd9, 0x95, 0xdf, 0x9f, 0x67, 0x57, 0x1e, 0xec, 0x35, 0x2c, 0xb7, 0xd9, + 0xab, 0xe5, 0x4d, 0xdc, 0x2e, 0x98, 0xd8, 0x69, 0x63, 0xa7, 0x60, 0xd5, 0xcc, 0xeb, 0x0d, 0x5c, + 0xe8, 0xdf, 0x28, 0xb4, 0x71, 0xbd, 0xd7, 0x42, 0x0e, 0xf9, 0xa3, 0xba, 0x3e, 0xfb, 0xa5, 0x7a, + 0xeb, 0xe6, 0xf5, 0xd0, 0x15, 0xf4, 0x6e, 0x68, 0x5d, 0x5b, 0xf5, 0xa7, 0xda, 0x8d, 0xbf, 0x02, + 0x00, 0x00, 0xff, 0xff, 0xcc, 0xc1, 0x35, 0x7d, 0x88, 0x0d, 0x00, 0x00, } func (m *ClientState) Marshal() (dAtA []byte, err error) { diff --git a/modules/light-clients/06-solomachine/types/solomachine_test.go b/modules/light-clients/06-solomachine/solomachine_test.go similarity index 85% rename from modules/light-clients/06-solomachine/types/solomachine_test.go rename to modules/light-clients/06-solomachine/solomachine_test.go index 56ebadee3d1..c913d776fc2 100644 --- a/modules/light-clients/06-solomachine/types/solomachine_test.go +++ b/modules/light-clients/06-solomachine/solomachine_test.go @@ -1,4 +1,4 @@ -package types_test +package solomachine_test import ( "testing" @@ -6,7 +6,6 @@ import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" @@ -14,7 +13,7 @@ import ( host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/exported" - "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types" + solomachine "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" ibctesting "github.com/cosmos/ibc-go/v3/testing" ) @@ -58,7 +57,7 @@ func (suite *SoloMachineTestSuite) GetSequenceFromStore() uint64 { } func (suite *SoloMachineTestSuite) GetInvalidProof() []byte { - invalidProof, err := suite.chainA.Codec.Marshal(&types.TimestampedSignatureData{Timestamp: suite.solomachine.Time}) + invalidProof, err := suite.chainA.Codec.Marshal(&solomachine.TimestampedSignatureData{Timestamp: suite.solomachine.Time}) suite.Require().NoError(err) return invalidProof @@ -68,17 +67,17 @@ func TestUnpackInterfaces_Header(t *testing.T) { registry := testdata.NewTestInterfaceRegistry() cryptocodec.RegisterInterfaces(registry) - pk := secp256k1.GenPrivKey().PubKey().(cryptotypes.PubKey) + pk := secp256k1.GenPrivKey().PubKey() any, err := codectypes.NewAnyWithValue(pk) require.NoError(t, err) - header := types.Header{ + header := solomachine.Header{ NewPublicKey: any, } bz, err := header.Marshal() require.NoError(t, err) - var header2 types.Header + var header2 solomachine.Header err = header2.Unmarshal(bz) require.NoError(t, err) @@ -92,17 +91,17 @@ func TestUnpackInterfaces_HeaderData(t *testing.T) { registry := testdata.NewTestInterfaceRegistry() cryptocodec.RegisterInterfaces(registry) - pk := secp256k1.GenPrivKey().PubKey().(cryptotypes.PubKey) + pk := secp256k1.GenPrivKey().PubKey() any, err := codectypes.NewAnyWithValue(pk) require.NoError(t, err) - hd := types.HeaderData{ + hd := solomachine.HeaderData{ NewPubKey: any, } bz, err := hd.Marshal() require.NoError(t, err) - var hd2 types.HeaderData + var hd2 solomachine.HeaderData err = hd2.Unmarshal(bz) require.NoError(t, err) diff --git a/modules/light-clients/06-solomachine/spec/01_concepts.md b/modules/light-clients/06-solomachine/spec/01_concepts.md index 75d31bf5340..602c9947f71 100644 --- a/modules/light-clients/06-solomachine/spec/01_concepts.md +++ b/modules/light-clients/06-solomachine/spec/01_concepts.md @@ -56,7 +56,7 @@ data := &ClientStateData{ dataBz, err := cdc.Marshal(data) ``` -The helper functions `...DataBytes()` in [proof.go](../types/proof.go) handle this +The helper functions `...DataBytes()` in [proof.go](../proof.go) handle this functionality. 2. Construct the `SignBytes` and marshal it. @@ -75,7 +75,7 @@ signBytes := &SignBytes{ signBz, err := cdc.Marshal(signBytes) ``` -The helper functions `...SignBytes()` in [proof.go](../types/proof.go) handle this functionality. +The helper functions `...SignBytes()` in [proof.go](../proof.go) handle this functionality. The `DataType` field is used to disambiguate what type of data was signed to prevent potential proto encoding overlap. @@ -100,7 +100,7 @@ as the proof parameter to the verification functions. For example: ```go -timestampedSignatureData := &types.TimestampedSignatureData{ +timestampedSignatureData := &solomachine.TimestampedSignatureData{ SignatureData: sigData, Timestamp: solomachine.Time, } diff --git a/modules/light-clients/06-solomachine/types/codec_test.go b/modules/light-clients/06-solomachine/types/codec_test.go deleted file mode 100644 index 1a0e3e0086f..00000000000 --- a/modules/light-clients/06-solomachine/types/codec_test.go +++ /dev/null @@ -1,190 +0,0 @@ -package types_test - -import ( - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" -) - -func (suite SoloMachineTestSuite) TestUnmarshalDataByType() { - var ( - data []byte - err error - ) - - // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - cdc := suite.chainA.App.AppCodec() - cases := []struct { - name string - dataType types.DataType - malleate func() - expPass bool - }{ - { - "empty data", types.CLIENT, func() { - data = []byte{} - }, false, - }, - { - "unspecified", types.UNSPECIFIED, func() { - path := solomachine.GetClientStatePath(counterpartyClientIdentifier) - data, err = types.ClientStateDataBytes(cdc, path, solomachine.ClientState()) - suite.Require().NoError(err) - }, false, - }, - { - "client", types.CLIENT, func() { - path := solomachine.GetClientStatePath(counterpartyClientIdentifier) - data, err = types.ClientStateDataBytes(cdc, path, solomachine.ClientState()) - suite.Require().NoError(err) - }, true, - }, - { - "bad client (provides consensus state data)", types.CLIENT, func() { - path := solomachine.GetConsensusStatePath(counterpartyClientIdentifier, clienttypes.NewHeight(0, 5)) - data, err = types.ConsensusStateDataBytes(cdc, path, solomachine.ConsensusState()) - suite.Require().NoError(err) - }, false, - }, - { - "consensus", types.CONSENSUS, func() { - path := solomachine.GetConsensusStatePath(counterpartyClientIdentifier, clienttypes.NewHeight(0, 5)) - data, err = types.ConsensusStateDataBytes(cdc, path, solomachine.ConsensusState()) - suite.Require().NoError(err) - - }, true, - }, - { - "bad consensus (provides client state data)", types.CONSENSUS, func() { - path := solomachine.GetClientStatePath(counterpartyClientIdentifier) - data, err = types.ClientStateDataBytes(cdc, path, solomachine.ClientState()) - suite.Require().NoError(err) - }, false, - }, - { - "connection", types.CONNECTION, func() { - counterparty := connectiontypes.NewCounterparty("clientB", testConnectionID, *prefix) - conn := connectiontypes.NewConnectionEnd(connectiontypes.OPEN, "clientA", counterparty, connectiontypes.ExportedVersionsToProto(connectiontypes.GetCompatibleVersions()), 0) - path := solomachine.GetConnectionStatePath("connectionID") - - data, err = types.ConnectionStateDataBytes(cdc, path, conn) - suite.Require().NoError(err) - - }, true, - }, - { - "bad connection (uses channel data)", types.CONNECTION, func() { - counterparty := channeltypes.NewCounterparty(testPortID, testChannelID) - ch := channeltypes.NewChannel(channeltypes.OPEN, channeltypes.ORDERED, counterparty, []string{testConnectionID}, "1.0.0") - path := solomachine.GetChannelStatePath("portID", "channelID") - - data, err = types.ChannelStateDataBytes(cdc, path, ch) - suite.Require().NoError(err) - }, false, - }, - { - "channel", types.CHANNEL, func() { - counterparty := channeltypes.NewCounterparty(testPortID, testChannelID) - ch := channeltypes.NewChannel(channeltypes.OPEN, channeltypes.ORDERED, counterparty, []string{testConnectionID}, "1.0.0") - path := solomachine.GetChannelStatePath("portID", "channelID") - - data, err = types.ChannelStateDataBytes(cdc, path, ch) - suite.Require().NoError(err) - }, true, - }, - { - "bad channel (uses connection data)", types.CHANNEL, func() { - counterparty := connectiontypes.NewCounterparty("clientB", testConnectionID, *prefix) - conn := connectiontypes.NewConnectionEnd(connectiontypes.OPEN, "clientA", counterparty, connectiontypes.ExportedVersionsToProto(connectiontypes.GetCompatibleVersions()), 0) - path := solomachine.GetConnectionStatePath("connectionID") - - data, err = types.ConnectionStateDataBytes(cdc, path, conn) - suite.Require().NoError(err) - - }, false, - }, - { - "packet commitment", types.PACKETCOMMITMENT, func() { - commitment := []byte("packet commitment") - path := solomachine.GetPacketCommitmentPath("portID", "channelID") - - data, err = types.PacketCommitmentDataBytes(cdc, path, commitment) - suite.Require().NoError(err) - }, true, - }, - { - "bad packet commitment (uses next seq recv)", types.PACKETCOMMITMENT, func() { - path := solomachine.GetNextSequenceRecvPath("portID", "channelID") - - data, err = types.NextSequenceRecvDataBytes(cdc, path, 10) - suite.Require().NoError(err) - }, false, - }, - { - "packet acknowledgement", types.PACKETACKNOWLEDGEMENT, func() { - commitment := []byte("packet acknowledgement") - path := solomachine.GetPacketAcknowledgementPath("portID", "channelID") - - data, err = types.PacketAcknowledgementDataBytes(cdc, path, commitment) - suite.Require().NoError(err) - }, true, - }, - { - "bad packet acknowledgement (uses next sequence recv)", types.PACKETACKNOWLEDGEMENT, func() { - path := solomachine.GetNextSequenceRecvPath("portID", "channelID") - - data, err = types.NextSequenceRecvDataBytes(cdc, path, 10) - suite.Require().NoError(err) - }, false, - }, - { - "packet acknowledgement absence", types.PACKETRECEIPTABSENCE, func() { - path := solomachine.GetPacketReceiptPath("portID", "channelID") - - data, err = types.PacketReceiptAbsenceDataBytes(cdc, path) - suite.Require().NoError(err) - }, true, - }, - { - "next sequence recv", types.NEXTSEQUENCERECV, func() { - path := solomachine.GetNextSequenceRecvPath("portID", "channelID") - - data, err = types.NextSequenceRecvDataBytes(cdc, path, 10) - suite.Require().NoError(err) - }, true, - }, - { - "bad next sequence recv (uses packet commitment)", types.NEXTSEQUENCERECV, func() { - commitment := []byte("packet commitment") - path := solomachine.GetPacketCommitmentPath("portID", "channelID") - - data, err = types.PacketCommitmentDataBytes(cdc, path, commitment) - suite.Require().NoError(err) - }, false, - }, - } - - for _, tc := range cases { - tc := tc - - suite.Run(tc.name, func() { - tc.malleate() - - data, err := types.UnmarshalDataByType(cdc, tc.dataType, data) - - if tc.expPass { - suite.Require().NoError(err) - suite.Require().NotNil(data) - } else { - suite.Require().Error(err) - suite.Require().Nil(data) - } - }) - } - } - -} diff --git a/modules/light-clients/06-solomachine/types/update.go b/modules/light-clients/06-solomachine/update.go similarity index 99% rename from modules/light-clients/06-solomachine/types/update.go rename to modules/light-clients/06-solomachine/update.go index e8c464772f7..1d903a6105a 100644 --- a/modules/light-clients/06-solomachine/types/update.go +++ b/modules/light-clients/06-solomachine/update.go @@ -1,4 +1,4 @@ -package types +package solomachine import ( "fmt" diff --git a/modules/light-clients/06-solomachine/types/update_test.go b/modules/light-clients/06-solomachine/update_test.go similarity index 66% rename from modules/light-clients/06-solomachine/types/update_test.go rename to modules/light-clients/06-solomachine/update_test.go index 223e468e87f..24dad99ab0b 100644 --- a/modules/light-clients/06-solomachine/types/update_test.go +++ b/modules/light-clients/06-solomachine/update_test.go @@ -1,4 +1,4 @@ -package types_test +package solomachine_test import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" @@ -7,19 +7,19 @@ import ( clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/exported" - "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + solomachine "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v3/testing" ) func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { var ( clientMsg exported.ClientMessage - clientState *types.ClientState + clientState *solomachine.ClientState ) // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { testCases := []struct { name string @@ -29,14 +29,14 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { { "successful header", func() { - clientMsg = solomachine.CreateHeader() + clientMsg = sm.CreateHeader() }, true, }, { "successful misbehaviour", func() { - clientMsg = solomachine.CreateMisbehaviour() + clientMsg = sm.CreateMisbehaviour() }, true, }, @@ -51,7 +51,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { "wrong sequence in header", func() { // store in temp before assigning to interface type - h := solomachine.CreateHeader() + h := sm.CreateHeader() h.Sequence++ clientMsg = h }, @@ -60,7 +60,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { { "invalid header Signature", func() { - h := solomachine.CreateHeader() + h := sm.CreateHeader() h.Signature = suite.GetInvalidProof() clientMsg = h }, false, @@ -68,7 +68,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { { "invalid timestamp in header", func() { - h := solomachine.CreateHeader() + h := sm.CreateHeader() h.Timestamp-- clientMsg = h }, false, @@ -77,8 +77,8 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { "signature uses wrong sequence", func() { - solomachine.Sequence++ - clientMsg = solomachine.CreateHeader() + sm.Sequence++ + clientMsg = sm.CreateHeader() }, false, }, @@ -86,13 +86,13 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { "signature uses new pubkey to sign", func() { // store in temp before assinging to interface type - cs := solomachine.ClientState() - h := solomachine.CreateHeader() + cs := sm.ClientState() + h := sm.CreateHeader() - publicKey, err := codectypes.NewAnyWithValue(solomachine.PublicKey) + publicKey, err := codectypes.NewAnyWithValue(sm.PublicKey) suite.NoError(err) - data := &types.HeaderData{ + data := &solomachine.HeaderData{ NewPubKey: publicKey, NewDiversifier: h.NewDiversifier, } @@ -101,18 +101,18 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { suite.Require().NoError(err) // generate invalid signature - signBytes := &types.SignBytes{ + signBytes := &solomachine.SignBytes{ Sequence: cs.Sequence, - Timestamp: solomachine.Time, - Diversifier: solomachine.Diversifier, - DataType: types.CLIENT, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + DataType: solomachine.CLIENT, Data: dataBz, } signBz, err := suite.chainA.Codec.Marshal(signBytes) suite.Require().NoError(err) - sig := solomachine.GenerateSignature(signBz) + sig := sm.GenerateSignature(signBz) suite.Require().NoError(err) h.Signature = sig @@ -126,13 +126,13 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { "signature signs over old pubkey", func() { // store in temp before assinging to interface type - cs := solomachine.ClientState() - oldPubKey := solomachine.PublicKey - h := solomachine.CreateHeader() + cs := sm.ClientState() + oldPubKey := sm.PublicKey + h := sm.CreateHeader() // generate invalid signature data := append(sdk.Uint64ToBigEndian(cs.Sequence), oldPubKey.Bytes()...) - sig := solomachine.GenerateSignature(data) + sig := sm.GenerateSignature(data) h.Signature = sig clientState = cs @@ -144,7 +144,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { "consensus state public key is nil - header", func() { clientState.ConsensusState.PublicKey = nil - clientMsg = solomachine.CreateHeader() + clientMsg = sm.CreateHeader() }, false, }, @@ -154,7 +154,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { tc := tc suite.Run(tc.name, func() { - clientState = solomachine.ClientState() + clientState = sm.ClientState() // setup test tc.setup() @@ -174,11 +174,11 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { var ( clientMsg exported.ClientMessage - clientState *types.ClientState + clientState *solomachine.ClientState ) // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { testCases := []struct { name string @@ -188,16 +188,16 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { { "successful misbehaviour", func() { - clientMsg = solomachine.CreateMisbehaviour() + clientMsg = sm.CreateMisbehaviour() }, true, }, { "old misbehaviour is successful (timestamp is less than current consensus state)", func() { - clientState = solomachine.ClientState() - solomachine.Time = solomachine.Time - 5 - clientMsg = solomachine.CreateMisbehaviour() + clientState = sm.ClientState() + sm.Time = sm.Time - 5 + clientMsg = sm.CreateMisbehaviour() }, true, }, { @@ -211,14 +211,14 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { "consensus state pubkey is nil", func() { clientState.ConsensusState.PublicKey = nil - clientMsg = solomachine.CreateMisbehaviour() + clientMsg = sm.CreateMisbehaviour() }, false, }, { "invalid SignatureOne SignatureData", func() { - m := solomachine.CreateMisbehaviour() + m := sm.CreateMisbehaviour() m.SignatureOne.Signature = suite.GetInvalidProof() clientMsg = m @@ -227,7 +227,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { { "invalid SignatureTwo SignatureData", func() { - m := solomachine.CreateMisbehaviour() + m := sm.CreateMisbehaviour() m.SignatureTwo.Signature = suite.GetInvalidProof() clientMsg = m @@ -236,7 +236,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { { "invalid SignatureOne timestamp", func() { - m := solomachine.CreateMisbehaviour() + m := sm.CreateMisbehaviour() m.SignatureOne.Timestamp = 1000000000000 clientMsg = m @@ -245,7 +245,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { { "invalid SignatureTwo timestamp", func() { - m := solomachine.CreateMisbehaviour() + m := sm.CreateMisbehaviour() m.SignatureTwo.Timestamp = 1000000000000 clientMsg = m @@ -255,21 +255,21 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { "invalid first signature data", func() { // store in temp before assigning to interface type - m := solomachine.CreateMisbehaviour() + m := sm.CreateMisbehaviour() msg := []byte("DATA ONE") - signBytes := &types.SignBytes{ - Sequence: solomachine.Sequence + 1, - Timestamp: solomachine.Time, - Diversifier: solomachine.Diversifier, - DataType: types.CLIENT, + signBytes := &solomachine.SignBytes{ + Sequence: sm.Sequence + 1, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + DataType: solomachine.CLIENT, Data: msg, } data, err := suite.chainA.Codec.Marshal(signBytes) suite.Require().NoError(err) - sig := solomachine.GenerateSignature(data) + sig := sm.GenerateSignature(data) m.SignatureOne.Signature = sig m.SignatureOne.Data = msg @@ -281,21 +281,21 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { "invalid second signature data", func() { // store in temp before assigning to interface type - m := solomachine.CreateMisbehaviour() + m := sm.CreateMisbehaviour() msg := []byte("DATA TWO") - signBytes := &types.SignBytes{ - Sequence: solomachine.Sequence + 1, - Timestamp: solomachine.Time, - Diversifier: solomachine.Diversifier, - DataType: types.CLIENT, + signBytes := &solomachine.SignBytes{ + Sequence: sm.Sequence + 1, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + DataType: solomachine.CLIENT, Data: msg, } data, err := suite.chainA.Codec.Marshal(signBytes) suite.Require().NoError(err) - sig := solomachine.GenerateSignature(data) + sig := sm.GenerateSignature(data) m.SignatureTwo.Signature = sig m.SignatureTwo.Data = msg @@ -306,11 +306,11 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { { "wrong pubkey generates first signature", func() { - badMisbehaviour := solomachine.CreateMisbehaviour() + badMisbehaviour := sm.CreateMisbehaviour() // update public key to a new one - solomachine.CreateHeader() - m := solomachine.CreateMisbehaviour() + sm.CreateHeader() + m := sm.CreateMisbehaviour() // set SignatureOne to use the wrong signature m.SignatureOne = badMisbehaviour.SignatureOne @@ -320,11 +320,11 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { { "wrong pubkey generates second signature", func() { - badMisbehaviour := solomachine.CreateMisbehaviour() + badMisbehaviour := sm.CreateMisbehaviour() // update public key to a new one - solomachine.CreateHeader() - m := solomachine.CreateMisbehaviour() + sm.CreateHeader() + m := sm.CreateMisbehaviour() // set SignatureTwo to use the wrong signature m.SignatureTwo = badMisbehaviour.SignatureTwo @@ -336,23 +336,23 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { func() { // store in temp before assigning to interface type - m := solomachine.CreateMisbehaviour() + m := sm.CreateMisbehaviour() // Signature One msg := []byte("DATA ONE") // sequence used is plus 1 - signBytes := &types.SignBytes{ - Sequence: solomachine.Sequence + 1, - Timestamp: solomachine.Time, - Diversifier: solomachine.Diversifier, - DataType: types.CLIENT, + signBytes := &solomachine.SignBytes{ + Sequence: sm.Sequence + 1, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + DataType: solomachine.CLIENT, Data: msg, } data, err := suite.chainA.Codec.Marshal(signBytes) suite.Require().NoError(err) - sig := solomachine.GenerateSignature(data) + sig := sm.GenerateSignature(data) m.SignatureOne.Signature = sig m.SignatureOne.Data = msg @@ -361,17 +361,17 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { msg = []byte("DATA TWO") // sequence used is minus 1 - signBytes = &types.SignBytes{ - Sequence: solomachine.Sequence - 1, - Timestamp: solomachine.Time, - Diversifier: solomachine.Diversifier, - DataType: types.CLIENT, + signBytes = &solomachine.SignBytes{ + Sequence: sm.Sequence - 1, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + DataType: solomachine.CLIENT, Data: msg, } data, err = suite.chainA.Codec.Marshal(signBytes) suite.Require().NoError(err) - sig = solomachine.GenerateSignature(data) + sig = sm.GenerateSignature(data) m.SignatureTwo.Signature = sig m.SignatureTwo.Data = msg @@ -386,7 +386,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { tc := tc suite.Run(tc.name, func() { - clientState = solomachine.ClientState() + clientState = sm.ClientState() // setup test tc.setup() @@ -410,7 +410,7 @@ func (suite *SoloMachineTestSuite) TestUpdateState() { ) // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { testCases := []struct { name string @@ -420,16 +420,16 @@ func (suite *SoloMachineTestSuite) TestUpdateState() { { "successful update", func() { - clientState = solomachine.ClientState() - clientMsg = solomachine.CreateHeader() + clientState = sm.ClientState() + clientMsg = sm.CreateHeader() }, true, }, { "invalid type misbehaviour", func() { - clientState = solomachine.ClientState() - clientMsg = solomachine.CreateMisbehaviour() + clientState = sm.ClientState() + clientMsg = sm.CreateMisbehaviour() }, false, }, @@ -451,13 +451,13 @@ func (suite *SoloMachineTestSuite) TestUpdateState() { suite.Require().Len(consensusHeights, 1) suite.Require().Equal(uint64(0), consensusHeights[0].GetRevisionNumber()) - suite.Require().Equal(newClientState.(*types.ClientState).Sequence, consensusHeights[0].GetRevisionHeight()) + suite.Require().Equal(newClientState.(*solomachine.ClientState).Sequence, consensusHeights[0].GetRevisionHeight()) - suite.Require().False(newClientState.(*types.ClientState).IsFrozen) - suite.Require().Equal(clientMsg.(*types.Header).Sequence+1, newClientState.(*types.ClientState).Sequence) - suite.Require().Equal(clientMsg.(*types.Header).NewPublicKey, newClientState.(*types.ClientState).ConsensusState.PublicKey) - suite.Require().Equal(clientMsg.(*types.Header).NewDiversifier, newClientState.(*types.ClientState).ConsensusState.Diversifier) - suite.Require().Equal(clientMsg.(*types.Header).Timestamp, newClientState.(*types.ClientState).ConsensusState.Timestamp) + suite.Require().False(newClientState.(*solomachine.ClientState).IsFrozen) + suite.Require().Equal(clientMsg.(*solomachine.Header).Sequence+1, newClientState.(*solomachine.ClientState).Sequence) + suite.Require().Equal(clientMsg.(*solomachine.Header).NewPublicKey, newClientState.(*solomachine.ClientState).ConsensusState.PublicKey) + suite.Require().Equal(clientMsg.(*solomachine.Header).NewDiversifier, newClientState.(*solomachine.ClientState).ConsensusState.Diversifier) + suite.Require().Equal(clientMsg.(*solomachine.Header).Timestamp, newClientState.(*solomachine.ClientState).ConsensusState.Timestamp) } else { suite.Require().Panics(func() { clientState.UpdateState(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, clientMsg) @@ -475,7 +475,7 @@ func (suite *SoloMachineTestSuite) TestCheckForMisbehaviour() { ) // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { testCases := []struct { name string malleate func() @@ -484,14 +484,14 @@ func (suite *SoloMachineTestSuite) TestCheckForMisbehaviour() { { "success", func() { - clientMsg = solomachine.CreateMisbehaviour() + clientMsg = sm.CreateMisbehaviour() }, true, }, { "normal header returns false", func() { - clientMsg = solomachine.CreateHeader() + clientMsg = sm.CreateHeader() }, false, }, @@ -501,7 +501,7 @@ func (suite *SoloMachineTestSuite) TestCheckForMisbehaviour() { tc := tc suite.Run(tc.name, func() { - clientState := solomachine.ClientState() + clientState := sm.ClientState() tc.malleate() @@ -520,7 +520,7 @@ func (suite *SoloMachineTestSuite) TestCheckForMisbehaviour() { func (suite *SoloMachineTestSuite) TestUpdateStateOnMisbehaviour() { // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { testCases := []struct { name string malleate func() @@ -537,7 +537,7 @@ func (suite *SoloMachineTestSuite) TestUpdateStateOnMisbehaviour() { tc := tc suite.Run(tc.name, func() { - clientState := solomachine.ClientState() + clientState := sm.ClientState() tc.malleate() @@ -549,7 +549,7 @@ func (suite *SoloMachineTestSuite) TestUpdateStateOnMisbehaviour() { newClientState := clienttypes.MustUnmarshalClientState(suite.chainA.Codec, clientStateBz) - suite.Require().True(newClientState.(*types.ClientState).IsFrozen) + suite.Require().True(newClientState.(*solomachine.ClientState).IsFrozen) } }) } diff --git a/modules/light-clients/07-tendermint/types/client_state.go b/modules/light-clients/07-tendermint/client_state.go similarity index 99% rename from modules/light-clients/07-tendermint/types/client_state.go rename to modules/light-clients/07-tendermint/client_state.go index 1593f76c959..27a78768571 100644 --- a/modules/light-clients/07-tendermint/types/client_state.go +++ b/modules/light-clients/07-tendermint/client_state.go @@ -1,4 +1,4 @@ -package types +package tendermint import ( "strings" diff --git a/modules/light-clients/07-tendermint/types/client_state_test.go b/modules/light-clients/07-tendermint/client_state_test.go similarity index 86% rename from modules/light-clients/07-tendermint/types/client_state_test.go rename to modules/light-clients/07-tendermint/client_state_test.go index 56bbf0909b3..f69aecd969d 100644 --- a/modules/light-clients/07-tendermint/types/client_state_test.go +++ b/modules/light-clients/07-tendermint/client_state_test.go @@ -1,4 +1,4 @@ -package types_test +package tendermint_test import ( "time" @@ -12,7 +12,7 @@ import ( commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/exported" - "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + tendermint "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v3/testing" ibcmock "github.com/cosmos/ibc-go/v3/testing/mock" ) @@ -30,7 +30,7 @@ var ( func (suite *TendermintTestSuite) TestStatus() { var ( path *ibctesting.Path - clientState *types.ClientState + clientState *tendermint.ClientState ) testCases := []struct { @@ -57,7 +57,7 @@ func (suite *TendermintTestSuite) TestStatus() { suite.coordinator.SetupClients(path) clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - clientState = path.EndpointA.GetClientState().(*types.ClientState) + clientState = path.EndpointA.GetClientState().(*tendermint.ClientState) tc.malleate() @@ -70,22 +70,22 @@ func (suite *TendermintTestSuite) TestStatus() { func (suite *TendermintTestSuite) TestValidate() { testCases := []struct { name string - clientState *types.ClientState + clientState *tendermint.ClientState expPass bool }{ { name: "valid client", - clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), expPass: true, }, { name: "valid client with nil upgrade path", - clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), nil, false, false), + clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), nil, false, false), expPass: true, }, { name: "invalid chainID", - clientState: types.NewClientState(" ", types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + clientState: tendermint.NewClientState(" ", tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), expPass: false, }, { @@ -93,7 +93,7 @@ func (suite *TendermintTestSuite) TestValidate() { // Do not only fix the test, fix the code! // https://github.com/cosmos/ibc-go/issues/177 name: "valid chainID - chainID validation failed for chainID of length 50! ", - clientState: types.NewClientState(fiftyCharChainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + clientState: tendermint.NewClientState(fiftyCharChainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), expPass: true, }, { @@ -101,52 +101,52 @@ func (suite *TendermintTestSuite) TestValidate() { // Do not only fix the test, fix the code! // https://github.com/cosmos/ibc-go/issues/177 name: "invalid chainID - chainID validation did not fail for chainID of length 51! ", - clientState: types.NewClientState(fiftyOneCharChainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + clientState: tendermint.NewClientState(fiftyOneCharChainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), expPass: false, }, { name: "invalid trust level", - clientState: types.NewClientState(chainID, types.Fraction{Numerator: 0, Denominator: 1}, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + clientState: tendermint.NewClientState(chainID, tendermint.Fraction{Numerator: 0, Denominator: 1}, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), expPass: false, }, { name: "invalid trusting period", - clientState: types.NewClientState(chainID, types.DefaultTrustLevel, 0, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, 0, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), expPass: false, }, { name: "invalid unbonding period", - clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, 0, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, trustingPeriod, 0, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), expPass: false, }, { name: "invalid max clock drift", - clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, 0, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod, 0, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), expPass: false, }, { name: "invalid revision number", - clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(1, 1), commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(1, 1), commitmenttypes.GetSDKSpecs(), upgradePath, false, false), expPass: false, }, { name: "invalid revision height", - clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.ZeroHeight(), commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.ZeroHeight(), commitmenttypes.GetSDKSpecs(), upgradePath, false, false), expPass: false, }, { name: "trusting period not less than unbonding period", - clientState: types.NewClientState(chainID, types.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), expPass: false, }, { name: "proof specs is nil", - clientState: types.NewClientState(chainID, types.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, nil, upgradePath, false, false), + clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, nil, upgradePath, false, false), expPass: false, }, { name: "proof specs contains nil", - clientState: types.NewClientState(chainID, types.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, []*ics23.ProofSpec{ics23.TendermintSpec, nil}, upgradePath, false, false), + clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, []*ics23.ProofSpec{ics23.TendermintSpec, nil}, upgradePath, false, false), expPass: false, }, } @@ -170,7 +170,7 @@ func (suite *TendermintTestSuite) TestInitialize() { }{ { name: "valid consensus", - consensusState: &types.ConsensusState{}, + consensusState: &tendermint.ConsensusState{}, expPass: true, }, { @@ -232,7 +232,7 @@ func (suite *TendermintTestSuite) TestVerifyMembership() { proof, proofHeight = suite.chainB.QueryProof(key) - consensusState := testingpath.EndpointB.GetConsensusState(testingpath.EndpointB.GetClientState().GetLatestHeight()).(*types.ConsensusState) + consensusState := testingpath.EndpointB.GetConsensusState(testingpath.EndpointB.GetClientState().GetLatestHeight()).(*tendermint.ConsensusState) value, err = suite.chainB.Codec.MarshalInterface(consensusState) suite.Require().NoError(err) }, @@ -442,13 +442,13 @@ func (suite *TendermintTestSuite) TestVerifyMembership() { proof, proofHeight = suite.chainB.QueryProof(key) - clientState := testingpath.EndpointB.GetClientState().(*types.ClientState) + clientState := testingpath.EndpointB.GetClientState().(*tendermint.ClientState) value, err = suite.chainB.Codec.MarshalInterface(clientState) suite.Require().NoError(err) tc.malleate() // make changes as necessary - clientState = testingpath.EndpointA.GetClientState().(*types.ClientState) + clientState = testingpath.EndpointA.GetClientState().(*tendermint.ClientState) ctx := suite.chainA.GetContext() store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, testingpath.EndpointA.ClientID) @@ -680,7 +680,7 @@ func (suite *TendermintTestSuite) TestVerifyNonMembership() { tc.malleate() // make changes as necessary - clientState := testingpath.EndpointA.GetClientState().(*types.ClientState) + clientState := testingpath.EndpointA.GetClientState().(*tendermint.ClientState) ctx := suite.chainA.GetContext() store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, testingpath.EndpointA.ClientID) diff --git a/modules/light-clients/07-tendermint/types/codec.go b/modules/light-clients/07-tendermint/codec.go similarity index 97% rename from modules/light-clients/07-tendermint/types/codec.go rename to modules/light-clients/07-tendermint/codec.go index 0652321ba16..931f66ca1b4 100644 --- a/modules/light-clients/07-tendermint/types/codec.go +++ b/modules/light-clients/07-tendermint/codec.go @@ -1,4 +1,4 @@ -package types +package tendermint import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" diff --git a/modules/light-clients/07-tendermint/types/consensus_state.go b/modules/light-clients/07-tendermint/consensus_state.go similarity index 99% rename from modules/light-clients/07-tendermint/types/consensus_state.go rename to modules/light-clients/07-tendermint/consensus_state.go index aa475daa733..10a551f36b1 100644 --- a/modules/light-clients/07-tendermint/types/consensus_state.go +++ b/modules/light-clients/07-tendermint/consensus_state.go @@ -1,4 +1,4 @@ -package types +package tendermint import ( "time" diff --git a/modules/light-clients/07-tendermint/types/consensus_state_test.go b/modules/light-clients/07-tendermint/consensus_state_test.go similarity index 81% rename from modules/light-clients/07-tendermint/types/consensus_state_test.go rename to modules/light-clients/07-tendermint/consensus_state_test.go index f45a4a5d24d..b2b73ca4b4a 100644 --- a/modules/light-clients/07-tendermint/types/consensus_state_test.go +++ b/modules/light-clients/07-tendermint/consensus_state_test.go @@ -1,49 +1,49 @@ -package types_test +package tendermint_test import ( "time" commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v3/modules/core/exported" - "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + tendermint "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ) func (suite *TendermintTestSuite) TestConsensusStateValidateBasic() { testCases := []struct { msg string - consensusState *types.ConsensusState + consensusState *tendermint.ConsensusState expectPass bool }{ {"success", - &types.ConsensusState{ + &tendermint.ConsensusState{ Timestamp: suite.now, Root: commitmenttypes.NewMerkleRoot([]byte("app_hash")), NextValidatorsHash: suite.valsHash, }, true}, {"success with sentinel", - &types.ConsensusState{ + &tendermint.ConsensusState{ Timestamp: suite.now, - Root: commitmenttypes.NewMerkleRoot([]byte(types.SentinelRoot)), + Root: commitmenttypes.NewMerkleRoot([]byte(tendermint.SentinelRoot)), NextValidatorsHash: suite.valsHash, }, true}, {"root is nil", - &types.ConsensusState{ + &tendermint.ConsensusState{ Timestamp: suite.now, Root: commitmenttypes.MerkleRoot{}, NextValidatorsHash: suite.valsHash, }, false}, {"root is empty", - &types.ConsensusState{ + &tendermint.ConsensusState{ Timestamp: suite.now, Root: commitmenttypes.MerkleRoot{}, NextValidatorsHash: suite.valsHash, }, false}, {"nextvalshash is invalid", - &types.ConsensusState{ + &tendermint.ConsensusState{ Timestamp: suite.now, Root: commitmenttypes.NewMerkleRoot([]byte("app_hash")), NextValidatorsHash: []byte("hi"), @@ -51,7 +51,7 @@ func (suite *TendermintTestSuite) TestConsensusStateValidateBasic() { false}, {"timestamp is zero", - &types.ConsensusState{ + &tendermint.ConsensusState{ Timestamp: time.Time{}, Root: commitmenttypes.NewMerkleRoot([]byte("app_hash")), NextValidatorsHash: suite.valsHash, diff --git a/modules/light-clients/07-tendermint/types/errors.go b/modules/light-clients/07-tendermint/errors.go similarity index 98% rename from modules/light-clients/07-tendermint/types/errors.go rename to modules/light-clients/07-tendermint/errors.go index 7b087e41af5..2839beb5291 100644 --- a/modules/light-clients/07-tendermint/types/errors.go +++ b/modules/light-clients/07-tendermint/errors.go @@ -1,4 +1,4 @@ -package types +package tendermint import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" diff --git a/modules/light-clients/07-tendermint/types/fraction.go b/modules/light-clients/07-tendermint/fraction.go similarity index 97% rename from modules/light-clients/07-tendermint/types/fraction.go rename to modules/light-clients/07-tendermint/fraction.go index e445f19ba6f..772c6d9c171 100644 --- a/modules/light-clients/07-tendermint/types/fraction.go +++ b/modules/light-clients/07-tendermint/fraction.go @@ -1,4 +1,4 @@ -package types +package tendermint import ( tmmath "github.com/tendermint/tendermint/libs/math" diff --git a/modules/light-clients/07-tendermint/types/genesis.go b/modules/light-clients/07-tendermint/genesis.go similarity index 97% rename from modules/light-clients/07-tendermint/types/genesis.go rename to modules/light-clients/07-tendermint/genesis.go index 82a996d3696..ea47828a075 100644 --- a/modules/light-clients/07-tendermint/types/genesis.go +++ b/modules/light-clients/07-tendermint/genesis.go @@ -1,4 +1,4 @@ -package types +package tendermint import ( sdk "github.com/cosmos/cosmos-sdk/types" diff --git a/modules/light-clients/07-tendermint/types/genesis_test.go b/modules/light-clients/07-tendermint/genesis_test.go similarity index 67% rename from modules/light-clients/07-tendermint/types/genesis_test.go rename to modules/light-clients/07-tendermint/genesis_test.go index 703cf5ce996..9feb66372cf 100644 --- a/modules/light-clients/07-tendermint/types/genesis_test.go +++ b/modules/light-clients/07-tendermint/genesis_test.go @@ -1,10 +1,10 @@ -package types_test +package tendermint_test import ( sdk "github.com/cosmos/cosmos-sdk/types" clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + tendermint "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v3/testing" ) @@ -19,26 +19,26 @@ func (suite *TendermintTestSuite) TestExportMetadata() { clientState := path.EndpointA.GetClientState() height := clientState.GetLatestHeight() - initIteration := types.GetIterationKey(clientStore, height) + initIteration := tendermint.GetIterationKey(clientStore, height) suite.Require().NotEqual(0, len(initIteration)) - initProcessedTime, found := types.GetProcessedTime(clientStore, height) + initProcessedTime, found := tendermint.GetProcessedTime(clientStore, height) suite.Require().True(found) - initProcessedHeight, found := types.GetProcessedHeight(clientStore, height) + initProcessedHeight, found := tendermint.GetProcessedHeight(clientStore, height) suite.Require().True(found) gm := clientState.ExportMetadata(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID)) suite.Require().NotNil(gm, "client with metadata returned nil exported metadata") suite.Require().Len(gm, 3, "exported metadata has unexpected length") - suite.Require().Equal(types.ProcessedHeightKey(height), gm[0].GetKey(), "metadata has unexpected key") + suite.Require().Equal(tendermint.ProcessedHeightKey(height), gm[0].GetKey(), "metadata has unexpected key") actualProcessedHeight, err := clienttypes.ParseHeight(string(gm[0].GetValue())) suite.Require().NoError(err) suite.Require().Equal(initProcessedHeight, actualProcessedHeight, "metadata has unexpected value") - suite.Require().Equal(types.ProcessedTimeKey(height), gm[1].GetKey(), "metadata has unexpected key") + suite.Require().Equal(tendermint.ProcessedTimeKey(height), gm[1].GetKey(), "metadata has unexpected key") suite.Require().Equal(initProcessedTime, sdk.BigEndianToUint64(gm[1].GetValue()), "metadata has unexpected value") - suite.Require().Equal(types.IterationKey(height), gm[2].GetKey(), "metadata has unexpected key") + suite.Require().Equal(tendermint.IterationKey(height), gm[2].GetKey(), "metadata has unexpected key") suite.Require().Equal(initIteration, gm[2].GetValue(), "metadata has unexpected value") // test updating client and exporting metadata @@ -48,11 +48,11 @@ func (suite *TendermintTestSuite) TestExportMetadata() { clientState = path.EndpointA.GetClientState() updateHeight := clientState.GetLatestHeight() - iteration := types.GetIterationKey(clientStore, updateHeight) + iteration := tendermint.GetIterationKey(clientStore, updateHeight) suite.Require().NotEqual(0, len(initIteration)) - processedTime, found := types.GetProcessedTime(clientStore, updateHeight) + processedTime, found := tendermint.GetProcessedTime(clientStore, updateHeight) suite.Require().True(found) - processedHeight, found := types.GetProcessedHeight(clientStore, updateHeight) + processedHeight, found := tendermint.GetProcessedHeight(clientStore, updateHeight) suite.Require().True(found) gm = clientState.ExportMetadata(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID)) @@ -63,27 +63,27 @@ func (suite *TendermintTestSuite) TestExportMetadata() { // initProcessedHeight, initProcessedTime, processedHeight, processedTime, initIteration, iteration // check init processed height and time - suite.Require().Equal(types.ProcessedHeightKey(height), gm[0].GetKey(), "metadata has unexpected key") + suite.Require().Equal(tendermint.ProcessedHeightKey(height), gm[0].GetKey(), "metadata has unexpected key") actualProcessedHeight, err = clienttypes.ParseHeight(string(gm[0].GetValue())) suite.Require().NoError(err) suite.Require().Equal(initProcessedHeight, actualProcessedHeight, "metadata has unexpected value") - suite.Require().Equal(types.ProcessedTimeKey(height), gm[1].GetKey(), "metadata has unexpected key") + suite.Require().Equal(tendermint.ProcessedTimeKey(height), gm[1].GetKey(), "metadata has unexpected key") suite.Require().Equal(initProcessedTime, sdk.BigEndianToUint64(gm[1].GetValue()), "metadata has unexpected value") // check processed height and time after update - suite.Require().Equal(types.ProcessedHeightKey(updateHeight), gm[2].GetKey(), "metadata has unexpected key") + suite.Require().Equal(tendermint.ProcessedHeightKey(updateHeight), gm[2].GetKey(), "metadata has unexpected key") actualProcessedHeight, err = clienttypes.ParseHeight(string(gm[2].GetValue())) suite.Require().NoError(err) suite.Require().Equal(processedHeight, actualProcessedHeight, "metadata has unexpected value") - suite.Require().Equal(types.ProcessedTimeKey(updateHeight), gm[3].GetKey(), "metadata has unexpected key") + suite.Require().Equal(tendermint.ProcessedTimeKey(updateHeight), gm[3].GetKey(), "metadata has unexpected key") suite.Require().Equal(processedTime, sdk.BigEndianToUint64(gm[3].GetValue()), "metadata has unexpected value") // check iteration keys - suite.Require().Equal(types.IterationKey(height), gm[4].GetKey(), "metadata has unexpected key") + suite.Require().Equal(tendermint.IterationKey(height), gm[4].GetKey(), "metadata has unexpected key") suite.Require().Equal(initIteration, gm[4].GetValue(), "metadata has unexpected value") - suite.Require().Equal(types.IterationKey(updateHeight), gm[5].GetKey(), "metadata has unexpected key") + suite.Require().Equal(tendermint.IterationKey(updateHeight), gm[5].GetKey(), "metadata has unexpected key") suite.Require().Equal(iteration, gm[5].GetValue(), "metadata has unexpected value") } diff --git a/modules/light-clients/07-tendermint/types/header.go b/modules/light-clients/07-tendermint/header.go similarity index 99% rename from modules/light-clients/07-tendermint/types/header.go rename to modules/light-clients/07-tendermint/header.go index b5c51ec2cbf..ee57b2c085f 100644 --- a/modules/light-clients/07-tendermint/types/header.go +++ b/modules/light-clients/07-tendermint/header.go @@ -1,4 +1,4 @@ -package types +package tendermint import ( "bytes" diff --git a/modules/light-clients/07-tendermint/types/header_test.go b/modules/light-clients/07-tendermint/header_test.go similarity index 94% rename from modules/light-clients/07-tendermint/types/header_test.go rename to modules/light-clients/07-tendermint/header_test.go index f57776c092f..b1df2128a1f 100644 --- a/modules/light-clients/07-tendermint/types/header_test.go +++ b/modules/light-clients/07-tendermint/header_test.go @@ -1,4 +1,4 @@ -package types_test +package tendermint_test import ( "time" @@ -7,7 +7,7 @@ import ( clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" "github.com/cosmos/ibc-go/v3/modules/core/exported" - "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + tendermint "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ) func (suite *TendermintTestSuite) TestGetHeight() { @@ -22,7 +22,7 @@ func (suite *TendermintTestSuite) TestGetTime() { func (suite *TendermintTestSuite) TestHeaderValidateBasic() { var ( - header *types.Header + header *tendermint.Header ) testCases := []struct { name string diff --git a/modules/light-clients/07-tendermint/types/misbehaviour.go b/modules/light-clients/07-tendermint/misbehaviour.go similarity index 99% rename from modules/light-clients/07-tendermint/types/misbehaviour.go rename to modules/light-clients/07-tendermint/misbehaviour.go index e2407e6830d..41eb6a11efd 100644 --- a/modules/light-clients/07-tendermint/types/misbehaviour.go +++ b/modules/light-clients/07-tendermint/misbehaviour.go @@ -1,4 +1,4 @@ -package types +package tendermint import ( "time" diff --git a/modules/light-clients/07-tendermint/types/misbehaviour_handle.go b/modules/light-clients/07-tendermint/misbehaviour_handle.go similarity index 99% rename from modules/light-clients/07-tendermint/types/misbehaviour_handle.go rename to modules/light-clients/07-tendermint/misbehaviour_handle.go index ea2ab19cc47..08ddfcffecb 100644 --- a/modules/light-clients/07-tendermint/types/misbehaviour_handle.go +++ b/modules/light-clients/07-tendermint/misbehaviour_handle.go @@ -1,4 +1,4 @@ -package types +package tendermint import ( "bytes" diff --git a/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go b/modules/light-clients/07-tendermint/misbehaviour_handle_test.go similarity index 95% rename from modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go rename to modules/light-clients/07-tendermint/misbehaviour_handle_test.go index dc8c07c1bcd..a8bfa024862 100644 --- a/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go +++ b/modules/light-clients/07-tendermint/misbehaviour_handle_test.go @@ -1,4 +1,4 @@ -package types_test +package tendermint_test import ( "fmt" @@ -9,8 +9,8 @@ import ( clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" "github.com/cosmos/ibc-go/v3/modules/core/exported" - smtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types" - "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + smtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" + tendermint "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v3/testing" ibctestingmock "github.com/cosmos/ibc-go/v3/testing/mock" ) @@ -50,7 +50,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -64,7 +64,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) suite.Require().True(found) - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+3, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -78,7 +78,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) suite.Require().True(found) - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+3, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Hour), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -92,7 +92,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) suite.Require().True(found) - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+1, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+1, trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -113,7 +113,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { trustedVals2, found := suite.chainB.GetValsAtHeight(int64(trustedHeight2.RevisionHeight) + 1) suite.Require().True(found) - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight1, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals1, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight2, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals2, suite.chainB.Signers), } @@ -134,7 +134,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -156,7 +156,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { futureRevision := fmt.Sprintf("%s-%d", strings.TrimSuffix(suite.chainB.ChainID, fmt.Sprintf("-%d", clienttypes.ParseChainID(suite.chainB.ChainID))), height.GetRevisionNumber()+1) - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(futureRevision, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(futureRevision, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -176,7 +176,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -200,7 +200,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { bothSigners := suite.chainB.Signers bothSigners[altValSet.Proposer.Address.String()] = altPrivVal - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), bothValSet, suite.chainB.NextVals, trustedVals, bothSigners), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, bothValSet, suite.chainB.NextVals, trustedVals, bothSigners), } @@ -219,7 +219,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) misbehaviourHeader := suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers) - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: misbehaviourHeader, Header2: misbehaviourHeader, } @@ -232,7 +232,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) suite.Require().True(found) - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+3, trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -250,7 +250,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader("evmos", int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader("evmos", int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -266,7 +266,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, altValSet, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, altValSet, suite.chainB.Signers), } @@ -279,7 +279,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) suite.Require().True(found) - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight.Increment().(clienttypes.Height), suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -304,7 +304,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { suite.chainA.ExpireClient(path.EndpointA.ClientConfig.(*ibctesting.TendermintConfig).TrustingPeriod) - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -322,7 +322,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), altValSet, suite.chainB.NextVals, trustedVals, altSigners), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -340,7 +340,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, altValSet, suite.chainB.NextVals, trustedVals, altSigners), } @@ -358,7 +358,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), altValSet, suite.chainB.NextVals, trustedVals, altSigners), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, altValSet, suite.chainB.NextVals, trustedVals, altSigners), } @@ -433,7 +433,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -447,7 +447,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) suite.Require().True(found) - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+3, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -461,7 +461,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) suite.Require().True(found) - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+3, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Hour), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -475,7 +475,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) suite.Require().True(found) - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+1, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+1, trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -496,7 +496,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { trustedVals2, found := suite.chainB.GetValsAtHeight(int64(trustedHeight2.RevisionHeight) + 1) suite.Require().True(found) - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight1, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals1, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight2, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals2, suite.chainB.Signers), } @@ -521,7 +521,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { bothSigners := suite.chainB.Signers bothSigners[altValSet.Proposer.Address.String()] = altPrivVal - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), bothValSet, suite.chainB.NextVals, trustedVals, bothSigners), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, bothValSet, suite.chainB.NextVals, trustedVals, bothSigners), } @@ -540,7 +540,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) misbehaviourHeader := suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers) - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: misbehaviourHeader, Header2: misbehaviourHeader, } @@ -553,7 +553,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) suite.Require().True(found) - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+3, trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -571,7 +571,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader("evmos", int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader("evmos", int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -587,7 +587,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, altValSet, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, altValSet, suite.chainB.Signers), } @@ -600,7 +600,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) suite.Require().True(found) - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight.Increment().(clienttypes.Height), suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -625,7 +625,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { suite.chainA.ExpireClient(path.EndpointA.ClientConfig.(*ibctesting.TendermintConfig).TrustingPeriod) - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -643,7 +643,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), altValSet, suite.chainB.NextVals, trustedVals, altSigners), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -661,7 +661,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, altValSet, suite.chainB.NextVals, trustedVals, altSigners), } @@ -679,7 +679,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) - misbehaviour = &types.Misbehaviour{ + misbehaviour = &tendermint.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), altValSet, suite.chainB.NextVals, trustedVals, altSigners), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, altValSet, suite.chainB.NextVals, trustedVals, altSigners), } diff --git a/modules/light-clients/07-tendermint/types/misbehaviour_test.go b/modules/light-clients/07-tendermint/misbehaviour_test.go similarity index 81% rename from modules/light-clients/07-tendermint/types/misbehaviour_test.go rename to modules/light-clients/07-tendermint/misbehaviour_test.go index fdd51df4e53..41aa89cbdec 100644 --- a/modules/light-clients/07-tendermint/types/misbehaviour_test.go +++ b/modules/light-clients/07-tendermint/misbehaviour_test.go @@ -1,4 +1,4 @@ -package types_test +package tendermint_test import ( "time" @@ -9,7 +9,7 @@ import ( clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" "github.com/cosmos/ibc-go/v3/modules/core/exported" - "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + tendermint "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v3/testing" ibctestingmock "github.com/cosmos/ibc-go/v3/testing/mock" ) @@ -17,7 +17,7 @@ import ( func (suite *TendermintTestSuite) TestMisbehaviour() { heightMinus1 := clienttypes.NewHeight(0, height.RevisionHeight-1) - misbehaviour := &types.Misbehaviour{ + misbehaviour := &tendermint.Misbehaviour{ Header1: suite.header, Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, suite.valSet, suite.valSet, suite.valSet, suite.signers), ClientId: clientID, @@ -47,130 +47,130 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { testCases := []struct { name string - misbehaviour *types.Misbehaviour - malleateMisbehaviour func(misbehaviour *types.Misbehaviour) error + misbehaviour *tendermint.Misbehaviour + malleateMisbehaviour func(misbehaviour *tendermint.Misbehaviour) error expPass bool }{ { "valid fork misbehaviour, two headers at same height have different time", - &types.Misbehaviour{ + &tendermint.Misbehaviour{ Header1: suite.header, Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now.Add(time.Minute), suite.valSet, suite.valSet, suite.valSet, suite.signers), ClientId: clientID, }, - func(misbehaviour *types.Misbehaviour) error { return nil }, + func(misbehaviour *tendermint.Misbehaviour) error { return nil }, true, }, { "valid time misbehaviour, both headers at different heights are at same time", - &types.Misbehaviour{ + &tendermint.Misbehaviour{ Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+5), heightMinus1, suite.now, suite.valSet, suite.valSet, suite.valSet, suite.signers), Header2: suite.header, ClientId: clientID, }, - func(misbehaviour *types.Misbehaviour) error { return nil }, + func(misbehaviour *tendermint.Misbehaviour) error { return nil }, true, }, { "misbehaviour Header1 is nil", - types.NewMisbehaviour(clientID, nil, suite.header), - func(m *types.Misbehaviour) error { return nil }, + tendermint.NewMisbehaviour(clientID, nil, suite.header), + func(m *tendermint.Misbehaviour) error { return nil }, false, }, { "misbehaviour Header2 is nil", - types.NewMisbehaviour(clientID, suite.header, nil), - func(m *types.Misbehaviour) error { return nil }, + tendermint.NewMisbehaviour(clientID, suite.header, nil), + func(m *tendermint.Misbehaviour) error { return nil }, false, }, { "valid misbehaviour with different trusted headers", - &types.Misbehaviour{ + &tendermint.Misbehaviour{ Header1: suite.header, Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), clienttypes.NewHeight(0, height.RevisionHeight-3), suite.now.Add(time.Minute), suite.valSet, suite.valSet, bothValSet, suite.signers), ClientId: clientID, }, - func(misbehaviour *types.Misbehaviour) error { return nil }, + func(misbehaviour *tendermint.Misbehaviour) error { return nil }, true, }, { "trusted height is 0 in Header1", - &types.Misbehaviour{ + &tendermint.Misbehaviour{ Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), clienttypes.ZeroHeight(), suite.now.Add(time.Minute), suite.valSet, suite.valSet, suite.valSet, suite.signers), Header2: suite.header, ClientId: clientID, }, - func(misbehaviour *types.Misbehaviour) error { return nil }, + func(misbehaviour *tendermint.Misbehaviour) error { return nil }, false, }, { "trusted height is 0 in Header2", - &types.Misbehaviour{ + &tendermint.Misbehaviour{ Header1: suite.header, Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), clienttypes.ZeroHeight(), suite.now.Add(time.Minute), suite.valSet, suite.valSet, suite.valSet, suite.signers), ClientId: clientID, }, - func(misbehaviour *types.Misbehaviour) error { return nil }, + func(misbehaviour *tendermint.Misbehaviour) error { return nil }, false, }, { "trusted valset is nil in Header1", - &types.Misbehaviour{ + &tendermint.Misbehaviour{ Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now.Add(time.Minute), suite.valSet, suite.valSet, nil, suite.signers), Header2: suite.header, ClientId: clientID, }, - func(misbehaviour *types.Misbehaviour) error { return nil }, + func(misbehaviour *tendermint.Misbehaviour) error { return nil }, false, }, { "trusted valset is nil in Header2", - &types.Misbehaviour{ + &tendermint.Misbehaviour{ Header1: suite.header, Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now.Add(time.Minute), suite.valSet, suite.valSet, nil, suite.signers), ClientId: clientID, }, - func(misbehaviour *types.Misbehaviour) error { return nil }, + func(misbehaviour *tendermint.Misbehaviour) error { return nil }, false, }, { "invalid client ID ", - &types.Misbehaviour{ + &tendermint.Misbehaviour{ Header1: suite.header, Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, suite.valSet, suite.valSet, suite.valSet, suite.signers), ClientId: "GAIA", }, - func(misbehaviour *types.Misbehaviour) error { return nil }, + func(misbehaviour *tendermint.Misbehaviour) error { return nil }, false, }, { "chainIDs do not match", - &types.Misbehaviour{ + &tendermint.Misbehaviour{ Header1: suite.header, Header2: suite.chainA.CreateTMClientHeader("ethermint", int64(height.RevisionHeight), heightMinus1, suite.now, suite.valSet, suite.valSet, suite.valSet, suite.signers), ClientId: clientID, }, - func(misbehaviour *types.Misbehaviour) error { return nil }, + func(misbehaviour *tendermint.Misbehaviour) error { return nil }, false, }, { "header2 height is greater", - &types.Misbehaviour{ + &tendermint.Misbehaviour{ Header1: suite.header, Header2: suite.chainA.CreateTMClientHeader(chainID, 6, clienttypes.NewHeight(0, height.RevisionHeight+1), suite.now, suite.valSet, suite.valSet, suite.valSet, suite.signers), ClientId: clientID, }, - func(misbehaviour *types.Misbehaviour) error { return nil }, + func(misbehaviour *tendermint.Misbehaviour) error { return nil }, false, }, { "header 1 doesn't have 2/3 majority", - &types.Misbehaviour{ + &tendermint.Misbehaviour{ Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, bothValSet, bothValSet, suite.valSet, bothSigners), Header2: suite.header, ClientId: clientID, }, - func(misbehaviour *types.Misbehaviour) error { + func(misbehaviour *tendermint.Misbehaviour) error { // voteSet contains only altVal which is less than 2/3 of total power (height/1height) wrongVoteSet := tmtypes.NewVoteSet(chainID, int64(misbehaviour.Header1.GetHeight().GetRevisionHeight()), 1, tmproto.PrecommitType, altValSet) blockID, err := tmtypes.BlockIDFromProto(&misbehaviour.Header1.Commit.BlockID) @@ -186,12 +186,12 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { }, { "header 2 doesn't have 2/3 majority", - &types.Misbehaviour{ + &tendermint.Misbehaviour{ Header1: suite.header, Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, bothValSet, bothValSet, suite.valSet, bothSigners), ClientId: clientID, }, - func(misbehaviour *types.Misbehaviour) error { + func(misbehaviour *tendermint.Misbehaviour) error { // voteSet contains only altVal which is less than 2/3 of total power (height/1height) wrongVoteSet := tmtypes.NewVoteSet(chainID, int64(misbehaviour.Header2.GetHeight().GetRevisionHeight()), 1, tmproto.PrecommitType, altValSet) blockID, err := tmtypes.BlockIDFromProto(&misbehaviour.Header2.Commit.BlockID) @@ -207,12 +207,12 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { }, { "validators sign off on wrong commit", - &types.Misbehaviour{ + &tendermint.Misbehaviour{ Header1: suite.header, Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, bothValSet, bothValSet, suite.valSet, bothSigners), ClientId: clientID, }, - func(misbehaviour *types.Misbehaviour) error { + func(misbehaviour *tendermint.Misbehaviour) error { tmBlockID := ibctesting.MakeBlockID(tmhash.Sum([]byte("other_hash")), 3, tmhash.Sum([]byte("other_partset"))) misbehaviour.Header2.Commit.BlockID = tmBlockID.ToProto() return nil diff --git a/modules/light-clients/07-tendermint/module.go b/modules/light-clients/07-tendermint/module.go index 429105b4d33..935bf34cafd 100644 --- a/modules/light-clients/07-tendermint/module.go +++ b/modules/light-clients/07-tendermint/module.go @@ -1,10 +1,6 @@ package tendermint -import ( - "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" -) - // Name returns the IBC client name func Name() string { - return types.SubModuleName + return SubModuleName } diff --git a/modules/light-clients/07-tendermint/types/proposal_handle.go b/modules/light-clients/07-tendermint/proposal_handle.go similarity index 97% rename from modules/light-clients/07-tendermint/types/proposal_handle.go rename to modules/light-clients/07-tendermint/proposal_handle.go index a938d181f65..62457e67876 100644 --- a/modules/light-clients/07-tendermint/types/proposal_handle.go +++ b/modules/light-clients/07-tendermint/proposal_handle.go @@ -1,4 +1,4 @@ -package types +package tendermint import ( "reflect" @@ -15,14 +15,14 @@ import ( // substitute. // // AllowUpdateAfterMisbehaviour and AllowUpdateAfterExpiry have been deprecated. -// Please see ADR 026 for more information. -// +// Please see ADR 026 for more information. +// // The following must always be true: // - The substitute client is the same type as the subject client // - The subject and substitute client states match in all parameters (expect frozen height, latest height, and chain-id) // // In case 1) before updating the client, the client will be unfrozen by resetting -// the FrozenHeight to the zero Height. +// the FrozenHeight to the zero Height. func (cs ClientState) CheckSubstituteAndUpdateState( ctx sdk.Context, cdc codec.BinaryCodec, subjectClientStore, substituteClientStore sdk.KVStore, substituteClient exported.ClientState, diff --git a/modules/light-clients/07-tendermint/types/proposal_handle_test.go b/modules/light-clients/07-tendermint/proposal_handle_test.go similarity index 86% rename from modules/light-clients/07-tendermint/types/proposal_handle_test.go rename to modules/light-clients/07-tendermint/proposal_handle_test.go index 8d7ca6c7330..a14e80bae7b 100644 --- a/modules/light-clients/07-tendermint/types/proposal_handle_test.go +++ b/modules/light-clients/07-tendermint/proposal_handle_test.go @@ -1,4 +1,4 @@ -package types_test +package tendermint_test import ( "time" @@ -6,7 +6,7 @@ import ( clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/exported" - "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + tendermint "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v3/testing" ) @@ -31,8 +31,8 @@ func (suite *TendermintTestSuite) TestCheckSubstituteUpdateStateBasic() { { "non-matching substitute", func() { suite.coordinator.SetupClients(substitutePath) - substituteClientState = suite.chainA.GetClientState(substitutePath.EndpointA.ClientID).(*types.ClientState) - tmClientState, ok := substituteClientState.(*types.ClientState) + substituteClientState = suite.chainA.GetClientState(substitutePath.EndpointA.ClientID).(*tendermint.ClientState) + tmClientState, ok := substituteClientState.(*tendermint.ClientState) suite.Require().True(ok) tmClientState.ChainId = tmClientState.ChainId + "different chain" @@ -50,7 +50,7 @@ func (suite *TendermintTestSuite) TestCheckSubstituteUpdateStateBasic() { substitutePath = ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupClients(subjectPath) - subjectClientState := suite.chainA.GetClientState(subjectPath.EndpointA.ClientID).(*types.ClientState) + subjectClientState := suite.chainA.GetClientState(subjectPath.EndpointA.ClientID).(*tendermint.ClientState) subjectClientState.AllowUpdateAfterMisbehaviour = true subjectClientState.AllowUpdateAfterExpiry = true @@ -137,7 +137,7 @@ func (suite *TendermintTestSuite) TestCheckSubstituteAndUpdateState() { // construct subject using test case parameters subjectPath := ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupClients(subjectPath) - subjectClientState := suite.chainA.GetClientState(subjectPath.EndpointA.ClientID).(*types.ClientState) + subjectClientState := suite.chainA.GetClientState(subjectPath.EndpointA.ClientID).(*tendermint.ClientState) subjectClientState.AllowUpdateAfterExpiry = tc.AllowUpdateAfterExpiry subjectClientState.AllowUpdateAfterMisbehaviour = tc.AllowUpdateAfterMisbehaviour @@ -159,7 +159,7 @@ func (suite *TendermintTestSuite) TestCheckSubstituteAndUpdateState() { substitutePath := ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupClients(substitutePath) - substituteClientState := suite.chainA.GetClientState(substitutePath.EndpointA.ClientID).(*types.ClientState) + substituteClientState := suite.chainA.GetClientState(substitutePath.EndpointA.ClientID).(*tendermint.ClientState) substituteClientState.AllowUpdateAfterExpiry = tc.AllowUpdateAfterExpiry substituteClientState.AllowUpdateAfterMisbehaviour = tc.AllowUpdateAfterMisbehaviour suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), substitutePath.EndpointA.ClientID, substituteClientState) @@ -173,7 +173,7 @@ func (suite *TendermintTestSuite) TestCheckSubstituteAndUpdateState() { } // get updated substitute - substituteClientState = suite.chainA.GetClientState(substitutePath.EndpointA.ClientID).(*types.ClientState) + substituteClientState = suite.chainA.GetClientState(substitutePath.EndpointA.ClientID).(*tendermint.ClientState) // test that subject gets updated chain-id newChainID := "new-chain-id" @@ -183,35 +183,35 @@ func (suite *TendermintTestSuite) TestCheckSubstituteAndUpdateState() { substituteClientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), substitutePath.EndpointA.ClientID) expectedConsState := substitutePath.EndpointA.GetConsensusState(substituteClientState.GetLatestHeight()) - expectedProcessedTime, found := types.GetProcessedTime(substituteClientStore, substituteClientState.GetLatestHeight()) + expectedProcessedTime, found := tendermint.GetProcessedTime(substituteClientStore, substituteClientState.GetLatestHeight()) suite.Require().True(found) - expectedProcessedHeight, found := types.GetProcessedTime(substituteClientStore, substituteClientState.GetLatestHeight()) + expectedProcessedHeight, found := tendermint.GetProcessedTime(substituteClientStore, substituteClientState.GetLatestHeight()) suite.Require().True(found) - expectedIterationKey := types.GetIterationKey(substituteClientStore, substituteClientState.GetLatestHeight()) + expectedIterationKey := tendermint.GetIterationKey(substituteClientStore, substituteClientState.GetLatestHeight()) updatedClient, err := subjectClientState.CheckSubstituteAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), subjectClientStore, substituteClientStore, substituteClientState) if tc.expPass { suite.Require().NoError(err) - suite.Require().Equal(clienttypes.ZeroHeight(), updatedClient.(*types.ClientState).FrozenHeight) + suite.Require().Equal(clienttypes.ZeroHeight(), updatedClient.(*tendermint.ClientState).FrozenHeight) subjectClientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), subjectPath.EndpointA.ClientID) // check that the correct consensus state was copied over suite.Require().Equal(substituteClientState.GetLatestHeight(), updatedClient.GetLatestHeight()) subjectConsState := subjectPath.EndpointA.GetConsensusState(updatedClient.GetLatestHeight()) - subjectProcessedTime, found := types.GetProcessedTime(subjectClientStore, updatedClient.GetLatestHeight()) + subjectProcessedTime, found := tendermint.GetProcessedTime(subjectClientStore, updatedClient.GetLatestHeight()) suite.Require().True(found) - subjectProcessedHeight, found := types.GetProcessedTime(substituteClientStore, updatedClient.GetLatestHeight()) + subjectProcessedHeight, found := tendermint.GetProcessedTime(substituteClientStore, updatedClient.GetLatestHeight()) suite.Require().True(found) - subjectIterationKey := types.GetIterationKey(substituteClientStore, updatedClient.GetLatestHeight()) + subjectIterationKey := tendermint.GetIterationKey(substituteClientStore, updatedClient.GetLatestHeight()) suite.Require().Equal(expectedConsState, subjectConsState) suite.Require().Equal(expectedProcessedTime, subjectProcessedTime) suite.Require().Equal(expectedProcessedHeight, subjectProcessedHeight) suite.Require().Equal(expectedIterationKey, subjectIterationKey) - suite.Require().Equal(newChainID, updatedClient.(*types.ClientState).ChainId) + suite.Require().Equal(newChainID, updatedClient.(*tendermint.ClientState).ChainId) // ensure updated client state is set in store bz := subjectClientStore.Get(host.ClientStateKey()) @@ -228,7 +228,7 @@ func (suite *TendermintTestSuite) TestCheckSubstituteAndUpdateState() { func (suite *TendermintTestSuite) TestIsMatchingClientState() { var ( subjectPath, substitutePath *ibctesting.Path - subjectClientState, substituteClientState *types.ClientState + subjectClientState, substituteClientState *tendermint.ClientState ) testCases := []struct { @@ -238,8 +238,8 @@ func (suite *TendermintTestSuite) TestIsMatchingClientState() { }{ { "matching clients", func() { - subjectClientState = suite.chainA.GetClientState(subjectPath.EndpointA.ClientID).(*types.ClientState) - substituteClientState = suite.chainA.GetClientState(substitutePath.EndpointA.ClientID).(*types.ClientState) + subjectClientState = suite.chainA.GetClientState(subjectPath.EndpointA.ClientID).(*tendermint.ClientState) + substituteClientState = suite.chainA.GetClientState(substitutePath.EndpointA.ClientID).(*tendermint.ClientState) }, true, }, { @@ -281,7 +281,7 @@ func (suite *TendermintTestSuite) TestIsMatchingClientState() { tc.malleate() - suite.Require().Equal(tc.expPass, types.IsMatchingClientState(*subjectClientState, *substituteClientState)) + suite.Require().Equal(tc.expPass, tendermint.IsMatchingClientState(*subjectClientState, *substituteClientState)) }) } diff --git a/modules/light-clients/07-tendermint/types/store.go b/modules/light-clients/07-tendermint/store.go similarity index 99% rename from modules/light-clients/07-tendermint/types/store.go rename to modules/light-clients/07-tendermint/store.go index fabd29f3161..2be5e94c97e 100644 --- a/modules/light-clients/07-tendermint/types/store.go +++ b/modules/light-clients/07-tendermint/store.go @@ -1,4 +1,4 @@ -package types +package tendermint import ( "bytes" diff --git a/modules/light-clients/07-tendermint/types/store_test.go b/modules/light-clients/07-tendermint/store_test.go similarity index 64% rename from modules/light-clients/07-tendermint/types/store_test.go rename to modules/light-clients/07-tendermint/store_test.go index 28877a49386..f5d57ded2cd 100644 --- a/modules/light-clients/07-tendermint/types/store_test.go +++ b/modules/light-clients/07-tendermint/store_test.go @@ -1,4 +1,4 @@ -package types_test +package tendermint_test import ( "math" @@ -8,8 +8,8 @@ import ( commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/exported" - solomachinetypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types" - "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + solomachine "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" + tendermint "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v3/testing" ) @@ -38,7 +38,7 @@ func (suite *TendermintTestSuite) TestGetConsensusState() { "not a consensus state interface", func() { // marshal an empty client state and set as consensus state store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - clientStateBz := suite.chainA.App.GetIBCKeeper().ClientKeeper.MustMarshalClientState(&types.ClientState{}) + clientStateBz := suite.chainA.App.GetIBCKeeper().ClientKeeper.MustMarshalClientState(&tendermint.ClientState{}) store.Set(host.ConsensusStateKey(height), clientStateBz) }, false, true, }, @@ -46,7 +46,7 @@ func (suite *TendermintTestSuite) TestGetConsensusState() { "invalid consensus state (solomachine)", func() { // marshal and set solomachine consensus state store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - consensusStateBz := suite.chainA.App.GetIBCKeeper().ClientKeeper.MustMarshalConsensusState(&solomachinetypes.ConsensusState{}) + consensusStateBz := suite.chainA.App.GetIBCKeeper().ClientKeeper.MustMarshalConsensusState(&solomachine.ConsensusState{}) store.Set(host.ConsensusStateKey(height), consensusStateBz) }, false, true, }, @@ -68,14 +68,14 @@ func (suite *TendermintTestSuite) TestGetConsensusState() { if tc.expPanic { suite.Require().Panics(func() { store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - types.GetConsensusState(store, suite.chainA.Codec, height) + tendermint.GetConsensusState(store, suite.chainA.Codec, height) }) return } store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - consensusState, found := types.GetConsensusState(store, suite.chainA.Codec, height) + consensusState, found := tendermint.GetConsensusState(store, suite.chainA.Codec, height) if tc.expPass { suite.Require().True(found) @@ -107,7 +107,7 @@ func (suite *TendermintTestSuite) TestGetProcessedTime() { height := clientState.GetLatestHeight() store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - actualTime, ok := types.GetProcessedTime(store, height) + actualTime, ok := tendermint.GetProcessedTime(store, height) suite.Require().True(ok, "could not retrieve processed time for stored consensus state") suite.Require().Equal(uint64(expectedTime.UnixNano()), actualTime, "retrieved processed time is not expected value") @@ -123,12 +123,12 @@ func (suite *TendermintTestSuite) TestGetProcessedTime() { height = clientState.GetLatestHeight() store = suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - actualTime, ok = types.GetProcessedTime(store, height) + actualTime, ok = tendermint.GetProcessedTime(store, height) suite.Require().True(ok, "could not retrieve processed time for stored consensus state") suite.Require().Equal(uint64(expectedTime.UnixNano()), actualTime, "retrieved processed time is not expected value") // try to get processed time for height that doesn't exist in store - _, ok = types.GetProcessedTime(store, clienttypes.NewHeight(1, 1)) + _, ok = tendermint.GetProcessedTime(store, clienttypes.NewHeight(1, 1)) suite.Require().False(ok, "retrieved processed time for a non-existent consensus state") } @@ -140,8 +140,8 @@ func (suite *TendermintTestSuite) TestIterationKey() { clienttypes.NewHeight(math.MaxUint64, math.MaxUint64), } for _, h := range testHeights { - k := types.IterationKey(h) - retrievedHeight := types.GetHeightFromIterationKey(k) + k := tendermint.IterationKey(h) + retrievedHeight := tendermint.GetHeightFromIterationKey(k) suite.Require().Equal(h, retrievedHeight, "retrieving height from iteration key failed") } } @@ -150,16 +150,16 @@ func (suite *TendermintTestSuite) TestIterateConsensusStates() { nextValsHash := []byte("nextVals") // Set iteration keys and consensus states - types.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), clienttypes.NewHeight(0, 1)) - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), "testClient", clienttypes.NewHeight(0, 1), types.NewConsensusState(time.Now(), commitmenttypes.NewMerkleRoot([]byte("hash0-1")), nextValsHash)) - types.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), clienttypes.NewHeight(4, 9)) - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), "testClient", clienttypes.NewHeight(4, 9), types.NewConsensusState(time.Now(), commitmenttypes.NewMerkleRoot([]byte("hash4-9")), nextValsHash)) - types.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), clienttypes.NewHeight(0, 10)) - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), "testClient", clienttypes.NewHeight(0, 10), types.NewConsensusState(time.Now(), commitmenttypes.NewMerkleRoot([]byte("hash0-10")), nextValsHash)) - types.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), clienttypes.NewHeight(0, 4)) - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), "testClient", clienttypes.NewHeight(0, 4), types.NewConsensusState(time.Now(), commitmenttypes.NewMerkleRoot([]byte("hash0-4")), nextValsHash)) - types.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), clienttypes.NewHeight(40, 1)) - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), "testClient", clienttypes.NewHeight(40, 1), types.NewConsensusState(time.Now(), commitmenttypes.NewMerkleRoot([]byte("hash40-1")), nextValsHash)) + tendermint.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), clienttypes.NewHeight(0, 1)) + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), "testClient", clienttypes.NewHeight(0, 1), tendermint.NewConsensusState(time.Now(), commitmenttypes.NewMerkleRoot([]byte("hash0-1")), nextValsHash)) + tendermint.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), clienttypes.NewHeight(4, 9)) + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), "testClient", clienttypes.NewHeight(4, 9), tendermint.NewConsensusState(time.Now(), commitmenttypes.NewMerkleRoot([]byte("hash4-9")), nextValsHash)) + tendermint.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), clienttypes.NewHeight(0, 10)) + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), "testClient", clienttypes.NewHeight(0, 10), tendermint.NewConsensusState(time.Now(), commitmenttypes.NewMerkleRoot([]byte("hash0-10")), nextValsHash)) + tendermint.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), clienttypes.NewHeight(0, 4)) + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), "testClient", clienttypes.NewHeight(0, 4), tendermint.NewConsensusState(time.Now(), commitmenttypes.NewMerkleRoot([]byte("hash0-4")), nextValsHash)) + tendermint.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), clienttypes.NewHeight(40, 1)) + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), "testClient", clienttypes.NewHeight(40, 1), tendermint.NewConsensusState(time.Now(), commitmenttypes.NewMerkleRoot([]byte("hash40-1")), nextValsHash)) var testArr []string cb := func(height exported.Height) bool { @@ -167,39 +167,39 @@ func (suite *TendermintTestSuite) TestIterateConsensusStates() { return false } - types.IterateConsensusStateAscending(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), cb) + tendermint.IterateConsensusStateAscending(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), cb) expectedArr := []string{"0-1", "0-4", "0-10", "4-9", "40-1"} suite.Require().Equal(expectedArr, testArr) } func (suite *TendermintTestSuite) TestGetNeighboringConsensusStates() { nextValsHash := []byte("nextVals") - cs01 := types.NewConsensusState(time.Now().UTC(), commitmenttypes.NewMerkleRoot([]byte("hash0-1")), nextValsHash) - cs04 := types.NewConsensusState(time.Now().UTC(), commitmenttypes.NewMerkleRoot([]byte("hash0-4")), nextValsHash) - cs49 := types.NewConsensusState(time.Now().UTC(), commitmenttypes.NewMerkleRoot([]byte("hash4-9")), nextValsHash) + cs01 := tendermint.NewConsensusState(time.Now().UTC(), commitmenttypes.NewMerkleRoot([]byte("hash0-1")), nextValsHash) + cs04 := tendermint.NewConsensusState(time.Now().UTC(), commitmenttypes.NewMerkleRoot([]byte("hash0-4")), nextValsHash) + cs49 := tendermint.NewConsensusState(time.Now().UTC(), commitmenttypes.NewMerkleRoot([]byte("hash4-9")), nextValsHash) height01 := clienttypes.NewHeight(0, 1) height04 := clienttypes.NewHeight(0, 4) height49 := clienttypes.NewHeight(4, 9) // Set iteration keys and consensus states - types.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), height01) + tendermint.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), height01) suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), "testClient", height01, cs01) - types.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), height04) + tendermint.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), height04) suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), "testClient", height04, cs04) - types.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), height49) + tendermint.SetIterationKey(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), height49) suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), "testClient", height49, cs49) - prevCs01, ok := types.GetPreviousConsensusState(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), suite.chainA.Codec, height01) + prevCs01, ok := tendermint.GetPreviousConsensusState(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), suite.chainA.Codec, height01) suite.Require().Nil(prevCs01, "consensus state exists before lowest consensus state") suite.Require().False(ok) - prevCs49, ok := types.GetPreviousConsensusState(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), suite.chainA.Codec, height49) + prevCs49, ok := tendermint.GetPreviousConsensusState(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), suite.chainA.Codec, height49) suite.Require().Equal(cs04, prevCs49, "previous consensus state is not returned correctly") suite.Require().True(ok) - nextCs01, ok := types.GetNextConsensusState(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), suite.chainA.Codec, height01) + nextCs01, ok := tendermint.GetNextConsensusState(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), suite.chainA.Codec, height01) suite.Require().Equal(cs04, nextCs01, "next consensus state not returned correctly") suite.Require().True(ok) - nextCs49, ok := types.GetNextConsensusState(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), suite.chainA.Codec, height49) + nextCs49, ok := tendermint.GetNextConsensusState(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "testClient"), suite.chainA.Codec, height49) suite.Require().Nil(nextCs49, "next consensus state exists after highest consensus state") suite.Require().False(ok) } diff --git a/modules/light-clients/07-tendermint/types/tendermint.pb.go b/modules/light-clients/07-tendermint/tendermint.pb.go similarity index 87% rename from modules/light-clients/07-tendermint/types/tendermint.pb.go rename to modules/light-clients/07-tendermint/tendermint.pb.go index 00d2cba20e1..7bac5c42672 100644 --- a/modules/light-clients/07-tendermint/types/tendermint.pb.go +++ b/modules/light-clients/07-tendermint/tendermint.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: ibc/lightclients/tendermint/v1/tendermint.proto -package types +package tendermint import ( fmt "fmt" @@ -323,75 +323,75 @@ func init() { } var fileDescriptor_c6d6cf2b288949be = []byte{ - // 1078 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xcf, 0x6f, 0xe3, 0x44, - 0x14, 0x6e, 0xda, 0xb2, 0x4d, 0x26, 0xe9, 0x76, 0x31, 0xa5, 0x9b, 0x96, 0x6e, 0x1c, 0x19, 0xa9, - 0xe4, 0x40, 0x6d, 0x92, 0x22, 0x21, 0x55, 0x5c, 0x70, 0x77, 0x51, 0x8b, 0x58, 0xa9, 0x72, 0xf9, - 0x21, 0x21, 0x21, 0x33, 0xb1, 0x27, 0xc9, 0x68, 0x6d, 0x8f, 0xe5, 0x99, 0x84, 0x96, 0xbf, 0x00, - 0x4e, 0xec, 0x11, 0x71, 0xe2, 0xc0, 0x1f, 0xb3, 0xc7, 0x1e, 0x39, 0x19, 0xd4, 0x5e, 0x39, 0xe5, - 0xc8, 0x09, 0xcd, 0x0f, 0xdb, 0xd3, 0x6c, 0x97, 0x6a, 0xb9, 0x44, 0xf3, 0xde, 0xfb, 0xde, 0xf7, - 0x65, 0xe6, 0xbd, 0x79, 0x63, 0xe0, 0xe0, 0x61, 0xe0, 0x44, 0x78, 0x3c, 0x61, 0x41, 0x84, 0x51, - 0xc2, 0xa8, 0xc3, 0x50, 0x12, 0xa2, 0x2c, 0xc6, 0x09, 0x73, 0x66, 0x7d, 0xcd, 0xb2, 0xd3, 0x8c, - 0x30, 0x62, 0x74, 0xf0, 0x30, 0xb0, 0xf5, 0x04, 0x5b, 0x83, 0xcc, 0xfa, 0x3b, 0x5d, 0x2d, 0x9f, - 0x5d, 0xa4, 0x88, 0x3a, 0x33, 0x18, 0xe1, 0x10, 0x32, 0x92, 0x49, 0x86, 0x9d, 0xdd, 0x97, 0x10, - 0xe2, 0x57, 0x45, 0x5b, 0x69, 0x46, 0xc8, 0xa8, 0xb0, 0x3a, 0x63, 0x42, 0xc6, 0x11, 0x72, 0x84, - 0x35, 0x9c, 0x8e, 0x9c, 0x70, 0x9a, 0x41, 0x86, 0x49, 0xa2, 0xe2, 0xe6, 0x62, 0x9c, 0xe1, 0x18, - 0x51, 0x06, 0xe3, 0xb4, 0x00, 0xf0, 0xfd, 0x05, 0x24, 0x43, 0x8e, 0xfc, 0xbb, 0x7c, 0x4f, 0x72, - 0xa5, 0x00, 0xef, 0x55, 0x00, 0x12, 0xc7, 0x98, 0xc5, 0x05, 0xa8, 0xb4, 0x14, 0x70, 0x73, 0x4c, - 0xc6, 0x44, 0x2c, 0x1d, 0xbe, 0x92, 0x5e, 0xeb, 0xef, 0x35, 0xd0, 0x3c, 0x12, 0x7c, 0x67, 0x0c, - 0x32, 0x64, 0x6c, 0x83, 0x7a, 0x30, 0x81, 0x38, 0xf1, 0x71, 0xd8, 0xae, 0x75, 0x6b, 0xbd, 0x86, - 0xb7, 0x26, 0xec, 0x93, 0xd0, 0x40, 0xa0, 0xc9, 0xb2, 0x29, 0x65, 0x7e, 0x84, 0x66, 0x28, 0x6a, - 0x2f, 0x77, 0x6b, 0xbd, 0xe6, 0xa0, 0x67, 0xff, 0xf7, 0x79, 0xda, 0x9f, 0x66, 0x30, 0xe0, 0x1b, - 0x76, 0x77, 0x5e, 0xe4, 0xe6, 0xd2, 0x3c, 0x37, 0x8d, 0x0b, 0x18, 0x47, 0x87, 0x96, 0x46, 0x65, - 0x79, 0x40, 0x58, 0x9f, 0x73, 0xc3, 0x18, 0x81, 0x0d, 0x61, 0xe1, 0x64, 0xec, 0xa7, 0x28, 0xc3, - 0x24, 0x6c, 0xaf, 0x08, 0xa9, 0x6d, 0x5b, 0x1e, 0x96, 0x5d, 0x1c, 0x96, 0xfd, 0x58, 0x1d, 0xa6, - 0x6b, 0x29, 0xee, 0x2d, 0x8d, 0xbb, 0xca, 0xb7, 0x7e, 0xf9, 0xd3, 0xac, 0x79, 0xf7, 0x0b, 0xef, - 0xa9, 0x70, 0x1a, 0x18, 0x3c, 0x98, 0x26, 0x43, 0x92, 0x84, 0x9a, 0xd0, 0xea, 0x5d, 0x42, 0xef, - 0x2a, 0xa1, 0x87, 0x52, 0x68, 0x91, 0x40, 0x2a, 0x6d, 0x94, 0x6e, 0x25, 0x85, 0xc0, 0x46, 0x0c, - 0xcf, 0xfd, 0x20, 0x22, 0xc1, 0x33, 0x3f, 0xcc, 0xf0, 0x88, 0xb5, 0xdf, 0x78, 0xcd, 0x2d, 0x2d, - 0xe4, 0x4b, 0xa1, 0xf5, 0x18, 0x9e, 0x1f, 0x71, 0xe7, 0x63, 0xee, 0x33, 0xbe, 0x05, 0xeb, 0xa3, - 0x8c, 0xfc, 0x80, 0x12, 0x7f, 0x82, 0x78, 0x41, 0xda, 0xf7, 0x84, 0xc8, 0x8e, 0x28, 0x11, 0x6f, - 0x11, 0x5b, 0x75, 0xce, 0xac, 0x6f, 0x1f, 0x0b, 0x84, 0xbb, 0xab, 0x54, 0x36, 0xa5, 0xca, 0x8d, - 0x74, 0xcb, 0x6b, 0x49, 0x5b, 0x62, 0x39, 0x7d, 0x04, 0x19, 0xa2, 0xac, 0xa0, 0x5f, 0x7b, 0x5d, - 0xfa, 0x1b, 0xe9, 0x96, 0xd7, 0x92, 0xb6, 0xa2, 0x3f, 0x01, 0x4d, 0x71, 0x75, 0x7c, 0x9a, 0xa2, - 0x80, 0xb6, 0xeb, 0xdd, 0x95, 0x5e, 0x73, 0xf0, 0xc0, 0xc6, 0x01, 0x1d, 0x1c, 0xd8, 0xa7, 0x3c, - 0x72, 0x96, 0xa2, 0xc0, 0xdd, 0xaa, 0x5a, 0x48, 0x83, 0x5b, 0x1e, 0x48, 0x0b, 0x08, 0x35, 0x0e, - 0x41, 0x6b, 0x9a, 0x8e, 0x33, 0x18, 0x22, 0x3f, 0x85, 0x6c, 0xd2, 0x6e, 0x74, 0x57, 0x7a, 0x0d, - 0xf7, 0xe1, 0x3c, 0x37, 0xdf, 0x52, 0x75, 0xd3, 0xa2, 0x96, 0xd7, 0x54, 0xe6, 0x29, 0x64, 0x13, - 0x03, 0x82, 0x6d, 0x18, 0x45, 0xe4, 0x7b, 0x7f, 0x9a, 0x86, 0x90, 0x21, 0x1f, 0x8e, 0x18, 0xca, - 0x7c, 0x74, 0x9e, 0xe2, 0xec, 0xa2, 0x0d, 0xba, 0xb5, 0x5e, 0xdd, 0xdd, 0x9b, 0xe7, 0x66, 0x57, - 0x12, 0xbd, 0x12, 0x6a, 0xb5, 0x6b, 0xde, 0x96, 0x88, 0x7e, 0x29, 0x82, 0x9f, 0xf0, 0xd8, 0x13, - 0x11, 0x32, 0x28, 0x30, 0x6f, 0xc9, 0x8b, 0x31, 0x1d, 0xa2, 0x09, 0x9c, 0x61, 0x32, 0xcd, 0xda, - 0x4d, 0x21, 0xf4, 0xfe, 0x3c, 0x37, 0xf7, 0x5e, 0x29, 0xa4, 0x27, 0x70, 0xb9, 0xdd, 0x45, 0xb9, - 0xa7, 0x1a, 0xe0, 0x70, 0xf5, 0xc7, 0xdf, 0xcc, 0x25, 0xeb, 0xf7, 0x65, 0x70, 0xff, 0x88, 0x24, - 0x14, 0x25, 0x74, 0x4a, 0xe5, 0x8d, 0x77, 0x41, 0xa3, 0x1c, 0x3a, 0xe2, 0xca, 0xf3, 0x92, 0x2e, - 0xb6, 0xe5, 0x17, 0x05, 0xc2, 0xad, 0xf3, 0x92, 0x3e, 0xe7, 0xdd, 0x57, 0xa5, 0x19, 0x1f, 0x83, - 0xd5, 0x8c, 0x10, 0xa6, 0x66, 0x82, 0xa5, 0x75, 0x44, 0x35, 0x85, 0x66, 0x7d, 0xfb, 0x29, 0xca, - 0x9e, 0x45, 0xc8, 0x23, 0x84, 0xb9, 0xab, 0x9c, 0xc6, 0x13, 0x59, 0xc6, 0x4f, 0x35, 0xb0, 0x99, - 0xa0, 0x73, 0xe6, 0x97, 0x93, 0x96, 0xfa, 0x13, 0x48, 0x27, 0xe2, 0xde, 0xb7, 0xdc, 0xaf, 0xe7, - 0xb9, 0xf9, 0x8e, 0x3c, 0x85, 0xdb, 0x50, 0xd6, 0x3f, 0xb9, 0xf9, 0xe1, 0x18, 0xb3, 0xc9, 0x74, - 0xc8, 0xe5, 0xf4, 0xf9, 0xaf, 0x2d, 0x23, 0x3c, 0xa4, 0xce, 0xf0, 0x82, 0x21, 0x6a, 0x1f, 0xa3, - 0x73, 0x97, 0x2f, 0x3c, 0x83, 0xd3, 0x7d, 0x55, 0xb2, 0x1d, 0x43, 0x3a, 0x51, 0xc7, 0xf4, 0xf3, - 0x32, 0x68, 0xe9, 0xa7, 0x67, 0x1c, 0x80, 0x86, 0x6c, 0xee, 0x72, 0x2e, 0x8a, 0x46, 0x7c, 0x20, - 0xff, 0x56, 0x19, 0xe2, 0x65, 0xa8, 0x4b, 0xeb, 0x24, 0x34, 0x20, 0xa8, 0x4f, 0x10, 0x0c, 0x51, - 0xe6, 0xf7, 0xd5, 0xc9, 0xec, 0xdd, 0x35, 0x2d, 0x8f, 0x05, 0xde, 0xed, 0x5c, 0xe5, 0xe6, 0x9a, - 0x5c, 0xf7, 0xe7, 0xb9, 0xb9, 0x21, 0x65, 0x0a, 0x32, 0xcb, 0x5b, 0x93, 0xcb, 0xbe, 0x26, 0x31, - 0x50, 0x53, 0xf2, 0x7f, 0x48, 0x0c, 0x5e, 0x92, 0x18, 0x94, 0x12, 0x03, 0x75, 0x22, 0xbf, 0xae, - 0x80, 0x7b, 0x12, 0x6d, 0x40, 0xb0, 0x4e, 0xf1, 0x38, 0x41, 0xa1, 0x2f, 0x21, 0xaa, 0x69, 0x3a, - 0xba, 0x8e, 0x7c, 0x11, 0xcf, 0x04, 0x4c, 0x09, 0xee, 0x5e, 0xe6, 0x66, 0xad, 0x9a, 0x05, 0x37, - 0x28, 0x2c, 0xaf, 0x45, 0x35, 0x2c, 0x1f, 0x35, 0x65, 0x95, 0x7d, 0x8a, 0x8a, 0xc6, 0xba, 0x45, - 0xa2, 0x2c, 0xdf, 0x19, 0x62, 0x6e, 0xbb, 0xa2, 0xbf, 0x91, 0x6e, 0x79, 0xad, 0x99, 0x86, 0x33, - 0xbe, 0x03, 0xf2, 0x31, 0x10, 0xfa, 0x62, 0x94, 0xad, 0xdc, 0x39, 0xca, 0x1e, 0xa9, 0x51, 0xf6, - 0xb6, 0xf6, 0xc4, 0x94, 0xf9, 0x96, 0xb7, 0xae, 0x1c, 0x6a, 0x98, 0x45, 0xc0, 0x28, 0x10, 0x55, - 0xbb, 0xaa, 0xe7, 0xe5, 0xae, 0x5d, 0x3c, 0x9a, 0xe7, 0xe6, 0xf6, 0x4d, 0x95, 0x8a, 0xc3, 0xf2, - 0xde, 0x54, 0xce, 0xaa, 0x71, 0xad, 0xcf, 0x40, 0xbd, 0x78, 0x66, 0x8d, 0x5d, 0xd0, 0x48, 0xa6, - 0x31, 0xca, 0x78, 0x44, 0x54, 0x66, 0xd5, 0xab, 0x1c, 0x46, 0x17, 0x34, 0x43, 0x94, 0x90, 0x18, - 0x27, 0x22, 0xbe, 0x2c, 0xe2, 0xba, 0xcb, 0xf5, 0x5f, 0x5c, 0x75, 0x6a, 0x97, 0x57, 0x9d, 0xda, - 0x5f, 0x57, 0x9d, 0xda, 0xf3, 0xeb, 0xce, 0xd2, 0xe5, 0x75, 0x67, 0xe9, 0x8f, 0xeb, 0xce, 0xd2, - 0x37, 0x4f, 0xb4, 0x4b, 0x16, 0x10, 0x1a, 0x13, 0xca, 0x3f, 0xbe, 0xf6, 0xc7, 0xc4, 0x99, 0x1d, - 0x38, 0x31, 0x09, 0xa7, 0x11, 0xa2, 0xf2, 0x53, 0x6c, 0xbf, 0xf8, 0x16, 0xfb, 0xe0, 0xa3, 0xfd, - 0xc5, 0x8f, 0xa5, 0xe1, 0x3d, 0x31, 0x54, 0x0e, 0xfe, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xdd, 0xc8, - 0x3e, 0xfe, 0xba, 0x09, 0x00, 0x00, + // 1082 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xcf, 0x6f, 0xe3, 0xc4, + 0x17, 0x6f, 0xda, 0x7e, 0xdb, 0x64, 0x92, 0x6e, 0xfb, 0x35, 0xa5, 0x9b, 0x96, 0x6e, 0x1c, 0x19, + 0xa9, 0xe4, 0x40, 0x6d, 0x92, 0x22, 0x21, 0x15, 0x2e, 0xb8, 0x0b, 0x6a, 0x57, 0xac, 0x54, 0xb9, + 0xfc, 0x90, 0x90, 0x90, 0x99, 0xd8, 0x93, 0x64, 0xb4, 0xb6, 0xc7, 0xf2, 0x4c, 0x42, 0xcb, 0x5f, + 0x00, 0x27, 0xf6, 0x88, 0x38, 0x71, 0xe0, 0x8f, 0xd9, 0x63, 0x8f, 0x9c, 0x0c, 0x6a, 0xaf, 0x9c, + 0x72, 0xe4, 0x84, 0xe6, 0x87, 0xed, 0x69, 0xb7, 0x4b, 0xb5, 0x5c, 0xa2, 0x79, 0xef, 0x7d, 0xde, + 0xe7, 0x93, 0x99, 0xf7, 0xe6, 0x8d, 0x81, 0x83, 0x87, 0x81, 0x13, 0xe1, 0xf1, 0x84, 0x05, 0x11, + 0x46, 0x09, 0xa3, 0x0e, 0x43, 0x49, 0x88, 0xb2, 0x18, 0x27, 0xcc, 0x99, 0xf5, 0x35, 0xcb, 0x4e, + 0x33, 0xc2, 0x88, 0xd1, 0xc1, 0xc3, 0xc0, 0xd6, 0x13, 0x6c, 0x0d, 0x32, 0xeb, 0xef, 0x74, 0xb5, + 0x7c, 0x76, 0x91, 0x22, 0xea, 0xcc, 0x60, 0x84, 0x43, 0xc8, 0x48, 0x26, 0x19, 0x76, 0x76, 0x5f, + 0x42, 0x88, 0x5f, 0x15, 0x6d, 0xa5, 0x19, 0x21, 0xa3, 0xc2, 0xea, 0x8c, 0x09, 0x19, 0x47, 0xc8, + 0x11, 0xd6, 0x70, 0x3a, 0x72, 0xc2, 0x69, 0x06, 0x19, 0x26, 0x89, 0x8a, 0x9b, 0xb7, 0xe3, 0x0c, + 0xc7, 0x88, 0x32, 0x18, 0xa7, 0x05, 0x80, 0xef, 0x2f, 0x20, 0x19, 0x72, 0xe4, 0xdf, 0xe5, 0x7b, + 0x92, 0x2b, 0x05, 0x78, 0xa7, 0x02, 0x90, 0x38, 0xc6, 0x2c, 0x2e, 0x40, 0xa5, 0xa5, 0x80, 0x9b, + 0x63, 0x32, 0x26, 0x62, 0xe9, 0xf0, 0x95, 0xf4, 0x5a, 0x7f, 0xad, 0x82, 0xe6, 0x91, 0xe0, 0x3b, + 0x63, 0x90, 0x21, 0x63, 0x1b, 0xd4, 0x83, 0x09, 0xc4, 0x89, 0x8f, 0xc3, 0x76, 0xad, 0x5b, 0xeb, + 0x35, 0xbc, 0x55, 0x61, 0x9f, 0x84, 0x06, 0x02, 0x4d, 0x96, 0x4d, 0x29, 0xf3, 0x23, 0x34, 0x43, + 0x51, 0x7b, 0xb1, 0x5b, 0xeb, 0x35, 0x07, 0x3d, 0xfb, 0xdf, 0xcf, 0xd3, 0xfe, 0x34, 0x83, 0x01, + 0xdf, 0xb0, 0xbb, 0xf3, 0x22, 0x37, 0x17, 0xe6, 0xb9, 0x69, 0x5c, 0xc0, 0x38, 0x3a, 0xb4, 0x34, + 0x2a, 0xcb, 0x03, 0xc2, 0xfa, 0x8c, 0x1b, 0xc6, 0x08, 0xac, 0x0b, 0x0b, 0x27, 0x63, 0x3f, 0x45, + 0x19, 0x26, 0x61, 0x7b, 0x49, 0x48, 0x6d, 0xdb, 0xf2, 0xb0, 0xec, 0xe2, 0xb0, 0xec, 0xc7, 0xea, + 0x30, 0x5d, 0x4b, 0x71, 0x6f, 0x69, 0xdc, 0x55, 0xbe, 0xf5, 0xf3, 0x1f, 0x66, 0xcd, 0x7b, 0x50, + 0x78, 0x4f, 0x85, 0xd3, 0xc0, 0x60, 0x63, 0x9a, 0x0c, 0x49, 0x12, 0x6a, 0x42, 0xcb, 0xf7, 0x09, + 0xbd, 0xad, 0x84, 0x1e, 0x4a, 0xa1, 0xdb, 0x04, 0x52, 0x69, 0xbd, 0x74, 0x2b, 0x29, 0x04, 0xd6, + 0x63, 0x78, 0xee, 0x07, 0x11, 0x09, 0x9e, 0xf9, 0x61, 0x86, 0x47, 0xac, 0xfd, 0xbf, 0xd7, 0xdc, + 0xd2, 0xad, 0x7c, 0x29, 0xb4, 0x16, 0xc3, 0xf3, 0x23, 0xee, 0x7c, 0xcc, 0x7d, 0xc6, 0x37, 0x60, + 0x6d, 0x94, 0x91, 0xef, 0x51, 0xe2, 0x4f, 0x10, 0x2f, 0x48, 0x7b, 0x45, 0x88, 0xec, 0x88, 0x12, + 0xf1, 0x16, 0xb1, 0x55, 0xe7, 0xcc, 0xfa, 0xf6, 0xb1, 0x40, 0xb8, 0xbb, 0x4a, 0x65, 0x53, 0xaa, + 0xdc, 0x48, 0xb7, 0xbc, 0x96, 0xb4, 0x25, 0x96, 0xd3, 0x47, 0x90, 0x21, 0xca, 0x0a, 0xfa, 0xd5, + 0xd7, 0xa5, 0xbf, 0x91, 0x6e, 0x79, 0x2d, 0x69, 0x2b, 0xfa, 0x13, 0xd0, 0x14, 0x57, 0xc7, 0xa7, + 0x29, 0x0a, 0x68, 0xbb, 0xde, 0x5d, 0xea, 0x35, 0x07, 0x1b, 0x36, 0x0e, 0xe8, 0xe0, 0xc0, 0x3e, + 0xe5, 0x91, 0xb3, 0x14, 0x05, 0xee, 0x56, 0xd5, 0x42, 0x1a, 0xdc, 0xf2, 0x40, 0x5a, 0x40, 0xa8, + 0x71, 0x08, 0x5a, 0xd3, 0x74, 0x9c, 0xc1, 0x10, 0xf9, 0x29, 0x64, 0x93, 0x76, 0xa3, 0xbb, 0xd4, + 0x6b, 0xb8, 0x0f, 0xe7, 0xb9, 0xf9, 0x86, 0xaa, 0x9b, 0x16, 0xb5, 0xbc, 0xa6, 0x32, 0x4f, 0x21, + 0x9b, 0x18, 0x10, 0x6c, 0xc3, 0x28, 0x22, 0xdf, 0xf9, 0xd3, 0x34, 0x84, 0x0c, 0xf9, 0x70, 0xc4, + 0x50, 0xe6, 0xa3, 0xf3, 0x14, 0x67, 0x17, 0x6d, 0xd0, 0xad, 0xf5, 0xea, 0xee, 0xde, 0x3c, 0x37, + 0xbb, 0x92, 0xe8, 0x95, 0x50, 0xab, 0x5d, 0xf3, 0xb6, 0x44, 0xf4, 0x0b, 0x11, 0xfc, 0x98, 0xc7, + 0x3e, 0x11, 0x21, 0x83, 0x02, 0xf3, 0x8e, 0xbc, 0x18, 0xd3, 0x21, 0x9a, 0xc0, 0x19, 0x26, 0xd3, + 0xac, 0xdd, 0x14, 0x42, 0xef, 0xce, 0x73, 0x73, 0xef, 0x95, 0x42, 0x7a, 0x02, 0x97, 0xdb, 0xbd, + 0x2d, 0xf7, 0x54, 0x03, 0x1c, 0x2e, 0xff, 0xf0, 0xab, 0xb9, 0x60, 0xfd, 0xb6, 0x08, 0x1e, 0x1c, + 0x91, 0x84, 0xa2, 0x84, 0x4e, 0xa9, 0xbc, 0xf1, 0x2e, 0x68, 0x94, 0x43, 0x47, 0x5c, 0x79, 0x5e, + 0xd2, 0xdb, 0x6d, 0xf9, 0x79, 0x81, 0x70, 0xeb, 0xbc, 0xa4, 0xcf, 0x79, 0xf7, 0x55, 0x69, 0xc6, + 0x47, 0x60, 0x39, 0x23, 0x84, 0xa9, 0x99, 0x60, 0x69, 0x1d, 0x51, 0x4d, 0xa1, 0x59, 0xdf, 0x7e, + 0x8a, 0xb2, 0x67, 0x11, 0xf2, 0x08, 0x61, 0xee, 0x32, 0xa7, 0xf1, 0x44, 0x96, 0xf1, 0x63, 0x0d, + 0x6c, 0x26, 0xe8, 0x9c, 0xf9, 0xe5, 0xa4, 0xa5, 0xfe, 0x04, 0xd2, 0x89, 0xb8, 0xf7, 0x2d, 0xf7, + 0xab, 0x79, 0x6e, 0xbe, 0x25, 0x4f, 0xe1, 0x2e, 0x94, 0xf5, 0x77, 0x6e, 0xbe, 0x3f, 0xc6, 0x6c, + 0x32, 0x1d, 0x72, 0x39, 0x7d, 0xfe, 0x6b, 0xcb, 0x08, 0x0f, 0xa9, 0x33, 0xbc, 0x60, 0x88, 0xda, + 0xc7, 0xe8, 0xdc, 0xe5, 0x0b, 0xcf, 0xe0, 0x74, 0x5f, 0x96, 0x6c, 0xc7, 0x90, 0x4e, 0xd4, 0x31, + 0xfd, 0xb4, 0x08, 0x5a, 0xfa, 0xe9, 0x19, 0x07, 0xa0, 0x21, 0x9b, 0xbb, 0x9c, 0x8b, 0xa2, 0x11, + 0x37, 0xe4, 0xdf, 0x2a, 0x43, 0xbc, 0x0c, 0x75, 0x69, 0x9d, 0x84, 0x06, 0x04, 0xf5, 0x09, 0x82, + 0x21, 0xca, 0xfc, 0xbe, 0x3a, 0x99, 0xbd, 0xfb, 0xa6, 0xe5, 0xb1, 0xc0, 0xbb, 0x9d, 0xab, 0xdc, + 0x5c, 0x95, 0xeb, 0xfe, 0x3c, 0x37, 0xd7, 0xa5, 0x4c, 0x41, 0x66, 0x79, 0xab, 0x72, 0xd9, 0xd7, + 0x24, 0x06, 0x6a, 0x4a, 0xfe, 0x07, 0x89, 0xc1, 0x4b, 0x12, 0x83, 0x52, 0x62, 0xa0, 0x4e, 0xe4, + 0x97, 0x25, 0xb0, 0x22, 0xd1, 0x06, 0x04, 0x6b, 0x14, 0x8f, 0x13, 0x14, 0xfa, 0x12, 0xa2, 0x9a, + 0xa6, 0xa3, 0xeb, 0xc8, 0x17, 0xf1, 0x4c, 0xc0, 0x94, 0xe0, 0xee, 0x65, 0x6e, 0xd6, 0xaa, 0x59, + 0x70, 0x83, 0xc2, 0xf2, 0x5a, 0x54, 0xc3, 0xf2, 0x51, 0x53, 0x56, 0xd9, 0xa7, 0xa8, 0x68, 0xac, + 0x3b, 0x24, 0xca, 0xf2, 0x9d, 0x21, 0xe6, 0xb6, 0x2b, 0xfa, 0x1b, 0xe9, 0x96, 0xd7, 0x9a, 0x69, + 0x38, 0xe3, 0x5b, 0x20, 0x1f, 0x03, 0xa1, 0x2f, 0x46, 0xd9, 0xd2, 0xbd, 0xa3, 0xec, 0x91, 0x1a, + 0x65, 0x6f, 0x6a, 0x4f, 0x4c, 0x99, 0x6f, 0x79, 0x6b, 0xca, 0xa1, 0x86, 0x59, 0x04, 0x8c, 0x02, + 0x51, 0xb5, 0xab, 0x7a, 0x5e, 0xee, 0xdb, 0xc5, 0xa3, 0x79, 0x6e, 0x6e, 0xdf, 0x54, 0xa9, 0x38, + 0x2c, 0xef, 0xff, 0xca, 0x59, 0x35, 0xae, 0xf5, 0x04, 0xd4, 0x8b, 0x67, 0xd6, 0xd8, 0x05, 0x8d, + 0x64, 0x1a, 0xa3, 0x8c, 0x47, 0x44, 0x65, 0x96, 0xbd, 0xca, 0x61, 0x74, 0x41, 0x33, 0x44, 0x09, + 0x89, 0x71, 0x22, 0xe2, 0x8b, 0x22, 0xae, 0xbb, 0xdc, 0xf0, 0xc5, 0x55, 0xa7, 0x76, 0x79, 0xd5, + 0xa9, 0xfd, 0x79, 0xd5, 0xa9, 0x3d, 0xbf, 0xee, 0x2c, 0x5c, 0x5e, 0x77, 0x16, 0x7e, 0xbf, 0xee, + 0x2c, 0x7c, 0xfd, 0x44, 0xbb, 0x64, 0x01, 0xa1, 0x31, 0xa1, 0xfc, 0xe3, 0x6b, 0x7f, 0x4c, 0x9c, + 0xd9, 0x81, 0x13, 0x93, 0x70, 0x1a, 0x21, 0x2a, 0x3f, 0xc5, 0xf6, 0x8b, 0x6f, 0xb1, 0xf7, 0x3e, + 0xd8, 0xaf, 0xf6, 0xfa, 0x61, 0xb5, 0x1c, 0xae, 0x88, 0xc9, 0x72, 0xf0, 0x4f, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x3e, 0x30, 0x59, 0x01, 0xbf, 0x09, 0x00, 0x00, } func (m *ClientState) Marshal() (dAtA []byte, err error) { diff --git a/modules/light-clients/07-tendermint/types/tendermint_test.go b/modules/light-clients/07-tendermint/tendermint_test.go similarity index 96% rename from modules/light-clients/07-tendermint/types/tendermint_test.go rename to modules/light-clients/07-tendermint/tendermint_test.go index 898a48efe72..843e1f71c57 100644 --- a/modules/light-clients/07-tendermint/types/tendermint_test.go +++ b/modules/light-clients/07-tendermint/tendermint_test.go @@ -1,4 +1,4 @@ -package types_test +package tendermint_test import ( "testing" @@ -12,7 +12,7 @@ import ( tmtypes "github.com/tendermint/tendermint/types" clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + tendermint "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v3/testing" ibctestingmock "github.com/cosmos/ibc-go/v3/testing/mock" "github.com/cosmos/ibc-go/v3/testing/simapp" @@ -50,7 +50,7 @@ type TendermintTestSuite struct { valSet *tmtypes.ValidatorSet signers map[string]tmtypes.PrivValidator valsHash tmbytes.HexBytes - header *ibctmtypes.Header + header *tendermint.Header now time.Time headerTime time.Time clientTime time.Time diff --git a/modules/light-clients/07-tendermint/types/update.go b/modules/light-clients/07-tendermint/update.go similarity index 99% rename from modules/light-clients/07-tendermint/types/update.go rename to modules/light-clients/07-tendermint/update.go index 31d16799118..f1606748b07 100644 --- a/modules/light-clients/07-tendermint/types/update.go +++ b/modules/light-clients/07-tendermint/update.go @@ -1,4 +1,4 @@ -package types +package tendermint import ( "bytes" diff --git a/modules/light-clients/07-tendermint/types/update_test.go b/modules/light-clients/07-tendermint/update_test.go similarity index 93% rename from modules/light-clients/07-tendermint/types/update_test.go rename to modules/light-clients/07-tendermint/update_test.go index abe9cfb9382..dec1e384a86 100644 --- a/modules/light-clients/07-tendermint/types/update_test.go +++ b/modules/light-clients/07-tendermint/update_test.go @@ -1,4 +1,4 @@ -package types_test +package tendermint_test import ( "time" @@ -8,8 +8,7 @@ import ( commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" - types "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + tendermint "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v3/testing" ibctestingmock "github.com/cosmos/ibc-go/v3/testing/mock" tmtypes "github.com/tendermint/tendermint/types" @@ -18,7 +17,7 @@ import ( func (suite *TendermintTestSuite) TestVerifyHeader() { var ( path *ibctesting.Path - header *ibctmtypes.Header + header *tendermint.Header ) // Setup different validators and signers for testing different types of updates @@ -322,12 +321,12 @@ func (suite *TendermintTestSuite) TestUpdateState() { }{ { "success with height later than latest height", func() { - tmHeader, ok := clientMessage.(*types.Header) + tmHeader, ok := clientMessage.(*tendermint.Header) suite.Require().True(ok) suite.Require().True(path.EndpointA.GetClientState().GetLatestHeight().LT(tmHeader.GetHeight())) }, func() { - tmHeader, ok := clientMessage.(*types.Header) + tmHeader, ok := clientMessage.(*tendermint.Header) suite.Require().True(ok) clientState := path.EndpointA.GetClientState() @@ -343,7 +342,7 @@ func (suite *TendermintTestSuite) TestUpdateState() { err := path.EndpointA.UpdateClient() suite.Require().NoError(err) - tmHeader, ok := clientMessage.(*types.Header) + tmHeader, ok := clientMessage.(*tendermint.Header) suite.Require().True(ok) suite.Require().True(path.EndpointA.GetClientState().GetLatestHeight().GT(tmHeader.GetHeight())) @@ -365,7 +364,7 @@ func (suite *TendermintTestSuite) TestUpdateState() { clientMessage, err = path.EndpointA.Chain.ConstructUpdateTMClientHeader(path.EndpointA.Counterparty.Chain, path.EndpointA.ClientID) suite.Require().NoError(err) - tmHeader, ok := clientMessage.(*types.Header) + tmHeader, ok := clientMessage.(*tendermint.Header) suite.Require().True(ok) suite.Require().Equal(path.EndpointA.GetClientState().GetLatestHeight(), tmHeader.GetHeight()) @@ -377,7 +376,7 @@ func (suite *TendermintTestSuite) TestUpdateState() { suite.Require().Equal(clientState, prevClientState) suite.Require().True(clientState.GetLatestHeight().EQ(consensusHeights[0])) - tmHeader, ok := clientMessage.(*types.Header) + tmHeader, ok := clientMessage.(*tendermint.Header) suite.Require().True(ok) suite.Require().Equal(path.EndpointA.GetConsensusState(tmHeader.GetHeight()), prevConsensusState) }, true, @@ -408,7 +407,7 @@ func (suite *TendermintTestSuite) TestUpdateState() { suite.Require().NoError(err) }, func() { - tmHeader, ok := clientMessage.(*types.Header) + tmHeader, ok := clientMessage.(*tendermint.Header) suite.Require().True(ok) clientState := path.EndpointA.GetClientState() @@ -422,7 +421,7 @@ func (suite *TendermintTestSuite) TestUpdateState() { }, { "invalid ClientMessage type", func() { - clientMessage = &types.Misbehaviour{} + clientMessage = &tendermint.Misbehaviour{} }, func() {}, false, @@ -451,8 +450,8 @@ func (suite *TendermintTestSuite) TestUpdateState() { if tc.expPass { consensusHeights = clientState.UpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, clientMessage) - header := clientMessage.(*types.Header) - expConsensusState := &types.ConsensusState{ + header := clientMessage.(*tendermint.Header) + expConsensusState := &tendermint.ConsensusState{ Timestamp: header.GetTime(), Root: commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), NextValidatorsHash: header.Header.NextValidatorsHash, @@ -488,7 +487,7 @@ func (suite *TendermintTestSuite) TestPruneConsensusState() { } ctx := path.EndpointA.Chain.GetContext() clientStore := path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) - err := types.IterateConsensusStateAscending(clientStore, getFirstHeightCb) + err := tendermint.IterateConsensusStateAscending(clientStore, getFirstHeightCb) suite.Require().Nil(err) // this height will be expired but not pruned @@ -500,11 +499,11 @@ func (suite *TendermintTestSuite) TestPruneConsensusState() { suite.Require().True(ok) ctx = path.EndpointA.Chain.GetContext() clientStore = path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) - expectedProcessTime, ok := types.GetProcessedTime(clientStore, expiredHeight) + expectedProcessTime, ok := tendermint.GetProcessedTime(clientStore, expiredHeight) suite.Require().True(ok) - expectedProcessHeight, ok := types.GetProcessedHeight(clientStore, expiredHeight) + expectedProcessHeight, ok := tendermint.GetProcessedHeight(clientStore, expiredHeight) suite.Require().True(ok) - expectedConsKey := types.GetIterationKey(clientStore, expiredHeight) + expectedConsKey := tendermint.GetIterationKey(clientStore, expiredHeight) suite.Require().NotNil(expectedConsKey) // Increment the time by a week @@ -526,15 +525,15 @@ func (suite *TendermintTestSuite) TestPruneConsensusState() { suite.Require().Nil(consState, "expired consensus state not pruned") suite.Require().False(ok) // check processed time metadata is pruned - processTime, ok := types.GetProcessedTime(clientStore, pruneHeight) + processTime, ok := tendermint.GetProcessedTime(clientStore, pruneHeight) suite.Require().Equal(uint64(0), processTime, "processed time metadata not pruned") suite.Require().False(ok) - processHeight, ok := types.GetProcessedHeight(clientStore, pruneHeight) + processHeight, ok := tendermint.GetProcessedHeight(clientStore, pruneHeight) suite.Require().Nil(processHeight, "processed height metadata not pruned") suite.Require().False(ok) // check iteration key metadata is pruned - consKey := types.GetIterationKey(clientStore, pruneHeight) + consKey := tendermint.GetIterationKey(clientStore, pruneHeight) suite.Require().Nil(consKey, "iteration key not pruned") // check that second expired consensus state doesn't get deleted @@ -543,17 +542,17 @@ func (suite *TendermintTestSuite) TestPruneConsensusState() { suite.Require().Equal(expectedConsState, consState, "consensus state incorrectly pruned") suite.Require().True(ok) // check processed time metadata is not pruned - processTime, ok = types.GetProcessedTime(clientStore, expiredHeight) + processTime, ok = tendermint.GetProcessedTime(clientStore, expiredHeight) suite.Require().Equal(expectedProcessTime, processTime, "processed time metadata incorrectly pruned") suite.Require().True(ok) // check processed height metadata is not pruned - processHeight, ok = types.GetProcessedHeight(clientStore, expiredHeight) + processHeight, ok = tendermint.GetProcessedHeight(clientStore, expiredHeight) suite.Require().Equal(expectedProcessHeight, processHeight, "processed height metadata incorrectly pruned") suite.Require().True(ok) // check iteration key metadata is not pruned - consKey = types.GetIterationKey(clientStore, expiredHeight) + consKey = tendermint.GetIterationKey(clientStore, expiredHeight) suite.Require().Equal(expectedConsKey, consKey, "iteration key incorrectly pruned") } @@ -576,16 +575,16 @@ func (suite *TendermintTestSuite) TestCheckForMisbehaviour() { { "consensus state already exists, already updated", func() { - header, ok := clientMessage.(*types.Header) + header, ok := clientMessage.(*tendermint.Header) suite.Require().True(ok) - consensusState := &types.ConsensusState{ + consensusState := &tendermint.ConsensusState{ Timestamp: header.GetTime(), Root: commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), NextValidatorsHash: header.Header.NextValidatorsHash, } - tmHeader, ok := clientMessage.(*types.Header) + tmHeader, ok := clientMessage.(*tendermint.Header) suite.Require().True(ok) suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), path.EndpointA.ClientID, tmHeader.GetHeight(), consensusState) }, @@ -594,16 +593,16 @@ func (suite *TendermintTestSuite) TestCheckForMisbehaviour() { { "consensus state already exists, app hash mismatch", func() { - header, ok := clientMessage.(*types.Header) + header, ok := clientMessage.(*tendermint.Header) suite.Require().True(ok) - consensusState := &types.ConsensusState{ + consensusState := &tendermint.ConsensusState{ Timestamp: header.GetTime(), Root: commitmenttypes.NewMerkleRoot([]byte{}), // empty bytes NextValidatorsHash: header.Header.NextValidatorsHash, } - tmHeader, ok := clientMessage.(*types.Header) + tmHeader, ok := clientMessage.(*tendermint.Header) suite.Require().True(ok) suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), path.EndpointA.ClientID, tmHeader.GetHeight(), consensusState) }, @@ -612,7 +611,7 @@ func (suite *TendermintTestSuite) TestCheckForMisbehaviour() { { "previous consensus state exists and header time is before previous consensus state time", func() { - header, ok := clientMessage.(*types.Header) + header, ok := clientMessage.(*tendermint.Header) suite.Require().True(ok) // offset header timestamp before previous consensus state timestamp @@ -623,7 +622,7 @@ func (suite *TendermintTestSuite) TestCheckForMisbehaviour() { { "next consensus state exists and header time is after next consensus state time", func() { - header, ok := clientMessage.(*types.Header) + header, ok := clientMessage.(*tendermint.Header) suite.Require().True(ok) // commit block and update client, adding a new consensus state @@ -653,7 +652,7 @@ func (suite *TendermintTestSuite) TestCheckForMisbehaviour() { // assign the same height, each header will have a different commit hash header1.Header.Height = header2.Header.Height - clientMessage = &types.Misbehaviour{ + clientMessage = &tendermint.Misbehaviour{ Header1: header1, Header2: header2, ClientId: path.EndpointA.ClientID, @@ -739,7 +738,7 @@ func (suite *TendermintTestSuite) TestUpdateStateOnMisbehaviour() { suite.Require().NotEmpty(clientStateBz) newClientState := clienttypes.MustUnmarshalClientState(suite.chainA.Codec, clientStateBz) - suite.Require().Equal(frozenHeight, newClientState.(*types.ClientState).FrozenHeight) + suite.Require().Equal(frozenHeight, newClientState.(*tendermint.ClientState).FrozenHeight) } }) } diff --git a/modules/light-clients/07-tendermint/types/upgrade.go b/modules/light-clients/07-tendermint/upgrade.go similarity index 99% rename from modules/light-clients/07-tendermint/types/upgrade.go rename to modules/light-clients/07-tendermint/upgrade.go index e295f8e4ba3..9d3e25ae5fa 100644 --- a/modules/light-clients/07-tendermint/types/upgrade.go +++ b/modules/light-clients/07-tendermint/upgrade.go @@ -1,4 +1,4 @@ -package types +package tendermint import ( "fmt" diff --git a/modules/light-clients/07-tendermint/types/upgrade_test.go b/modules/light-clients/07-tendermint/upgrade_test.go similarity index 93% rename from modules/light-clients/07-tendermint/types/upgrade_test.go rename to modules/light-clients/07-tendermint/upgrade_test.go index 218991fc4e9..601ab53d046 100644 --- a/modules/light-clients/07-tendermint/types/upgrade_test.go +++ b/modules/light-clients/07-tendermint/upgrade_test.go @@ -1,4 +1,4 @@ -package types_test +package tendermint_test import ( upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" @@ -6,7 +6,7 @@ import ( clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v3/modules/core/exported" - "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + tendermint "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v3/testing" ) @@ -54,7 +54,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { { name: "successful upgrade to same revision", setup: func() { - upgradedClient = types.NewClientState(suite.chainB.ChainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, clienttypes.NewHeight(clienttypes.ParseChainID(suite.chainB.ChainID), upgradedClient.GetLatestHeight().GetRevisionHeight()+10), commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + upgradedClient = tendermint.NewClientState(suite.chainB.ChainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, clienttypes.NewHeight(clienttypes.ParseChainID(suite.chainB.ChainID), upgradedClient.GetLatestHeight().GetRevisionHeight()+10), commitmenttypes.GetSDKSpecs(), upgradePath, false, false) upgradedClient = upgradedClient.ZeroCustomFields() upgradedClientBz, err = clienttypes.MarshalClientState(suite.chainA.App.AppCodec(), upgradedClient) suite.Require().NoError(err) @@ -109,7 +109,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { name: "unsuccessful upgrade: committed client does not have zeroed custom fields", setup: func() { // non-zeroed upgrade client - upgradedClient = types.NewClientState(newChainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + upgradedClient = tendermint.NewClientState(newChainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) upgradedClientBz, err = clienttypes.MarshalClientState(suite.chainA.App.AppCodec(), upgradedClient) suite.Require().NoError(err) @@ -145,7 +145,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) // change upgradedClient client-specified parameters - upgradedClient = types.NewClientState("wrongchainID", types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, true, true) + upgradedClient = tendermint.NewClientState("wrongchainID", tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, true, true) suite.coordinator.CommitBlock(suite.chainB) err := path.EndpointA.UpdateClient() @@ -167,7 +167,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) // change upgradedClient client-specified parameters - upgradedClient = types.NewClientState(newChainID, types.DefaultTrustLevel, ubdPeriod, ubdPeriod+trustingPeriod, maxClockDrift+5, lastHeight, commitmenttypes.GetSDKSpecs(), upgradePath, true, false) + upgradedClient = tendermint.NewClientState(newChainID, tendermint.DefaultTrustLevel, ubdPeriod, ubdPeriod+trustingPeriod, maxClockDrift+5, lastHeight, commitmenttypes.GetSDKSpecs(), upgradePath, true, false) suite.coordinator.CommitBlock(suite.chainB) err := path.EndpointA.UpdateClient() @@ -192,7 +192,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) // change submitted upgradedConsensusState - upgradedConsState = &types.ConsensusState{ + upgradedConsState = &tendermint.ConsensusState{ NextValidatorsHash: []byte("maliciousValidators"), } @@ -296,7 +296,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { proofUpgradedConsState, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) // SetClientState with empty upgrade path - tmClient, _ := cs.(*types.ClientState) + tmClient, _ := cs.(*tendermint.ClientState) tmClient.UpgradePath = []string{""} suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID, tmClient) }, @@ -398,7 +398,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { name: "unsuccessful upgrade: final client is not valid", setup: func() { // new client has smaller unbonding period such that old trusting period is no longer valid - upgradedClient = types.NewClientState(newChainID, types.DefaultTrustLevel, trustingPeriod, trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + upgradedClient = tendermint.NewClientState(newChainID, tendermint.DefaultTrustLevel, trustingPeriod, trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) upgradedClientBz, err = clienttypes.MarshalClientState(suite.chainA.App.AppCodec(), upgradedClient) suite.Require().NoError(err) @@ -434,19 +434,19 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { suite.coordinator.SetupClients(path) - clientState := path.EndpointA.GetClientState().(*types.ClientState) + clientState := path.EndpointA.GetClientState().(*tendermint.ClientState) revisionNumber := clienttypes.ParseChainID(clientState.ChainId) var err error newChainID, err = clienttypes.SetRevisionNumber(clientState.ChainId, revisionNumber+1) suite.Require().NoError(err) - upgradedClient = types.NewClientState(newChainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, clienttypes.NewHeight(revisionNumber+1, clientState.GetLatestHeight().GetRevisionHeight()+1), commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + upgradedClient = tendermint.NewClientState(newChainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, clienttypes.NewHeight(revisionNumber+1, clientState.GetLatestHeight().GetRevisionHeight()+1), commitmenttypes.GetSDKSpecs(), upgradePath, false, false) upgradedClient = upgradedClient.ZeroCustomFields() upgradedClientBz, err = clienttypes.MarshalClientState(suite.chainA.App.AppCodec(), upgradedClient) suite.Require().NoError(err) - upgradedConsState = &types.ConsensusState{ + upgradedConsState = &tendermint.ConsensusState{ NextValidatorsHash: []byte("nextValsHash"), } upgradedConsStateBz, err = clienttypes.MarshalConsensusState(suite.chainA.App.AppCodec(), upgradedConsState) diff --git a/proto/ibc/lightclients/solomachine/v2/solomachine.proto b/proto/ibc/lightclients/solomachine/v2/solomachine.proto index f4f36910eb2..5f0de59c6d7 100644 --- a/proto/ibc/lightclients/solomachine/v2/solomachine.proto +++ b/proto/ibc/lightclients/solomachine/v2/solomachine.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.lightclients.solomachine.v2; -option go_package = "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types"; +option go_package = "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine;solomachine"; import "ibc/core/connection/v1/connection.proto"; import "ibc/core/channel/v1/channel.proto"; diff --git a/proto/ibc/lightclients/tendermint/v1/tendermint.proto b/proto/ibc/lightclients/tendermint/v1/tendermint.proto index 7221f6502fa..89ab3595684 100644 --- a/proto/ibc/lightclients/tendermint/v1/tendermint.proto +++ b/proto/ibc/lightclients/tendermint/v1/tendermint.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.lightclients.tendermint.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types"; +option go_package = "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint;tendermint"; import "tendermint/types/validator.proto"; import "tendermint/types/types.proto"; diff --git a/testing/chain.go b/testing/chain.go index 769acd8f7d1..9b6dbf40590 100644 --- a/testing/chain.go +++ b/testing/chain.go @@ -30,7 +30,7 @@ import ( host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/exported" "github.com/cosmos/ibc-go/v3/modules/core/types" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" "github.com/cosmos/ibc-go/v3/testing/mock" "github.com/cosmos/ibc-go/v3/testing/simapp" ) diff --git a/testing/config.go b/testing/config.go index a47dea1701b..673e374b5ba 100644 --- a/testing/config.go +++ b/testing/config.go @@ -6,7 +6,7 @@ import ( connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" "github.com/cosmos/ibc-go/v3/testing/mock" ) diff --git a/testing/endpoint.go b/testing/endpoint.go index 093f657e5b6..d332ae97a6e 100644 --- a/testing/endpoint.go +++ b/testing/endpoint.go @@ -13,7 +13,7 @@ import ( commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" ) // Endpoint is a which represents a channel endpoint and its associated diff --git a/testing/solomachine.go b/testing/solomachine.go index 485fb75be0e..5eea01fbb19 100644 --- a/testing/solomachine.go +++ b/testing/solomachine.go @@ -16,7 +16,7 @@ import ( commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" host "github.com/cosmos/ibc-go/v3/modules/core/24-host" "github.com/cosmos/ibc-go/v3/modules/core/exported" - solomachinetypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine/types" + solomachinetypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" ) // Solomachine is a testing helper used to simulate a counterparty diff --git a/testing/values.go b/testing/values.go index 655a4731a74..3591711e24a 100644 --- a/testing/values.go +++ b/testing/values.go @@ -12,7 +12,7 @@ import ( ibctransfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" "github.com/cosmos/ibc-go/v3/testing/mock" "github.com/cosmos/ibc-go/v3/testing/simapp" ) From 04df7cda329d50faa0768bf0f032c290f5581f54 Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Fri, 15 Jul 2022 22:23:55 +0100 Subject: [PATCH 50/71] chore: adding upgrade handler for 09-localhost removal (#1671) * adding localhost migration code with tests * updating test cases * renaming test func * updating migration docs and moving to v4-to-v5.md * fixing indentation of code snippet * Update docs/migrations/v4-to-v5.md * adding sample query check for localhost client to docs * updating changelog to provide info on localhost upgrade handler * updating migrations docs with nit * renaming upgrades to migrations * updating function namings, tests and docs * Update CHANGELOG.md Co-authored-by: Carlos Rodriguez Co-authored-by: Carlos Rodriguez --- CHANGELOG.md | 2 +- docs/migrations/v3-to-v4.md | 46 +------ docs/migrations/v4-to-v5.md | 101 +++++++++++++++ modules/core/migrations/v5/migrations.go | 44 +++++++ modules/core/migrations/v5/migrations_test.go | 116 ++++++++++++++++++ 5 files changed, 263 insertions(+), 46 deletions(-) create mode 100644 docs/migrations/v4-to-v5.md create mode 100644 modules/core/migrations/v5/migrations.go create mode 100644 modules/core/migrations/v5/migrations_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index e9c0c464864..db0f3cdace0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,7 +57,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (modules/light-clients/07-tendermint) [\#1118](https://github.com/cosmos/ibc-go/pull/1118) Deprecating `AllowUpdateAfterExpiry and AllowUpdateAfterMisbehaviour`. See ADR-026 for context. * (modules/core/02-client) [\#1188](https://github.com/cosmos/ibc-go/pull/1188/files) Routing `MsgSubmitMisbehaviour` to `UpdateClient` keeper function. Deprecating `SubmitMisbehaviour` endpoint. * (modules/core/02-client) [\#1208](https://github.com/cosmos/ibc-go/pull/1208) Replace `CheckHeaderAndUpdateState` usage in 02-client with calls to `VerifyClientMessage`, `CheckForMisbehaviour`, `UpdateStateOnMisbehaviour` and `UpdateState`. -* (modules/light-clients/09-localhost) [\#1187](https://github.com/cosmos/ibc-go/pull/1187/) Removing localhost light client implementation as it is not functional. +* (modules/light-clients/09-localhost) [\#1187](https://github.com/cosmos/ibc-go/pull/1187/) Removing localhost light client implementation as it is not functional. An upgrade handler is provided in `modules/migrations/v5` to prune `09-localhost` clients and consensus states from the store. * [\#1186](https://github.com/cosmos/ibc-go/pull/1186/files) Removing `GetRoot` function from ConsensusState interface in `02-client`. `GetRoot` is unused by core IBC. * (modules/core/02-client) [\#1196](https://github.com/cosmos/ibc-go/pull/1196) Adding VerifyClientMessage to ClientState interface. * (modules/core/02-client) [\#1198](https://github.com/cosmos/ibc-go/pull/1198) Adding UpdateStateOnMisbehaviour to ClientState interface. diff --git a/docs/migrations/v3-to-v4.md b/docs/migrations/v3-to-v4.md index f558e25ab60..df7dedabbae 100644 --- a/docs/migrations/v3-to-v4.md +++ b/docs/migrations/v3-to-v4.md @@ -1,4 +1,4 @@ -# Migrating from ibc-go v2 to v3 +# Migrating from ibc-go v3 to v4 This document is intended to highlight significant changes which may require more information than presented in the CHANGELOG. Any changes that must be done by a user of ibc-go should be documented here. @@ -24,47 +24,3 @@ The `WriteAcknowledgement` API now takes the `exported.Acknowledgement` type ins This is an API breaking change and as such IBC application developers will have to update any calls to `WriteAcknowledgement`. ## IBC Light Clients - -### `ClientState` interface changes - -The `VerifyUpgradeAndUpdateState` function has been modified. The client state and consensus state return values have been removed. - -Light clients **must** handle all management of client and consensus states including the setting of updated client state and consensus state in the client store. - -The `CheckHeaderAndUpdateState` function has been split into 4 new functions: - -- `VerifyClientMessage` verifies a `ClientMessage`. A `ClientMessage` could be a `Header`, `Misbehaviour`, or batch update. Calls to `CheckForMisbehaviour`, `UpdateState`, and `UpdateStateOnMisbehaviour` will assume that the content of the `ClientMessage` has been verified and can be trusted. An error should be returned if the `ClientMessage` fails to verify. - -- `CheckForMisbehaviour` checks for evidence of a misbehaviour in `Header` or `Misbehaviour` types. - -- `UpdateStateOnMisbehaviour` performs appropriate state changes on a `ClientState` given that misbehaviour has been detected and verified. - -- `UpdateState` updates and stores as necessary any associated information for an IBC client, such as the `ClientState` and corresponding `ConsensusState`. An error is returned if `ClientMessage` is of type `Misbehaviour`. Upon successful update, a list containing the updated consensus state height is returned. - -The `CheckMisbehaviourAndUpdateState` function has been removed from `ClientState` interface. This functionality is now encapsulated by the usage of `VerifyClientMessage`, `CheckForMisbehaviour`, `UpdateStateOnMisbehaviour`, `UpdateState`. - -The function `GetTimestampAtHeight` has been added to the `ClientState` interface. It should return the timestamp for a consensus state associated with the provided height. - -### `Header` and `Misbehaviour` - -`exported.Header` and `exported.Misbehaviour` interface types have been merged and renamed to `ClientMessage` interface. - -`GetHeight` function has been removed from `exported.Header` and thus is not included in the `ClientMessage` interface - -### `ConsensusState` - -The `GetRoot` function has been removed from consensus state interface since it was not used by core IBC. - -### Light client implementations - -09-localhost light client implementation has been removed because it is currently non-functional. - -### Client Keeper - -Keeper function `CheckMisbehaviourAndUpdateState` has been removed since function `UpdateClient` can now handle updating `ClientState` on `ClientMessage` type which can be any `Misbehaviour` implementations. - -### SDK Message - -`MsgSubmitMisbehaviour` is deprecated since `MsgUpdateClient` can now submit a `ClientMessage` type which can be any `Misbehaviour` implementations. - -The field `header` in `MsgUpdateClient` has been renamed to `client message`. diff --git a/docs/migrations/v4-to-v5.md b/docs/migrations/v4-to-v5.md new file mode 100644 index 00000000000..a02e953f984 --- /dev/null +++ b/docs/migrations/v4-to-v5.md @@ -0,0 +1,101 @@ +# Migrating from ibc-go v4 to v5 + +This document is intended to highlight significant changes which may require more information than presented in the CHANGELOG. +Any changes that must be done by a user of ibc-go should be documented here. + +There are four sections based on the four potential user groups of this document: +- Chains +- IBC Apps +- Relayers +- IBC Light Clients + +**Note:** ibc-go supports golang semantic versioning and therefore all imports must be updated to bump the version number on major releases. + +## Chains + +- No relevant changes were made in this release. + +## IBC Apps + +- No relevant changes were made in this release. + +## Relayers + +- No relevant changes were made in this release. + +## IBC Light Clients + +### `ClientState` interface changes + +The `VerifyUpgradeAndUpdateState` function has been modified. The client state and consensus state return values have been removed. + +Light clients **must** handle all management of client and consensus states including the setting of updated client state and consensus state in the client store. + +The `CheckHeaderAndUpdateState` function has been split into 4 new functions: + +- `VerifyClientMessage` verifies a `ClientMessage`. A `ClientMessage` could be a `Header`, `Misbehaviour`, or batch update. Calls to `CheckForMisbehaviour`, `UpdateState`, and `UpdateStateOnMisbehaviour` will assume that the content of the `ClientMessage` has been verified and can be trusted. An error should be returned if the `ClientMessage` fails to verify. + +- `CheckForMisbehaviour` checks for evidence of a misbehaviour in `Header` or `Misbehaviour` types. + +- `UpdateStateOnMisbehaviour` performs appropriate state changes on a `ClientState` given that misbehaviour has been detected and verified. + +- `UpdateState` updates and stores as necessary any associated information for an IBC client, such as the `ClientState` and corresponding `ConsensusState`. An error is returned if `ClientMessage` is of type `Misbehaviour`. Upon successful update, a list containing the updated consensus state height is returned. + +The `CheckMisbehaviourAndUpdateState` function has been removed from `ClientState` interface. This functionality is now encapsulated by the usage of `VerifyClientMessage`, `CheckForMisbehaviour`, `UpdateStateOnMisbehaviour`, `UpdateState`. + +The function `GetTimestampAtHeight` has been added to the `ClientState` interface. It should return the timestamp for a consensus state associated with the provided height. + +### `Header` and `Misbehaviour` + +`exported.Header` and `exported.Misbehaviour` interface types have been merged and renamed to `ClientMessage` interface. + +`GetHeight` function has been removed from `exported.Header` and thus is not included in the `ClientMessage` interface + +### `ConsensusState` + +The `GetRoot` function has been removed from consensus state interface since it was not used by core IBC. + +### Light client implementations + +The `09-localhost` light client implementation has been removed because it is currently non-functional. + +An upgrade handler has been added to supply chain developers with the logic needed to prune the ibc client store and successfully complete the removal of `09-localhost`. +Add the following to the application upgrade handler in `app/app.go`, calling `MigrateToV5` to perform store migration logic. + +```go +import ( + // ... + ibcv5 "github.com/cosmos/ibc-go/v5/modules/core/migrations/v5" +) + +// ... + +app.UpgradeKeeper.SetUpgradeHandler( + upgradeName, + func(ctx sdk.Context, _ upgradetypes.Plan, _ module.VersionMap) (module.VersionMap, error) { + // prune the 09-localhost client from the ibc client store + ibcv5.MigrateToV5(ctx, app.IBCKeeper.ClientKeeper) + + return app.mm.RunMigrations(ctx, app.configurator, fromVM) + }, +) +``` + +Please note the above upgrade handler is optional and should only be run if chains have an existing `09-localhost` client stored in state. +A simple query can be performed to check for a `09-localhost` client on chain. + +For example: + +``` +simd query ibc client states | grep 09-localhost +``` + +### Client Keeper + +Keeper function `CheckMisbehaviourAndUpdateState` has been removed since function `UpdateClient` can now handle updating `ClientState` on `ClientMessage` type which can be any `Misbehaviour` implementations. + +### SDK Message + +`MsgSubmitMisbehaviour` is deprecated since `MsgUpdateClient` can now submit a `ClientMessage` type which can be any `Misbehaviour` implementations. + +The field `header` in `MsgUpdateClient` has been renamed to `client_message`. diff --git a/modules/core/migrations/v5/migrations.go b/modules/core/migrations/v5/migrations.go new file mode 100644 index 00000000000..c08992cf7d3 --- /dev/null +++ b/modules/core/migrations/v5/migrations.go @@ -0,0 +1,44 @@ +package v5 + +import ( + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" + + clientkeeper "github.com/cosmos/ibc-go/v3/modules/core/02-client/keeper" + clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + "github.com/cosmos/ibc-go/v3/modules/core/exported" +) + +// Localhost is the client type for a localhost client. It is also used as the clientID +// for the localhost client. +const Localhost string = "09-localhost" + +// MigrateToV5 prunes the 09-Localhost client and associated consensus states from the ibc store +func MigrateToV5(ctx sdk.Context, clientKeeper clientkeeper.Keeper) { + clientStore := clientKeeper.ClientStore(ctx, Localhost) + + iterator := sdk.KVStorePrefixIterator(clientStore, []byte(host.KeyConsensusStatePrefix)) + var heights []exported.Height + + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + keySplit := strings.Split(string(iterator.Key()), "/") + // key is in the format "consensusStates/" + if len(keySplit) != 2 || keySplit[0] != string(host.KeyConsensusStatePrefix) { + continue + } + + // collect consensus states to be pruned + heights = append(heights, clienttypes.MustParseHeight(keySplit[1])) + } + + // delete all consensus states + for _, height := range heights { + clientStore.Delete(host.ConsensusStateKey(height)) + } + + // delete the client state + clientStore.Delete(host.ClientStateKey()) +} diff --git a/modules/core/migrations/v5/migrations_test.go b/modules/core/migrations/v5/migrations_test.go new file mode 100644 index 00000000000..0c81e5c973d --- /dev/null +++ b/modules/core/migrations/v5/migrations_test.go @@ -0,0 +1,116 @@ +package v5_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/suite" + + clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + "github.com/cosmos/ibc-go/v3/modules/core/exported" + v5 "github.com/cosmos/ibc-go/v3/modules/core/migrations/v5" + ibctesting "github.com/cosmos/ibc-go/v3/testing" +) + +type MigrationsV5TestSuite struct { + suite.Suite + + coordinator *ibctesting.Coordinator + + chainA *ibctesting.TestChain + chainB *ibctesting.TestChain +} + +func (suite *MigrationsV5TestSuite) SetupTest() { + suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) + + suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1)) + suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2)) +} + +func TestIBCTestSuite(t *testing.T) { + suite.Run(t, new(MigrationsV5TestSuite)) +} + +func (suite *MigrationsV5TestSuite) TestMigrateToV5() { + var clientStore sdk.KVStore + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success: prune localhost client state", + func() { + clientStore.Set(host.ClientStateKey(), []byte("clientState")) + }, + true, + }, + { + "success: prune localhost client state and consensus states", + func() { + clientStore.Set(host.ClientStateKey(), []byte("clientState")) + + for i := 0; i < 10; i++ { + clientStore.Set(host.ConsensusStateKey(clienttypes.NewHeight(1, uint64(i))), []byte("consensusState")) + } + }, + true, + }, + { + "07-tendermint client state and consensus states remain in client store", + func() { + clientStore = suite.chainA.GetSimApp().IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clienttypes.FormatClientIdentifier(exported.Tendermint, 0)) + clientStore.Set(host.ClientStateKey(), []byte("clientState")) + + for i := 0; i < 10; i++ { + clientStore.Set(host.ConsensusStateKey(clienttypes.NewHeight(1, uint64(i))), []byte("consensusState")) + } + }, + false, + }, + { + "06-solomachine client state and consensus states remain in client store", + func() { + clientStore = suite.chainA.GetSimApp().IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clienttypes.FormatClientIdentifier(exported.Solomachine, 0)) + clientStore.Set(host.ClientStateKey(), []byte("clientState")) + + for i := 0; i < 10; i++ { + clientStore.Set(host.ConsensusStateKey(clienttypes.NewHeight(1, uint64(i))), []byte("consensusState")) + } + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() // reset + + ctx := suite.chainA.GetContext() + clientStore = suite.chainA.GetSimApp().IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), v5.Localhost) + + tc.malleate() + + v5.MigrateToV5(ctx, suite.chainA.GetSimApp().IBCKeeper.ClientKeeper) + + if tc.expPass { + suite.Require().False(clientStore.Has(host.ClientStateKey())) + + for i := 0; i < 10; i++ { + suite.Require().False(clientStore.Has(host.ConsensusStateKey(clienttypes.NewHeight(1, uint64(i))))) + } + } else { + suite.Require().True(clientStore.Has(host.ClientStateKey())) + + for i := 0; i < 10; i++ { + suite.Require().True(clientStore.Has(host.ConsensusStateKey(clienttypes.NewHeight(1, uint64(i))))) + } + } + }) + } +} From d8ac28aab8c29d1171199eaa5bdad57701eb5b6d Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Wed, 27 Jul 2022 10:50:37 +0100 Subject: [PATCH 51/71] Emit event upon setting upgrade consensus state (#1741) --- CHANGELOG.md | 3 +- modules/core/02-client/abci.go | 1 + modules/core/02-client/abci_test.go | 56 ++++++++++++++++++++++++- modules/core/02-client/keeper/events.go | 13 ++++++ modules/core/02-client/types/events.go | 15 ++++--- 5 files changed, 80 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db0f3cdace0..c670b35a8c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -66,7 +66,8 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (modules/core/02-client) [\#1195](https://github.com/cosmos/ibc-go/pull/1210) Removing `CheckHeaderAndUpdateState` from `ClientState` interface & associated light client implementations. * (modules/core/02-client) [\#1189](https://github.com/cosmos/ibc-go/pull/1212) Removing `CheckMisbehaviourAndUpdateState` from `ClientState` interface & associated light client implementations. * (modules/core/exported) [\#1206](https://github.com/cosmos/ibc-go/pull/1206) Adding new method `UpdateState` to `ClientState` interface. -* (testing) [\#1302](https://github.com/cosmos/ibc-go/pull/1302) Change testchain default behaviour to use a chainID in the revision format. Set `ChainIDSuffix` to an empty string to disable this functionality. +* (testing) [\#1302](https://github.com/cosmos/ibc-go/pull/1302) Change testchain default behaviour to use a chainID in the revision format. Set `ChainIDSuffix` to an empty string to disable this functionality. +* (modules/core/02-client) [\#1741](https://github.com/cosmos/ibc-go/pull/1741) Emitting a new `upgrade_chain` event upon setting upgrade consensus state. ### Features diff --git a/modules/core/02-client/abci.go b/modules/core/02-client/abci.go index f32fd129309..0fb1cb5233a 100644 --- a/modules/core/02-client/abci.go +++ b/modules/core/02-client/abci.go @@ -26,6 +26,7 @@ func BeginBlocker(ctx sdk.Context, k keeper.Keeper) { bz := k.MustMarshalConsensusState(upgradedConsState) k.SetUpgradedConsensusState(ctx, plan.Height, bz) + keeper.EmitUpgradeChainEvent(ctx, plan.Height) } } } diff --git a/modules/core/02-client/abci_test.go b/modules/core/02-client/abci_test.go index 16aee80088a..72e9fbe44dd 100644 --- a/modules/core/02-client/abci_test.go +++ b/modules/core/02-client/abci_test.go @@ -1,13 +1,15 @@ package client_test import ( + "strings" "testing" + sdk "github.com/cosmos/cosmos-sdk/types" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" "github.com/stretchr/testify/suite" abci "github.com/tendermint/tendermint/abci/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" client "github.com/cosmos/ibc-go/v3/modules/core/02-client" "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" @@ -74,3 +76,55 @@ func (suite *ClientTestSuite) TestBeginBlockerConsensusState() { suite.Require().NoError(err) suite.Require().Equal(bz, consState) } + +func (suite *ClientTestSuite) TestBeginBlockerUpgradeEvents() { + plan := &upgradetypes.Plan{ + Name: "test", + Height: suite.chainA.GetContext().BlockHeight() + 1, + } + // set upgrade plan in the upgrade store + store := suite.chainA.GetContext().KVStore(suite.chainA.GetSimApp().GetKey(upgradetypes.StoreKey)) + bz := suite.chainA.App.AppCodec().MustMarshal(plan) + store.Set(upgradetypes.PlanKey(), bz) + + nextValsHash := []byte("nextValsHash") + newCtx := suite.chainA.GetContext().WithBlockHeader(tmproto.Header{ + Height: suite.chainA.GetContext().BlockHeight(), + NextValidatorsHash: nextValsHash, + }) + + err := suite.chainA.GetSimApp().UpgradeKeeper.SetUpgradedClient(newCtx, plan.Height, []byte("client state")) + suite.Require().NoError(err) + + cacheCtx, writeCache := suite.chainA.GetContext().CacheContext() + + client.BeginBlocker(cacheCtx, suite.chainA.App.GetIBCKeeper().ClientKeeper) + writeCache() + + suite.requireContainsEvent(cacheCtx.EventManager().Events(), types.EventTypeUpgradeChain, true) +} + +func (suite *ClientTestSuite) TestBeginBlockerUpgradeEventsAbsence() { + cacheCtx, writeCache := suite.chainA.GetContext().CacheContext() + client.BeginBlocker(suite.chainA.GetContext(), suite.chainA.App.GetIBCKeeper().ClientKeeper) + writeCache() + suite.requireContainsEvent(cacheCtx.EventManager().Events(), types.EventTypeUpgradeChain, false) +} + +// requireContainsEvent verifies if an event of a specific type was emitted. +func (suite *ClientTestSuite) requireContainsEvent(events sdk.Events, eventType string, shouldContain bool) { + found := false + var eventTypes []string + for _, e := range events { + eventTypes = append(eventTypes, e.Type) + if e.Type == eventType { + found = true + break + } + } + if shouldContain { + suite.Require().True(found, "event type %s was not found in %s", eventType, strings.Join(eventTypes, ",")) + } else { + suite.Require().False(found, "event type %s was found in %s", eventType, strings.Join(eventTypes, ",")) + } +} diff --git a/modules/core/02-client/keeper/events.go b/modules/core/02-client/keeper/events.go index ec15ca41f4e..56c537b224b 100644 --- a/modules/core/02-client/keeper/events.go +++ b/modules/core/02-client/keeper/events.go @@ -2,10 +2,12 @@ package keeper import ( "encoding/hex" + "strconv" "strings" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" "github.com/cosmos/ibc-go/v3/modules/core/exported" @@ -101,3 +103,14 @@ func EmitSubmitMisbehaviourEvent(ctx sdk.Context, clientID string, clientState e ), ) } + +// EmitUpgradeChainEvent emits an upgrade chain event. +func EmitUpgradeChainEvent(ctx sdk.Context, height int64) { + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeUpgradeChain, + sdk.NewAttribute(types.AttributeKeyUpgradePlanHeight, strconv.FormatInt(height, 10)), + sdk.NewAttribute(types.AttributeKeyUpgradeStore, upgradetypes.StoreKey), // which store to query proof of consensus state from + ), + }) +} diff --git a/modules/core/02-client/types/events.go b/modules/core/02-client/types/events.go index 05708a84859..6975bcd6e66 100644 --- a/modules/core/02-client/types/events.go +++ b/modules/core/02-client/types/events.go @@ -8,12 +8,14 @@ import ( // IBC client events const ( - AttributeKeyClientID = "client_id" - AttributeKeySubjectClientID = "subject_client_id" - AttributeKeyClientType = "client_type" - AttributeKeyConsensusHeight = "consensus_height" - AttributeKeyConsensusHeights = "consensus_heights" - AttributeKeyHeader = "header" + AttributeKeyClientID = "client_id" + AttributeKeySubjectClientID = "subject_client_id" + AttributeKeyClientType = "client_type" + AttributeKeyConsensusHeight = "consensus_height" + AttributeKeyConsensusHeights = "consensus_heights" + AttributeKeyHeader = "header" + AttributeKeyUpgradeStore = "upgrade_store" + AttributeKeyUpgradePlanHeight = "upgrade_plan_height" ) // IBC client events vars @@ -23,6 +25,7 @@ var ( EventTypeUpgradeClient = "upgrade_client" EventTypeSubmitMisbehaviour = "client_misbehaviour" EventTypeUpdateClientProposal = "update_client_proposal" + EventTypeUpgradeChain = "upgrade_chain" AttributeValueCategory = fmt.Sprintf("%s_%s", host.ModuleName, SubModuleName) ) From d3d91ed69e7f57e84581290607da8fc2b1cece9d Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Fri, 29 Jul 2022 11:27:06 +0200 Subject: [PATCH 52/71] chore: removing GetHeight legacy method from 06-solomachine (#1810) --- modules/light-clients/06-solomachine/misbehaviour.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/modules/light-clients/06-solomachine/misbehaviour.go b/modules/light-clients/06-solomachine/misbehaviour.go index 145841e4b0e..430ed61c9be 100644 --- a/modules/light-clients/06-solomachine/misbehaviour.go +++ b/modules/light-clients/06-solomachine/misbehaviour.go @@ -70,9 +70,3 @@ func (sd SignatureAndData) ValidateBasic() error { return nil } - -// TODO: Remove GetHeight() -// GetHeight implements the curret exported.Header interface, to be updated -func (misbehaviour Misbehaviour) GetHeight() exported.Height { - return nil -} From b0a58a80c04024766c149af27bd0a25186a4f00f Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Tue, 2 Aug 2022 17:30:34 +0200 Subject: [PATCH 53/71] refactor: solomachine generic verification methods and signbytes simplification (#1687) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * adding new SignBytes type, generic membership verification implementation and tests * adding protodocs * updating comment * refactor: solomachine misbehaviour checking (#1715) * adding SignatureAndDataV2 proto message type * updating misbehaviour checking * removing dead solomachine code (#1716) * updating tests with concrete ibc core types * refactor: solomachine generic VerifyNonMembership (#1720) * adding verification of non-membership with tests * refactor common code to produceVerificationArgs * removing unused produce args func * Update modules/light-clients/06-solomachine/client_state_test.go Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> * removing V2 suffix from SignBytes and SignatureAndData types * use current diversifier when verifying header details * Add test for new diversifier for solomachine (#1860) * add test for successful new diversifier * add changelog entry * fix tests * restoring solomachine/v2 protos, updadting v2 codegen path and adding solomachine/v3 protobuf defs * adding changelog entries Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com> --- CHANGELOG.md | 3 + docs/ibc/proto-docs.md | 175 +- .../02-client/migrations/v6/solomachine.pb.go | 4127 +++++++++++++++++ modules/core/02-client/types/codec_test.go | 2 +- modules/core/02-client/types/msgs_test.go | 5 +- .../06-solomachine/client_state.go | 361 +- .../06-solomachine/client_state_test.go | 1102 +++-- modules/light-clients/06-solomachine/codec.go | 89 - .../06-solomachine/codec_test.go | 190 - .../light-clients/06-solomachine/errors.go | 1 - .../06-solomachine/header_test.go | 2 +- .../06-solomachine/misbehaviour.go | 4 +- .../06-solomachine/misbehaviour_handle.go | 23 +- .../06-solomachine/misbehaviour_test.go | 8 +- modules/light-clients/06-solomachine/proof.go | 433 -- .../06-solomachine/proof_test.go | 35 - .../06-solomachine/solomachine.go | 12 - .../06-solomachine/solomachine.pb.go | 3024 +++--------- .../light-clients/06-solomachine/update.go | 20 +- .../06-solomachine/update_test.go | 41 +- .../solomachine/v2/solomachine.proto | 14 +- .../solomachine/v3/solomachine.proto | 103 + testing/sdk_test.go | 279 -- testing/solomachine.go | 40 +- 24 files changed, 5718 insertions(+), 4375 deletions(-) create mode 100644 modules/core/02-client/migrations/v6/solomachine.pb.go delete mode 100644 modules/light-clients/06-solomachine/codec_test.go create mode 100644 proto/ibc/lightclients/solomachine/v3/solomachine.proto delete mode 100644 testing/sdk_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index c670b35a8c9..176830f33d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,8 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (transfer) [\#1250](https://github.com/cosmos/ibc-go/pull/1250) Deprecate `GetTransferAccount` since the `transfer` module account is never used. * (06-solomachine) [\#1679](https://github.com/cosmos/ibc-go/pull/1679) Remove `types` sub-package from `06-solomachine` lightclient directory. * (07-tendermint) [\#1677](https://github.com/cosmos/ibc-go/pull/1677) Remove `types` sub-package from `07-tendermint` lightclient directory. +* (06-solomachine) [\#1687](https://github.com/cosmos/ibc-go/pull/1687) Bump `06-solomachine` protobuf version from `v2` to `v3`. +* (06-solomachine) [\#1687](https://github.com/cosmos/ibc-go/pull/1687) Removed `DataType` enum and associated message types from `06-solomachine`. `DataType` has been removed from `SignBytes` and `SignatureAndData` in favour of `path`. ### State Machine Breaking @@ -79,6 +81,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Bug Fixes +* (light-clients/solomachine) [#1839](https://github.com/cosmos/ibc-go/issues/1839) Fixed usage of the new diversifier in validation of changing diversifiers for the solo machine. The current diversifier must sign over the new diversifier. * (modules/core/04-channel) [\#1130](https://github.com/cosmos/ibc-go/pull/1130) Call `packet.GetSequence()` rather than passing func in `WriteAcknowledgement` log output * (apps/29-fee) [\#1278](https://github.com/cosmos/ibc-go/pull/1278) The URI path for the query to get all incentivized packets for a specifc channel did not follow the same format as the rest of queries. * (07-tendermint) [\#1674](https://github.com/cosmos/ibc-go/pull/1674) Submitted ClientState is zeroed out before checking the proof in order to prevent the proposal from containing information governance is not actually voting on. diff --git a/docs/ibc/proto-docs.md b/docs/ibc/proto-docs.md index 523958ffe83..4cde071460e 100644 --- a/docs/ibc/proto-docs.md +++ b/docs/ibc/proto-docs.md @@ -326,6 +326,16 @@ - [DataType](#ibc.lightclients.solomachine.v2.DataType) +- [ibc/lightclients/solomachine/v3/solomachine.proto](#ibc/lightclients/solomachine/v3/solomachine.proto) + - [ClientState](#ibc.lightclients.solomachine.v3.ClientState) + - [ConsensusState](#ibc.lightclients.solomachine.v3.ConsensusState) + - [Header](#ibc.lightclients.solomachine.v3.Header) + - [HeaderData](#ibc.lightclients.solomachine.v3.HeaderData) + - [Misbehaviour](#ibc.lightclients.solomachine.v3.Misbehaviour) + - [SignBytes](#ibc.lightclients.solomachine.v3.SignBytes) + - [SignatureAndData](#ibc.lightclients.solomachine.v3.SignatureAndData) + - [TimestampedSignatureData](#ibc.lightclients.solomachine.v3.TimestampedSignatureData) + - [ibc/lightclients/tendermint/v1/tendermint.proto](#ibc/lightclients/tendermint/v1/tendermint.proto) - [ClientState](#ibc.lightclients.tendermint.v1.ClientState) - [ConsensusState](#ibc.lightclients.tendermint.v1.ConsensusState) @@ -4641,7 +4651,7 @@ of a sequence and two signatures over different messages at that sequence. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `client_id` | [string](#string) | | **Deprecated.** ClientID is deprecated | +| `client_id` | [string](#string) | | | | `sequence` | [uint64](#uint64) | | | | `signature_one` | [SignatureAndData](#ibc.lightclients.solomachine.v2.SignatureAndData) | | | | `signature_two` | [SignatureAndData](#ibc.lightclients.solomachine.v2.SignatureAndData) | | | @@ -4803,6 +4813,169 @@ to preserve uniqueness of different data sign byte encodings. + +

    Top

    + +## ibc/lightclients/solomachine/v3/solomachine.proto + + + + + +### ClientState +ClientState defines a solo machine client that tracks the current consensus +state and if the client is frozen. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `sequence` | [uint64](#uint64) | | latest sequence of the client state | +| `is_frozen` | [bool](#bool) | | frozen sequence of the solo machine | +| `consensus_state` | [ConsensusState](#ibc.lightclients.solomachine.v3.ConsensusState) | | | +| `allow_update_after_proposal` | [bool](#bool) | | when set to true, will allow governance to update a solo machine client. The client will be unfrozen if it is frozen. | + + + + + + + + +### ConsensusState +ConsensusState defines a solo machine consensus state. The sequence of a +consensus state is contained in the "height" key used in storing the +consensus state. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `public_key` | [google.protobuf.Any](#google.protobuf.Any) | | public key of the solo machine | +| `diversifier` | [string](#string) | | diversifier allows the same public key to be re-used across different solo machine clients (potentially on different chains) without being considered misbehaviour. | +| `timestamp` | [uint64](#uint64) | | | + + + + + + + + +### Header +Header defines a solo machine consensus header + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `sequence` | [uint64](#uint64) | | sequence to update solo machine public key at | +| `timestamp` | [uint64](#uint64) | | | +| `signature` | [bytes](#bytes) | | | +| `new_public_key` | [google.protobuf.Any](#google.protobuf.Any) | | | +| `new_diversifier` | [string](#string) | | | + + + + + + + + +### HeaderData +HeaderData returns the SignBytes data for update verification. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `new_pub_key` | [google.protobuf.Any](#google.protobuf.Any) | | header public key | +| `new_diversifier` | [string](#string) | | header diversifier | + + + + + + + + +### Misbehaviour +Misbehaviour defines misbehaviour for a solo machine which consists +of a sequence and two signatures over different messages at that sequence. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `client_id` | [string](#string) | | **Deprecated.** ClientID is deprecated | +| `sequence` | [uint64](#uint64) | | | +| `signature_one` | [SignatureAndData](#ibc.lightclients.solomachine.v3.SignatureAndData) | | | +| `signature_two` | [SignatureAndData](#ibc.lightclients.solomachine.v3.SignatureAndData) | | | + + + + + + + + +### SignBytes +SignBytes defines the signed bytes used for signature verification. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `sequence` | [uint64](#uint64) | | the sequence number | +| `timestamp` | [uint64](#uint64) | | the proof timestamp | +| `diversifier` | [string](#string) | | the public key diversifier | +| `path` | [bytes](#bytes) | | the standardised path bytes | +| `data` | [bytes](#bytes) | | the marshaled data bytes | + + + + + + + + +### SignatureAndData +SignatureAndData contains a signature and the data signed over to create that +signature. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `signature` | [bytes](#bytes) | | | +| `path` | [bytes](#bytes) | | | +| `data` | [bytes](#bytes) | | | +| `timestamp` | [uint64](#uint64) | | | + + + + + + + + +### TimestampedSignatureData +TimestampedSignatureData contains the signature data and the timestamp of the +signature. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `signature_data` | [bytes](#bytes) | | | +| `timestamp` | [uint64](#uint64) | | | + + + + + + + + + + + + + + +

    Top

    diff --git a/modules/core/02-client/migrations/v6/solomachine.pb.go b/modules/core/02-client/migrations/v6/solomachine.pb.go new file mode 100644 index 00000000000..c92f04029f2 --- /dev/null +++ b/modules/core/02-client/migrations/v6/solomachine.pb.go @@ -0,0 +1,4127 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/lightclients/solomachine/v2/solomachine.proto + +package v6 + +import ( + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/codec/types" + types1 "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" + types2 "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// DataType defines the type of solo machine proof being created. This is done +// to preserve uniqueness of different data sign byte encodings. +type DataType int32 + +const ( + // Default State + UNSPECIFIED DataType = 0 + // Data type for client state verification + CLIENT DataType = 1 + // Data type for consensus state verification + CONSENSUS DataType = 2 + // Data type for connection state verification + CONNECTION DataType = 3 + // Data type for channel state verification + CHANNEL DataType = 4 + // Data type for packet commitment verification + PACKETCOMMITMENT DataType = 5 + // Data type for packet acknowledgement verification + PACKETACKNOWLEDGEMENT DataType = 6 + // Data type for packet receipt absence verification + PACKETRECEIPTABSENCE DataType = 7 + // Data type for next sequence recv verification + NEXTSEQUENCERECV DataType = 8 + // Data type for header verification + HEADER DataType = 9 +) + +var DataType_name = map[int32]string{ + 0: "DATA_TYPE_UNINITIALIZED_UNSPECIFIED", + 1: "DATA_TYPE_CLIENT_STATE", + 2: "DATA_TYPE_CONSENSUS_STATE", + 3: "DATA_TYPE_CONNECTION_STATE", + 4: "DATA_TYPE_CHANNEL_STATE", + 5: "DATA_TYPE_PACKET_COMMITMENT", + 6: "DATA_TYPE_PACKET_ACKNOWLEDGEMENT", + 7: "DATA_TYPE_PACKET_RECEIPT_ABSENCE", + 8: "DATA_TYPE_NEXT_SEQUENCE_RECV", + 9: "DATA_TYPE_HEADER", +} + +var DataType_value = map[string]int32{ + "DATA_TYPE_UNINITIALIZED_UNSPECIFIED": 0, + "DATA_TYPE_CLIENT_STATE": 1, + "DATA_TYPE_CONSENSUS_STATE": 2, + "DATA_TYPE_CONNECTION_STATE": 3, + "DATA_TYPE_CHANNEL_STATE": 4, + "DATA_TYPE_PACKET_COMMITMENT": 5, + "DATA_TYPE_PACKET_ACKNOWLEDGEMENT": 6, + "DATA_TYPE_PACKET_RECEIPT_ABSENCE": 7, + "DATA_TYPE_NEXT_SEQUENCE_RECV": 8, + "DATA_TYPE_HEADER": 9, +} + +func (x DataType) String() string { + return proto.EnumName(DataType_name, int32(x)) +} + +func (DataType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_141333b361aae010, []int{0} +} + +// ClientState defines a solo machine client that tracks the current consensus +// state and if the client is frozen. +type ClientState struct { + // latest sequence of the client state + Sequence uint64 `protobuf:"varint,1,opt,name=sequence,proto3" json:"sequence,omitempty"` + // frozen sequence of the solo machine + IsFrozen bool `protobuf:"varint,2,opt,name=is_frozen,json=isFrozen,proto3" json:"is_frozen,omitempty" yaml:"is_frozen"` + ConsensusState *ConsensusState `protobuf:"bytes,3,opt,name=consensus_state,json=consensusState,proto3" json:"consensus_state,omitempty" yaml:"consensus_state"` + // when set to true, will allow governance to update a solo machine client. + // The client will be unfrozen if it is frozen. + AllowUpdateAfterProposal bool `protobuf:"varint,4,opt,name=allow_update_after_proposal,json=allowUpdateAfterProposal,proto3" json:"allow_update_after_proposal,omitempty" yaml:"allow_update_after_proposal"` +} + +func (m *ClientState) Reset() { *m = ClientState{} } +func (m *ClientState) String() string { return proto.CompactTextString(m) } +func (*ClientState) ProtoMessage() {} +func (*ClientState) Descriptor() ([]byte, []int) { + return fileDescriptor_141333b361aae010, []int{0} +} +func (m *ClientState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ClientState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ClientState.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 *ClientState) XXX_Merge(src proto.Message) { + xxx_messageInfo_ClientState.Merge(m, src) +} +func (m *ClientState) XXX_Size() int { + return m.Size() +} +func (m *ClientState) XXX_DiscardUnknown() { + xxx_messageInfo_ClientState.DiscardUnknown(m) +} + +var xxx_messageInfo_ClientState proto.InternalMessageInfo + +// ConsensusState defines a solo machine consensus state. The sequence of a +// consensus state is contained in the "height" key used in storing the +// consensus state. +type ConsensusState struct { + // public key of the solo machine + PublicKey *types.Any `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty" yaml:"public_key"` + // diversifier allows the same public key to be re-used across different solo + // machine clients (potentially on different chains) without being considered + // misbehaviour. + Diversifier string `protobuf:"bytes,2,opt,name=diversifier,proto3" json:"diversifier,omitempty"` + Timestamp uint64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` +} + +func (m *ConsensusState) Reset() { *m = ConsensusState{} } +func (m *ConsensusState) String() string { return proto.CompactTextString(m) } +func (*ConsensusState) ProtoMessage() {} +func (*ConsensusState) Descriptor() ([]byte, []int) { + return fileDescriptor_141333b361aae010, []int{1} +} +func (m *ConsensusState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ConsensusState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ConsensusState.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 *ConsensusState) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConsensusState.Merge(m, src) +} +func (m *ConsensusState) XXX_Size() int { + return m.Size() +} +func (m *ConsensusState) XXX_DiscardUnknown() { + xxx_messageInfo_ConsensusState.DiscardUnknown(m) +} + +var xxx_messageInfo_ConsensusState proto.InternalMessageInfo + +// Header defines a solo machine consensus header +type Header struct { + // sequence to update solo machine public key at + Sequence uint64 `protobuf:"varint,1,opt,name=sequence,proto3" json:"sequence,omitempty"` + Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Signature []byte `protobuf:"bytes,3,opt,name=signature,proto3" json:"signature,omitempty"` + NewPublicKey *types.Any `protobuf:"bytes,4,opt,name=new_public_key,json=newPublicKey,proto3" json:"new_public_key,omitempty" yaml:"new_public_key"` + NewDiversifier string `protobuf:"bytes,5,opt,name=new_diversifier,json=newDiversifier,proto3" json:"new_diversifier,omitempty" yaml:"new_diversifier"` +} + +func (m *Header) Reset() { *m = Header{} } +func (m *Header) String() string { return proto.CompactTextString(m) } +func (*Header) ProtoMessage() {} +func (*Header) Descriptor() ([]byte, []int) { + return fileDescriptor_141333b361aae010, []int{2} +} +func (m *Header) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Header) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Header.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 *Header) XXX_Merge(src proto.Message) { + xxx_messageInfo_Header.Merge(m, src) +} +func (m *Header) XXX_Size() int { + return m.Size() +} +func (m *Header) XXX_DiscardUnknown() { + xxx_messageInfo_Header.DiscardUnknown(m) +} + +var xxx_messageInfo_Header proto.InternalMessageInfo + +// Misbehaviour defines misbehaviour for a solo machine which consists +// of a sequence and two signatures over different messages at that sequence. +type Misbehaviour struct { + ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` + Sequence uint64 `protobuf:"varint,2,opt,name=sequence,proto3" json:"sequence,omitempty"` + SignatureOne *SignatureAndData `protobuf:"bytes,3,opt,name=signature_one,json=signatureOne,proto3" json:"signature_one,omitempty" yaml:"signature_one"` + SignatureTwo *SignatureAndData `protobuf:"bytes,4,opt,name=signature_two,json=signatureTwo,proto3" json:"signature_two,omitempty" yaml:"signature_two"` +} + +func (m *Misbehaviour) Reset() { *m = Misbehaviour{} } +func (m *Misbehaviour) String() string { return proto.CompactTextString(m) } +func (*Misbehaviour) ProtoMessage() {} +func (*Misbehaviour) Descriptor() ([]byte, []int) { + return fileDescriptor_141333b361aae010, []int{3} +} +func (m *Misbehaviour) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Misbehaviour) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Misbehaviour.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 *Misbehaviour) XXX_Merge(src proto.Message) { + xxx_messageInfo_Misbehaviour.Merge(m, src) +} +func (m *Misbehaviour) XXX_Size() int { + return m.Size() +} +func (m *Misbehaviour) XXX_DiscardUnknown() { + xxx_messageInfo_Misbehaviour.DiscardUnknown(m) +} + +var xxx_messageInfo_Misbehaviour proto.InternalMessageInfo + +// SignatureAndData contains a signature and the data signed over to create that +// signature. +type SignatureAndData struct { + Signature []byte `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` + DataType DataType `protobuf:"varint,2,opt,name=data_type,json=dataType,proto3,enum=ibc.lightclients.solomachine.v2.DataType" json:"data_type,omitempty" yaml:"data_type"` + Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` + Timestamp uint64 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` +} + +func (m *SignatureAndData) Reset() { *m = SignatureAndData{} } +func (m *SignatureAndData) String() string { return proto.CompactTextString(m) } +func (*SignatureAndData) ProtoMessage() {} +func (*SignatureAndData) Descriptor() ([]byte, []int) { + return fileDescriptor_141333b361aae010, []int{4} +} +func (m *SignatureAndData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignatureAndData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignatureAndData.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 *SignatureAndData) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignatureAndData.Merge(m, src) +} +func (m *SignatureAndData) XXX_Size() int { + return m.Size() +} +func (m *SignatureAndData) XXX_DiscardUnknown() { + xxx_messageInfo_SignatureAndData.DiscardUnknown(m) +} + +var xxx_messageInfo_SignatureAndData proto.InternalMessageInfo + +// TimestampedSignatureData contains the signature data and the timestamp of the +// signature. +type TimestampedSignatureData struct { + SignatureData []byte `protobuf:"bytes,1,opt,name=signature_data,json=signatureData,proto3" json:"signature_data,omitempty" yaml:"signature_data"` + Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` +} + +func (m *TimestampedSignatureData) Reset() { *m = TimestampedSignatureData{} } +func (m *TimestampedSignatureData) String() string { return proto.CompactTextString(m) } +func (*TimestampedSignatureData) ProtoMessage() {} +func (*TimestampedSignatureData) Descriptor() ([]byte, []int) { + return fileDescriptor_141333b361aae010, []int{5} +} +func (m *TimestampedSignatureData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TimestampedSignatureData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TimestampedSignatureData.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 *TimestampedSignatureData) XXX_Merge(src proto.Message) { + xxx_messageInfo_TimestampedSignatureData.Merge(m, src) +} +func (m *TimestampedSignatureData) XXX_Size() int { + return m.Size() +} +func (m *TimestampedSignatureData) XXX_DiscardUnknown() { + xxx_messageInfo_TimestampedSignatureData.DiscardUnknown(m) +} + +var xxx_messageInfo_TimestampedSignatureData proto.InternalMessageInfo + +// SignBytes defines the signed bytes used for signature verification. +type SignBytes struct { + Sequence uint64 `protobuf:"varint,1,opt,name=sequence,proto3" json:"sequence,omitempty"` + Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Diversifier string `protobuf:"bytes,3,opt,name=diversifier,proto3" json:"diversifier,omitempty"` + // type of the data used + DataType DataType `protobuf:"varint,4,opt,name=data_type,json=dataType,proto3,enum=ibc.lightclients.solomachine.v2.DataType" json:"data_type,omitempty" yaml:"data_type"` + // marshaled data + Data []byte `protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"` +} + +func (m *SignBytes) Reset() { *m = SignBytes{} } +func (m *SignBytes) String() string { return proto.CompactTextString(m) } +func (*SignBytes) ProtoMessage() {} +func (*SignBytes) Descriptor() ([]byte, []int) { + return fileDescriptor_141333b361aae010, []int{6} +} +func (m *SignBytes) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignBytes) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignBytes.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 *SignBytes) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignBytes.Merge(m, src) +} +func (m *SignBytes) XXX_Size() int { + return m.Size() +} +func (m *SignBytes) XXX_DiscardUnknown() { + xxx_messageInfo_SignBytes.DiscardUnknown(m) +} + +var xxx_messageInfo_SignBytes proto.InternalMessageInfo + +// HeaderData returns the SignBytes data for update verification. +type HeaderData struct { + // header public key + NewPubKey *types.Any `protobuf:"bytes,1,opt,name=new_pub_key,json=newPubKey,proto3" json:"new_pub_key,omitempty" yaml:"new_pub_key"` + // header diversifier + NewDiversifier string `protobuf:"bytes,2,opt,name=new_diversifier,json=newDiversifier,proto3" json:"new_diversifier,omitempty" yaml:"new_diversifier"` +} + +func (m *HeaderData) Reset() { *m = HeaderData{} } +func (m *HeaderData) String() string { return proto.CompactTextString(m) } +func (*HeaderData) ProtoMessage() {} +func (*HeaderData) Descriptor() ([]byte, []int) { + return fileDescriptor_141333b361aae010, []int{7} +} +func (m *HeaderData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *HeaderData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_HeaderData.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 *HeaderData) XXX_Merge(src proto.Message) { + xxx_messageInfo_HeaderData.Merge(m, src) +} +func (m *HeaderData) XXX_Size() int { + return m.Size() +} +func (m *HeaderData) XXX_DiscardUnknown() { + xxx_messageInfo_HeaderData.DiscardUnknown(m) +} + +var xxx_messageInfo_HeaderData proto.InternalMessageInfo + +// ClientStateData returns the SignBytes data for client state verification. +type ClientStateData struct { + Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + ClientState *types.Any `protobuf:"bytes,2,opt,name=client_state,json=clientState,proto3" json:"client_state,omitempty" yaml:"client_state"` +} + +func (m *ClientStateData) Reset() { *m = ClientStateData{} } +func (m *ClientStateData) String() string { return proto.CompactTextString(m) } +func (*ClientStateData) ProtoMessage() {} +func (*ClientStateData) Descriptor() ([]byte, []int) { + return fileDescriptor_141333b361aae010, []int{8} +} +func (m *ClientStateData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ClientStateData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ClientStateData.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 *ClientStateData) XXX_Merge(src proto.Message) { + xxx_messageInfo_ClientStateData.Merge(m, src) +} +func (m *ClientStateData) XXX_Size() int { + return m.Size() +} +func (m *ClientStateData) XXX_DiscardUnknown() { + xxx_messageInfo_ClientStateData.DiscardUnknown(m) +} + +var xxx_messageInfo_ClientStateData proto.InternalMessageInfo + +// ConsensusStateData returns the SignBytes data for consensus state +// verification. +type ConsensusStateData struct { + Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + ConsensusState *types.Any `protobuf:"bytes,2,opt,name=consensus_state,json=consensusState,proto3" json:"consensus_state,omitempty" yaml:"consensus_state"` +} + +func (m *ConsensusStateData) Reset() { *m = ConsensusStateData{} } +func (m *ConsensusStateData) String() string { return proto.CompactTextString(m) } +func (*ConsensusStateData) ProtoMessage() {} +func (*ConsensusStateData) Descriptor() ([]byte, []int) { + return fileDescriptor_141333b361aae010, []int{9} +} +func (m *ConsensusStateData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ConsensusStateData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ConsensusStateData.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 *ConsensusStateData) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConsensusStateData.Merge(m, src) +} +func (m *ConsensusStateData) XXX_Size() int { + return m.Size() +} +func (m *ConsensusStateData) XXX_DiscardUnknown() { + xxx_messageInfo_ConsensusStateData.DiscardUnknown(m) +} + +var xxx_messageInfo_ConsensusStateData proto.InternalMessageInfo + +// ConnectionStateData returns the SignBytes data for connection state +// verification. +type ConnectionStateData struct { + Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + Connection *types1.ConnectionEnd `protobuf:"bytes,2,opt,name=connection,proto3" json:"connection,omitempty"` +} + +func (m *ConnectionStateData) Reset() { *m = ConnectionStateData{} } +func (m *ConnectionStateData) String() string { return proto.CompactTextString(m) } +func (*ConnectionStateData) ProtoMessage() {} +func (*ConnectionStateData) Descriptor() ([]byte, []int) { + return fileDescriptor_141333b361aae010, []int{10} +} +func (m *ConnectionStateData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ConnectionStateData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ConnectionStateData.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 *ConnectionStateData) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConnectionStateData.Merge(m, src) +} +func (m *ConnectionStateData) XXX_Size() int { + return m.Size() +} +func (m *ConnectionStateData) XXX_DiscardUnknown() { + xxx_messageInfo_ConnectionStateData.DiscardUnknown(m) +} + +var xxx_messageInfo_ConnectionStateData proto.InternalMessageInfo + +// ChannelStateData returns the SignBytes data for channel state +// verification. +type ChannelStateData struct { + Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + Channel *types2.Channel `protobuf:"bytes,2,opt,name=channel,proto3" json:"channel,omitempty"` +} + +func (m *ChannelStateData) Reset() { *m = ChannelStateData{} } +func (m *ChannelStateData) String() string { return proto.CompactTextString(m) } +func (*ChannelStateData) ProtoMessage() {} +func (*ChannelStateData) Descriptor() ([]byte, []int) { + return fileDescriptor_141333b361aae010, []int{11} +} +func (m *ChannelStateData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ChannelStateData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ChannelStateData.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 *ChannelStateData) XXX_Merge(src proto.Message) { + xxx_messageInfo_ChannelStateData.Merge(m, src) +} +func (m *ChannelStateData) XXX_Size() int { + return m.Size() +} +func (m *ChannelStateData) XXX_DiscardUnknown() { + xxx_messageInfo_ChannelStateData.DiscardUnknown(m) +} + +var xxx_messageInfo_ChannelStateData proto.InternalMessageInfo + +// PacketCommitmentData returns the SignBytes data for packet commitment +// verification. +type PacketCommitmentData struct { + Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + Commitment []byte `protobuf:"bytes,2,opt,name=commitment,proto3" json:"commitment,omitempty"` +} + +func (m *PacketCommitmentData) Reset() { *m = PacketCommitmentData{} } +func (m *PacketCommitmentData) String() string { return proto.CompactTextString(m) } +func (*PacketCommitmentData) ProtoMessage() {} +func (*PacketCommitmentData) Descriptor() ([]byte, []int) { + return fileDescriptor_141333b361aae010, []int{12} +} +func (m *PacketCommitmentData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PacketCommitmentData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PacketCommitmentData.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 *PacketCommitmentData) XXX_Merge(src proto.Message) { + xxx_messageInfo_PacketCommitmentData.Merge(m, src) +} +func (m *PacketCommitmentData) XXX_Size() int { + return m.Size() +} +func (m *PacketCommitmentData) XXX_DiscardUnknown() { + xxx_messageInfo_PacketCommitmentData.DiscardUnknown(m) +} + +var xxx_messageInfo_PacketCommitmentData proto.InternalMessageInfo + +func (m *PacketCommitmentData) GetPath() []byte { + if m != nil { + return m.Path + } + return nil +} + +func (m *PacketCommitmentData) GetCommitment() []byte { + if m != nil { + return m.Commitment + } + return nil +} + +// PacketAcknowledgementData returns the SignBytes data for acknowledgement +// verification. +type PacketAcknowledgementData struct { + Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + Acknowledgement []byte `protobuf:"bytes,2,opt,name=acknowledgement,proto3" json:"acknowledgement,omitempty"` +} + +func (m *PacketAcknowledgementData) Reset() { *m = PacketAcknowledgementData{} } +func (m *PacketAcknowledgementData) String() string { return proto.CompactTextString(m) } +func (*PacketAcknowledgementData) ProtoMessage() {} +func (*PacketAcknowledgementData) Descriptor() ([]byte, []int) { + return fileDescriptor_141333b361aae010, []int{13} +} +func (m *PacketAcknowledgementData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PacketAcknowledgementData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PacketAcknowledgementData.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 *PacketAcknowledgementData) XXX_Merge(src proto.Message) { + xxx_messageInfo_PacketAcknowledgementData.Merge(m, src) +} +func (m *PacketAcknowledgementData) XXX_Size() int { + return m.Size() +} +func (m *PacketAcknowledgementData) XXX_DiscardUnknown() { + xxx_messageInfo_PacketAcknowledgementData.DiscardUnknown(m) +} + +var xxx_messageInfo_PacketAcknowledgementData proto.InternalMessageInfo + +func (m *PacketAcknowledgementData) GetPath() []byte { + if m != nil { + return m.Path + } + return nil +} + +func (m *PacketAcknowledgementData) GetAcknowledgement() []byte { + if m != nil { + return m.Acknowledgement + } + return nil +} + +// PacketReceiptAbsenceData returns the SignBytes data for +// packet receipt absence verification. +type PacketReceiptAbsenceData struct { + Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` +} + +func (m *PacketReceiptAbsenceData) Reset() { *m = PacketReceiptAbsenceData{} } +func (m *PacketReceiptAbsenceData) String() string { return proto.CompactTextString(m) } +func (*PacketReceiptAbsenceData) ProtoMessage() {} +func (*PacketReceiptAbsenceData) Descriptor() ([]byte, []int) { + return fileDescriptor_141333b361aae010, []int{14} +} +func (m *PacketReceiptAbsenceData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PacketReceiptAbsenceData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PacketReceiptAbsenceData.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 *PacketReceiptAbsenceData) XXX_Merge(src proto.Message) { + xxx_messageInfo_PacketReceiptAbsenceData.Merge(m, src) +} +func (m *PacketReceiptAbsenceData) XXX_Size() int { + return m.Size() +} +func (m *PacketReceiptAbsenceData) XXX_DiscardUnknown() { + xxx_messageInfo_PacketReceiptAbsenceData.DiscardUnknown(m) +} + +var xxx_messageInfo_PacketReceiptAbsenceData proto.InternalMessageInfo + +func (m *PacketReceiptAbsenceData) GetPath() []byte { + if m != nil { + return m.Path + } + return nil +} + +// NextSequenceRecvData returns the SignBytes data for verification of the next +// sequence to be received. +type NextSequenceRecvData struct { + Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + NextSeqRecv uint64 `protobuf:"varint,2,opt,name=next_seq_recv,json=nextSeqRecv,proto3" json:"next_seq_recv,omitempty" yaml:"next_seq_recv"` +} + +func (m *NextSequenceRecvData) Reset() { *m = NextSequenceRecvData{} } +func (m *NextSequenceRecvData) String() string { return proto.CompactTextString(m) } +func (*NextSequenceRecvData) ProtoMessage() {} +func (*NextSequenceRecvData) Descriptor() ([]byte, []int) { + return fileDescriptor_141333b361aae010, []int{15} +} +func (m *NextSequenceRecvData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *NextSequenceRecvData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_NextSequenceRecvData.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 *NextSequenceRecvData) XXX_Merge(src proto.Message) { + xxx_messageInfo_NextSequenceRecvData.Merge(m, src) +} +func (m *NextSequenceRecvData) XXX_Size() int { + return m.Size() +} +func (m *NextSequenceRecvData) XXX_DiscardUnknown() { + xxx_messageInfo_NextSequenceRecvData.DiscardUnknown(m) +} + +var xxx_messageInfo_NextSequenceRecvData proto.InternalMessageInfo + +func (m *NextSequenceRecvData) GetPath() []byte { + if m != nil { + return m.Path + } + return nil +} + +func (m *NextSequenceRecvData) GetNextSeqRecv() uint64 { + if m != nil { + return m.NextSeqRecv + } + return 0 +} + +func init() { + proto.RegisterEnum("ibc.lightclients.solomachine.v2.DataType", DataType_name, DataType_value) + proto.RegisterType((*ClientState)(nil), "ibc.lightclients.solomachine.v2.ClientState") + proto.RegisterType((*ConsensusState)(nil), "ibc.lightclients.solomachine.v2.ConsensusState") + proto.RegisterType((*Header)(nil), "ibc.lightclients.solomachine.v2.Header") + proto.RegisterType((*Misbehaviour)(nil), "ibc.lightclients.solomachine.v2.Misbehaviour") + proto.RegisterType((*SignatureAndData)(nil), "ibc.lightclients.solomachine.v2.SignatureAndData") + proto.RegisterType((*TimestampedSignatureData)(nil), "ibc.lightclients.solomachine.v2.TimestampedSignatureData") + proto.RegisterType((*SignBytes)(nil), "ibc.lightclients.solomachine.v2.SignBytes") + proto.RegisterType((*HeaderData)(nil), "ibc.lightclients.solomachine.v2.HeaderData") + proto.RegisterType((*ClientStateData)(nil), "ibc.lightclients.solomachine.v2.ClientStateData") + proto.RegisterType((*ConsensusStateData)(nil), "ibc.lightclients.solomachine.v2.ConsensusStateData") + proto.RegisterType((*ConnectionStateData)(nil), "ibc.lightclients.solomachine.v2.ConnectionStateData") + proto.RegisterType((*ChannelStateData)(nil), "ibc.lightclients.solomachine.v2.ChannelStateData") + proto.RegisterType((*PacketCommitmentData)(nil), "ibc.lightclients.solomachine.v2.PacketCommitmentData") + proto.RegisterType((*PacketAcknowledgementData)(nil), "ibc.lightclients.solomachine.v2.PacketAcknowledgementData") + proto.RegisterType((*PacketReceiptAbsenceData)(nil), "ibc.lightclients.solomachine.v2.PacketReceiptAbsenceData") + proto.RegisterType((*NextSequenceRecvData)(nil), "ibc.lightclients.solomachine.v2.NextSequenceRecvData") +} + +func init() { + proto.RegisterFile("ibc/lightclients/solomachine/v2/solomachine.proto", fileDescriptor_141333b361aae010) +} + +var fileDescriptor_141333b361aae010 = []byte{ + // 1373 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0x5f, 0x8f, 0xdb, 0x44, + 0x10, 0x3f, 0xa7, 0xe9, 0xf5, 0xb2, 0xb9, 0xde, 0x05, 0x37, 0x6d, 0x73, 0x6e, 0x95, 0x18, 0x23, + 0xca, 0x81, 0x68, 0xcc, 0x5d, 0x45, 0x85, 0x2a, 0x04, 0x75, 0x1c, 0x97, 0xa6, 0xbd, 0xf3, 0x05, + 0xc7, 0x07, 0xb4, 0x42, 0xb2, 0x1c, 0x7b, 0x2f, 0xb1, 0x9a, 0x78, 0xd3, 0x78, 0x93, 0x34, 0x48, + 0x48, 0x88, 0xa7, 0x12, 0xf1, 0xc0, 0x17, 0x88, 0x84, 0x40, 0x7c, 0x0e, 0xde, 0x80, 0xc7, 0x3e, + 0xf2, 0x14, 0x50, 0xfb, 0x0d, 0xf2, 0x09, 0x90, 0xbd, 0x9b, 0xd8, 0xce, 0xf5, 0x72, 0xe2, 0xdf, + 0xdb, 0xee, 0xfc, 0x66, 0x7e, 0x33, 0x3b, 0x33, 0x9e, 0x5d, 0x83, 0x1d, 0xa7, 0x6e, 0x89, 0x2d, + 0xa7, 0xd1, 0xc4, 0x56, 0xcb, 0x81, 0x2e, 0xf6, 0x44, 0x0f, 0xb5, 0x50, 0xdb, 0xb4, 0x9a, 0x8e, + 0x0b, 0xc5, 0xfe, 0x6e, 0x74, 0x5b, 0xec, 0x74, 0x11, 0x46, 0x6c, 0xc1, 0xa9, 0x5b, 0xc5, 0xa8, + 0x49, 0x31, 0xaa, 0xd3, 0xdf, 0xe5, 0xde, 0xf0, 0x39, 0x2d, 0xd4, 0x85, 0xa2, 0x85, 0x5c, 0x17, + 0x5a, 0xd8, 0x41, 0xae, 0xd8, 0xdf, 0x89, 0xec, 0x08, 0x13, 0xf7, 0x6a, 0xa8, 0xd8, 0x34, 0x5d, + 0x17, 0xb6, 0x02, 0x2d, 0xb2, 0xa4, 0x2a, 0xd9, 0x06, 0x6a, 0xa0, 0x60, 0x29, 0xfa, 0x2b, 0x2a, + 0xdd, 0x6a, 0x20, 0xd4, 0x68, 0x41, 0x31, 0xd8, 0xd5, 0x7b, 0x47, 0xa2, 0xe9, 0x0e, 0x09, 0x24, + 0xfc, 0x9c, 0x00, 0x69, 0x39, 0x88, 0xab, 0x86, 0x4d, 0x0c, 0x59, 0x0e, 0xac, 0x79, 0xf0, 0x71, + 0x0f, 0xba, 0x16, 0xcc, 0x31, 0x3c, 0xb3, 0x9d, 0xd4, 0xe6, 0x7b, 0x76, 0x07, 0xa4, 0x1c, 0xcf, + 0x38, 0xea, 0xa2, 0x2f, 0xa0, 0x9b, 0x4b, 0xf0, 0xcc, 0xf6, 0x5a, 0x29, 0x3b, 0x9d, 0x14, 0x32, + 0x43, 0xb3, 0xdd, 0xba, 0x25, 0xcc, 0x21, 0x41, 0x5b, 0x73, 0xbc, 0x3b, 0xc1, 0x92, 0xc5, 0x60, + 0xd3, 0x42, 0xae, 0x07, 0x5d, 0xaf, 0xe7, 0x19, 0x9e, 0xef, 0x21, 0x77, 0x86, 0x67, 0xb6, 0xd3, + 0xbb, 0x62, 0xf1, 0x94, 0xb4, 0x14, 0xe5, 0x99, 0x5d, 0x10, 0x58, 0x89, 0x9b, 0x4e, 0x0a, 0x97, + 0x88, 0xa7, 0x05, 0x46, 0x41, 0xdb, 0xb0, 0x62, 0xba, 0x2c, 0x04, 0x57, 0xcc, 0x56, 0x0b, 0x0d, + 0x8c, 0x5e, 0xc7, 0x36, 0x31, 0x34, 0xcc, 0x23, 0x0c, 0xbb, 0x46, 0xa7, 0x8b, 0x3a, 0xc8, 0x33, + 0x5b, 0xb9, 0x64, 0x10, 0xfa, 0xb5, 0xe9, 0xa4, 0x20, 0x10, 0xc2, 0x25, 0xca, 0x82, 0x96, 0x0b, + 0xd0, 0xc3, 0x00, 0x94, 0x7c, 0xac, 0x4a, 0xa1, 0x5b, 0xc9, 0xa7, 0xdf, 0x17, 0x56, 0x84, 0x1f, + 0x18, 0xb0, 0x11, 0x8f, 0x95, 0xbd, 0x07, 0x40, 0xa7, 0x57, 0x6f, 0x39, 0x96, 0xf1, 0x08, 0x0e, + 0x83, 0x34, 0xa6, 0x77, 0xb3, 0x45, 0x52, 0x84, 0xe2, 0xac, 0x08, 0x45, 0xc9, 0x1d, 0x96, 0x2e, + 0x4e, 0x27, 0x85, 0x57, 0x48, 0x10, 0xa1, 0x85, 0xa0, 0xa5, 0xc8, 0xe6, 0x3e, 0x1c, 0xb2, 0x3c, + 0x48, 0xdb, 0x4e, 0x1f, 0x76, 0x3d, 0xe7, 0xc8, 0x81, 0xdd, 0x20, 0xed, 0x29, 0x2d, 0x2a, 0x62, + 0xaf, 0x82, 0x14, 0x76, 0xda, 0xd0, 0xc3, 0x66, 0xbb, 0x13, 0x64, 0x37, 0xa9, 0x85, 0x02, 0x1a, + 0xe4, 0xd7, 0x09, 0xb0, 0x7a, 0x17, 0x9a, 0x36, 0xec, 0x2e, 0xad, 0x70, 0x8c, 0x2a, 0xb1, 0x40, + 0xe5, 0xa3, 0x9e, 0xd3, 0x70, 0x4d, 0xdc, 0xeb, 0x92, 0x32, 0xae, 0x6b, 0xa1, 0x80, 0x3d, 0x04, + 0x1b, 0x2e, 0x1c, 0x18, 0x91, 0x83, 0x27, 0x97, 0x1c, 0x7c, 0x6b, 0x3a, 0x29, 0x5c, 0x24, 0x07, + 0x8f, 0x5b, 0x09, 0xda, 0xba, 0x0b, 0x07, 0xd5, 0xf9, 0xf9, 0x65, 0xb0, 0xe9, 0x2b, 0x44, 0x73, + 0x70, 0xd6, 0xcf, 0x41, 0xb4, 0x21, 0x16, 0x14, 0x04, 0xcd, 0x8f, 0xa4, 0x1c, 0x0a, 0x68, 0x12, + 0x7e, 0x4d, 0x80, 0xf5, 0x7d, 0xc7, 0xab, 0xc3, 0xa6, 0xd9, 0x77, 0x50, 0xaf, 0xeb, 0x37, 0x34, + 0x69, 0x3e, 0xc3, 0xb1, 0x83, 0x5c, 0xa4, 0xa2, 0x0d, 0x3d, 0x87, 0x04, 0x6d, 0x8d, 0xac, 0x2b, + 0x76, 0x2c, 0x7b, 0x89, 0x85, 0xec, 0x75, 0xc0, 0xf9, 0x79, 0x3a, 0x0c, 0xe4, 0xce, 0x5a, 0x7d, + 0xe7, 0xd4, 0x56, 0xaf, 0xcd, 0xac, 0x24, 0xd7, 0x2e, 0x9b, 0xd8, 0x2c, 0xe5, 0xa6, 0x93, 0x42, + 0x96, 0x44, 0x11, 0x63, 0x14, 0xb4, 0xf5, 0xf9, 0xfe, 0xc0, 0x5d, 0xf0, 0x88, 0x07, 0x88, 0xa6, + 0xfc, 0xbf, 0xf2, 0x88, 0x07, 0x28, 0xea, 0x51, 0x1f, 0x20, 0x9a, 0xc9, 0x5f, 0x18, 0x90, 0x59, + 0xa4, 0x88, 0xb7, 0x07, 0xb3, 0xd8, 0x1e, 0x9f, 0x83, 0x94, 0x6d, 0x62, 0xd3, 0xc0, 0xc3, 0x0e, + 0xc9, 0xdc, 0xc6, 0xee, 0x9b, 0xa7, 0x86, 0xe9, 0xf3, 0xea, 0xc3, 0x0e, 0x8c, 0x96, 0x65, 0xce, + 0x22, 0x68, 0x6b, 0x36, 0xc5, 0x59, 0x16, 0x24, 0xfd, 0x35, 0xed, 0xca, 0x60, 0x1d, 0x6f, 0xe6, + 0xe4, 0xcb, 0xbf, 0x8b, 0xaf, 0x18, 0x90, 0xd3, 0x67, 0x32, 0x68, 0xcf, 0xcf, 0x14, 0x1c, 0xe8, + 0x36, 0xd8, 0x08, 0x73, 0x11, 0xd0, 0x07, 0xa7, 0x8a, 0xf6, 0x6e, 0x1c, 0x17, 0xb4, 0xb0, 0x1c, + 0xe5, 0x63, 0x21, 0x24, 0x5e, 0x1e, 0xc2, 0x1f, 0x0c, 0x48, 0xf9, 0x7e, 0x4b, 0x43, 0x0c, 0xbd, + 0x7f, 0xf1, 0x75, 0x2e, 0x0c, 0x8a, 0x33, 0xc7, 0x07, 0x45, 0xac, 0x04, 0xc9, 0xff, 0xab, 0x04, + 0x67, 0xc3, 0x12, 0xd0, 0x13, 0xfe, 0xc4, 0x00, 0x40, 0x86, 0x4f, 0x90, 0x94, 0x3d, 0x90, 0xa6, + 0x9f, 0xfc, 0xa9, 0xe3, 0xf1, 0xd2, 0x74, 0x52, 0x60, 0x63, 0x53, 0x82, 0xce, 0x47, 0x32, 0x22, + 0x4e, 0x98, 0x0f, 0x89, 0x7f, 0x38, 0x1f, 0xbe, 0x04, 0x9b, 0x91, 0xab, 0x30, 0x88, 0x95, 0x05, + 0xc9, 0x8e, 0x89, 0x9b, 0xb4, 0x9d, 0x83, 0x35, 0x5b, 0x05, 0xeb, 0x74, 0x34, 0x90, 0x0b, 0x2d, + 0xb1, 0xe4, 0x00, 0x97, 0xa7, 0x93, 0xc2, 0x85, 0xd8, 0x38, 0xa1, 0x57, 0x56, 0xda, 0x0a, 0x3d, + 0x51, 0xf7, 0xdf, 0x30, 0x80, 0x8d, 0x5f, 0x24, 0x27, 0x86, 0xf0, 0xe0, 0xf8, 0xb5, 0xba, 0x2c, + 0x8a, 0xbf, 0x71, 0x77, 0xd2, 0x58, 0xfa, 0xe0, 0x82, 0x3c, 0x7f, 0x7e, 0x2c, 0x8f, 0x45, 0x01, + 0x20, 0x7c, 0xa9, 0xd0, 0x30, 0x5e, 0x0f, 0xda, 0xca, 0x7f, 0xaa, 0x14, 0x23, 0xaf, 0x98, 0xfe, + 0x4e, 0x31, 0x24, 0x55, 0x5c, 0x5b, 0x8b, 0x18, 0x52, 0xbf, 0x36, 0xc8, 0xc8, 0xe4, 0x41, 0xb3, + 0xdc, 0xe9, 0x4d, 0x70, 0x8e, 0x3e, 0x7c, 0xa8, 0xc7, 0xab, 0x11, 0x8f, 0xf4, 0x45, 0xe4, 0xbb, + 0x23, 0x4b, 0x6d, 0xa6, 0x4c, 0xbd, 0xdc, 0x03, 0xd9, 0xaa, 0x69, 0x3d, 0x82, 0x58, 0x46, 0xed, + 0xb6, 0x83, 0xdb, 0xd0, 0xc5, 0x27, 0x7a, 0xca, 0xfb, 0xc7, 0x9b, 0x69, 0x05, 0xce, 0xd6, 0xb5, + 0x88, 0x44, 0x78, 0x00, 0xb6, 0x08, 0x97, 0x64, 0x3d, 0x72, 0xd1, 0xa0, 0x05, 0xed, 0x06, 0x5c, + 0x4a, 0xb8, 0x0d, 0x36, 0xcd, 0xb8, 0x2a, 0x65, 0x5d, 0x14, 0x0b, 0x45, 0x90, 0x23, 0xd4, 0x1a, + 0xb4, 0xa0, 0xd3, 0xc1, 0x52, 0xdd, 0xf3, 0xe7, 0xc0, 0x49, 0xcc, 0x42, 0x13, 0x64, 0x55, 0xf8, + 0x04, 0xd7, 0xe8, 0xbc, 0xd0, 0xa0, 0xd5, 0x3f, 0x31, 0x8a, 0xf7, 0xc1, 0x79, 0x17, 0x3e, 0xc1, + 0x86, 0x07, 0x1f, 0x1b, 0x5d, 0x68, 0xf5, 0xc9, 0x3c, 0x89, 0x5e, 0x03, 0x31, 0x58, 0xd0, 0xd2, + 0x2e, 0xa1, 0xf6, 0x59, 0xdf, 0xfa, 0x36, 0x09, 0xd6, 0x66, 0x83, 0x81, 0x7d, 0x0f, 0xbc, 0x56, + 0x96, 0x74, 0xc9, 0xd0, 0x1f, 0x54, 0x15, 0xe3, 0x50, 0xad, 0xa8, 0x15, 0xbd, 0x22, 0xed, 0x55, + 0x1e, 0x2a, 0x65, 0xe3, 0x50, 0xad, 0x55, 0x15, 0xb9, 0x72, 0xa7, 0xa2, 0x94, 0x33, 0x2b, 0xdc, + 0xe6, 0x68, 0xcc, 0xa7, 0x23, 0x22, 0xf6, 0x1a, 0xb8, 0x14, 0x5a, 0xca, 0x7b, 0x15, 0x45, 0xd5, + 0x8d, 0x9a, 0x2e, 0xe9, 0x4a, 0x86, 0xe1, 0xc0, 0x68, 0xcc, 0xaf, 0x12, 0x19, 0xfb, 0x36, 0xd8, + 0x8a, 0xe8, 0x1d, 0xa8, 0x35, 0x45, 0xad, 0x1d, 0xd6, 0xa8, 0x6a, 0x82, 0x3b, 0x3f, 0x1a, 0xf3, + 0xa9, 0xb9, 0x98, 0x2d, 0x02, 0x2e, 0xa6, 0xad, 0x2a, 0xb2, 0x5e, 0x39, 0x50, 0xa9, 0xfa, 0x19, + 0x6e, 0x63, 0x34, 0xe6, 0x41, 0x28, 0x67, 0xb7, 0xc1, 0xe5, 0x88, 0xfe, 0x5d, 0x49, 0x55, 0x95, + 0x3d, 0xaa, 0x9c, 0xe4, 0xd2, 0xa3, 0x31, 0x7f, 0x8e, 0x0a, 0xd9, 0x77, 0xc1, 0x95, 0x50, 0xb3, + 0x2a, 0xc9, 0xf7, 0x15, 0xdd, 0x90, 0x0f, 0xf6, 0xf7, 0x2b, 0xfa, 0xbe, 0xa2, 0xea, 0x99, 0xb3, + 0x5c, 0x76, 0x34, 0xe6, 0x33, 0x04, 0x08, 0xe5, 0xec, 0x87, 0x80, 0x3f, 0x66, 0x26, 0xc9, 0xf7, + 0xd5, 0x83, 0x4f, 0xf7, 0x94, 0xf2, 0x47, 0x4a, 0x60, 0xbb, 0xca, 0x6d, 0x8d, 0xc6, 0xfc, 0x45, + 0x82, 0x2e, 0x80, 0xec, 0x07, 0x2f, 0x21, 0xd0, 0x14, 0x59, 0xa9, 0x54, 0x75, 0x43, 0x2a, 0xd5, + 0x14, 0x55, 0x56, 0x32, 0xe7, 0xb8, 0xdc, 0x68, 0xcc, 0x67, 0x09, 0x4a, 0x41, 0x8a, 0xb1, 0x37, + 0xc1, 0xd5, 0xd0, 0x5e, 0x55, 0x3e, 0xd3, 0x8d, 0x9a, 0xf2, 0xf1, 0xa1, 0x0f, 0xf9, 0x34, 0x9f, + 0x64, 0xd6, 0x48, 0xe0, 0x3e, 0x32, 0x03, 0x7c, 0x39, 0xcb, 0x83, 0x4c, 0x68, 0x77, 0x57, 0x91, + 0xca, 0x8a, 0x96, 0x49, 0x91, 0xca, 0x90, 0x1d, 0x97, 0x7c, 0xfa, 0x63, 0x7e, 0xa5, 0xf4, 0xf0, + 0xb7, 0xe7, 0x79, 0xe6, 0xd9, 0xf3, 0x3c, 0xf3, 0xe7, 0xf3, 0x3c, 0xf3, 0xdd, 0x8b, 0xfc, 0xca, + 0xb3, 0x17, 0xf9, 0x95, 0xdf, 0x5f, 0xe4, 0x57, 0x1e, 0xde, 0x6e, 0x38, 0xb8, 0xd9, 0xab, 0x17, + 0x2d, 0xd4, 0x16, 0x2d, 0xe4, 0xb5, 0x91, 0x27, 0x3a, 0x75, 0xeb, 0x7a, 0x03, 0x89, 0xfd, 0x1b, + 0x62, 0x1b, 0xd9, 0xbd, 0x16, 0xf4, 0xc8, 0x2f, 0xcd, 0x3b, 0xbb, 0xd7, 0xc9, 0x48, 0x14, 0xdb, + 0x4e, 0xa3, 0x6b, 0xfa, 0x33, 0xc1, 0x13, 0xfb, 0x37, 0xeb, 0xab, 0xc1, 0x24, 0xbb, 0xf1, 0x57, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xb7, 0xf1, 0xad, 0x65, 0x7a, 0x0d, 0x00, 0x00, +} + +func (m *ClientState) 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 *ClientState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ClientState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.AllowUpdateAfterProposal { + i-- + if m.AllowUpdateAfterProposal { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } + if m.ConsensusState != nil { + { + size, err := m.ConsensusState.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSolomachine(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.IsFrozen { + i-- + if m.IsFrozen { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if m.Sequence != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.Sequence)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *ConsensusState) 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 *ConsensusState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ConsensusState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Timestamp != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x18 + } + if len(m.Diversifier) > 0 { + i -= len(m.Diversifier) + copy(dAtA[i:], m.Diversifier) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Diversifier))) + i-- + dAtA[i] = 0x12 + } + if m.PublicKey != nil { + { + size, err := m.PublicKey.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSolomachine(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Header) 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 *Header) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Header) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.NewDiversifier) > 0 { + i -= len(m.NewDiversifier) + copy(dAtA[i:], m.NewDiversifier) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.NewDiversifier))) + i-- + dAtA[i] = 0x2a + } + if m.NewPublicKey != nil { + { + size, err := m.NewPublicKey.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSolomachine(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if len(m.Signature) > 0 { + i -= len(m.Signature) + copy(dAtA[i:], m.Signature) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Signature))) + i-- + dAtA[i] = 0x1a + } + if m.Timestamp != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x10 + } + if m.Sequence != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.Sequence)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Misbehaviour) 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 *Misbehaviour) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Misbehaviour) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.SignatureTwo != nil { + { + size, err := m.SignatureTwo.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSolomachine(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if m.SignatureOne != nil { + { + size, err := m.SignatureOne.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSolomachine(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.Sequence != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.Sequence)) + i-- + dAtA[i] = 0x10 + } + if len(m.ClientId) > 0 { + i -= len(m.ClientId) + copy(dAtA[i:], m.ClientId) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.ClientId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SignatureAndData) 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 *SignatureAndData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignatureAndData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Timestamp != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x20 + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x1a + } + if m.DataType != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.DataType)) + i-- + dAtA[i] = 0x10 + } + if len(m.Signature) > 0 { + i -= len(m.Signature) + copy(dAtA[i:], m.Signature) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Signature))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TimestampedSignatureData) 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 *TimestampedSignatureData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TimestampedSignatureData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Timestamp != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x10 + } + if len(m.SignatureData) > 0 { + i -= len(m.SignatureData) + copy(dAtA[i:], m.SignatureData) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.SignatureData))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SignBytes) 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 *SignBytes) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignBytes) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x2a + } + if m.DataType != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.DataType)) + i-- + dAtA[i] = 0x20 + } + if len(m.Diversifier) > 0 { + i -= len(m.Diversifier) + copy(dAtA[i:], m.Diversifier) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Diversifier))) + i-- + dAtA[i] = 0x1a + } + if m.Timestamp != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x10 + } + if m.Sequence != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.Sequence)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *HeaderData) 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 *HeaderData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *HeaderData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.NewDiversifier) > 0 { + i -= len(m.NewDiversifier) + copy(dAtA[i:], m.NewDiversifier) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.NewDiversifier))) + i-- + dAtA[i] = 0x12 + } + if m.NewPubKey != nil { + { + size, err := m.NewPubKey.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSolomachine(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ClientStateData) 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 *ClientStateData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ClientStateData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ClientState != nil { + { + size, err := m.ClientState.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSolomachine(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ConsensusStateData) 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 *ConsensusStateData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ConsensusStateData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ConsensusState != nil { + { + size, err := m.ConsensusState.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSolomachine(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ConnectionStateData) 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 *ConnectionStateData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ConnectionStateData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Connection != nil { + { + size, err := m.Connection.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSolomachine(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ChannelStateData) 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 *ChannelStateData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ChannelStateData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Channel != nil { + { + size, err := m.Channel.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSolomachine(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PacketCommitmentData) 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 *PacketCommitmentData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PacketCommitmentData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Commitment) > 0 { + i -= len(m.Commitment) + copy(dAtA[i:], m.Commitment) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Commitment))) + i-- + dAtA[i] = 0x12 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PacketAcknowledgementData) 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 *PacketAcknowledgementData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PacketAcknowledgementData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Acknowledgement) > 0 { + i -= len(m.Acknowledgement) + copy(dAtA[i:], m.Acknowledgement) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Acknowledgement))) + i-- + dAtA[i] = 0x12 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PacketReceiptAbsenceData) 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 *PacketReceiptAbsenceData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PacketReceiptAbsenceData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *NextSequenceRecvData) 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 *NextSequenceRecvData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *NextSequenceRecvData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.NextSeqRecv != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.NextSeqRecv)) + i-- + dAtA[i] = 0x10 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintSolomachine(dAtA []byte, offset int, v uint64) int { + offset -= sovSolomachine(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *ClientState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Sequence != 0 { + n += 1 + sovSolomachine(uint64(m.Sequence)) + } + if m.IsFrozen { + n += 2 + } + if m.ConsensusState != nil { + l = m.ConsensusState.Size() + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.AllowUpdateAfterProposal { + n += 2 + } + return n +} + +func (m *ConsensusState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.PublicKey != nil { + l = m.PublicKey.Size() + n += 1 + l + sovSolomachine(uint64(l)) + } + l = len(m.Diversifier) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.Timestamp != 0 { + n += 1 + sovSolomachine(uint64(m.Timestamp)) + } + return n +} + +func (m *Header) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Sequence != 0 { + n += 1 + sovSolomachine(uint64(m.Sequence)) + } + if m.Timestamp != 0 { + n += 1 + sovSolomachine(uint64(m.Timestamp)) + } + l = len(m.Signature) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.NewPublicKey != nil { + l = m.NewPublicKey.Size() + n += 1 + l + sovSolomachine(uint64(l)) + } + l = len(m.NewDiversifier) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + return n +} + +func (m *Misbehaviour) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClientId) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.Sequence != 0 { + n += 1 + sovSolomachine(uint64(m.Sequence)) + } + if m.SignatureOne != nil { + l = m.SignatureOne.Size() + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.SignatureTwo != nil { + l = m.SignatureTwo.Size() + n += 1 + l + sovSolomachine(uint64(l)) + } + return n +} + +func (m *SignatureAndData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Signature) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.DataType != 0 { + n += 1 + sovSolomachine(uint64(m.DataType)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.Timestamp != 0 { + n += 1 + sovSolomachine(uint64(m.Timestamp)) + } + return n +} + +func (m *TimestampedSignatureData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.SignatureData) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.Timestamp != 0 { + n += 1 + sovSolomachine(uint64(m.Timestamp)) + } + return n +} + +func (m *SignBytes) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Sequence != 0 { + n += 1 + sovSolomachine(uint64(m.Sequence)) + } + if m.Timestamp != 0 { + n += 1 + sovSolomachine(uint64(m.Timestamp)) + } + l = len(m.Diversifier) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.DataType != 0 { + n += 1 + sovSolomachine(uint64(m.DataType)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + return n +} + +func (m *HeaderData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.NewPubKey != nil { + l = m.NewPubKey.Size() + n += 1 + l + sovSolomachine(uint64(l)) + } + l = len(m.NewDiversifier) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + return n +} + +func (m *ClientStateData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Path) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.ClientState != nil { + l = m.ClientState.Size() + n += 1 + l + sovSolomachine(uint64(l)) + } + return n +} + +func (m *ConsensusStateData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Path) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.ConsensusState != nil { + l = m.ConsensusState.Size() + n += 1 + l + sovSolomachine(uint64(l)) + } + return n +} + +func (m *ConnectionStateData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Path) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.Connection != nil { + l = m.Connection.Size() + n += 1 + l + sovSolomachine(uint64(l)) + } + return n +} + +func (m *ChannelStateData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Path) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.Channel != nil { + l = m.Channel.Size() + n += 1 + l + sovSolomachine(uint64(l)) + } + return n +} + +func (m *PacketCommitmentData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Path) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + l = len(m.Commitment) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + return n +} + +func (m *PacketAcknowledgementData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Path) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + l = len(m.Acknowledgement) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + return n +} + +func (m *PacketReceiptAbsenceData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Path) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + return n +} + +func (m *NextSequenceRecvData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Path) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.NextSeqRecv != 0 { + n += 1 + sovSolomachine(uint64(m.NextSeqRecv)) + } + return n +} + +func sovSolomachine(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozSolomachine(x uint64) (n int) { + return sovSolomachine(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *ClientState) 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 ErrIntOverflowSolomachine + } + 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: ClientState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ClientState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) + } + m.Sequence = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Sequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsFrozen", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.IsFrozen = bool(v != 0) + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsensusState", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ConsensusState == nil { + m.ConsensusState = &ConsensusState{} + } + if err := m.ConsensusState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AllowUpdateAfterProposal", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.AllowUpdateAfterProposal = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ConsensusState) 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 ErrIntOverflowSolomachine + } + 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: ConsensusState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ConsensusState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PublicKey", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.PublicKey == nil { + m.PublicKey = &types.Any{} + } + if err := m.PublicKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Diversifier", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + 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 ErrInvalidLengthSolomachine + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Diversifier = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Timestamp |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Header) 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 ErrIntOverflowSolomachine + } + 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: Header: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Header: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) + } + m.Sequence = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Sequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Timestamp |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) + if m.Signature == nil { + m.Signature = []byte{} + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NewPublicKey", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.NewPublicKey == nil { + m.NewPublicKey = &types.Any{} + } + if err := m.NewPublicKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NewDiversifier", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + 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 ErrInvalidLengthSolomachine + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NewDiversifier = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Misbehaviour) 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 ErrIntOverflowSolomachine + } + 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: Misbehaviour: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Misbehaviour: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + 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 ErrInvalidLengthSolomachine + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ClientId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) + } + m.Sequence = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Sequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SignatureOne", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.SignatureOne == nil { + m.SignatureOne = &SignatureAndData{} + } + if err := m.SignatureOne.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SignatureTwo", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.SignatureTwo == nil { + m.SignatureTwo = &SignatureAndData{} + } + if err := m.SignatureTwo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SignatureAndData) 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 ErrIntOverflowSolomachine + } + 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: SignatureAndData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignatureAndData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) + if m.Signature == nil { + m.Signature = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DataType", wireType) + } + m.DataType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DataType |= DataType(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Timestamp |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TimestampedSignatureData) 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 ErrIntOverflowSolomachine + } + 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: TimestampedSignatureData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TimestampedSignatureData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SignatureData", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SignatureData = append(m.SignatureData[:0], dAtA[iNdEx:postIndex]...) + if m.SignatureData == nil { + m.SignatureData = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Timestamp |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SignBytes) 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 ErrIntOverflowSolomachine + } + 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: SignBytes: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignBytes: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) + } + m.Sequence = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Sequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Timestamp |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Diversifier", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + 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 ErrInvalidLengthSolomachine + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Diversifier = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DataType", wireType) + } + m.DataType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DataType |= DataType(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *HeaderData) 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 ErrIntOverflowSolomachine + } + 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: HeaderData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: HeaderData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NewPubKey", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.NewPubKey == nil { + m.NewPubKey = &types.Any{} + } + if err := m.NewPubKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NewDiversifier", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + 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 ErrInvalidLengthSolomachine + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NewDiversifier = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ClientStateData) 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 ErrIntOverflowSolomachine + } + 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: ClientStateData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ClientStateData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) + if m.Path == nil { + m.Path = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientState", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ClientState == nil { + m.ClientState = &types.Any{} + } + if err := m.ClientState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ConsensusStateData) 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 ErrIntOverflowSolomachine + } + 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: ConsensusStateData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ConsensusStateData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) + if m.Path == nil { + m.Path = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsensusState", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ConsensusState == nil { + m.ConsensusState = &types.Any{} + } + if err := m.ConsensusState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ConnectionStateData) 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 ErrIntOverflowSolomachine + } + 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: ConnectionStateData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ConnectionStateData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) + if m.Path == nil { + m.Path = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Connection", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Connection == nil { + m.Connection = &types1.ConnectionEnd{} + } + if err := m.Connection.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ChannelStateData) 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 ErrIntOverflowSolomachine + } + 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: ChannelStateData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ChannelStateData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) + if m.Path == nil { + m.Path = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Channel", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Channel == nil { + m.Channel = &types2.Channel{} + } + if err := m.Channel.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PacketCommitmentData) 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 ErrIntOverflowSolomachine + } + 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: PacketCommitmentData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PacketCommitmentData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) + if m.Path == nil { + m.Path = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Commitment", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Commitment = append(m.Commitment[:0], dAtA[iNdEx:postIndex]...) + if m.Commitment == nil { + m.Commitment = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PacketAcknowledgementData) 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 ErrIntOverflowSolomachine + } + 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: PacketAcknowledgementData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PacketAcknowledgementData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) + if m.Path == nil { + m.Path = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Acknowledgement", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Acknowledgement = append(m.Acknowledgement[:0], dAtA[iNdEx:postIndex]...) + if m.Acknowledgement == nil { + m.Acknowledgement = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PacketReceiptAbsenceData) 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 ErrIntOverflowSolomachine + } + 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: PacketReceiptAbsenceData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PacketReceiptAbsenceData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) + if m.Path == nil { + m.Path = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *NextSequenceRecvData) 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 ErrIntOverflowSolomachine + } + 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: NextSequenceRecvData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: NextSequenceRecvData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) + if m.Path == nil { + m.Path = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NextSeqRecv", wireType) + } + m.NextSeqRecv = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NextSeqRecv |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipSolomachine(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowSolomachine + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowSolomachine + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowSolomachine + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthSolomachine + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupSolomachine + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthSolomachine + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthSolomachine = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowSolomachine = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupSolomachine = fmt.Errorf("proto: unexpected end of group") +) diff --git a/modules/core/02-client/types/codec_test.go b/modules/core/02-client/types/codec_test.go index 2db68dbb244..5dd9b99620c 100644 --- a/modules/core/02-client/types/codec_test.go +++ b/modules/core/02-client/types/codec_test.go @@ -118,7 +118,7 @@ func (suite *TypesTestSuite) TestPackClientMessage() { }{ { "solo machine header", - ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2).CreateHeader(), + ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2).CreateHeader("solomachine"), true, }, { diff --git a/modules/core/02-client/types/msgs_test.go b/modules/core/02-client/types/msgs_test.go index c51ebfce957..03148e73686 100644 --- a/modules/core/02-client/types/msgs_test.go +++ b/modules/core/02-client/types/msgs_test.go @@ -204,7 +204,7 @@ func (suite *TypesTestSuite) TestMarshalMsgUpdateClient() { { "solo machine client", func() { soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2) - msg, err = types.NewMsgUpdateClient(soloMachine.ClientID, soloMachine.CreateHeader(), suite.chainA.SenderAccount.GetAddress().String()) + msg, err = types.NewMsgUpdateClient(soloMachine.ClientID, soloMachine.CreateHeader(soloMachine.Diversifier), suite.chainA.SenderAccount.GetAddress().String()) suite.Require().NoError(err) }, }, @@ -293,7 +293,8 @@ func (suite *TypesTestSuite) TestMsgUpdateClient_ValidateBasic() { "valid - solomachine header", func() { soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2) - msg, err = types.NewMsgUpdateClient(soloMachine.ClientID, soloMachine.CreateHeader(), suite.chainA.SenderAccount.GetAddress().String()) + msg, err = types.NewMsgUpdateClient(soloMachine.ClientID, soloMachine.CreateHeader(soloMachine.Diversifier), suite.chainA.SenderAccount.GetAddress().String()) + suite.Require().NoError(err) }, true, diff --git a/modules/light-clients/06-solomachine/client_state.go b/modules/light-clients/06-solomachine/client_state.go index b985bbcf3be..159ea5052f6 100644 --- a/modules/light-clients/06-solomachine/client_state.go +++ b/modules/light-clients/06-solomachine/client_state.go @@ -105,232 +105,38 @@ func (cs ClientState) VerifyUpgradeAndUpdateState( return sdkerrors.Wrap(clienttypes.ErrInvalidUpgradeClient, "cannot upgrade solomachine client") } -// VerifyClientState verifies a proof of the client state of the running chain -// stored on the solo machine. -func (cs *ClientState) VerifyClientState( - store sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - prefix exported.Prefix, - counterpartyClientIdentifier string, - proof []byte, - clientState exported.ClientState, -) error { - // NOTE: the proof height sequence is incremented by one due to the connection handshake verification ordering - height = clienttypes.NewHeight(height.GetRevisionNumber(), height.GetRevisionHeight()+1) - - publicKey, sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof) - if err != nil { - return err - } - - clientPrefixedPath := commitmenttypes.NewMerklePath(host.FullClientStatePath(counterpartyClientIdentifier)) - path, err := commitmenttypes.ApplyPrefix(prefix, clientPrefixedPath) - if err != nil { - return err - } - - signBz, err := ClientStateSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path, clientState) - if err != nil { - return err - } - - if err := VerifySignature(publicKey, signBz, sigData); err != nil { - return err - } - - cs.Sequence++ - cs.ConsensusState.Timestamp = timestamp - setClientState(store, cdc, cs) - return nil -} - -// VerifyClientConsensusState verifies a proof of the consensus state of the -// running chain stored on the solo machine. -func (cs *ClientState) VerifyClientConsensusState( - store sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - counterpartyClientIdentifier string, - consensusHeight exported.Height, - prefix exported.Prefix, - proof []byte, - consensusState exported.ConsensusState, -) error { - // NOTE: the proof height sequence is incremented by two due to the connection handshake verification ordering - height = clienttypes.NewHeight(height.GetRevisionNumber(), height.GetRevisionHeight()+2) - - publicKey, sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof) - if err != nil { - return err - } - - clientPrefixedPath := commitmenttypes.NewMerklePath(host.FullConsensusStatePath(counterpartyClientIdentifier, consensusHeight)) - path, err := commitmenttypes.ApplyPrefix(prefix, clientPrefixedPath) - if err != nil { - return err - } - - signBz, err := ConsensusStateSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path, consensusState) - if err != nil { - return err - } - - if err := VerifySignature(publicKey, signBz, sigData); err != nil { - return err - } - - cs.Sequence++ - cs.ConsensusState.Timestamp = timestamp - setClientState(store, cdc, cs) - return nil -} - -// VerifyConnectionState verifies a proof of the connection state of the -// specified connection end stored on the target machine. -func (cs *ClientState) VerifyConnectionState( - store sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - prefix exported.Prefix, - proof []byte, - connectionID string, - connectionEnd exported.ConnectionI, -) error { - publicKey, sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof) - if err != nil { - return err - } - - connectionPath := commitmenttypes.NewMerklePath(host.ConnectionPath(connectionID)) - path, err := commitmenttypes.ApplyPrefix(prefix, connectionPath) - if err != nil { - return err - } - - signBz, err := ConnectionStateSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path, connectionEnd) - if err != nil { - return err - } - - if err := VerifySignature(publicKey, signBz, sigData); err != nil { - return err - } - - cs.Sequence++ - cs.ConsensusState.Timestamp = timestamp - setClientState(store, cdc, cs) - return nil -} - -// VerifyChannelState verifies a proof of the channel state of the specified -// channel end, under the specified port, stored on the target machine. -func (cs *ClientState) VerifyChannelState( - store sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - prefix exported.Prefix, - proof []byte, - portID, - channelID string, - channel exported.ChannelI, -) error { - publicKey, sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof) - if err != nil { - return err - } - - channelPath := commitmenttypes.NewMerklePath(host.ChannelPath(portID, channelID)) - path, err := commitmenttypes.ApplyPrefix(prefix, channelPath) - if err != nil { - return err - } - - signBz, err := ChannelStateSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path, channel) - if err != nil { - return err - } - - if err := VerifySignature(publicKey, signBz, sigData); err != nil { - return err - } - - cs.Sequence++ - cs.ConsensusState.Timestamp = timestamp - setClientState(store, cdc, cs) - return nil -} - -// VerifyPacketCommitment verifies a proof of an outgoing packet commitment at -// the specified port, specified channel, and specified sequence. -func (cs *ClientState) VerifyPacketCommitment( +// VerifyMembership is a generic proof verification method which verifies a proof of the existence of a value at a given CommitmentPath at the specified height. +// The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). +func (cs *ClientState) VerifyMembership( ctx sdk.Context, - store sdk.KVStore, + clientStore sdk.KVStore, cdc codec.BinaryCodec, height exported.Height, - _ uint64, - _ uint64, - prefix exported.Prefix, + delayTimePeriod uint64, + delayBlockPeriod uint64, proof []byte, - portID, - channelID string, - packetSequence uint64, - commitmentBytes []byte, + path []byte, + value []byte, ) error { - publicKey, sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof) - if err != nil { - return err - } - - commitmentPath := commitmenttypes.NewMerklePath(host.PacketCommitmentPath(portID, channelID, packetSequence)) - path, err := commitmenttypes.ApplyPrefix(prefix, commitmentPath) - if err != nil { - return err - } - - signBz, err := PacketCommitmentSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path, commitmentBytes) + publicKey, sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, proof) if err != nil { return err } - if err := VerifySignature(publicKey, signBz, sigData); err != nil { - return err - } - - cs.Sequence++ - cs.ConsensusState.Timestamp = timestamp - setClientState(store, cdc, cs) - return nil -} - -// VerifyPacketAcknowledgement verifies a proof of an incoming packet -// acknowledgement at the specified port, specified channel, and specified sequence. -func (cs *ClientState) VerifyPacketAcknowledgement( - ctx sdk.Context, - store sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - _ uint64, - _ uint64, - prefix exported.Prefix, - proof []byte, - portID, - channelID string, - packetSequence uint64, - acknowledgement []byte, -) error { - publicKey, sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof) - if err != nil { - return err + var merklePath commitmenttypes.MerklePath + if err := cdc.Unmarshal(path, &merklePath); err != nil { + return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "failed to unmarshal path into ICS 23 commitment merkle path") } - ackPath := commitmenttypes.NewMerklePath(host.PacketAcknowledgementPath(portID, channelID, packetSequence)) - path, err := commitmenttypes.ApplyPrefix(prefix, ackPath) - if err != nil { - return err + signBytes := &SignBytes{ + Sequence: sequence, + Timestamp: timestamp, + Diversifier: cs.ConsensusState.Diversifier, + Path: []byte(merklePath.String()), + Data: value, } - signBz, err := PacketAcknowledgementSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path, acknowledgement) + signBz, err := cdc.Marshal(signBytes) if err != nil { return err } @@ -341,79 +147,42 @@ func (cs *ClientState) VerifyPacketAcknowledgement( cs.Sequence++ cs.ConsensusState.Timestamp = timestamp - setClientState(store, cdc, cs) + setClientState(clientStore, cdc, cs) + return nil } -// VerifyPacketReceiptAbsence verifies a proof of the absence of an -// incoming packet receipt at the specified port, specified channel, and -// specified sequence. -func (cs *ClientState) VerifyPacketReceiptAbsence( +// VerifyNonMembership is a generic proof verification method which verifies the absense of a given CommitmentPath at a specified height. +// The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). +func (cs *ClientState) VerifyNonMembership( ctx sdk.Context, - store sdk.KVStore, + clientStore sdk.KVStore, cdc codec.BinaryCodec, height exported.Height, - _ uint64, - _ uint64, - prefix exported.Prefix, + delayTimePeriod uint64, + delayBlockPeriod uint64, proof []byte, - portID, - channelID string, - packetSequence uint64, + path []byte, ) error { - publicKey, sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof) + publicKey, sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, proof) if err != nil { return err } - receiptPath := commitmenttypes.NewMerklePath(host.PacketReceiptPath(portID, channelID, packetSequence)) - path, err := commitmenttypes.ApplyPrefix(prefix, receiptPath) - if err != nil { - return err - } - - signBz, err := PacketReceiptAbsenceSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path) - if err != nil { - return err - } - - if err := VerifySignature(publicKey, signBz, sigData); err != nil { - return err - } - - cs.Sequence++ - cs.ConsensusState.Timestamp = timestamp - setClientState(store, cdc, cs) - return nil -} - -// VerifyNextSequenceRecv verifies a proof of the next sequence number to be -// received of the specified channel at the specified port. -func (cs *ClientState) VerifyNextSequenceRecv( - ctx sdk.Context, - store sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - _ uint64, - _ uint64, - prefix exported.Prefix, - proof []byte, - portID, - channelID string, - nextSequenceRecv uint64, -) error { - publicKey, sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof) - if err != nil { - return err + var merklePath commitmenttypes.MerklePath + if err := cdc.Unmarshal(path, &merklePath); err != nil { + return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "failed to unmarshal path into ICS 23 commitment merkle path") } - nextSequenceRecvPath := commitmenttypes.NewMerklePath(host.NextSequenceRecvPath(portID, channelID)) - path, err := commitmenttypes.ApplyPrefix(prefix, nextSequenceRecvPath) - if err != nil { - return err + signBytes := &SignBytes{ + Sequence: sequence, + Timestamp: timestamp, + Diversifier: cs.ConsensusState.Diversifier, + Path: []byte(merklePath.String()), + Data: nil, } - signBz, err := NextSequenceRecvSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path, nextSequenceRecv) + signBz, err := cdc.Marshal(signBytes) if err != nil { return err } @@ -424,40 +193,8 @@ func (cs *ClientState) VerifyNextSequenceRecv( cs.Sequence++ cs.ConsensusState.Timestamp = timestamp - setClientState(store, cdc, cs) - return nil -} - -// VerifyMembership is a generic proof verification method which verifies a proof of the existence of a value at a given CommitmentPath at the specified height. -// The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). -func (cs *ClientState) VerifyMembership( - ctx sdk.Context, - clientStore sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - delayTimePeriod uint64, - delayBlockPeriod uint64, - proof []byte, - path []byte, - value []byte, -) error { - // TODO: Implement 06-solomachine VerifyMembership - return nil -} + setClientState(clientStore, cdc, cs) -// VerifyNonMembership is a generic proof verification method which verifies the absense of a given CommitmentPath at a specified height. -// The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). -func (cs *ClientState) VerifyNonMembership( - ctx sdk.Context, - clientStore sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - delayTimePeriod uint64, - delayBlockPeriod uint64, - proof []byte, - path []byte, -) error { - // TODO: Implement 06-solomachine VerifyNonMembership return nil } @@ -469,34 +206,22 @@ func produceVerificationArgs( cdc codec.BinaryCodec, cs *ClientState, height exported.Height, - prefix exported.Prefix, proof []byte, ) (cryptotypes.PubKey, signing.SignatureData, uint64, uint64, error) { if revision := height.GetRevisionNumber(); revision != 0 { return nil, nil, 0, 0, sdkerrors.Wrapf(sdkerrors.ErrInvalidHeight, "revision must be 0 for solomachine, got revision-number: %d", revision) } - // sequence is encoded in the revision height of height struct - sequence := height.GetRevisionHeight() - if prefix == nil { - return nil, nil, 0, 0, sdkerrors.Wrap(commitmenttypes.ErrInvalidPrefix, "prefix cannot be empty") - } - - _, ok := prefix.(*commitmenttypes.MerklePrefix) - if !ok { - return nil, nil, 0, 0, sdkerrors.Wrapf(commitmenttypes.ErrInvalidPrefix, "invalid prefix type %T, expected MerklePrefix", prefix) - } if proof == nil { return nil, nil, 0, 0, sdkerrors.Wrap(ErrInvalidProof, "proof cannot be empty") } - timestampedSigData := &TimestampedSignatureData{} - if err := cdc.Unmarshal(proof, timestampedSigData); err != nil { + var timestampedSigData TimestampedSignatureData + if err := cdc.Unmarshal(proof, ×tampedSigData); err != nil { return nil, nil, 0, 0, sdkerrors.Wrapf(err, "failed to unmarshal proof into type %T", timestampedSigData) } timestamp := timestampedSigData.Timestamp - if len(timestampedSigData.SignatureData) == 0 { return nil, nil, 0, 0, sdkerrors.Wrap(ErrInvalidProof, "signature data cannot be empty") } @@ -510,6 +235,8 @@ func produceVerificationArgs( return nil, nil, 0, 0, sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "consensus state cannot be empty") } + // sequence is encoded in the revision height of height struct + sequence := height.GetRevisionHeight() latestSequence := cs.GetLatestHeight().GetRevisionHeight() if latestSequence != sequence { return nil, nil, 0, 0, sdkerrors.Wrapf( diff --git a/modules/light-clients/06-solomachine/client_state_test.go b/modules/light-clients/06-solomachine/client_state_test.go index ab1d7c66a08..49fedc102a9 100644 --- a/modules/light-clients/06-solomachine/client_state_test.go +++ b/modules/light-clients/06-solomachine/client_state_test.go @@ -1,8 +1,9 @@ package solomachine_test import ( + sdk "github.com/cosmos/cosmos-sdk/types" + clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v3/modules/core/exported" @@ -18,13 +19,6 @@ const ( testPortID = "testportid" ) -var ( - prefix = &commitmenttypes.MerklePrefix{ - KeyPrefix: []byte("ibc"), - } - consensusHeight = clienttypes.ZeroHeight() -) - func (suite *SoloMachineTestSuite) TestStatus() { clientState := suite.solomachine.ClientState() // solo machine discards arguments @@ -144,213 +138,416 @@ func (suite *SoloMachineTestSuite) TestInitialize() { } } -func (suite *SoloMachineTestSuite) TestVerifyClientState() { - // create client for tendermint so we can use client state for verification - tmPath := ibctesting.NewPath(suite.chainA, suite.chainB) - suite.coordinator.SetupClients(tmPath) - clientState := suite.chainA.GetClientState(tmPath.EndpointA.ClientID) - path := suite.solomachine.GetClientStatePath(counterpartyClientIdentifier) - +func (suite *SoloMachineTestSuite) TestVerifyMembership() { // test singlesig and multisig public keys for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - value, err := solomachine.ClientStateSignBytes(suite.chainA.Codec, sm.Sequence, sm.Time, sm.Diversifier, path, clientState) - suite.Require().NoError(err) - - sig := sm.GenerateSignature(value) - - signatureDoc := &solomachine.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: sm.Time, - } - - proof, err := suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - - testCases := []struct { - name string + var ( clientState *solomachine.ClientState - prefix exported.Prefix + err error + height clienttypes.Height + path []byte proof []byte - expPass bool + testingPath *ibctesting.Path + signBytes solomachine.SignBytes + ) + + testCases := []struct { + name string + malleate func() + expPass bool }{ { - "successful verification", - sm.ClientState(), - prefix, - proof, + "success", + func() {}, true, }, { - "ApplyPrefix failed", - sm.ClientState(), - nil, - proof, - false, + "success: client state verification", + func() { + clientState = sm.ClientState() + clientStateBz, err := suite.chainA.Codec.Marshal(clientState) + suite.Require().NoError(err) + + merklePath := suite.solomachine.GetClientStatePath(counterpartyClientIdentifier) + signBytes = solomachine.SignBytes{ + Sequence: sm.GetHeight().GetRevisionHeight(), + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte(merklePath.String()), + Data: clientStateBz, + } + + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } + + path, err = suite.chainA.Codec.Marshal(&merklePath) + suite.Require().NoError(err) + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + + }, + true, }, { - "consensus state in client state is nil", - solomachine.NewClientState(1, nil, false), - prefix, - proof, - false, + "success: consensus state verification", + func() { + clientState = sm.ClientState() + consensusState := clientState.ConsensusState + consensusStateBz, err := suite.chainA.Codec.Marshal(consensusState) + suite.Require().NoError(err) + + merklePath := sm.GetConsensusStatePath(counterpartyClientIdentifier, height) + signBytes = solomachine.SignBytes{ + Sequence: sm.Sequence, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte(merklePath.String()), + Data: consensusStateBz, + } + + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } + + path, err = suite.chainA.Codec.Marshal(&merklePath) + suite.Require().NoError(err) + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + true, }, { - "client state latest height is less than sequence", - solomachine.NewClientState(sm.Sequence-1, - &solomachine.ConsensusState{ - Timestamp: sm.Time, - PublicKey: sm.ConsensusState().PublicKey, - }, false), - prefix, - proof, - false, + "success: connection state verification", + func() { + suite.coordinator.SetupConnections(testingPath) + + connectionEnd, found := suite.chainA.GetSimApp().IBCKeeper.ConnectionKeeper.GetConnection(suite.chainA.GetContext(), ibctesting.FirstConnectionID) + suite.Require().True(found) + + connectionEndBz, err := suite.chainA.Codec.Marshal(&connectionEnd) + suite.Require().NoError(err) + + merklePath := sm.GetConnectionStatePath(ibctesting.FirstConnectionID) + signBytes = solomachine.SignBytes{ + Sequence: sm.Sequence, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte(merklePath.String()), + Data: connectionEndBz, + } + + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } + + path, err = suite.chainA.Codec.Marshal(&merklePath) + suite.Require().NoError(err) + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + true, }, { - "consensus state timestamp is greater than signature", - solomachine.NewClientState(sm.Sequence, - &solomachine.ConsensusState{ - Timestamp: sm.Time + 1, - PublicKey: sm.ConsensusState().PublicKey, - }, false), - prefix, - proof, - false, - }, + "success: channel state verification", + func() { + suite.coordinator.SetupConnections(testingPath) + suite.coordinator.CreateMockChannels(testingPath) - { - "proof is nil", - sm.ClientState(), - prefix, - nil, - false, + channelEnd, found := suite.chainA.GetSimApp().IBCKeeper.ChannelKeeper.GetChannel(suite.chainA.GetContext(), ibctesting.MockPort, ibctesting.FirstChannelID) + suite.Require().True(found) + + channelEndBz, err := suite.chainA.Codec.Marshal(&channelEnd) + suite.Require().NoError(err) + + merklePath := sm.GetChannelStatePath(ibctesting.MockPort, ibctesting.FirstChannelID) + signBytes = solomachine.SignBytes{ + Sequence: sm.Sequence, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte(merklePath.String()), + Data: channelEndBz, + } + + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } + + path, err = suite.chainA.Codec.Marshal(&merklePath) + suite.Require().NoError(err) + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + true, }, { - "proof verification failed", - sm.ClientState(), - prefix, - suite.GetInvalidProof(), - false, - }, - } + "success: next sequence recv verification", + func() { + suite.coordinator.SetupConnections(testingPath) + suite.coordinator.CreateMockChannels(testingPath) - for _, tc := range testCases { - tc := tc + nextSeqRecv, found := suite.chainA.GetSimApp().IBCKeeper.ChannelKeeper.GetNextSequenceRecv(suite.chainA.GetContext(), ibctesting.MockPort, ibctesting.FirstChannelID) + suite.Require().True(found) - suite.Run(tc.name, func() { + merklePath := sm.GetNextSequenceRecvPath(ibctesting.MockPort, ibctesting.FirstChannelID) + signBytes = solomachine.SignBytes{ + Sequence: sm.Sequence, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte(merklePath.String()), + Data: sdk.Uint64ToBigEndian(nextSeqRecv), + } - var expSeq uint64 - if tc.clientState.ConsensusState != nil { - expSeq = tc.clientState.Sequence + 1 - } + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) - // NOTE: to replicate the ordering of connection handshake, we must decrement proof height by 1 - height := clienttypes.NewHeight(sm.GetHeight().GetRevisionNumber(), sm.GetHeight().GetRevisionHeight()-1) + sig := sm.GenerateSignature(signBz) - err := tc.clientState.VerifyClientState( - suite.store, suite.chainA.Codec, height, tc.prefix, counterpartyClientIdentifier, tc.proof, clientState, - ) + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } - if tc.expPass { + path, err = suite.chainA.Codec.Marshal(&merklePath) suite.Require().NoError(err) - suite.Require().Equal(expSeq, tc.clientState.Sequence) - suite.Require().Equal(expSeq, suite.GetSequenceFromStore(), "sequence not updated in the store (%d) on valid test case %s", suite.GetSequenceFromStore(), tc.name) - } else { - suite.Require().Error(err) - } - }) - } - } -} -func (suite *SoloMachineTestSuite) TestVerifyClientConsensusState() { - // create client for tendermint so we can use consensus state for verification - tmPath := ibctesting.NewPath(suite.chainA, suite.chainB) - suite.coordinator.SetupClients(tmPath) - clientState := suite.chainA.GetClientState(tmPath.EndpointA.ClientID) - consensusState, found := suite.chainA.GetConsensusState(tmPath.EndpointA.ClientID, clientState.GetLatestHeight()) - suite.Require().True(found) + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + true, + }, + { + "success: packet commitment verification", + func() { + packet := channeltypes.NewPacket( + ibctesting.MockPacketData, + 1, + ibctesting.MockPort, + ibctesting.FirstChannelID, + ibctesting.MockPort, + ibctesting.FirstChannelID, + clienttypes.NewHeight(0, 10), + 0, + ) + + commitmentBz := channeltypes.CommitPacket(suite.chainA.Codec, packet) + merklePath := sm.GetPacketCommitmentPath(ibctesting.MockPort, ibctesting.FirstChannelID) + signBytes = solomachine.SignBytes{ + Sequence: sm.Sequence, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte(merklePath.String()), + Data: commitmentBz, + } + + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) - path := suite.solomachine.GetConsensusStatePath(counterpartyClientIdentifier, consensusHeight) + sig := sm.GenerateSignature(signBz) - // test singlesig and multisig public keys - for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } - value, err := solomachine.ConsensusStateSignBytes(suite.chainA.Codec, sm.Sequence, sm.Time, sm.Diversifier, path, consensusState) - suite.Require().NoError(err) + path, err = suite.chainA.Codec.Marshal(&merklePath) + suite.Require().NoError(err) - sig := sm.GenerateSignature(value) - signatureDoc := &solomachine.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: sm.Time, - } + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + true, + }, + { + "success: packet acknowledgement verification", + func() { + merklePath := sm.GetPacketAcknowledgementPath(ibctesting.MockPort, ibctesting.FirstChannelID) + signBytes = solomachine.SignBytes{ + Sequence: sm.Sequence, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte(merklePath.String()), + Data: ibctesting.MockAcknowledgement, + } - proof, err := suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) - testCases := []struct { - name string - clientState *solomachine.ClientState - prefix exported.Prefix - proof []byte - expPass bool - }{ - { - "successful verification", - sm.ClientState(), - prefix, - proof, + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } + + path, err = suite.chainA.Codec.Marshal(&merklePath) + suite.Require().NoError(err) + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, true, }, { - "ApplyPrefix failed", - sm.ClientState(), - nil, - proof, - false, + "success: packet receipt verification", + func() { + merklePath := sm.GetPacketReceiptPath(ibctesting.MockPort, ibctesting.FirstChannelID) + signBytes = solomachine.SignBytes{ + Sequence: sm.Sequence, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte(merklePath.String()), + Data: []byte{byte(1)}, // packet receipt is stored as a single byte + } + + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } + + path, err = suite.chainA.Codec.Marshal(&merklePath) + suite.Require().NoError(err) + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + true, }, { "consensus state in client state is nil", - solomachine.NewClientState(1, nil, false), - prefix, - proof, + func() { + clientState = solomachine.NewClientState(1, nil, false) + }, false, }, { "client state latest height is less than sequence", - solomachine.NewClientState(sm.Sequence-1, - &solomachine.ConsensusState{ + func() { + consensusState := &solomachine.ConsensusState{ Timestamp: sm.Time, PublicKey: sm.ConsensusState().PublicKey, - }, false), - prefix, - proof, + } + + clientState = solomachine.NewClientState(sm.Sequence-1, consensusState, false) + }, + false, + }, + { + "height revision number is not zero", + func() { + height = clienttypes.NewHeight(1, sm.GetHeight().GetRevisionHeight()) + }, + false, + }, + { + "malformed merkle path fails to unmarshal", + func() { + path = []byte("invalid path") + }, + false, + }, + { + "malformed proof fails to unmarshal", + func() { + merklePath := suite.solomachine.GetClientStatePath(counterpartyClientIdentifier) + path, err = suite.chainA.Codec.Marshal(&merklePath) + suite.Require().NoError(err) + + proof = []byte("invalid proof") + }, false, }, { "consensus state timestamp is greater than signature", - solomachine.NewClientState(sm.Sequence, - &solomachine.ConsensusState{ + func() { + consensusState := &solomachine.ConsensusState{ Timestamp: sm.Time + 1, PublicKey: sm.ConsensusState().PublicKey, - }, false), - prefix, - proof, + } + + clientState = solomachine.NewClientState(sm.Sequence, consensusState, false) + }, false, }, + { + "signature data is nil", + func() { + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: nil, + Timestamp: sm.Time, + } + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + false, + }, + { + "consensus state public key is nil", + func() { + clientState.ConsensusState.PublicKey = nil + }, + false, + }, + { + "malformed signature data fails to unmarshal", + func() { + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: []byte("invalid signature data"), + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + false, + }, { "proof is nil", - sm.ClientState(), - prefix, - nil, + func() { + proof = nil + }, false, }, { "proof verification failed", - sm.ClientState(), - prefix, - suite.GetInvalidProof(), + func() { + signBytes.Data = []byte("invalid membership data value") + }, false, }, } @@ -359,22 +556,53 @@ func (suite *SoloMachineTestSuite) TestVerifyClientConsensusState() { tc := tc suite.Run(tc.name, func() { + suite.SetupTest() + testingPath = ibctesting.NewPath(suite.chainA, suite.chainB) + + clientState = sm.ClientState() + height = clienttypes.NewHeight(sm.GetHeight().GetRevisionNumber(), sm.GetHeight().GetRevisionHeight()) + + merklePath := commitmenttypes.NewMerklePath("ibc", "solomachine") + signBytes = solomachine.SignBytes{ + Sequence: sm.GetHeight().GetRevisionHeight(), + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte(merklePath.String()), + Data: []byte("solomachine"), + } - var expSeq uint64 - if tc.clientState.ConsensusState != nil { - expSeq = tc.clientState.Sequence + 1 + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, } - // NOTE: to replicate the ordering of connection handshake, we must decrement proof height by 1 - height := clienttypes.NewHeight(sm.GetHeight().GetRevisionNumber(), sm.GetHeight().GetRevisionHeight()-2) + path, err = suite.chainA.Codec.Marshal(&merklePath) + suite.Require().NoError(err) + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) - err := tc.clientState.VerifyClientConsensusState( - suite.store, suite.chainA.Codec, height, counterpartyClientIdentifier, consensusHeight, tc.prefix, tc.proof, consensusState, + tc.malleate() + + var expSeq uint64 + if clientState.ConsensusState != nil { + expSeq = clientState.Sequence + 1 + } + + err = clientState.VerifyMembership( + suite.chainA.GetContext(), suite.store, suite.chainA.Codec, + height, 0, 0, // solomachine does not check delay periods + proof, path, signBytes.Data, ) if tc.expPass { suite.Require().NoError(err) - suite.Require().Equal(expSeq, tc.clientState.Sequence) + suite.Require().Equal(expSeq, clientState.Sequence) suite.Require().Equal(expSeq, suite.GetSequenceFromStore(), "sequence not updated in the store (%d) on valid test case %s", suite.GetSequenceFromStore(), tc.name) } else { suite.Require().Error(err) @@ -384,464 +612,230 @@ func (suite *SoloMachineTestSuite) TestVerifyClientConsensusState() { } } -func (suite *SoloMachineTestSuite) TestVerifyConnectionState() { - counterparty := connectiontypes.NewCounterparty("clientB", testConnectionID, *prefix) - conn := connectiontypes.NewConnectionEnd(connectiontypes.OPEN, "clientA", counterparty, connectiontypes.ExportedVersionsToProto(connectiontypes.GetCompatibleVersions()), 0) - - path := suite.solomachine.GetConnectionStatePath(testConnectionID) - +func (suite *SoloMachineTestSuite) TestVerifyNonMembership() { // test singlesig and multisig public keys for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - value, err := solomachine.ConnectionStateSignBytes(suite.chainA.Codec, sm.Sequence, sm.Time, sm.Diversifier, path, conn) - suite.Require().NoError(err) - - sig := sm.GenerateSignature(value) - signatureDoc := &solomachine.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: sm.Time, - } - - proof, err := suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - - testCases := []struct { - name string + var ( clientState *solomachine.ClientState - prefix exported.Prefix + err error + height clienttypes.Height + path []byte proof []byte - expPass bool + signBytes solomachine.SignBytes + ) + + testCases := []struct { + name string + malleate func() + expPass bool }{ { - "successful verification", - sm.ClientState(), - prefix, - proof, + "success", + func() {}, true, }, { - "ApplyPrefix failed", - sm.ClientState(), - commitmenttypes.NewMerklePrefix([]byte{}), - proof, - false, - }, - { - "proof is nil", - sm.ClientState(), - prefix, - nil, - false, - }, - { - "proof verification failed", - sm.ClientState(), - prefix, - suite.GetInvalidProof(), - false, - }, - } - - for i, tc := range testCases { - tc := tc - - expSeq := tc.clientState.Sequence + 1 - - err := tc.clientState.VerifyConnectionState( - suite.store, suite.chainA.Codec, sm.GetHeight(), tc.prefix, tc.proof, testConnectionID, conn, - ) - - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - suite.Require().Equal(expSeq, tc.clientState.Sequence) - suite.Require().Equal(expSeq, suite.GetSequenceFromStore(), "sequence not updated in the store (%d) on valid test case %d: %s", suite.GetSequenceFromStore(), i, tc.name) - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - } - } - } -} - -func (suite *SoloMachineTestSuite) TestVerifyChannelState() { - counterparty := channeltypes.NewCounterparty(testPortID, testChannelID) - ch := channeltypes.NewChannel(channeltypes.OPEN, channeltypes.ORDERED, counterparty, []string{testConnectionID}, "1.0.0") - - path := suite.solomachine.GetChannelStatePath(testPortID, testChannelID) + "success: packet receipt absence verification", + func() { + merklePath := suite.solomachine.GetPacketReceiptPath(ibctesting.MockPort, ibctesting.FirstChannelID) + signBytes = solomachine.SignBytes{ + Sequence: sm.GetHeight().GetRevisionHeight(), + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte(merklePath.String()), + Data: nil, + } - // test singlesig and multisig public keys - for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) - value, err := solomachine.ChannelStateSignBytes(suite.chainA.Codec, sm.Sequence, sm.Time, sm.Diversifier, path, ch) - suite.Require().NoError(err) + sig := sm.GenerateSignature(signBz) - sig := sm.GenerateSignature(value) - signatureDoc := &solomachine.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: sm.Time, - } + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } - proof, err := suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) + path, err = suite.chainA.Codec.Marshal(&merklePath) + suite.Require().NoError(err) - testCases := []struct { - name string - clientState *solomachine.ClientState - prefix exported.Prefix - proof []byte - expPass bool - }{ - { - "successful verification", - sm.ClientState(), - prefix, - proof, + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, true, }, { - "ApplyPrefix failed", - sm.ClientState(), - nil, - proof, + "consensus state in client state is nil", + func() { + clientState = solomachine.NewClientState(1, nil, false) + }, false, }, { - "proof is nil", - sm.ClientState(), - prefix, - nil, + "client state latest height is less than sequence", + func() { + consensusState := &solomachine.ConsensusState{ + Timestamp: sm.Time, + PublicKey: sm.ConsensusState().PublicKey, + } + + clientState = solomachine.NewClientState(sm.Sequence-1, consensusState, false) + }, false, }, { - "proof verification failed", - sm.ClientState(), - prefix, - suite.GetInvalidProof(), + "height revision number is not zero", + func() { + height = clienttypes.NewHeight(1, sm.GetHeight().GetRevisionHeight()) + }, false, }, - } - - for i, tc := range testCases { - tc := tc - - expSeq := tc.clientState.Sequence + 1 - - err := tc.clientState.VerifyChannelState( - suite.store, suite.chainA.Codec, sm.GetHeight(), tc.prefix, tc.proof, testPortID, testChannelID, ch, - ) - - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - suite.Require().Equal(expSeq, tc.clientState.Sequence) - suite.Require().Equal(expSeq, suite.GetSequenceFromStore(), "sequence not updated in the store (%d) on valid test case %d: %s", suite.GetSequenceFromStore(), i, tc.name) - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - } - } - } -} - -func (suite *SoloMachineTestSuite) TestVerifyPacketCommitment() { - commitmentBytes := []byte("COMMITMENT BYTES") - - // test singlesig and multisig public keys - for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - path := sm.GetPacketCommitmentPath(testPortID, testChannelID) - - value, err := solomachine.PacketCommitmentSignBytes(suite.chainA.Codec, sm.Sequence, sm.Time, sm.Diversifier, path, commitmentBytes) - suite.Require().NoError(err) - - sig := sm.GenerateSignature(value) - signatureDoc := &solomachine.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: sm.Time, - } - - proof, err := suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - - testCases := []struct { - name string - clientState *solomachine.ClientState - prefix exported.Prefix - proof []byte - expPass bool - }{ { - "successful verification", - sm.ClientState(), - prefix, - proof, - true, + "malformed merkle path fails to unmarshal", + func() { + path = []byte("invalid path") + }, + false, }, { - "ApplyPrefix failed", - sm.ClientState(), - commitmenttypes.NewMerklePrefix([]byte{}), - proof, + "malformed proof fails to unmarshal", + func() { + merklePath := suite.solomachine.GetClientStatePath(counterpartyClientIdentifier) + path, err = suite.chainA.Codec.Marshal(&merklePath) + suite.Require().NoError(err) + + proof = []byte("invalid proof") + }, false, }, { - "proof is nil", - sm.ClientState(), - prefix, - nil, + "consensus state timestamp is greater than signature", + func() { + consensusState := &solomachine.ConsensusState{ + Timestamp: sm.Time + 1, + PublicKey: sm.ConsensusState().PublicKey, + } + + clientState = solomachine.NewClientState(sm.Sequence, consensusState, false) + }, false, }, { - "proof verification failed", - sm.ClientState(), - prefix, - suite.GetInvalidProof(), + "signature data is nil", + func() { + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: nil, + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, false, }, - } - - for i, tc := range testCases { - tc := tc - - expSeq := tc.clientState.Sequence + 1 - ctx := suite.chainA.GetContext() - - err := tc.clientState.VerifyPacketCommitment( - ctx, suite.store, suite.chainA.Codec, sm.GetHeight(), 0, 0, tc.prefix, tc.proof, testPortID, testChannelID, sm.Sequence, commitmentBytes, - ) - - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - suite.Require().Equal(expSeq, suite.GetSequenceFromStore(), "sequence not updated in the store (%d) on valid test case %d: %s", suite.GetSequenceFromStore(), i, tc.name) - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - } - } - } -} - -func (suite *SoloMachineTestSuite) TestVerifyPacketAcknowledgement() { - ack := []byte("ACK") - // test singlesig and multisig public keys - for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - path := sm.GetPacketAcknowledgementPath(testPortID, testChannelID) - - value, err := solomachine.PacketAcknowledgementSignBytes(suite.chainA.Codec, sm.Sequence, sm.Time, sm.Diversifier, path, ack) - suite.Require().NoError(err) - - sig := sm.GenerateSignature(value) - signatureDoc := &solomachine.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: sm.Time, - } - - proof, err := suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - - testCases := []struct { - name string - clientState *solomachine.ClientState - prefix exported.Prefix - proof []byte - expPass bool - }{ { - "successful verification", - sm.ClientState(), - prefix, - proof, - true, + "consensus state public key is nil", + func() { + clientState.ConsensusState.PublicKey = nil + }, + false, }, { - "ApplyPrefix failed", - sm.ClientState(), - commitmenttypes.NewMerklePrefix([]byte{}), - proof, + "malformed signature data fails to unmarshal", + func() { + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: []byte("invalid signature data"), + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, false, }, { "proof is nil", - sm.ClientState(), - prefix, - nil, + func() { + proof = nil + }, false, }, { "proof verification failed", - sm.ClientState(), - prefix, - suite.GetInvalidProof(), - false, - }, - } - - for i, tc := range testCases { - tc := tc - - expSeq := tc.clientState.Sequence + 1 - ctx := suite.chainA.GetContext() + func() { + signBytes.Data = []byte("invalid non-membership data value") - err := tc.clientState.VerifyPacketAcknowledgement( - ctx, suite.store, suite.chainA.Codec, sm.GetHeight(), 0, 0, tc.prefix, tc.proof, testPortID, testChannelID, sm.Sequence, ack, - ) - - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - suite.Require().Equal(expSeq, suite.GetSequenceFromStore(), "sequence not updated in the store (%d) on valid test case %d: %s", suite.GetSequenceFromStore(), i, tc.name) - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - } - } - } -} - -func (suite *SoloMachineTestSuite) TestVerifyPacketReceiptAbsence() { - // test singlesig and multisig public keys - for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - // absence uses receipt path as well - path := sm.GetPacketReceiptPath(testPortID, testChannelID) - - value, err := solomachine.PacketReceiptAbsenceSignBytes(suite.chainA.Codec, sm.Sequence, sm.Time, sm.Diversifier, path) - suite.Require().NoError(err) + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) - sig := sm.GenerateSignature(value) - signatureDoc := &solomachine.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: sm.Time, - } + sig := sm.GenerateSignature(signBz) - proof, err := suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } - testCases := []struct { - name string - clientState *solomachine.ClientState - prefix exported.Prefix - proof []byte - expPass bool - }{ - { - "successful verification", - sm.ClientState(), - prefix, - proof, - true, - }, - { - "ApplyPrefix failed", - sm.ClientState(), - commitmenttypes.NewMerklePrefix([]byte{}), - proof, - false, - }, - { - "proof is nil", - sm.ClientState(), - prefix, - nil, - false, - }, - { - "proof verification failed", - sm.ClientState(), - prefix, - suite.GetInvalidProof(), + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, false, }, } - for i, tc := range testCases { + for _, tc := range testCases { tc := tc - expSeq := tc.clientState.Sequence + 1 - ctx := suite.chainA.GetContext() - - err := tc.clientState.VerifyPacketReceiptAbsence( - ctx, suite.store, suite.chainA.Codec, sm.GetHeight(), 0, 0, tc.prefix, tc.proof, testPortID, testChannelID, sm.Sequence, - ) - - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - suite.Require().Equal(expSeq, suite.GetSequenceFromStore(), "sequence not updated in the store (%d) on valid test case %d: %s", suite.GetSequenceFromStore(), i, tc.name) - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - } - } - } -} - -func (suite *SoloMachineTestSuite) TestVerifyNextSeqRecv() { - // test singlesig and multisig public keys - for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + suite.Run(tc.name, func() { + clientState = sm.ClientState() + height = clienttypes.NewHeight(sm.GetHeight().GetRevisionNumber(), sm.GetHeight().GetRevisionHeight()) + + merklePath := commitmenttypes.NewMerklePath("ibc", "solomachine") + signBytes = solomachine.SignBytes{ + Sequence: sm.GetHeight().GetRevisionHeight(), + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte(merklePath.String()), + Data: nil, + } - nextSeqRecv := sm.Sequence + 1 - path := sm.GetNextSequenceRecvPath(testPortID, testChannelID) + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) - value, err := solomachine.NextSequenceRecvSignBytes(suite.chainA.Codec, sm.Sequence, sm.Time, sm.Diversifier, path, nextSeqRecv) - suite.Require().NoError(err) + sig := sm.GenerateSignature(signBz) - sig := sm.GenerateSignature(value) - signatureDoc := &solomachine.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: sm.Time, - } + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } - proof, err := suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) + path, err = suite.chainA.Codec.Marshal(&merklePath) + suite.Require().NoError(err) - testCases := []struct { - name string - clientState *solomachine.ClientState - prefix exported.Prefix - proof []byte - expPass bool - }{ - { - "successful verification", - sm.ClientState(), - prefix, - proof, - true, - }, - { - "ApplyPrefix failed", - sm.ClientState(), - commitmenttypes.NewMerklePrefix([]byte{}), - proof, - false, - }, - { - "proof is nil", - sm.ClientState(), - prefix, - nil, - false, - }, - { - "proof verification failed", - sm.ClientState(), - prefix, - suite.GetInvalidProof(), - false, - }, - } + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) - for i, tc := range testCases { - tc := tc + tc.malleate() - expSeq := tc.clientState.Sequence + 1 - ctx := suite.chainA.GetContext() + var expSeq uint64 + if clientState.ConsensusState != nil { + expSeq = clientState.Sequence + 1 + } - err := tc.clientState.VerifyNextSequenceRecv( - ctx, suite.store, suite.chainA.Codec, sm.GetHeight(), 0, 0, tc.prefix, tc.proof, testPortID, testChannelID, nextSeqRecv, - ) + err = clientState.VerifyNonMembership( + suite.chainA.GetContext(), suite.store, suite.chainA.Codec, + height, 0, 0, // solomachine does not check delay periods + proof, path, + ) - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - suite.Require().Equal(expSeq, tc.clientState.Sequence) - suite.Require().Equal(expSeq, suite.GetSequenceFromStore(), "sequence not updated in the store (%d) on valid test case %d: %s", suite.GetSequenceFromStore(), i, tc.name) - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - } + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(expSeq, clientState.Sequence) + suite.Require().Equal(expSeq, suite.GetSequenceFromStore(), "sequence not updated in the store (%d) on valid test case %s", suite.GetSequenceFromStore(), tc.name) + } else { + suite.Require().Error(err) + } + }) } } } diff --git a/modules/light-clients/06-solomachine/codec.go b/modules/light-clients/06-solomachine/codec.go index a857433ed56..ed040d7b2c2 100644 --- a/modules/light-clients/06-solomachine/codec.go +++ b/modules/light-clients/06-solomachine/codec.go @@ -6,7 +6,6 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/tx/signing" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" "github.com/cosmos/ibc-go/v3/modules/core/exported" ) @@ -41,91 +40,3 @@ func UnmarshalSignatureData(cdc codec.BinaryCodec, data []byte) (signing.Signatu return sigData, nil } - -// UnmarshalDataByType attempts to unmarshal the data to the specified type. An error is -// return if it fails. -func UnmarshalDataByType(cdc codec.BinaryCodec, dataType DataType, data []byte) (Data, error) { - if len(data) == 0 { - return nil, sdkerrors.Wrap(ErrInvalidSignatureAndData, "data cannot be empty") - } - - switch dataType { - case UNSPECIFIED: - return nil, sdkerrors.Wrap(ErrInvalidDataType, "data type cannot be UNSPECIFIED") - - case CLIENT: - clientData := &ClientStateData{} - if err := cdc.Unmarshal(data, clientData); err != nil { - return nil, err - } - - // unpack any - if _, err := clienttypes.UnpackClientState(clientData.ClientState); err != nil { - return nil, err - } - return clientData, nil - - case CONSENSUS: - consensusData := &ConsensusStateData{} - if err := cdc.Unmarshal(data, consensusData); err != nil { - return nil, err - } - - // unpack any - if _, err := clienttypes.UnpackConsensusState(consensusData.ConsensusState); err != nil { - return nil, err - } - return consensusData, nil - - case CONNECTION: - connectionData := &ConnectionStateData{} - if err := cdc.Unmarshal(data, connectionData); err != nil { - return nil, err - } - - return connectionData, nil - - case CHANNEL: - channelData := &ChannelStateData{} - if err := cdc.Unmarshal(data, channelData); err != nil { - return nil, err - } - - return channelData, nil - - case PACKETCOMMITMENT: - commitmentData := &PacketCommitmentData{} - if err := cdc.Unmarshal(data, commitmentData); err != nil { - return nil, err - } - - return commitmentData, nil - - case PACKETACKNOWLEDGEMENT: - ackData := &PacketAcknowledgementData{} - if err := cdc.Unmarshal(data, ackData); err != nil { - return nil, err - } - - return ackData, nil - - case PACKETRECEIPTABSENCE: - receiptAbsenceData := &PacketReceiptAbsenceData{} - if err := cdc.Unmarshal(data, receiptAbsenceData); err != nil { - return nil, err - } - - return receiptAbsenceData, nil - - case NEXTSEQUENCERECV: - nextSeqRecvData := &NextSequenceRecvData{} - if err := cdc.Unmarshal(data, nextSeqRecvData); err != nil { - return nil, err - } - - return nextSeqRecvData, nil - - default: - return nil, sdkerrors.Wrapf(ErrInvalidDataType, "unsupported data type %T", dataType) - } -} diff --git a/modules/light-clients/06-solomachine/codec_test.go b/modules/light-clients/06-solomachine/codec_test.go deleted file mode 100644 index ff7d5084043..00000000000 --- a/modules/light-clients/06-solomachine/codec_test.go +++ /dev/null @@ -1,190 +0,0 @@ -package solomachine_test - -import ( - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - solomachine "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" - ibctesting "github.com/cosmos/ibc-go/v3/testing" -) - -func (suite SoloMachineTestSuite) TestUnmarshalDataByType() { - var ( - data []byte - err error - ) - - // test singlesig and multisig public keys - for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - cdc := suite.chainA.App.AppCodec() - cases := []struct { - name string - dataType solomachine.DataType - malleate func() - expPass bool - }{ - { - "empty data", solomachine.CLIENT, func() { - data = []byte{} - }, false, - }, - { - "unspecified", solomachine.UNSPECIFIED, func() { - path := sm.GetClientStatePath(counterpartyClientIdentifier) - data, err = solomachine.ClientStateDataBytes(cdc, path, sm.ClientState()) - suite.Require().NoError(err) - }, false, - }, - { - "client", solomachine.CLIENT, func() { - path := sm.GetClientStatePath(counterpartyClientIdentifier) - data, err = solomachine.ClientStateDataBytes(cdc, path, sm.ClientState()) - suite.Require().NoError(err) - }, true, - }, - { - "bad client (provides consensus state data)", solomachine.CLIENT, func() { - path := sm.GetConsensusStatePath(counterpartyClientIdentifier, clienttypes.NewHeight(0, 5)) - data, err = solomachine.ConsensusStateDataBytes(cdc, path, sm.ConsensusState()) - suite.Require().NoError(err) - }, false, - }, - { - "consensus", solomachine.CONSENSUS, func() { - path := sm.GetConsensusStatePath(counterpartyClientIdentifier, clienttypes.NewHeight(0, 5)) - data, err = solomachine.ConsensusStateDataBytes(cdc, path, sm.ConsensusState()) - suite.Require().NoError(err) - - }, true, - }, - { - "bad consensus (provides client state data)", solomachine.CONSENSUS, func() { - path := sm.GetClientStatePath(counterpartyClientIdentifier) - data, err = solomachine.ClientStateDataBytes(cdc, path, sm.ClientState()) - suite.Require().NoError(err) - }, false, - }, - { - "connection", solomachine.CONNECTION, func() { - counterparty := connectiontypes.NewCounterparty("clientB", testConnectionID, *prefix) - conn := connectiontypes.NewConnectionEnd(connectiontypes.OPEN, "clientA", counterparty, connectiontypes.ExportedVersionsToProto(connectiontypes.GetCompatibleVersions()), 0) - path := sm.GetConnectionStatePath("connectionID") - - data, err = solomachine.ConnectionStateDataBytes(cdc, path, conn) - suite.Require().NoError(err) - - }, true, - }, - { - "bad connection (uses channel data)", solomachine.CONNECTION, func() { - counterparty := channeltypes.NewCounterparty(testPortID, testChannelID) - ch := channeltypes.NewChannel(channeltypes.OPEN, channeltypes.ORDERED, counterparty, []string{testConnectionID}, "1.0.0") - path := sm.GetChannelStatePath("portID", "channelID") - - data, err = solomachine.ChannelStateDataBytes(cdc, path, ch) - suite.Require().NoError(err) - }, false, - }, - { - "channel", solomachine.CHANNEL, func() { - counterparty := channeltypes.NewCounterparty(testPortID, testChannelID) - ch := channeltypes.NewChannel(channeltypes.OPEN, channeltypes.ORDERED, counterparty, []string{testConnectionID}, "1.0.0") - path := sm.GetChannelStatePath("portID", "channelID") - - data, err = solomachine.ChannelStateDataBytes(cdc, path, ch) - suite.Require().NoError(err) - }, true, - }, - { - "bad channel (uses connection data)", solomachine.CHANNEL, func() { - counterparty := connectiontypes.NewCounterparty("clientB", testConnectionID, *prefix) - conn := connectiontypes.NewConnectionEnd(connectiontypes.OPEN, "clientA", counterparty, connectiontypes.ExportedVersionsToProto(connectiontypes.GetCompatibleVersions()), 0) - path := sm.GetConnectionStatePath("connectionID") - - data, err = solomachine.ConnectionStateDataBytes(cdc, path, conn) - suite.Require().NoError(err) - - }, false, - }, - { - "packet commitment", solomachine.PACKETCOMMITMENT, func() { - commitment := []byte("packet commitment") - path := sm.GetPacketCommitmentPath("portID", "channelID") - - data, err = solomachine.PacketCommitmentDataBytes(cdc, path, commitment) - suite.Require().NoError(err) - }, true, - }, - { - "bad packet commitment (uses next seq recv)", solomachine.PACKETCOMMITMENT, func() { - path := sm.GetNextSequenceRecvPath("portID", "channelID") - - data, err = solomachine.NextSequenceRecvDataBytes(cdc, path, 10) - suite.Require().NoError(err) - }, false, - }, - { - "packet acknowledgement", solomachine.PACKETACKNOWLEDGEMENT, func() { - commitment := []byte("packet acknowledgement") - path := sm.GetPacketAcknowledgementPath("portID", "channelID") - - data, err = solomachine.PacketAcknowledgementDataBytes(cdc, path, commitment) - suite.Require().NoError(err) - }, true, - }, - { - "bad packet acknowledgement (uses next sequence recv)", solomachine.PACKETACKNOWLEDGEMENT, func() { - path := sm.GetNextSequenceRecvPath("portID", "channelID") - - data, err = solomachine.NextSequenceRecvDataBytes(cdc, path, 10) - suite.Require().NoError(err) - }, false, - }, - { - "packet acknowledgement absence", solomachine.PACKETRECEIPTABSENCE, func() { - path := sm.GetPacketReceiptPath("portID", "channelID") - - data, err = solomachine.PacketReceiptAbsenceDataBytes(cdc, path) - suite.Require().NoError(err) - }, true, - }, - { - "next sequence recv", solomachine.NEXTSEQUENCERECV, func() { - path := sm.GetNextSequenceRecvPath("portID", "channelID") - - data, err = solomachine.NextSequenceRecvDataBytes(cdc, path, 10) - suite.Require().NoError(err) - }, true, - }, - { - "bad next sequence recv (uses packet commitment)", solomachine.NEXTSEQUENCERECV, func() { - commitment := []byte("packet commitment") - path := sm.GetPacketCommitmentPath("portID", "channelID") - - data, err = solomachine.PacketCommitmentDataBytes(cdc, path, commitment) - suite.Require().NoError(err) - }, false, - }, - } - - for _, tc := range cases { - tc := tc - - suite.Run(tc.name, func() { - tc.malleate() - - data, err := solomachine.UnmarshalDataByType(cdc, tc.dataType, data) - - if tc.expPass { - suite.Require().NoError(err) - suite.Require().NotNil(data) - } else { - suite.Require().Error(err) - suite.Require().Nil(data) - } - }) - } - } - -} diff --git a/modules/light-clients/06-solomachine/errors.go b/modules/light-clients/06-solomachine/errors.go index 73ca5999dbc..7f41349337d 100644 --- a/modules/light-clients/06-solomachine/errors.go +++ b/modules/light-clients/06-solomachine/errors.go @@ -14,5 +14,4 @@ var ( ErrInvalidSignatureAndData = sdkerrors.Register(SubModuleName, 4, "invalid signature and data") ErrSignatureVerificationFailed = sdkerrors.Register(SubModuleName, 5, "signature verification failed") ErrInvalidProof = sdkerrors.Register(SubModuleName, 6, "invalid solo machine proof") - ErrInvalidDataType = sdkerrors.Register(SubModuleName, 7, "invalid data type") ) diff --git a/modules/light-clients/06-solomachine/header_test.go b/modules/light-clients/06-solomachine/header_test.go index 5b02b41c555..a2b96ea7d62 100644 --- a/modules/light-clients/06-solomachine/header_test.go +++ b/modules/light-clients/06-solomachine/header_test.go @@ -10,7 +10,7 @@ func (suite *SoloMachineTestSuite) TestHeaderValidateBasic() { // test singlesig and multisig public keys for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - header := sm.CreateHeader() + header := sm.CreateHeader(sm.Diversifier) cases := []struct { name string diff --git a/modules/light-clients/06-solomachine/misbehaviour.go b/modules/light-clients/06-solomachine/misbehaviour.go index 430ed61c9be..6e58a8c76f8 100644 --- a/modules/light-clients/06-solomachine/misbehaviour.go +++ b/modules/light-clients/06-solomachine/misbehaviour.go @@ -61,8 +61,8 @@ func (sd SignatureAndData) ValidateBasic() error { if len(sd.Data) == 0 { return sdkerrors.Wrap(ErrInvalidSignatureAndData, "data for signature cannot be empty") } - if sd.DataType == UNSPECIFIED { - return sdkerrors.Wrap(ErrInvalidSignatureAndData, "data type cannot be UNSPECIFIED") + if len(sd.Path) == 0 { + return sdkerrors.Wrap(ErrInvalidSignatureAndData, "path for signature cannot be empty") } if sd.Timestamp == 0 { return sdkerrors.Wrap(ErrInvalidSignatureAndData, "timestamp cannot be 0") diff --git a/modules/light-clients/06-solomachine/misbehaviour_handle.go b/modules/light-clients/06-solomachine/misbehaviour_handle.go index c5f53239fd2..80eb3f04097 100644 --- a/modules/light-clients/06-solomachine/misbehaviour_handle.go +++ b/modules/light-clients/06-solomachine/misbehaviour_handle.go @@ -2,27 +2,28 @@ package solomachine import ( "github.com/cosmos/cosmos-sdk/codec" + + commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" ) // verifySignatureAndData verifies that the currently registered public key has signed // over the provided data and that the data is valid. The data is valid if it can be // unmarshaled into the specified data type. func (cs ClientState) verifySignatureAndData(cdc codec.BinaryCodec, misbehaviour *Misbehaviour, sigAndData *SignatureAndData) error { - // do not check misbehaviour timestamp since we want to allow processing of past misbehaviour - - // ensure data can be unmarshaled to the specified data type - if _, err := UnmarshalDataByType(cdc, sigAndData.DataType, sigAndData.Data); err != nil { + if err := cdc.Unmarshal(sigAndData.Path, new(commitmenttypes.MerklePath)); err != nil { return err } - data, err := MisbehaviourSignBytes( - cdc, - misbehaviour.Sequence, sigAndData.Timestamp, - cs.ConsensusState.Diversifier, - sigAndData.DataType, - sigAndData.Data, - ) + signBytes := SignBytes{ + Sequence: misbehaviour.Sequence, + Timestamp: sigAndData.Timestamp, + Diversifier: cs.ConsensusState.Diversifier, + Path: sigAndData.Path, + Data: sigAndData.Data, + } + + data, err := cdc.Marshal(&signBytes) if err != nil { return err } diff --git a/modules/light-clients/06-solomachine/misbehaviour_test.go b/modules/light-clients/06-solomachine/misbehaviour_test.go index 0bb533a7660..552fc9c7e57 100644 --- a/modules/light-clients/06-solomachine/misbehaviour_test.go +++ b/modules/light-clients/06-solomachine/misbehaviour_test.go @@ -83,15 +83,15 @@ func (suite *SoloMachineTestSuite) TestMisbehaviourValidateBasic() { false, }, { - "data type for SignatureOne is unspecified", + "data path for SignatureOne is unspecified", func(misbehaviour *solomachine.Misbehaviour) { - misbehaviour.SignatureOne.DataType = solomachine.UNSPECIFIED + misbehaviour.SignatureOne.Path = []byte{} }, false, }, { - "data type for SignatureTwo is unspecified", + "data path for SignatureTwo is unspecified", func(misbehaviour *solomachine.Misbehaviour) { - misbehaviour.SignatureTwo.DataType = solomachine.UNSPECIFIED + misbehaviour.SignatureTwo.Path = []byte{} }, false, }, { diff --git a/modules/light-clients/06-solomachine/proof.go b/modules/light-clients/06-solomachine/proof.go index cdbd8f19983..386830340cf 100644 --- a/modules/light-clients/06-solomachine/proof.go +++ b/modules/light-clients/06-solomachine/proof.go @@ -1,17 +1,10 @@ package solomachine import ( - "github.com/cosmos/cosmos-sdk/codec" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/crypto/types/multisig" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/tx/signing" - - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" ) // VerifySignature verifies if the the provided public key generated the signature @@ -48,429 +41,3 @@ func VerifySignature(pubKey cryptotypes.PubKey, signBytes []byte, sigData signin return nil } - -// MisbehaviourSignBytes returns the sign bytes for verification of misbehaviour. -func MisbehaviourSignBytes( - cdc codec.BinaryCodec, - sequence, timestamp uint64, - diversifier string, - dataType DataType, - data []byte) ([]byte, error) { - signBytes := &SignBytes{ - Sequence: sequence, - Timestamp: timestamp, - Diversifier: diversifier, - DataType: dataType, - Data: data, - } - - return cdc.Marshal(signBytes) -} - -// HeaderSignBytes returns the sign bytes for verification of misbehaviour. -func HeaderSignBytes( - cdc codec.BinaryCodec, - header *Header, -) ([]byte, error) { - data := &HeaderData{ - NewPubKey: header.NewPublicKey, - NewDiversifier: header.NewDiversifier, - } - - dataBz, err := cdc.Marshal(data) - if err != nil { - return nil, err - } - - signBytes := &SignBytes{ - Sequence: header.Sequence, - Timestamp: header.Timestamp, - Diversifier: header.NewDiversifier, - DataType: HEADER, - Data: dataBz, - } - - return cdc.Marshal(signBytes) -} - -// ClientStateSignBytes returns the sign bytes for verification of the -// client state. -func ClientStateSignBytes( - cdc codec.BinaryCodec, - sequence, timestamp uint64, - diversifier string, - path commitmenttypes.MerklePath, - clientState exported.ClientState, -) ([]byte, error) { - dataBz, err := ClientStateDataBytes(cdc, path, clientState) - if err != nil { - return nil, err - } - - signBytes := &SignBytes{ - Sequence: sequence, - Timestamp: timestamp, - Diversifier: diversifier, - DataType: CLIENT, - Data: dataBz, - } - - return cdc.Marshal(signBytes) -} - -// ClientStateDataBytes returns the client state data bytes used in constructing -// SignBytes. -func ClientStateDataBytes( - cdc codec.BinaryCodec, - path commitmenttypes.MerklePath, // nolint: interfacer - clientState exported.ClientState, -) ([]byte, error) { - any, err := clienttypes.PackClientState(clientState) - if err != nil { - return nil, err - } - - data := &ClientStateData{ - Path: []byte(path.String()), - ClientState: any, - } - - dataBz, err := cdc.Marshal(data) - if err != nil { - return nil, err - } - - return dataBz, nil -} - -// ConsensusStateSignBytes returns the sign bytes for verification of the -// consensus state. -func ConsensusStateSignBytes( - cdc codec.BinaryCodec, - sequence, timestamp uint64, - diversifier string, - path commitmenttypes.MerklePath, - consensusState exported.ConsensusState, -) ([]byte, error) { - dataBz, err := ConsensusStateDataBytes(cdc, path, consensusState) - if err != nil { - return nil, err - } - - signBytes := &SignBytes{ - Sequence: sequence, - Timestamp: timestamp, - Diversifier: diversifier, - DataType: CONSENSUS, - Data: dataBz, - } - - return cdc.Marshal(signBytes) -} - -// ConsensusStateDataBytes returns the consensus state data bytes used in constructing -// SignBytes. -func ConsensusStateDataBytes( - cdc codec.BinaryCodec, - path commitmenttypes.MerklePath, // nolint: interfacer - consensusState exported.ConsensusState, -) ([]byte, error) { - any, err := clienttypes.PackConsensusState(consensusState) - if err != nil { - return nil, err - } - - data := &ConsensusStateData{ - Path: []byte(path.String()), - ConsensusState: any, - } - - dataBz, err := cdc.Marshal(data) - if err != nil { - return nil, err - } - - return dataBz, nil -} - -// ConnectionStateSignBytes returns the sign bytes for verification of the -// connection state. -func ConnectionStateSignBytes( - cdc codec.BinaryCodec, - sequence, timestamp uint64, - diversifier string, - path commitmenttypes.MerklePath, - connectionEnd exported.ConnectionI, -) ([]byte, error) { - dataBz, err := ConnectionStateDataBytes(cdc, path, connectionEnd) - if err != nil { - return nil, err - } - - signBytes := &SignBytes{ - Sequence: sequence, - Timestamp: timestamp, - Diversifier: diversifier, - DataType: CONNECTION, - Data: dataBz, - } - - return cdc.Marshal(signBytes) -} - -// ConnectionStateDataBytes returns the connection state data bytes used in constructing -// SignBytes. -func ConnectionStateDataBytes( - cdc codec.BinaryCodec, - path commitmenttypes.MerklePath, // nolint: interfacer - connectionEnd exported.ConnectionI, -) ([]byte, error) { - connection, ok := connectionEnd.(connectiontypes.ConnectionEnd) - if !ok { - return nil, sdkerrors.Wrapf( - connectiontypes.ErrInvalidConnection, - "expected type %T, got %T", connectiontypes.ConnectionEnd{}, connectionEnd, - ) - } - - data := &ConnectionStateData{ - Path: []byte(path.String()), - Connection: &connection, - } - - dataBz, err := cdc.Marshal(data) - if err != nil { - return nil, err - } - - return dataBz, nil -} - -// ChannelStateSignBytes returns the sign bytes for verification of the -// channel state. -func ChannelStateSignBytes( - cdc codec.BinaryCodec, - sequence, timestamp uint64, - diversifier string, - path commitmenttypes.MerklePath, - channelEnd exported.ChannelI, -) ([]byte, error) { - dataBz, err := ChannelStateDataBytes(cdc, path, channelEnd) - if err != nil { - return nil, err - } - - signBytes := &SignBytes{ - Sequence: sequence, - Timestamp: timestamp, - Diversifier: diversifier, - DataType: CHANNEL, - Data: dataBz, - } - - return cdc.Marshal(signBytes) -} - -// ChannelStateDataBytes returns the channel state data bytes used in constructing -// SignBytes. -func ChannelStateDataBytes( - cdc codec.BinaryCodec, - path commitmenttypes.MerklePath, // nolint: interfacer - channelEnd exported.ChannelI, -) ([]byte, error) { - channel, ok := channelEnd.(channeltypes.Channel) - if !ok { - return nil, sdkerrors.Wrapf( - channeltypes.ErrInvalidChannel, - "expected channel type %T, got %T", channeltypes.Channel{}, channelEnd) - } - - data := &ChannelStateData{ - Path: []byte(path.String()), - Channel: &channel, - } - - dataBz, err := cdc.Marshal(data) - if err != nil { - return nil, err - } - - return dataBz, nil -} - -// PacketCommitmentSignBytes returns the sign bytes for verification of the -// packet commitment. -func PacketCommitmentSignBytes( - cdc codec.BinaryCodec, - sequence, timestamp uint64, - diversifier string, - path commitmenttypes.MerklePath, - commitmentBytes []byte, -) ([]byte, error) { - dataBz, err := PacketCommitmentDataBytes(cdc, path, commitmentBytes) - if err != nil { - return nil, err - } - - signBytes := &SignBytes{ - Sequence: sequence, - Timestamp: timestamp, - Diversifier: diversifier, - DataType: PACKETCOMMITMENT, - Data: dataBz, - } - - return cdc.Marshal(signBytes) -} - -// PacketCommitmentDataBytes returns the packet commitment data bytes used in constructing -// SignBytes. -func PacketCommitmentDataBytes( - cdc codec.BinaryCodec, - path commitmenttypes.MerklePath, // nolint: interfacer - commitmentBytes []byte, -) ([]byte, error) { - data := &PacketCommitmentData{ - Path: []byte(path.String()), - Commitment: commitmentBytes, - } - - dataBz, err := cdc.Marshal(data) - if err != nil { - return nil, err - } - - return dataBz, nil -} - -// PacketAcknowledgementSignBytes returns the sign bytes for verification of -// the acknowledgement. -func PacketAcknowledgementSignBytes( - cdc codec.BinaryCodec, - sequence, timestamp uint64, - diversifier string, - path commitmenttypes.MerklePath, - acknowledgement []byte, -) ([]byte, error) { - dataBz, err := PacketAcknowledgementDataBytes(cdc, path, acknowledgement) - if err != nil { - return nil, err - } - - signBytes := &SignBytes{ - Sequence: sequence, - Timestamp: timestamp, - Diversifier: diversifier, - DataType: PACKETACKNOWLEDGEMENT, - Data: dataBz, - } - - return cdc.Marshal(signBytes) -} - -// PacketAcknowledgementDataBytes returns the packet acknowledgement data bytes used in constructing -// SignBytes. -func PacketAcknowledgementDataBytes( - cdc codec.BinaryCodec, - path commitmenttypes.MerklePath, // nolint: interfacer - acknowledgement []byte, -) ([]byte, error) { - data := &PacketAcknowledgementData{ - Path: []byte(path.String()), - Acknowledgement: acknowledgement, - } - - dataBz, err := cdc.Marshal(data) - if err != nil { - return nil, err - } - - return dataBz, nil -} - -// PacketReceiptAbsenceSignBytes returns the sign bytes for verification -// of the absence of an receipt. -func PacketReceiptAbsenceSignBytes( - cdc codec.BinaryCodec, - sequence, timestamp uint64, - diversifier string, - path commitmenttypes.MerklePath, -) ([]byte, error) { - dataBz, err := PacketReceiptAbsenceDataBytes(cdc, path) - if err != nil { - return nil, err - } - - signBytes := &SignBytes{ - Sequence: sequence, - Timestamp: timestamp, - Diversifier: diversifier, - DataType: PACKETRECEIPTABSENCE, - Data: dataBz, - } - - return cdc.Marshal(signBytes) -} - -// PacketReceiptAbsenceDataBytes returns the packet receipt absence data bytes -// used in constructing SignBytes. -func PacketReceiptAbsenceDataBytes( - cdc codec.BinaryCodec, - path commitmenttypes.MerklePath, // nolint: interfacer -) ([]byte, error) { - data := &PacketReceiptAbsenceData{ - Path: []byte(path.String()), - } - - dataBz, err := cdc.Marshal(data) - if err != nil { - return nil, err - } - - return dataBz, nil -} - -// NextSequenceRecvSignBytes returns the sign bytes for verification of the next -// sequence to be received. -func NextSequenceRecvSignBytes( - cdc codec.BinaryCodec, - sequence, timestamp uint64, - diversifier string, - path commitmenttypes.MerklePath, - nextSequenceRecv uint64, -) ([]byte, error) { - dataBz, err := NextSequenceRecvDataBytes(cdc, path, nextSequenceRecv) - if err != nil { - return nil, err - } - - signBytes := &SignBytes{ - Sequence: sequence, - Timestamp: timestamp, - Diversifier: diversifier, - DataType: NEXTSEQUENCERECV, - Data: dataBz, - } - - return cdc.Marshal(signBytes) -} - -// NextSequenceRecvDataBytes returns the next sequence recv data bytes used in constructing -// SignBytes. -func NextSequenceRecvDataBytes( - cdc codec.BinaryCodec, - path commitmenttypes.MerklePath, // nolint: interfacer - nextSequenceRecv uint64, -) ([]byte, error) { - data := &NextSequenceRecvData{ - Path: []byte(path.String()), - NextSeqRecv: nextSequenceRecv, - } - - dataBz, err := cdc.Marshal(data) - if err != nil { - return nil, err - } - - return dataBz, nil -} diff --git a/modules/light-clients/06-solomachine/proof_test.go b/modules/light-clients/06-solomachine/proof_test.go index dd7bb52ec1e..0ea96ee4116 100644 --- a/modules/light-clients/06-solomachine/proof_test.go +++ b/modules/light-clients/06-solomachine/proof_test.go @@ -5,7 +5,6 @@ import ( "github.com/cosmos/cosmos-sdk/types/tx/signing" solomachine "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" - ibctesting "github.com/cosmos/ibc-go/v3/testing" ) func (suite *SoloMachineTestSuite) TestVerifySignature() { @@ -66,37 +65,3 @@ func (suite *SoloMachineTestSuite) TestVerifySignature() { }) } } - -func (suite *SoloMachineTestSuite) TestClientStateSignBytes() { - cdc := suite.chainA.App.AppCodec() - - for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - // success - path := sm.GetClientStatePath(counterpartyClientIdentifier) - bz, err := solomachine.ClientStateSignBytes(cdc, sm.Sequence, sm.Time, sm.Diversifier, path, sm.ClientState()) - suite.Require().NoError(err) - suite.Require().NotNil(bz) - - // nil client state - bz, err = solomachine.ClientStateSignBytes(cdc, sm.Sequence, sm.Time, sm.Diversifier, path, nil) - suite.Require().Error(err) - suite.Require().Nil(bz) - } -} - -func (suite *SoloMachineTestSuite) TestConsensusStateSignBytes() { - cdc := suite.chainA.App.AppCodec() - - for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - // success - path := sm.GetConsensusStatePath(counterpartyClientIdentifier, consensusHeight) - bz, err := solomachine.ConsensusStateSignBytes(cdc, sm.Sequence, sm.Time, sm.Diversifier, path, sm.ConsensusState()) - suite.Require().NoError(err) - suite.Require().NotNil(bz) - - // nil consensus state - bz, err = solomachine.ConsensusStateSignBytes(cdc, sm.Sequence, sm.Time, sm.Diversifier, path, nil) - suite.Require().Error(err) - suite.Require().Nil(bz) - } -} diff --git a/modules/light-clients/06-solomachine/solomachine.go b/modules/light-clients/06-solomachine/solomachine.go index e3d58781ea8..41511910bf4 100644 --- a/modules/light-clients/06-solomachine/solomachine.go +++ b/modules/light-clients/06-solomachine/solomachine.go @@ -3,8 +3,6 @@ package solomachine import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - - "github.com/cosmos/ibc-go/v3/modules/core/exported" ) // Interface implementation checks. @@ -32,13 +30,3 @@ func (h Header) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { func (hd HeaderData) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { return unpacker.UnpackAny(hd.NewPubKey, new(cryptotypes.PubKey)) } - -// UnpackInterfaces implements the UnpackInterfaceMessages.UnpackInterfaces method -func (csd ClientStateData) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { - return unpacker.UnpackAny(csd.ClientState, new(exported.ClientState)) -} - -// UnpackInterfaces implements the UnpackInterfaceMessages.UnpackInterfaces method -func (csd ConsensusStateData) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { - return unpacker.UnpackAny(csd.ConsensusState, new(exported.ConsensusState)) -} diff --git a/modules/light-clients/06-solomachine/solomachine.pb.go b/modules/light-clients/06-solomachine/solomachine.pb.go index 845195afbdd..dbc9ad634aa 100644 --- a/modules/light-clients/06-solomachine/solomachine.pb.go +++ b/modules/light-clients/06-solomachine/solomachine.pb.go @@ -1,13 +1,11 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: ibc/lightclients/solomachine/v2/solomachine.proto +// source: ibc/lightclients/solomachine/v3/solomachine.proto package solomachine import ( fmt "fmt" types "github.com/cosmos/cosmos-sdk/codec/types" - types1 "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - types2 "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" io "io" @@ -26,67 +24,6 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package -// DataType defines the type of solo machine proof being created. This is done -// to preserve uniqueness of different data sign byte encodings. -type DataType int32 - -const ( - // Default State - UNSPECIFIED DataType = 0 - // Data type for client state verification - CLIENT DataType = 1 - // Data type for consensus state verification - CONSENSUS DataType = 2 - // Data type for connection state verification - CONNECTION DataType = 3 - // Data type for channel state verification - CHANNEL DataType = 4 - // Data type for packet commitment verification - PACKETCOMMITMENT DataType = 5 - // Data type for packet acknowledgement verification - PACKETACKNOWLEDGEMENT DataType = 6 - // Data type for packet receipt absence verification - PACKETRECEIPTABSENCE DataType = 7 - // Data type for next sequence recv verification - NEXTSEQUENCERECV DataType = 8 - // Data type for header verification - HEADER DataType = 9 -) - -var DataType_name = map[int32]string{ - 0: "DATA_TYPE_UNINITIALIZED_UNSPECIFIED", - 1: "DATA_TYPE_CLIENT_STATE", - 2: "DATA_TYPE_CONSENSUS_STATE", - 3: "DATA_TYPE_CONNECTION_STATE", - 4: "DATA_TYPE_CHANNEL_STATE", - 5: "DATA_TYPE_PACKET_COMMITMENT", - 6: "DATA_TYPE_PACKET_ACKNOWLEDGEMENT", - 7: "DATA_TYPE_PACKET_RECEIPT_ABSENCE", - 8: "DATA_TYPE_NEXT_SEQUENCE_RECV", - 9: "DATA_TYPE_HEADER", -} - -var DataType_value = map[string]int32{ - "DATA_TYPE_UNINITIALIZED_UNSPECIFIED": 0, - "DATA_TYPE_CLIENT_STATE": 1, - "DATA_TYPE_CONSENSUS_STATE": 2, - "DATA_TYPE_CONNECTION_STATE": 3, - "DATA_TYPE_CHANNEL_STATE": 4, - "DATA_TYPE_PACKET_COMMITMENT": 5, - "DATA_TYPE_PACKET_ACKNOWLEDGEMENT": 6, - "DATA_TYPE_PACKET_RECEIPT_ABSENCE": 7, - "DATA_TYPE_NEXT_SEQUENCE_RECV": 8, - "DATA_TYPE_HEADER": 9, -} - -func (x DataType) String() string { - return proto.EnumName(DataType_name, int32(x)) -} - -func (DataType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_141333b361aae010, []int{0} -} - // ClientState defines a solo machine client that tracks the current consensus // state and if the client is frozen. type ClientState struct { @@ -104,7 +41,7 @@ func (m *ClientState) Reset() { *m = ClientState{} } func (m *ClientState) String() string { return proto.CompactTextString(m) } func (*ClientState) ProtoMessage() {} func (*ClientState) Descriptor() ([]byte, []int) { - return fileDescriptor_141333b361aae010, []int{0} + return fileDescriptor_264187157b9220a4, []int{0} } func (m *ClientState) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -150,7 +87,7 @@ func (m *ConsensusState) Reset() { *m = ConsensusState{} } func (m *ConsensusState) String() string { return proto.CompactTextString(m) } func (*ConsensusState) ProtoMessage() {} func (*ConsensusState) Descriptor() ([]byte, []int) { - return fileDescriptor_141333b361aae010, []int{1} + return fileDescriptor_264187157b9220a4, []int{1} } func (m *ConsensusState) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -193,7 +130,7 @@ func (m *Header) Reset() { *m = Header{} } func (m *Header) String() string { return proto.CompactTextString(m) } func (*Header) ProtoMessage() {} func (*Header) Descriptor() ([]byte, []int) { - return fileDescriptor_141333b361aae010, []int{2} + return fileDescriptor_264187157b9220a4, []int{2} } func (m *Header) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -236,7 +173,7 @@ func (m *Misbehaviour) Reset() { *m = Misbehaviour{} } func (m *Misbehaviour) String() string { return proto.CompactTextString(m) } func (*Misbehaviour) ProtoMessage() {} func (*Misbehaviour) Descriptor() ([]byte, []int) { - return fileDescriptor_141333b361aae010, []int{3} + return fileDescriptor_264187157b9220a4, []int{3} } func (m *Misbehaviour) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -268,17 +205,17 @@ var xxx_messageInfo_Misbehaviour proto.InternalMessageInfo // SignatureAndData contains a signature and the data signed over to create that // signature. type SignatureAndData struct { - Signature []byte `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` - DataType DataType `protobuf:"varint,2,opt,name=data_type,json=dataType,proto3,enum=ibc.lightclients.solomachine.v2.DataType" json:"data_type,omitempty" yaml:"data_type"` - Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` - Timestamp uint64 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Signature []byte `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` + Path []byte `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` + Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` + Timestamp uint64 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` } func (m *SignatureAndData) Reset() { *m = SignatureAndData{} } func (m *SignatureAndData) String() string { return proto.CompactTextString(m) } func (*SignatureAndData) ProtoMessage() {} func (*SignatureAndData) Descriptor() ([]byte, []int) { - return fileDescriptor_141333b361aae010, []int{4} + return fileDescriptor_264187157b9220a4, []int{4} } func (m *SignatureAndData) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -318,7 +255,7 @@ func (m *TimestampedSignatureData) Reset() { *m = TimestampedSignatureDa func (m *TimestampedSignatureData) String() string { return proto.CompactTextString(m) } func (*TimestampedSignatureData) ProtoMessage() {} func (*TimestampedSignatureData) Descriptor() ([]byte, []int) { - return fileDescriptor_141333b361aae010, []int{5} + return fileDescriptor_264187157b9220a4, []int{5} } func (m *TimestampedSignatureData) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -349,12 +286,15 @@ var xxx_messageInfo_TimestampedSignatureData proto.InternalMessageInfo // SignBytes defines the signed bytes used for signature verification. type SignBytes struct { - Sequence uint64 `protobuf:"varint,1,opt,name=sequence,proto3" json:"sequence,omitempty"` - Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + // the sequence number + Sequence uint64 `protobuf:"varint,1,opt,name=sequence,proto3" json:"sequence,omitempty"` + // the proof timestamp + Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + // the public key diversifier Diversifier string `protobuf:"bytes,3,opt,name=diversifier,proto3" json:"diversifier,omitempty"` - // type of the data used - DataType DataType `protobuf:"varint,4,opt,name=data_type,json=dataType,proto3,enum=ibc.lightclients.solomachine.v2.DataType" json:"data_type,omitempty" yaml:"data_type"` - // marshaled data + // the standardised path bytes + Path []byte `protobuf:"bytes,4,opt,name=path,proto3" json:"path,omitempty"` + // the marshaled data bytes Data []byte `protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"` } @@ -362,7 +302,7 @@ func (m *SignBytes) Reset() { *m = SignBytes{} } func (m *SignBytes) String() string { return proto.CompactTextString(m) } func (*SignBytes) ProtoMessage() {} func (*SignBytes) Descriptor() ([]byte, []int) { - return fileDescriptor_141333b361aae010, []int{6} + return fileDescriptor_264187157b9220a4, []int{6} } func (m *SignBytes) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -403,7 +343,7 @@ func (m *HeaderData) Reset() { *m = HeaderData{} } func (m *HeaderData) String() string { return proto.CompactTextString(m) } func (*HeaderData) ProtoMessage() {} func (*HeaderData) Descriptor() ([]byte, []int) { - return fileDescriptor_141333b361aae010, []int{7} + return fileDescriptor_264187157b9220a4, []int{7} } func (m *HeaderData) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -432,485 +372,72 @@ func (m *HeaderData) XXX_DiscardUnknown() { var xxx_messageInfo_HeaderData proto.InternalMessageInfo -// ClientStateData returns the SignBytes data for client state verification. -type ClientStateData struct { - Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` - ClientState *types.Any `protobuf:"bytes,2,opt,name=client_state,json=clientState,proto3" json:"client_state,omitempty" yaml:"client_state"` -} - -func (m *ClientStateData) Reset() { *m = ClientStateData{} } -func (m *ClientStateData) String() string { return proto.CompactTextString(m) } -func (*ClientStateData) ProtoMessage() {} -func (*ClientStateData) Descriptor() ([]byte, []int) { - return fileDescriptor_141333b361aae010, []int{8} -} -func (m *ClientStateData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ClientStateData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ClientStateData.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 *ClientStateData) XXX_Merge(src proto.Message) { - xxx_messageInfo_ClientStateData.Merge(m, src) -} -func (m *ClientStateData) XXX_Size() int { - return m.Size() -} -func (m *ClientStateData) XXX_DiscardUnknown() { - xxx_messageInfo_ClientStateData.DiscardUnknown(m) -} - -var xxx_messageInfo_ClientStateData proto.InternalMessageInfo - -// ConsensusStateData returns the SignBytes data for consensus state -// verification. -type ConsensusStateData struct { - Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` - ConsensusState *types.Any `protobuf:"bytes,2,opt,name=consensus_state,json=consensusState,proto3" json:"consensus_state,omitempty" yaml:"consensus_state"` -} - -func (m *ConsensusStateData) Reset() { *m = ConsensusStateData{} } -func (m *ConsensusStateData) String() string { return proto.CompactTextString(m) } -func (*ConsensusStateData) ProtoMessage() {} -func (*ConsensusStateData) Descriptor() ([]byte, []int) { - return fileDescriptor_141333b361aae010, []int{9} -} -func (m *ConsensusStateData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ConsensusStateData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ConsensusStateData.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 *ConsensusStateData) XXX_Merge(src proto.Message) { - xxx_messageInfo_ConsensusStateData.Merge(m, src) -} -func (m *ConsensusStateData) XXX_Size() int { - return m.Size() -} -func (m *ConsensusStateData) XXX_DiscardUnknown() { - xxx_messageInfo_ConsensusStateData.DiscardUnknown(m) -} - -var xxx_messageInfo_ConsensusStateData proto.InternalMessageInfo - -// ConnectionStateData returns the SignBytes data for connection state -// verification. -type ConnectionStateData struct { - Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` - Connection *types1.ConnectionEnd `protobuf:"bytes,2,opt,name=connection,proto3" json:"connection,omitempty"` -} - -func (m *ConnectionStateData) Reset() { *m = ConnectionStateData{} } -func (m *ConnectionStateData) String() string { return proto.CompactTextString(m) } -func (*ConnectionStateData) ProtoMessage() {} -func (*ConnectionStateData) Descriptor() ([]byte, []int) { - return fileDescriptor_141333b361aae010, []int{10} -} -func (m *ConnectionStateData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ConnectionStateData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ConnectionStateData.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 *ConnectionStateData) XXX_Merge(src proto.Message) { - xxx_messageInfo_ConnectionStateData.Merge(m, src) -} -func (m *ConnectionStateData) XXX_Size() int { - return m.Size() -} -func (m *ConnectionStateData) XXX_DiscardUnknown() { - xxx_messageInfo_ConnectionStateData.DiscardUnknown(m) -} - -var xxx_messageInfo_ConnectionStateData proto.InternalMessageInfo - -// ChannelStateData returns the SignBytes data for channel state -// verification. -type ChannelStateData struct { - Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` - Channel *types2.Channel `protobuf:"bytes,2,opt,name=channel,proto3" json:"channel,omitempty"` -} - -func (m *ChannelStateData) Reset() { *m = ChannelStateData{} } -func (m *ChannelStateData) String() string { return proto.CompactTextString(m) } -func (*ChannelStateData) ProtoMessage() {} -func (*ChannelStateData) Descriptor() ([]byte, []int) { - return fileDescriptor_141333b361aae010, []int{11} -} -func (m *ChannelStateData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ChannelStateData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ChannelStateData.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 *ChannelStateData) XXX_Merge(src proto.Message) { - xxx_messageInfo_ChannelStateData.Merge(m, src) -} -func (m *ChannelStateData) XXX_Size() int { - return m.Size() -} -func (m *ChannelStateData) XXX_DiscardUnknown() { - xxx_messageInfo_ChannelStateData.DiscardUnknown(m) -} - -var xxx_messageInfo_ChannelStateData proto.InternalMessageInfo - -// PacketCommitmentData returns the SignBytes data for packet commitment -// verification. -type PacketCommitmentData struct { - Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` - Commitment []byte `protobuf:"bytes,2,opt,name=commitment,proto3" json:"commitment,omitempty"` -} - -func (m *PacketCommitmentData) Reset() { *m = PacketCommitmentData{} } -func (m *PacketCommitmentData) String() string { return proto.CompactTextString(m) } -func (*PacketCommitmentData) ProtoMessage() {} -func (*PacketCommitmentData) Descriptor() ([]byte, []int) { - return fileDescriptor_141333b361aae010, []int{12} -} -func (m *PacketCommitmentData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *PacketCommitmentData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_PacketCommitmentData.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 *PacketCommitmentData) XXX_Merge(src proto.Message) { - xxx_messageInfo_PacketCommitmentData.Merge(m, src) -} -func (m *PacketCommitmentData) XXX_Size() int { - return m.Size() -} -func (m *PacketCommitmentData) XXX_DiscardUnknown() { - xxx_messageInfo_PacketCommitmentData.DiscardUnknown(m) -} - -var xxx_messageInfo_PacketCommitmentData proto.InternalMessageInfo - -func (m *PacketCommitmentData) GetPath() []byte { - if m != nil { - return m.Path - } - return nil -} - -func (m *PacketCommitmentData) GetCommitment() []byte { - if m != nil { - return m.Commitment - } - return nil -} - -// PacketAcknowledgementData returns the SignBytes data for acknowledgement -// verification. -type PacketAcknowledgementData struct { - Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` - Acknowledgement []byte `protobuf:"bytes,2,opt,name=acknowledgement,proto3" json:"acknowledgement,omitempty"` -} - -func (m *PacketAcknowledgementData) Reset() { *m = PacketAcknowledgementData{} } -func (m *PacketAcknowledgementData) String() string { return proto.CompactTextString(m) } -func (*PacketAcknowledgementData) ProtoMessage() {} -func (*PacketAcknowledgementData) Descriptor() ([]byte, []int) { - return fileDescriptor_141333b361aae010, []int{13} -} -func (m *PacketAcknowledgementData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *PacketAcknowledgementData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_PacketAcknowledgementData.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 *PacketAcknowledgementData) XXX_Merge(src proto.Message) { - xxx_messageInfo_PacketAcknowledgementData.Merge(m, src) -} -func (m *PacketAcknowledgementData) XXX_Size() int { - return m.Size() -} -func (m *PacketAcknowledgementData) XXX_DiscardUnknown() { - xxx_messageInfo_PacketAcknowledgementData.DiscardUnknown(m) -} - -var xxx_messageInfo_PacketAcknowledgementData proto.InternalMessageInfo - -func (m *PacketAcknowledgementData) GetPath() []byte { - if m != nil { - return m.Path - } - return nil -} - -func (m *PacketAcknowledgementData) GetAcknowledgement() []byte { - if m != nil { - return m.Acknowledgement - } - return nil -} - -// PacketReceiptAbsenceData returns the SignBytes data for -// packet receipt absence verification. -type PacketReceiptAbsenceData struct { - Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` -} - -func (m *PacketReceiptAbsenceData) Reset() { *m = PacketReceiptAbsenceData{} } -func (m *PacketReceiptAbsenceData) String() string { return proto.CompactTextString(m) } -func (*PacketReceiptAbsenceData) ProtoMessage() {} -func (*PacketReceiptAbsenceData) Descriptor() ([]byte, []int) { - return fileDescriptor_141333b361aae010, []int{14} -} -func (m *PacketReceiptAbsenceData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *PacketReceiptAbsenceData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_PacketReceiptAbsenceData.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 *PacketReceiptAbsenceData) XXX_Merge(src proto.Message) { - xxx_messageInfo_PacketReceiptAbsenceData.Merge(m, src) -} -func (m *PacketReceiptAbsenceData) XXX_Size() int { - return m.Size() -} -func (m *PacketReceiptAbsenceData) XXX_DiscardUnknown() { - xxx_messageInfo_PacketReceiptAbsenceData.DiscardUnknown(m) -} - -var xxx_messageInfo_PacketReceiptAbsenceData proto.InternalMessageInfo - -func (m *PacketReceiptAbsenceData) GetPath() []byte { - if m != nil { - return m.Path - } - return nil -} - -// NextSequenceRecvData returns the SignBytes data for verification of the next -// sequence to be received. -type NextSequenceRecvData struct { - Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` - NextSeqRecv uint64 `protobuf:"varint,2,opt,name=next_seq_recv,json=nextSeqRecv,proto3" json:"next_seq_recv,omitempty" yaml:"next_seq_recv"` -} - -func (m *NextSequenceRecvData) Reset() { *m = NextSequenceRecvData{} } -func (m *NextSequenceRecvData) String() string { return proto.CompactTextString(m) } -func (*NextSequenceRecvData) ProtoMessage() {} -func (*NextSequenceRecvData) Descriptor() ([]byte, []int) { - return fileDescriptor_141333b361aae010, []int{15} -} -func (m *NextSequenceRecvData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *NextSequenceRecvData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_NextSequenceRecvData.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 *NextSequenceRecvData) XXX_Merge(src proto.Message) { - xxx_messageInfo_NextSequenceRecvData.Merge(m, src) -} -func (m *NextSequenceRecvData) XXX_Size() int { - return m.Size() -} -func (m *NextSequenceRecvData) XXX_DiscardUnknown() { - xxx_messageInfo_NextSequenceRecvData.DiscardUnknown(m) -} - -var xxx_messageInfo_NextSequenceRecvData proto.InternalMessageInfo - -func (m *NextSequenceRecvData) GetPath() []byte { - if m != nil { - return m.Path - } - return nil -} - -func (m *NextSequenceRecvData) GetNextSeqRecv() uint64 { - if m != nil { - return m.NextSeqRecv - } - return 0 -} - func init() { - proto.RegisterEnum("ibc.lightclients.solomachine.v2.DataType", DataType_name, DataType_value) - proto.RegisterType((*ClientState)(nil), "ibc.lightclients.solomachine.v2.ClientState") - proto.RegisterType((*ConsensusState)(nil), "ibc.lightclients.solomachine.v2.ConsensusState") - proto.RegisterType((*Header)(nil), "ibc.lightclients.solomachine.v2.Header") - proto.RegisterType((*Misbehaviour)(nil), "ibc.lightclients.solomachine.v2.Misbehaviour") - proto.RegisterType((*SignatureAndData)(nil), "ibc.lightclients.solomachine.v2.SignatureAndData") - proto.RegisterType((*TimestampedSignatureData)(nil), "ibc.lightclients.solomachine.v2.TimestampedSignatureData") - proto.RegisterType((*SignBytes)(nil), "ibc.lightclients.solomachine.v2.SignBytes") - proto.RegisterType((*HeaderData)(nil), "ibc.lightclients.solomachine.v2.HeaderData") - proto.RegisterType((*ClientStateData)(nil), "ibc.lightclients.solomachine.v2.ClientStateData") - proto.RegisterType((*ConsensusStateData)(nil), "ibc.lightclients.solomachine.v2.ConsensusStateData") - proto.RegisterType((*ConnectionStateData)(nil), "ibc.lightclients.solomachine.v2.ConnectionStateData") - proto.RegisterType((*ChannelStateData)(nil), "ibc.lightclients.solomachine.v2.ChannelStateData") - proto.RegisterType((*PacketCommitmentData)(nil), "ibc.lightclients.solomachine.v2.PacketCommitmentData") - proto.RegisterType((*PacketAcknowledgementData)(nil), "ibc.lightclients.solomachine.v2.PacketAcknowledgementData") - proto.RegisterType((*PacketReceiptAbsenceData)(nil), "ibc.lightclients.solomachine.v2.PacketReceiptAbsenceData") - proto.RegisterType((*NextSequenceRecvData)(nil), "ibc.lightclients.solomachine.v2.NextSequenceRecvData") + proto.RegisterType((*ClientState)(nil), "ibc.lightclients.solomachine.v3.ClientState") + proto.RegisterType((*ConsensusState)(nil), "ibc.lightclients.solomachine.v3.ConsensusState") + proto.RegisterType((*Header)(nil), "ibc.lightclients.solomachine.v3.Header") + proto.RegisterType((*Misbehaviour)(nil), "ibc.lightclients.solomachine.v3.Misbehaviour") + proto.RegisterType((*SignatureAndData)(nil), "ibc.lightclients.solomachine.v3.SignatureAndData") + proto.RegisterType((*TimestampedSignatureData)(nil), "ibc.lightclients.solomachine.v3.TimestampedSignatureData") + proto.RegisterType((*SignBytes)(nil), "ibc.lightclients.solomachine.v3.SignBytes") + proto.RegisterType((*HeaderData)(nil), "ibc.lightclients.solomachine.v3.HeaderData") } func init() { - proto.RegisterFile("ibc/lightclients/solomachine/v2/solomachine.proto", fileDescriptor_141333b361aae010) -} - -var fileDescriptor_141333b361aae010 = []byte{ - // 1372 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0x5f, 0x6f, 0xdb, 0x54, - 0x14, 0xaf, 0xb3, 0xac, 0x6b, 0x4e, 0xba, 0x36, 0x78, 0xd9, 0x96, 0x7a, 0x53, 0x62, 0x8c, 0x18, - 0x05, 0xb1, 0x84, 0x76, 0x62, 0x42, 0x03, 0x01, 0x8e, 0xe3, 0xb1, 0x6c, 0xad, 0x1b, 0x1c, 0x17, - 0xd8, 0x84, 0x64, 0x39, 0xce, 0x6d, 0x62, 0x2d, 0xf1, 0xcd, 0x62, 0x27, 0x59, 0x90, 0x90, 0x10, - 0x4f, 0x23, 0xe2, 0x81, 0x2f, 0x10, 0x09, 0x81, 0xf8, 0x1c, 0xbc, 0x21, 0x78, 0xdb, 0x23, 0x4f, - 0x01, 0x6d, 0xdf, 0x20, 0x9f, 0x00, 0xd9, 0xf7, 0x26, 0xb6, 0xd3, 0x35, 0x15, 0xff, 0xde, 0xee, - 0x3d, 0xbf, 0x73, 0x7e, 0xe7, 0xcf, 0x3d, 0x3e, 0xf7, 0x1a, 0x76, 0xac, 0x9a, 0x59, 0x68, 0x59, - 0x8d, 0xa6, 0x6b, 0xb6, 0x2c, 0x64, 0xbb, 0x4e, 0xc1, 0xc1, 0x2d, 0xdc, 0x36, 0xcc, 0xa6, 0x65, - 0xa3, 0x42, 0x7f, 0x37, 0xbc, 0xcd, 0x77, 0xba, 0xd8, 0xc5, 0x6c, 0xce, 0xaa, 0x99, 0xf9, 0xb0, - 0x49, 0x3e, 0xac, 0xd3, 0xdf, 0xe5, 0x5e, 0xf3, 0x38, 0x4d, 0xdc, 0x45, 0x05, 0x13, 0xdb, 0x36, - 0x32, 0x5d, 0x0b, 0xdb, 0x85, 0xfe, 0x4e, 0x68, 0x47, 0x98, 0xb8, 0x97, 0x03, 0xc5, 0xa6, 0x61, - 0xdb, 0xa8, 0xe5, 0x6b, 0x91, 0x25, 0x55, 0x49, 0x37, 0x70, 0x03, 0xfb, 0xcb, 0x82, 0xb7, 0xa2, - 0xd2, 0xad, 0x06, 0xc6, 0x8d, 0x16, 0x2a, 0xf8, 0xbb, 0x5a, 0xef, 0xa8, 0x60, 0xd8, 0x43, 0x02, - 0x09, 0x3f, 0xc7, 0x20, 0x29, 0xf9, 0x71, 0x55, 0x5d, 0xc3, 0x45, 0x2c, 0x07, 0x6b, 0x0e, 0x7a, - 0xd4, 0x43, 0xb6, 0x89, 0x32, 0x0c, 0xcf, 0x6c, 0xc7, 0xd5, 0xf9, 0x9e, 0xdd, 0x81, 0x84, 0xe5, - 0xe8, 0x47, 0x5d, 0xfc, 0x05, 0xb2, 0x33, 0x31, 0x9e, 0xd9, 0x5e, 0x2b, 0xa6, 0xa7, 0x93, 0x5c, - 0x6a, 0x68, 0xb4, 0x5b, 0xb7, 0x84, 0x39, 0x24, 0xa8, 0x6b, 0x96, 0x73, 0xdb, 0x5f, 0xb2, 0x2e, - 0x6c, 0x9a, 0xd8, 0x76, 0x90, 0xed, 0xf4, 0x1c, 0xdd, 0xf1, 0x3c, 0x64, 0xce, 0xf0, 0xcc, 0x76, - 0x72, 0xb7, 0x90, 0x3f, 0xa5, 0x2c, 0x79, 0x69, 0x66, 0xe7, 0x07, 0x56, 0xe4, 0xa6, 0x93, 0xdc, - 0x25, 0xe2, 0x69, 0x81, 0x51, 0x50, 0x37, 0xcc, 0x88, 0x2e, 0x8b, 0xe0, 0x8a, 0xd1, 0x6a, 0xe1, - 0x81, 0xde, 0xeb, 0xd4, 0x0d, 0x17, 0xe9, 0xc6, 0x91, 0x8b, 0xba, 0x7a, 0xa7, 0x8b, 0x3b, 0xd8, - 0x31, 0x5a, 0x99, 0xb8, 0x1f, 0xfa, 0xb5, 0xe9, 0x24, 0x27, 0x10, 0xc2, 0x25, 0xca, 0x82, 0x9a, - 0xf1, 0xd1, 0x43, 0x1f, 0x14, 0x3d, 0xac, 0x42, 0xa1, 0x5b, 0xf1, 0x27, 0xdf, 0xe7, 0x56, 0x84, - 0x1f, 0x18, 0xd8, 0x88, 0xc6, 0xca, 0xde, 0x05, 0xe8, 0xf4, 0x6a, 0x2d, 0xcb, 0xd4, 0x1f, 0xa2, - 0xa1, 0x5f, 0xc6, 0xe4, 0x6e, 0x3a, 0x4f, 0x0e, 0x21, 0x3f, 0x3b, 0x84, 0xbc, 0x68, 0x0f, 0x8b, - 0x17, 0xa7, 0x93, 0xdc, 0x4b, 0x24, 0x88, 0xc0, 0x42, 0x50, 0x13, 0x64, 0x73, 0x0f, 0x0d, 0x59, - 0x1e, 0x92, 0x75, 0xab, 0x8f, 0xba, 0x8e, 0x75, 0x64, 0xa1, 0xae, 0x5f, 0xf6, 0x84, 0x1a, 0x16, - 0xb1, 0x57, 0x21, 0xe1, 0x5a, 0x6d, 0xe4, 0xb8, 0x46, 0xbb, 0xe3, 0x57, 0x37, 0xae, 0x06, 0x02, - 0x1a, 0xe4, 0xd7, 0x31, 0x58, 0xbd, 0x83, 0x8c, 0x3a, 0xea, 0x2e, 0x3d, 0xe1, 0x08, 0x55, 0x6c, - 0x81, 0xca, 0x43, 0x1d, 0xab, 0x61, 0x1b, 0x6e, 0xaf, 0x4b, 0x8e, 0x71, 0x5d, 0x0d, 0x04, 0xec, - 0x21, 0x6c, 0xd8, 0x68, 0xa0, 0x87, 0x12, 0x8f, 0x2f, 0x49, 0x7c, 0x6b, 0x3a, 0xc9, 0x5d, 0x24, - 0x89, 0x47, 0xad, 0x04, 0x75, 0xdd, 0x46, 0x83, 0xca, 0x3c, 0x7f, 0x09, 0x36, 0x3d, 0x85, 0x70, - 0x0d, 0xce, 0x7a, 0x35, 0x08, 0x37, 0xc4, 0x82, 0x82, 0xa0, 0x7a, 0x91, 0x94, 0x02, 0x01, 0x2d, - 0xc2, 0x6f, 0x31, 0x58, 0xdf, 0xb7, 0x9c, 0x1a, 0x6a, 0x1a, 0x7d, 0x0b, 0xf7, 0xba, 0xec, 0x0d, - 0x48, 0x90, 0xe6, 0xd3, 0xad, 0xba, 0x5f, 0x8b, 0x44, 0xf1, 0x52, 0xd0, 0xd0, 0x73, 0x48, 0xc8, - 0x30, 0xea, 0x1a, 0xd9, 0x95, 0xeb, 0x91, 0xfa, 0xc5, 0x16, 0xea, 0xd7, 0x81, 0xf3, 0xf3, 0x82, - 0xe8, 0xd8, 0x9e, 0x35, 0xfb, 0xce, 0xa9, 0xcd, 0x5e, 0x9d, 0x59, 0x89, 0x76, 0xbd, 0x64, 0xb8, - 0x46, 0x31, 0x33, 0x9d, 0xe4, 0xd2, 0x24, 0x8e, 0x08, 0xa3, 0xa0, 0xae, 0xcf, 0xf7, 0x07, 0xf6, - 0x82, 0x47, 0x77, 0x80, 0x69, 0xd1, 0xff, 0x2b, 0x8f, 0xee, 0x00, 0x87, 0x3d, 0x6a, 0x03, 0x4c, - 0x6b, 0xf9, 0x0b, 0x03, 0xa9, 0x45, 0x8a, 0x68, 0x83, 0x30, 0x8b, 0x0d, 0xf2, 0x39, 0x24, 0xea, - 0x86, 0x6b, 0xe8, 0xee, 0xb0, 0x43, 0x2a, 0xb7, 0xb1, 0xfb, 0xfa, 0xa9, 0x61, 0x7a, 0xbc, 0xda, - 0xb0, 0x83, 0xc2, 0x93, 0x66, 0xce, 0x22, 0xa8, 0x6b, 0x75, 0x8a, 0xb3, 0x2c, 0xc4, 0xbd, 0x35, - 0xed, 0x4b, 0x7f, 0x1d, 0x6d, 0xe7, 0xf8, 0x8b, 0xbf, 0x8c, 0xaf, 0x18, 0xc8, 0x68, 0x33, 0x19, - 0xaa, 0xcf, 0x73, 0xf2, 0x13, 0xfa, 0x10, 0x36, 0x82, 0x5a, 0xf8, 0xf4, 0x7e, 0x56, 0xe1, 0xee, - 0x8d, 0xe2, 0x82, 0x1a, 0x1c, 0x47, 0xe9, 0x58, 0x08, 0xb1, 0x17, 0x87, 0xf0, 0x07, 0x03, 0x09, - 0xcf, 0x6f, 0x71, 0xe8, 0x22, 0xe7, 0x5f, 0x7c, 0x9f, 0x0b, 0xa3, 0xe2, 0xcc, 0xf1, 0x51, 0x11, - 0x39, 0x82, 0xf8, 0xff, 0x75, 0x04, 0x67, 0x83, 0x23, 0xa0, 0x19, 0xfe, 0xc4, 0x00, 0x90, 0xf1, - 0xe3, 0x17, 0x65, 0x0f, 0x92, 0xf4, 0xa3, 0x3f, 0x75, 0x40, 0x7a, 0xdf, 0x23, 0x1b, 0x99, 0x13, - 0x74, 0x42, 0x92, 0x21, 0x71, 0xc2, 0x84, 0x88, 0xfd, 0xc3, 0x09, 0xf1, 0x25, 0x6c, 0x86, 0x2e, - 0x43, 0x3f, 0x56, 0x16, 0xe2, 0x1d, 0xc3, 0x6d, 0xd2, 0x76, 0xf6, 0xd7, 0x6c, 0x05, 0xd6, 0xe9, - 0x70, 0x20, 0x57, 0x5a, 0x6c, 0x49, 0x02, 0x97, 0xa7, 0x93, 0xdc, 0x85, 0xc8, 0x40, 0xa1, 0x97, - 0x56, 0xd2, 0x0c, 0x3c, 0x51, 0xf7, 0xdf, 0x30, 0xc0, 0x46, 0xaf, 0x92, 0x13, 0x43, 0xb8, 0x7f, - 0xfc, 0x62, 0x5d, 0x16, 0xc5, 0xdf, 0xb8, 0x3d, 0x69, 0x2c, 0x7d, 0xb8, 0x20, 0xcd, 0x1f, 0x20, - 0xcb, 0x63, 0x91, 0x01, 0x82, 0xb7, 0x0a, 0x0d, 0xe3, 0x55, 0xbf, 0xad, 0xbc, 0xc7, 0x4a, 0x3e, - 0xf4, 0x8e, 0xe9, 0xef, 0xe4, 0x03, 0x52, 0xd9, 0xae, 0xab, 0x21, 0x43, 0xea, 0xb7, 0x0e, 0x29, - 0x89, 0x3c, 0x69, 0x96, 0x3b, 0xbd, 0x09, 0xe7, 0xe8, 0xd3, 0x87, 0x7a, 0xbc, 0x1a, 0xf2, 0x48, - 0xdf, 0x44, 0x9e, 0x3b, 0xb2, 0x54, 0x67, 0xca, 0xd4, 0xcb, 0x5d, 0x48, 0x57, 0x0c, 0xf3, 0x21, - 0x72, 0x25, 0xdc, 0x6e, 0x5b, 0x6e, 0x1b, 0xd9, 0xee, 0x89, 0x9e, 0xb2, 0x5e, 0x7a, 0x33, 0x2d, - 0xdf, 0xd9, 0xba, 0x1a, 0x92, 0x08, 0xf7, 0x61, 0x8b, 0x70, 0x89, 0xe6, 0x43, 0x1b, 0x0f, 0x5a, - 0xa8, 0xde, 0x40, 0x4b, 0x09, 0xb7, 0x61, 0xd3, 0x88, 0xaa, 0x52, 0xd6, 0x45, 0xb1, 0x90, 0x87, - 0x0c, 0xa1, 0x56, 0x91, 0x89, 0xac, 0x8e, 0x2b, 0xd6, 0x1c, 0x6f, 0x0e, 0x9c, 0xc4, 0x2c, 0x34, - 0x21, 0xad, 0xa0, 0xc7, 0x6e, 0x95, 0xce, 0x0b, 0x15, 0x99, 0xfd, 0x13, 0xa3, 0x78, 0x0f, 0xce, - 0xdb, 0xe8, 0xb1, 0xab, 0x3b, 0xe8, 0x91, 0xde, 0x45, 0x66, 0x9f, 0xcc, 0x93, 0xf0, 0x35, 0x10, - 0x81, 0x05, 0x35, 0x69, 0x13, 0x6a, 0x8f, 0xf5, 0x8d, 0x6f, 0xe3, 0xb0, 0x36, 0x1b, 0x0c, 0xec, - 0x3b, 0xf0, 0x4a, 0x49, 0xd4, 0x44, 0x5d, 0xbb, 0x5f, 0x91, 0xf5, 0x43, 0xa5, 0xac, 0x94, 0xb5, - 0xb2, 0xb8, 0x57, 0x7e, 0x20, 0x97, 0xf4, 0x43, 0xa5, 0x5a, 0x91, 0xa5, 0xf2, 0xed, 0xb2, 0x5c, - 0x4a, 0xad, 0x70, 0x9b, 0xa3, 0x31, 0x9f, 0x0c, 0x89, 0xd8, 0x6b, 0x70, 0x29, 0xb0, 0x94, 0xf6, - 0xca, 0xb2, 0xa2, 0xe9, 0x55, 0x4d, 0xd4, 0xe4, 0x14, 0xc3, 0xc1, 0x68, 0xcc, 0xaf, 0x12, 0x19, - 0xfb, 0x26, 0x6c, 0x85, 0xf4, 0x0e, 0x94, 0xaa, 0xac, 0x54, 0x0f, 0xab, 0x54, 0x35, 0xc6, 0x9d, - 0x1f, 0x8d, 0xf9, 0xc4, 0x5c, 0xcc, 0xe6, 0x81, 0x8b, 0x68, 0x2b, 0xb2, 0xa4, 0x95, 0x0f, 0x14, - 0xaa, 0x7e, 0x86, 0xdb, 0x18, 0x8d, 0x79, 0x08, 0xe4, 0xec, 0x36, 0x5c, 0x0e, 0xe9, 0xdf, 0x11, - 0x15, 0x45, 0xde, 0xa3, 0xca, 0x71, 0x2e, 0x39, 0x1a, 0xf3, 0xe7, 0xa8, 0x90, 0x7d, 0x1b, 0xae, - 0x04, 0x9a, 0x15, 0x51, 0xba, 0x27, 0x6b, 0xba, 0x74, 0xb0, 0xbf, 0x5f, 0xd6, 0xf6, 0x65, 0x45, - 0x4b, 0x9d, 0xe5, 0xd2, 0xa3, 0x31, 0x9f, 0x22, 0x40, 0x20, 0x67, 0x3f, 0x00, 0xfe, 0x98, 0x99, - 0x28, 0xdd, 0x53, 0x0e, 0x3e, 0xdd, 0x93, 0x4b, 0x1f, 0xc9, 0xbe, 0xed, 0x2a, 0xb7, 0x35, 0x1a, - 0xf3, 0x17, 0x09, 0xba, 0x00, 0xb2, 0xef, 0xbf, 0x80, 0x40, 0x95, 0x25, 0xb9, 0x5c, 0xd1, 0x74, - 0xb1, 0x58, 0x95, 0x15, 0x49, 0x4e, 0x9d, 0xe3, 0x32, 0xa3, 0x31, 0x9f, 0x26, 0x28, 0x05, 0x29, - 0xc6, 0xde, 0x84, 0xab, 0x81, 0xbd, 0x22, 0x7f, 0xa6, 0xe9, 0x55, 0xf9, 0xe3, 0x43, 0x0f, 0xf2, - 0x68, 0x3e, 0x49, 0xad, 0x91, 0xc0, 0x3d, 0x64, 0x06, 0x78, 0x72, 0x96, 0x87, 0x54, 0x60, 0x77, - 0x47, 0x16, 0x4b, 0xb2, 0x9a, 0x4a, 0x90, 0x93, 0x21, 0x3b, 0x2e, 0xfe, 0xe4, 0xc7, 0xec, 0x4a, - 0xf1, 0xe8, 0xd7, 0x67, 0x59, 0xe6, 0xe9, 0xb3, 0x2c, 0xf3, 0xe7, 0xb3, 0x2c, 0xf3, 0xdd, 0xf3, - 0xec, 0xca, 0xd3, 0xe7, 0xd9, 0x95, 0xdf, 0x9f, 0x67, 0x57, 0x1e, 0xec, 0x35, 0x2c, 0xb7, 0xd9, - 0xab, 0xe5, 0x4d, 0xdc, 0x2e, 0x98, 0xd8, 0x69, 0x63, 0xa7, 0x60, 0xd5, 0xcc, 0xeb, 0x0d, 0x5c, - 0xe8, 0xdf, 0x28, 0xb4, 0x71, 0xbd, 0xd7, 0x42, 0x0e, 0xf9, 0xa3, 0xba, 0x3e, 0xfb, 0xa5, 0x7a, - 0xeb, 0xe6, 0xf5, 0xd0, 0x15, 0xf4, 0x6e, 0x68, 0x5d, 0x5b, 0xf5, 0xa7, 0xda, 0x8d, 0xbf, 0x02, - 0x00, 0x00, 0xff, 0xff, 0xcc, 0xc1, 0x35, 0x7d, 0x88, 0x0d, 0x00, 0x00, + proto.RegisterFile("ibc/lightclients/solomachine/v3/solomachine.proto", fileDescriptor_264187157b9220a4) +} + +var fileDescriptor_264187157b9220a4 = []byte{ + // 783 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0x4d, 0x4f, 0xe3, 0x46, + 0x18, 0x8e, 0x8d, 0x41, 0xc9, 0x24, 0x0d, 0xd4, 0x0a, 0xc8, 0xa4, 0x55, 0x1c, 0xf9, 0x50, 0x71, + 0xc1, 0x2e, 0x44, 0xea, 0x81, 0x5e, 0x4a, 0x40, 0x55, 0x3f, 0xa8, 0x8a, 0x0c, 0x5c, 0x7a, 0xb1, + 0xc6, 0xf6, 0xc4, 0x19, 0xd5, 0xf1, 0xb8, 0x9e, 0x71, 0xa2, 0x54, 0x3d, 0x54, 0x3d, 0xf5, 0xd8, + 0x4b, 0xef, 0x55, 0xa5, 0xfe, 0x8e, 0x5e, 0x77, 0x6f, 0x1c, 0xf7, 0x14, 0xad, 0xe0, 0x1f, 0xe4, + 0x17, 0xac, 0x3c, 0x76, 0xe2, 0x0f, 0x58, 0x90, 0x76, 0xf7, 0x36, 0xf3, 0x7e, 0x3e, 0xef, 0x33, + 0xcf, 0x6b, 0x83, 0x23, 0x6c, 0x3b, 0x86, 0x8f, 0xbd, 0x31, 0x73, 0x7c, 0x8c, 0x02, 0x46, 0x0d, + 0x4a, 0x7c, 0x32, 0x81, 0xce, 0x18, 0x07, 0xc8, 0x98, 0x0e, 0x8a, 0x57, 0x3d, 0x8c, 0x08, 0x23, + 0xb2, 0x8a, 0x6d, 0x47, 0x2f, 0xa6, 0xe8, 0xc5, 0x98, 0xe9, 0xa0, 0xdb, 0xf1, 0x88, 0x47, 0x78, + 0xac, 0x91, 0x9c, 0xd2, 0xb4, 0xee, 0xbe, 0x47, 0x88, 0xe7, 0x23, 0x83, 0xdf, 0xec, 0x78, 0x64, + 0xc0, 0x60, 0x9e, 0xba, 0xb4, 0xff, 0x45, 0xd0, 0x3c, 0xe3, 0xb5, 0xae, 0x18, 0x64, 0x48, 0xee, + 0x82, 0x3a, 0x45, 0xbf, 0xc4, 0x28, 0x70, 0x90, 0x22, 0xf4, 0x85, 0x03, 0xc9, 0x5c, 0xdf, 0xe5, + 0x23, 0xd0, 0xc0, 0xd4, 0x1a, 0x45, 0xe4, 0x57, 0x14, 0x28, 0x62, 0x5f, 0x38, 0xa8, 0x0f, 0x3b, + 0xcb, 0x85, 0xba, 0x33, 0x87, 0x13, 0xff, 0x44, 0x5b, 0xbb, 0x34, 0xb3, 0x8e, 0xe9, 0xd7, 0xfc, + 0x28, 0x33, 0xb0, 0xed, 0x90, 0x80, 0xa2, 0x80, 0xc6, 0xd4, 0xa2, 0x49, 0x07, 0x65, 0xa3, 0x2f, + 0x1c, 0x34, 0x8f, 0x0d, 0xfd, 0x99, 0x51, 0xf4, 0xb3, 0x55, 0x1e, 0x07, 0x36, 0xec, 0x2e, 0x17, + 0xea, 0x5e, 0xda, 0xa9, 0x52, 0x51, 0x33, 0xdb, 0x4e, 0x29, 0x56, 0x46, 0xe0, 0x13, 0xe8, 0xfb, + 0x64, 0x66, 0xc5, 0xa1, 0x0b, 0x19, 0xb2, 0xe0, 0x88, 0xa1, 0xc8, 0x0a, 0x23, 0x12, 0x12, 0x0a, + 0x7d, 0x45, 0xe2, 0xd0, 0x3f, 0x5b, 0x2e, 0x54, 0x2d, 0x2d, 0xf8, 0x44, 0xb0, 0x66, 0x2a, 0xdc, + 0x7b, 0xc3, 0x9d, 0xa7, 0x89, 0xef, 0x32, 0x73, 0x9d, 0x48, 0x7f, 0xfe, 0xa3, 0xd6, 0xb4, 0x7f, + 0x05, 0xd0, 0x2e, 0x63, 0x95, 0xbf, 0x03, 0x20, 0x8c, 0x6d, 0x1f, 0x3b, 0xd6, 0xcf, 0x68, 0xce, + 0x69, 0x6c, 0x1e, 0x77, 0xf4, 0xf4, 0x11, 0xf4, 0xd5, 0x23, 0xe8, 0xa7, 0xc1, 0x7c, 0xb8, 0xbb, + 0x5c, 0xa8, 0x1f, 0xa7, 0x20, 0xf2, 0x0c, 0xcd, 0x6c, 0xa4, 0x97, 0xef, 0xd1, 0x5c, 0xee, 0x83, + 0xa6, 0x8b, 0xa7, 0x28, 0xa2, 0x78, 0x84, 0x51, 0xc4, 0x69, 0x6f, 0x98, 0x45, 0x93, 0xfc, 0x29, + 0x68, 0x30, 0x3c, 0x41, 0x94, 0xc1, 0x49, 0xc8, 0xd9, 0x95, 0xcc, 0xdc, 0x90, 0x81, 0xfc, 0x43, + 0x04, 0x5b, 0xdf, 0x20, 0xe8, 0xa2, 0xe8, 0xc9, 0x17, 0x2e, 0x95, 0x12, 0x2b, 0xa5, 0x12, 0x2f, + 0xc5, 0x5e, 0x00, 0x59, 0x1c, 0xa5, 0xcf, 0xd8, 0x32, 0x73, 0x83, 0x7c, 0x03, 0xda, 0x01, 0x9a, + 0x59, 0x85, 0xc1, 0xa5, 0x27, 0x06, 0xdf, 0x5f, 0x2e, 0xd4, 0xdd, 0x74, 0xf0, 0x72, 0x96, 0x66, + 0xb6, 0x02, 0x34, 0xbb, 0x5c, 0xcf, 0x7f, 0x06, 0xb6, 0x93, 0x80, 0x22, 0x07, 0x9b, 0x09, 0x07, + 0x45, 0x41, 0x54, 0x02, 0x34, 0x33, 0x41, 0x72, 0x9e, 0x1b, 0x32, 0x12, 0x5e, 0x8a, 0xa0, 0xf5, + 0x03, 0xa6, 0x36, 0x1a, 0xc3, 0x29, 0x26, 0x71, 0x24, 0x0f, 0x40, 0x23, 0x15, 0x9f, 0x85, 0x5d, + 0xce, 0x45, 0x63, 0xb8, 0x97, 0x0b, 0x7a, 0xed, 0xd2, 0x14, 0xc1, 0xac, 0xa7, 0xb7, 0x6f, 0xdd, + 0x12, 0x7f, 0x62, 0x85, 0xbf, 0x10, 0x7c, 0xb4, 0x26, 0xc4, 0x22, 0xc1, 0x4a, 0xec, 0x47, 0xcf, + 0x8a, 0xfd, 0x6a, 0x95, 0x75, 0x1a, 0xb8, 0xe7, 0x90, 0xc1, 0xa1, 0xb2, 0x5c, 0xa8, 0x9d, 0x14, + 0x47, 0xa9, 0xa2, 0x66, 0xb6, 0xd6, 0xf7, 0x1f, 0x83, 0x4a, 0x47, 0x36, 0x23, 0x19, 0xe9, 0x1f, + 0xaa, 0x23, 0x9b, 0x91, 0x62, 0xc7, 0xeb, 0x19, 0xc9, 0xb8, 0xfc, 0x0d, 0xec, 0x54, 0x2b, 0x94, + 0xf5, 0x21, 0x54, 0xf5, 0x21, 0x03, 0x29, 0x84, 0x6c, 0xcc, 0x39, 0x6b, 0x99, 0xfc, 0x9c, 0xd8, + 0x5c, 0xc8, 0x60, 0x26, 0x26, 0x7e, 0x2e, 0x6b, 0x50, 0x7a, 0x5c, 0xce, 0xbf, 0x0b, 0x40, 0xb9, + 0x5e, 0xd9, 0x90, 0xbb, 0x46, 0xc2, 0x61, 0x7c, 0x05, 0xda, 0xf9, 0x00, 0xbc, 0x3c, 0xc7, 0x52, + 0x94, 0x5c, 0xd9, 0xaf, 0x99, 0x39, 0x87, 0xe7, 0x0f, 0x20, 0x88, 0x8f, 0x43, 0xf8, 0x5b, 0x00, + 0x8d, 0xa4, 0xef, 0x70, 0xce, 0x10, 0x7d, 0x8f, 0xa5, 0xaa, 0xec, 0xf7, 0xc6, 0xc3, 0xfd, 0x5e, + 0x11, 0x27, 0x3d, 0x42, 0xdc, 0x66, 0x4e, 0x5c, 0x86, 0xeb, 0x3f, 0x01, 0x80, 0x74, 0xd3, 0xf9, + 0x28, 0x17, 0xa0, 0x99, 0xed, 0xd7, 0xb3, 0xdf, 0xa2, 0x44, 0xfa, 0x72, 0x69, 0x25, 0xb3, 0x8f, + 0x51, 0xba, 0x8f, 0x6f, 0x59, 0x46, 0xf1, 0xdd, 0x96, 0x71, 0x38, 0x7a, 0x71, 0xd7, 0x13, 0x6e, + 0xef, 0x7a, 0xc2, 0xeb, 0xbb, 0x9e, 0xf0, 0xd7, 0x7d, 0xaf, 0x76, 0x7b, 0xdf, 0xab, 0xbd, 0xba, + 0xef, 0xd5, 0x7e, 0xba, 0xf0, 0x30, 0x1b, 0xc7, 0xb6, 0xee, 0x90, 0x89, 0xe1, 0x10, 0x3a, 0x21, + 0xd4, 0xc0, 0xb6, 0x73, 0xe8, 0x91, 0xe4, 0x9f, 0x38, 0x21, 0x6e, 0xec, 0x23, 0x9a, 0xfe, 0x37, + 0x0f, 0x57, 0x3f, 0xce, 0xcf, 0xbf, 0x38, 0x2c, 0xc8, 0xfb, 0xcb, 0xc2, 0xd9, 0xde, 0xe2, 0x33, + 0x0e, 0xde, 0x04, 0x00, 0x00, 0xff, 0xff, 0x49, 0x19, 0x81, 0xe4, 0x6e, 0x07, 0x00, 0x00, } func (m *ClientState) Marshal() (dAtA []byte, err error) { @@ -1170,10 +697,12 @@ func (m *SignatureAndData) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x1a } - if m.DataType != 0 { - i = encodeVarintSolomachine(dAtA, i, uint64(m.DataType)) + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) i-- - dAtA[i] = 0x10 + dAtA[i] = 0x12 } if len(m.Signature) > 0 { i -= len(m.Signature) @@ -1247,10 +776,12 @@ func (m *SignBytes) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x2a } - if m.DataType != 0 { - i = encodeVarintSolomachine(dAtA, i, uint64(m.DataType)) + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) i-- - dAtA[i] = 0x20 + dAtA[i] = 0x22 } if len(m.Diversifier) > 0 { i -= len(m.Diversifier) @@ -1314,1541 +845,201 @@ func (m *HeaderData) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *ClientStateData) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err +func encodeVarintSolomachine(dAtA []byte, offset int, v uint64) int { + offset -= sovSolomachine(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ } - return dAtA[:n], nil -} - -func (m *ClientStateData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) + dAtA[offset] = uint8(v) + return base } - -func (m *ClientStateData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i +func (m *ClientState) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l - if m.ClientState != nil { - { - size, err := m.ClientState.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintSolomachine(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 - } - if len(m.Path) > 0 { - i -= len(m.Path) - copy(dAtA[i:], m.Path) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) - i-- - dAtA[i] = 0xa + if m.Sequence != 0 { + n += 1 + sovSolomachine(uint64(m.Sequence)) } - return len(dAtA) - i, nil -} - -func (m *ConsensusStateData) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err + if m.IsFrozen { + n += 2 } - return dAtA[:n], nil -} - -func (m *ConsensusStateData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *ConsensusStateData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l if m.ConsensusState != nil { - { - size, err := m.ConsensusState.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintSolomachine(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 + l = m.ConsensusState.Size() + n += 1 + l + sovSolomachine(uint64(l)) } - if len(m.Path) > 0 { - i -= len(m.Path) - copy(dAtA[i:], m.Path) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) - i-- - dAtA[i] = 0xa + if m.AllowUpdateAfterProposal { + n += 2 } - return len(dAtA) - i, nil + return n } -func (m *ConnectionStateData) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err +func (m *ConsensusState) Size() (n int) { + if m == nil { + return 0 } - return dAtA[:n], nil -} - -func (m *ConnectionStateData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *ConnectionStateData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i var l int _ = l - if m.Connection != nil { - { - size, err := m.Connection.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintSolomachine(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 - } - if len(m.Path) > 0 { - i -= len(m.Path) - copy(dAtA[i:], m.Path) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *ChannelStateData) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err + if m.PublicKey != nil { + l = m.PublicKey.Size() + n += 1 + l + sovSolomachine(uint64(l)) } - return dAtA[:n], nil -} - -func (m *ChannelStateData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *ChannelStateData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.Channel != nil { - { - size, err := m.Channel.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintSolomachine(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 + l = len(m.Diversifier) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) } - if len(m.Path) > 0 { - i -= len(m.Path) - copy(dAtA[i:], m.Path) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) - i-- - dAtA[i] = 0xa + if m.Timestamp != 0 { + n += 1 + sovSolomachine(uint64(m.Timestamp)) } - return len(dAtA) - i, nil + return n } -func (m *PacketCommitmentData) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err +func (m *Header) Size() (n int) { + if m == nil { + return 0 } - return dAtA[:n], nil -} - -func (m *PacketCommitmentData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *PacketCommitmentData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i var l int _ = l - if len(m.Commitment) > 0 { - i -= len(m.Commitment) - copy(dAtA[i:], m.Commitment) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Commitment))) - i-- - dAtA[i] = 0x12 - } - if len(m.Path) > 0 { - i -= len(m.Path) - copy(dAtA[i:], m.Path) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *PacketAcknowledgementData) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err + if m.Sequence != 0 { + n += 1 + sovSolomachine(uint64(m.Sequence)) } - return dAtA[:n], nil -} - -func (m *PacketAcknowledgementData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *PacketAcknowledgementData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Acknowledgement) > 0 { - i -= len(m.Acknowledgement) - copy(dAtA[i:], m.Acknowledgement) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Acknowledgement))) - i-- - dAtA[i] = 0x12 + if m.Timestamp != 0 { + n += 1 + sovSolomachine(uint64(m.Timestamp)) } - if len(m.Path) > 0 { - i -= len(m.Path) - copy(dAtA[i:], m.Path) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) - i-- - dAtA[i] = 0xa + l = len(m.Signature) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) } - return len(dAtA) - i, nil -} - -func (m *PacketReceiptAbsenceData) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err + if m.NewPublicKey != nil { + l = m.NewPublicKey.Size() + n += 1 + l + sovSolomachine(uint64(l)) } - return dAtA[:n], nil -} - -func (m *PacketReceiptAbsenceData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *PacketReceiptAbsenceData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Path) > 0 { - i -= len(m.Path) - copy(dAtA[i:], m.Path) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) - i-- - dAtA[i] = 0xa + l = len(m.NewDiversifier) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) } - return len(dAtA) - i, nil + return n } -func (m *NextSequenceRecvData) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err +func (m *Misbehaviour) Size() (n int) { + if m == nil { + return 0 } - return dAtA[:n], nil -} - -func (m *NextSequenceRecvData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *NextSequenceRecvData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i var l int _ = l - if m.NextSeqRecv != 0 { - i = encodeVarintSolomachine(dAtA, i, uint64(m.NextSeqRecv)) - i-- - dAtA[i] = 0x10 + l = len(m.ClientId) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) } - if len(m.Path) > 0 { - i -= len(m.Path) - copy(dAtA[i:], m.Path) - i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func encodeVarintSolomachine(dAtA []byte, offset int, v uint64) int { - offset -= sovSolomachine(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ - } - dAtA[offset] = uint8(v) - return base -} -func (m *ClientState) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Sequence != 0 { - n += 1 + sovSolomachine(uint64(m.Sequence)) - } - if m.IsFrozen { - n += 2 - } - if m.ConsensusState != nil { - l = m.ConsensusState.Size() - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.AllowUpdateAfterProposal { - n += 2 - } - return n -} - -func (m *ConsensusState) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.PublicKey != nil { - l = m.PublicKey.Size() - n += 1 + l + sovSolomachine(uint64(l)) - } - l = len(m.Diversifier) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.Timestamp != 0 { - n += 1 + sovSolomachine(uint64(m.Timestamp)) - } - return n -} - -func (m *Header) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Sequence != 0 { - n += 1 + sovSolomachine(uint64(m.Sequence)) - } - if m.Timestamp != 0 { - n += 1 + sovSolomachine(uint64(m.Timestamp)) - } - l = len(m.Signature) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.NewPublicKey != nil { - l = m.NewPublicKey.Size() - n += 1 + l + sovSolomachine(uint64(l)) - } - l = len(m.NewDiversifier) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *Misbehaviour) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.ClientId) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.Sequence != 0 { - n += 1 + sovSolomachine(uint64(m.Sequence)) + if m.Sequence != 0 { + n += 1 + sovSolomachine(uint64(m.Sequence)) } if m.SignatureOne != nil { l = m.SignatureOne.Size() - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.SignatureTwo != nil { - l = m.SignatureTwo.Size() - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *SignatureAndData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Signature) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.DataType != 0 { - n += 1 + sovSolomachine(uint64(m.DataType)) - } - l = len(m.Data) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.Timestamp != 0 { - n += 1 + sovSolomachine(uint64(m.Timestamp)) - } - return n -} - -func (m *TimestampedSignatureData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.SignatureData) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.Timestamp != 0 { - n += 1 + sovSolomachine(uint64(m.Timestamp)) - } - return n -} - -func (m *SignBytes) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Sequence != 0 { - n += 1 + sovSolomachine(uint64(m.Sequence)) - } - if m.Timestamp != 0 { - n += 1 + sovSolomachine(uint64(m.Timestamp)) - } - l = len(m.Diversifier) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.DataType != 0 { - n += 1 + sovSolomachine(uint64(m.DataType)) - } - l = len(m.Data) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *HeaderData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.NewPubKey != nil { - l = m.NewPubKey.Size() - n += 1 + l + sovSolomachine(uint64(l)) - } - l = len(m.NewDiversifier) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *ClientStateData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Path) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.ClientState != nil { - l = m.ClientState.Size() - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *ConsensusStateData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Path) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.ConsensusState != nil { - l = m.ConsensusState.Size() - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *ConnectionStateData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Path) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.Connection != nil { - l = m.Connection.Size() - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *ChannelStateData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Path) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.Channel != nil { - l = m.Channel.Size() - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *PacketCommitmentData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Path) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - l = len(m.Commitment) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *PacketAcknowledgementData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Path) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - l = len(m.Acknowledgement) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *PacketReceiptAbsenceData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Path) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - return n -} - -func (m *NextSequenceRecvData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Path) - if l > 0 { - n += 1 + l + sovSolomachine(uint64(l)) - } - if m.NextSeqRecv != 0 { - n += 1 + sovSolomachine(uint64(m.NextSeqRecv)) - } - return n -} - -func sovSolomachine(x uint64) (n int) { - return (math_bits.Len64(x|1) + 6) / 7 -} -func sozSolomachine(x uint64) (n int) { - return sovSolomachine(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (m *ClientState) 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 ErrIntOverflowSolomachine - } - 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: ClientState: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ClientState: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) - } - m.Sequence = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Sequence |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field IsFrozen", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.IsFrozen = bool(v != 0) - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ConsensusState", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.ConsensusState == nil { - m.ConsensusState = &ConsensusState{} - } - if err := m.ConsensusState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 4: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field AllowUpdateAfterProposal", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.AllowUpdateAfterProposal = bool(v != 0) - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ConsensusState) 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 ErrIntOverflowSolomachine - } - 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: ConsensusState: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ConsensusState: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field PublicKey", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.PublicKey == nil { - m.PublicKey = &types.Any{} - } - if err := m.PublicKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Diversifier", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - 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 ErrInvalidLengthSolomachine - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Diversifier = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) - } - m.Timestamp = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Timestamp |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *Header) 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 ErrIntOverflowSolomachine - } - 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: Header: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Header: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) - } - m.Sequence = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Sequence |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) - } - m.Timestamp = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Timestamp |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) - if m.Signature == nil { - m.Signature = []byte{} - } - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NewPublicKey", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.NewPublicKey == nil { - m.NewPublicKey = &types.Any{} - } - if err := m.NewPublicKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NewDiversifier", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - 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 ErrInvalidLengthSolomachine - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.NewDiversifier = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *Misbehaviour) 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 ErrIntOverflowSolomachine - } - 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: Misbehaviour: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Misbehaviour: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ClientId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - 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 ErrInvalidLengthSolomachine - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ClientId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) - } - m.Sequence = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Sequence |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SignatureOne", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.SignatureOne == nil { - m.SignatureOne = &SignatureAndData{} - } - if err := m.SignatureOne.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SignatureTwo", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.SignatureTwo == nil { - m.SignatureTwo = &SignatureAndData{} - } - if err := m.SignatureTwo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *SignatureAndData) 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 ErrIntOverflowSolomachine - } - 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: SignatureAndData: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: SignatureAndData: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) - if m.Signature == nil { - m.Signature = []byte{} - } - iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field DataType", wireType) - } - m.DataType = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.DataType |= DataType(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) - if m.Data == nil { - m.Data = []byte{} - } - iNdEx = postIndex - case 4: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) - } - m.Timestamp = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Timestamp |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *TimestampedSignatureData) 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 ErrIntOverflowSolomachine - } - 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: TimestampedSignatureData: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: TimestampedSignatureData: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SignatureData", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.SignatureData = append(m.SignatureData[:0], dAtA[iNdEx:postIndex]...) - if m.SignatureData == nil { - m.SignatureData = []byte{} - } - iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) - } - m.Timestamp = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Timestamp |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.SignatureTwo != nil { + l = m.SignatureTwo.Size() + n += 1 + l + sovSolomachine(uint64(l)) } + return n +} - if iNdEx > l { - return io.ErrUnexpectedEOF +func (m *SignatureAndData) Size() (n int) { + if m == nil { + return 0 } - return nil + var l int + _ = l + l = len(m.Signature) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + l = len(m.Path) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.Timestamp != 0 { + n += 1 + sovSolomachine(uint64(m.Timestamp)) + } + return n } -func (m *SignBytes) Unmarshal(dAtA []byte) error { + +func (m *TimestampedSignatureData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.SignatureData) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.Timestamp != 0 { + n += 1 + sovSolomachine(uint64(m.Timestamp)) + } + return n +} + +func (m *SignBytes) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Sequence != 0 { + n += 1 + sovSolomachine(uint64(m.Sequence)) + } + if m.Timestamp != 0 { + n += 1 + sovSolomachine(uint64(m.Timestamp)) + } + l = len(m.Diversifier) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + l = len(m.Path) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + return n +} + +func (m *HeaderData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.NewPubKey != nil { + l = m.NewPubKey.Size() + n += 1 + l + sovSolomachine(uint64(l)) + } + l = len(m.NewDiversifier) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + return n +} + +func sovSolomachine(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozSolomachine(x uint64) (n int) { + return sovSolomachine(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *ClientState) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -2871,10 +1062,10 @@ func (m *SignBytes) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: SignBytes: wiretype end group for non-group") + return fmt.Errorf("proto: ClientState: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: SignBytes: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ClientState: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -2898,9 +1089,9 @@ func (m *SignBytes) Unmarshal(dAtA []byte) error { } case 2: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field IsFrozen", wireType) } - m.Timestamp = 0 + var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowSolomachine @@ -2910,149 +1101,15 @@ func (m *SignBytes) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Timestamp |= uint64(b&0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } } + m.IsFrozen = bool(v != 0) case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Diversifier", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - 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 ErrInvalidLengthSolomachine - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Diversifier = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 4: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field DataType", wireType) - } - m.DataType = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.DataType |= DataType(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSolomachine - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) - if m.Data == nil { - m.Data = []byte{} - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *HeaderData) 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 ErrIntOverflowSolomachine - } - 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: HeaderData: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: HeaderData: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NewPubKey", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ConsensusState", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -3079,18 +1136,18 @@ func (m *HeaderData) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.NewPubKey == nil { - m.NewPubKey = &types.Any{} + if m.ConsensusState == nil { + m.ConsensusState = &ConsensusState{} } - if err := m.NewPubKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.ConsensusState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NewDiversifier", wireType) + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AllowUpdateAfterProposal", wireType) } - var stringLen uint64 + var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowSolomachine @@ -3100,24 +1157,12 @@ func (m *HeaderData) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.NewDiversifier = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex + m.AllowUpdateAfterProposal = bool(v != 0) default: iNdEx = preIndex skippy, err := skipSolomachine(dAtA[iNdEx:]) @@ -3139,7 +1184,7 @@ func (m *HeaderData) Unmarshal(dAtA []byte) error { } return nil } -func (m *ClientStateData) Unmarshal(dAtA []byte) error { +func (m *ConsensusState) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -3162,17 +1207,17 @@ func (m *ClientStateData) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ClientStateData: wiretype end group for non-group") + return fmt.Errorf("proto: ConsensusState: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ClientStateData: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ConsensusState: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field PublicKey", wireType) } - var byteLen int + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowSolomachine @@ -3182,31 +1227,33 @@ func (m *ClientStateData) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= int(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - if byteLen < 0 { + if msglen < 0 { return ErrInvalidLengthSolomachine } - postIndex := iNdEx + byteLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthSolomachine } if postIndex > l { return io.ErrUnexpectedEOF } - m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) - if m.Path == nil { - m.Path = []byte{} + if m.PublicKey == nil { + m.PublicKey = &types.Any{} + } + if err := m.PublicKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err } iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ClientState", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Diversifier", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowSolomachine @@ -3216,28 +1263,43 @@ func (m *ClientStateData) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLengthSolomachine } - postIndex := iNdEx + msglen + postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthSolomachine } if postIndex > l { return io.ErrUnexpectedEOF } - if m.ClientState == nil { - m.ClientState = &types.Any{} + m.Diversifier = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) } - if err := m.ClientState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err + m.Timestamp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Timestamp |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } } - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipSolomachine(dAtA[iNdEx:]) @@ -3259,7 +1321,7 @@ func (m *ClientStateData) Unmarshal(dAtA []byte) error { } return nil } -func (m *ConsensusStateData) Unmarshal(dAtA []byte) error { +func (m *Header) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -3282,15 +1344,53 @@ func (m *ConsensusStateData) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ConsensusStateData: wiretype end group for non-group") + return fmt.Errorf("proto: Header: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ConsensusStateData: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: Header: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) + } + m.Sequence = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Sequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Timestamp |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { @@ -3317,14 +1417,14 @@ func (m *ConsensusStateData) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) - if m.Path == nil { - m.Path = []byte{} + m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) + if m.Signature == nil { + m.Signature = []byte{} } iNdEx = postIndex - case 2: + case 4: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ConsensusState", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field NewPublicKey", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -3351,13 +1451,45 @@ func (m *ConsensusStateData) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.ConsensusState == nil { - m.ConsensusState = &types.Any{} + if m.NewPublicKey == nil { + m.NewPublicKey = &types.Any{} } - if err := m.ConsensusState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.NewPublicKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NewDiversifier", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + 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 ErrInvalidLengthSolomachine + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NewDiversifier = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipSolomachine(dAtA[iNdEx:]) @@ -3379,7 +1511,7 @@ func (m *ConsensusStateData) Unmarshal(dAtA []byte) error { } return nil } -func (m *ConnectionStateData) Unmarshal(dAtA []byte) error { +func (m *Misbehaviour) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -3402,17 +1534,17 @@ func (m *ConnectionStateData) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ConnectionStateData: wiretype end group for non-group") + return fmt.Errorf("proto: Misbehaviour: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ConnectionStateData: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: Misbehaviour: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ClientId", wireType) } - var byteLen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowSolomachine @@ -3422,31 +1554,29 @@ func (m *ConnectionStateData) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if byteLen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLengthSolomachine } - postIndex := iNdEx + byteLen + postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthSolomachine } if postIndex > l { return io.ErrUnexpectedEOF } - m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) - if m.Path == nil { - m.Path = []byte{} - } + m.ClientId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Connection", wireType) + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) } - var msglen int + m.Sequence = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowSolomachine @@ -3456,83 +1586,16 @@ func (m *ConnectionStateData) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + m.Sequence |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Connection == nil { - m.Connection = &types1.ConnectionEnd{} - } - if err := m.Connection.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipSolomachine(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthSolomachine - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ChannelStateData) 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 ErrIntOverflowSolomachine - } - 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: ChannelStateData: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ChannelStateData: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: + case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field SignatureOne", wireType) } - var byteLen int + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowSolomachine @@ -3542,29 +1605,31 @@ func (m *ChannelStateData) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= int(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - if byteLen < 0 { + if msglen < 0 { return ErrInvalidLengthSolomachine } - postIndex := iNdEx + byteLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthSolomachine } if postIndex > l { return io.ErrUnexpectedEOF } - m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) - if m.Path == nil { - m.Path = []byte{} + if m.SignatureOne == nil { + m.SignatureOne = &SignatureAndData{} + } + if err := m.SignatureOne.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err } iNdEx = postIndex - case 2: + case 4: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Channel", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field SignatureTwo", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -3591,10 +1656,10 @@ func (m *ChannelStateData) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.Channel == nil { - m.Channel = &types2.Channel{} + if m.SignatureTwo == nil { + m.SignatureTwo = &SignatureAndData{} } - if err := m.Channel.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.SignatureTwo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -3619,7 +1684,7 @@ func (m *ChannelStateData) Unmarshal(dAtA []byte) error { } return nil } -func (m *PacketCommitmentData) Unmarshal(dAtA []byte) error { +func (m *SignatureAndData) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -3642,13 +1707,47 @@ func (m *PacketCommitmentData) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: PacketCommitmentData: wiretype end group for non-group") + return fmt.Errorf("proto: SignatureAndData: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: PacketCommitmentData: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: SignatureAndData: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) + if m.Signature == nil { + m.Signature = []byte{} + } + iNdEx = postIndex + case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) } @@ -3682,9 +1781,9 @@ func (m *PacketCommitmentData) Unmarshal(dAtA []byte) error { m.Path = []byte{} } iNdEx = postIndex - case 2: + case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Commitment", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { @@ -3711,11 +1810,30 @@ func (m *PacketCommitmentData) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Commitment = append(m.Commitment[:0], dAtA[iNdEx:postIndex]...) - if m.Commitment == nil { - m.Commitment = []byte{} + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} } iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Timestamp |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipSolomachine(dAtA[iNdEx:]) @@ -3737,7 +1855,7 @@ func (m *PacketCommitmentData) Unmarshal(dAtA []byte) error { } return nil } -func (m *PacketAcknowledgementData) Unmarshal(dAtA []byte) error { +func (m *TimestampedSignatureData) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -3760,15 +1878,15 @@ func (m *PacketAcknowledgementData) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: PacketAcknowledgementData: wiretype end group for non-group") + return fmt.Errorf("proto: TimestampedSignatureData: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: PacketAcknowledgementData: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: TimestampedSignatureData: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field SignatureData", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { @@ -3795,16 +1913,16 @@ func (m *PacketAcknowledgementData) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) - if m.Path == nil { - m.Path = []byte{} + m.SignatureData = append(m.SignatureData[:0], dAtA[iNdEx:postIndex]...) + if m.SignatureData == nil { + m.SignatureData = []byte{} } iNdEx = postIndex case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Acknowledgement", wireType) + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) } - var byteLen int + m.Timestamp = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowSolomachine @@ -3814,26 +1932,11 @@ func (m *PacketAcknowledgementData) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= int(b&0x7F) << shift + m.Timestamp |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if byteLen < 0 { - return ErrInvalidLengthSolomachine - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthSolomachine - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Acknowledgement = append(m.Acknowledgement[:0], dAtA[iNdEx:postIndex]...) - if m.Acknowledgement == nil { - m.Acknowledgement = []byte{} - } - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipSolomachine(dAtA[iNdEx:]) @@ -3855,7 +1958,7 @@ func (m *PacketAcknowledgementData) Unmarshal(dAtA []byte) error { } return nil } -func (m *PacketReceiptAbsenceData) Unmarshal(dAtA []byte) error { +func (m *SignBytes) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -3878,13 +1981,83 @@ func (m *PacketReceiptAbsenceData) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: PacketReceiptAbsenceData: wiretype end group for non-group") + return fmt.Errorf("proto: SignBytes: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: PacketReceiptAbsenceData: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: SignBytes: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) + } + m.Sequence = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Sequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Timestamp |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Diversifier", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + 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 ErrInvalidLengthSolomachine + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Diversifier = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) } @@ -3918,6 +2091,40 @@ func (m *PacketReceiptAbsenceData) Unmarshal(dAtA []byte) error { m.Path = []byte{} } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipSolomachine(dAtA[iNdEx:]) @@ -3939,7 +2146,7 @@ func (m *PacketReceiptAbsenceData) Unmarshal(dAtA []byte) error { } return nil } -func (m *NextSequenceRecvData) Unmarshal(dAtA []byte) error { +func (m *HeaderData) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -3962,17 +2169,17 @@ func (m *NextSequenceRecvData) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: NextSequenceRecvData: wiretype end group for non-group") + return fmt.Errorf("proto: HeaderData: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: NextSequenceRecvData: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: HeaderData: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field NewPubKey", wireType) } - var byteLen int + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowSolomachine @@ -3982,31 +2189,33 @@ func (m *NextSequenceRecvData) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= int(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - if byteLen < 0 { + if msglen < 0 { return ErrInvalidLengthSolomachine } - postIndex := iNdEx + byteLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthSolomachine } if postIndex > l { return io.ErrUnexpectedEOF } - m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) - if m.Path == nil { - m.Path = []byte{} + if m.NewPubKey == nil { + m.NewPubKey = &types.Any{} + } + if err := m.NewPubKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err } iNdEx = postIndex case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field NextSeqRecv", wireType) + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NewDiversifier", wireType) } - m.NextSeqRecv = 0 + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowSolomachine @@ -4016,11 +2225,24 @@ func (m *NextSequenceRecvData) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.NextSeqRecv |= uint64(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NewDiversifier = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipSolomachine(dAtA[iNdEx:]) diff --git a/modules/light-clients/06-solomachine/update.go b/modules/light-clients/06-solomachine/update.go index 1d903a6105a..d514603f784 100644 --- a/modules/light-clients/06-solomachine/update.go +++ b/modules/light-clients/06-solomachine/update.go @@ -44,7 +44,25 @@ func (cs ClientState) verifyHeader(ctx sdk.Context, cdc codec.BinaryCodec, clien } // assert currently registered public key signed over the new public key with correct sequence - data, err := HeaderSignBytes(cdc, header) + headerData := &HeaderData{ + NewPubKey: header.NewPublicKey, + NewDiversifier: header.NewDiversifier, + } + + dataBz, err := cdc.Marshal(headerData) + if err != nil { + return err + } + + signBytes := &SignBytes{ + Sequence: header.Sequence, + Timestamp: header.Timestamp, + Diversifier: cs.ConsensusState.Diversifier, + Path: []byte{}, + Data: dataBz, + } + + data, err := cdc.Marshal(signBytes) if err != nil { return err } diff --git a/modules/light-clients/06-solomachine/update_test.go b/modules/light-clients/06-solomachine/update_test.go index 24dad99ab0b..e205886bc25 100644 --- a/modules/light-clients/06-solomachine/update_test.go +++ b/modules/light-clients/06-solomachine/update_test.go @@ -29,7 +29,14 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { { "successful header", func() { - clientMsg = sm.CreateHeader() + clientMsg = sm.CreateHeader(sm.Diversifier) + }, + true, + }, + { + "successful header with new diversifier", + func() { + clientMsg = sm.CreateHeader(sm.Diversifier + "0") }, true, }, @@ -51,7 +58,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { "wrong sequence in header", func() { // store in temp before assigning to interface type - h := sm.CreateHeader() + h := sm.CreateHeader(sm.Diversifier) h.Sequence++ clientMsg = h }, @@ -60,7 +67,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { { "invalid header Signature", func() { - h := sm.CreateHeader() + h := sm.CreateHeader(sm.Diversifier) h.Signature = suite.GetInvalidProof() clientMsg = h }, false, @@ -68,7 +75,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { { "invalid timestamp in header", func() { - h := sm.CreateHeader() + h := sm.CreateHeader(sm.Diversifier) h.Timestamp-- clientMsg = h }, false, @@ -78,7 +85,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { func() { sm.Sequence++ - clientMsg = sm.CreateHeader() + clientMsg = sm.CreateHeader(sm.Diversifier) }, false, }, @@ -87,7 +94,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { func() { // store in temp before assinging to interface type cs := sm.ClientState() - h := sm.CreateHeader() + h := sm.CreateHeader(sm.Diversifier) publicKey, err := codectypes.NewAnyWithValue(sm.PublicKey) suite.NoError(err) @@ -105,7 +112,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { Sequence: cs.Sequence, Timestamp: sm.Time, Diversifier: sm.Diversifier, - DataType: solomachine.CLIENT, + Path: []byte("invalid signature data"), Data: dataBz, } @@ -128,7 +135,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { // store in temp before assinging to interface type cs := sm.ClientState() oldPubKey := sm.PublicKey - h := sm.CreateHeader() + h := sm.CreateHeader(sm.Diversifier) // generate invalid signature data := append(sdk.Uint64ToBigEndian(cs.Sequence), oldPubKey.Bytes()...) @@ -144,7 +151,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { "consensus state public key is nil - header", func() { clientState.ConsensusState.PublicKey = nil - clientMsg = sm.CreateHeader() + clientMsg = sm.CreateHeader(sm.Diversifier) }, false, }, @@ -262,7 +269,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { Sequence: sm.Sequence + 1, Timestamp: sm.Time, Diversifier: sm.Diversifier, - DataType: solomachine.CLIENT, + Path: []byte("invalid signature data"), Data: msg, } @@ -288,7 +295,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { Sequence: sm.Sequence + 1, Timestamp: sm.Time, Diversifier: sm.Diversifier, - DataType: solomachine.CLIENT, + Path: []byte("invalid signature data"), Data: msg, } @@ -309,7 +316,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { badMisbehaviour := sm.CreateMisbehaviour() // update public key to a new one - sm.CreateHeader() + sm.CreateHeader(sm.Diversifier) m := sm.CreateMisbehaviour() // set SignatureOne to use the wrong signature @@ -323,7 +330,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { badMisbehaviour := sm.CreateMisbehaviour() // update public key to a new one - sm.CreateHeader() + sm.CreateHeader(sm.Diversifier) m := sm.CreateMisbehaviour() // set SignatureTwo to use the wrong signature @@ -345,7 +352,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { Sequence: sm.Sequence + 1, Timestamp: sm.Time, Diversifier: sm.Diversifier, - DataType: solomachine.CLIENT, + Path: []byte("invalid signature data"), Data: msg, } @@ -365,7 +372,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { Sequence: sm.Sequence - 1, Timestamp: sm.Time, Diversifier: sm.Diversifier, - DataType: solomachine.CLIENT, + Path: []byte("invalid signature data"), Data: msg, } data, err = suite.chainA.Codec.Marshal(signBytes) @@ -421,7 +428,7 @@ func (suite *SoloMachineTestSuite) TestUpdateState() { "successful update", func() { clientState = sm.ClientState() - clientMsg = sm.CreateHeader() + clientMsg = sm.CreateHeader(sm.Diversifier) }, true, }, @@ -491,7 +498,7 @@ func (suite *SoloMachineTestSuite) TestCheckForMisbehaviour() { { "normal header returns false", func() { - clientMsg = sm.CreateHeader() + clientMsg = sm.CreateHeader(sm.Diversifier) }, false, }, diff --git a/proto/ibc/lightclients/solomachine/v2/solomachine.proto b/proto/ibc/lightclients/solomachine/v2/solomachine.proto index 5f0de59c6d7..5b828483bfc 100644 --- a/proto/ibc/lightclients/solomachine/v2/solomachine.proto +++ b/proto/ibc/lightclients/solomachine/v2/solomachine.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.lightclients.solomachine.v2; -option go_package = "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine;solomachine"; +option go_package = "github.com/cosmos/ibc-go/v3/modules/core/02-client/migrations/v6"; import "ibc/core/connection/v1/connection.proto"; import "ibc/core/channel/v1/channel.proto"; @@ -52,12 +52,10 @@ message Header { // of a sequence and two signatures over different messages at that sequence. message Misbehaviour { option (gogoproto.goproto_getters) = false; - - // ClientID is deprecated - string client_id = 1 [deprecated = true, (gogoproto.moretags) = "yaml:\"client_id\""]; - uint64 sequence = 2; - SignatureAndData signature_one = 3 [(gogoproto.moretags) = "yaml:\"signature_one\""]; - SignatureAndData signature_two = 4 [(gogoproto.moretags) = "yaml:\"signature_two\""]; + string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + uint64 sequence = 2; + SignatureAndData signature_one = 3 [(gogoproto.moretags) = "yaml:\"signature_one\""]; + SignatureAndData signature_two = 4 [(gogoproto.moretags) = "yaml:\"signature_two\""]; } // SignatureAndData contains a signature and the data signed over to create that @@ -188,4 +186,4 @@ message PacketReceiptAbsenceData { message NextSequenceRecvData { bytes path = 1; uint64 next_seq_recv = 2 [(gogoproto.moretags) = "yaml:\"next_seq_recv\""]; -} +} \ No newline at end of file diff --git a/proto/ibc/lightclients/solomachine/v3/solomachine.proto b/proto/ibc/lightclients/solomachine/v3/solomachine.proto new file mode 100644 index 00000000000..63ce566782d --- /dev/null +++ b/proto/ibc/lightclients/solomachine/v3/solomachine.proto @@ -0,0 +1,103 @@ +syntax = "proto3"; + +package ibc.lightclients.solomachine.v3; + +option go_package = "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine;solomachine"; + +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; + +// ClientState defines a solo machine client that tracks the current consensus +// state and if the client is frozen. +message ClientState { + option (gogoproto.goproto_getters) = false; + // latest sequence of the client state + uint64 sequence = 1; + // frozen sequence of the solo machine + bool is_frozen = 2 [(gogoproto.moretags) = "yaml:\"is_frozen\""]; + ConsensusState consensus_state = 3 [(gogoproto.moretags) = "yaml:\"consensus_state\""]; + // when set to true, will allow governance to update a solo machine client. + // The client will be unfrozen if it is frozen. + bool allow_update_after_proposal = 4 [(gogoproto.moretags) = "yaml:\"allow_update_after_proposal\""]; +} + +// ConsensusState defines a solo machine consensus state. The sequence of a +// consensus state is contained in the "height" key used in storing the +// consensus state. +message ConsensusState { + option (gogoproto.goproto_getters) = false; + // public key of the solo machine + google.protobuf.Any public_key = 1 [(gogoproto.moretags) = "yaml:\"public_key\""]; + // diversifier allows the same public key to be re-used across different solo + // machine clients (potentially on different chains) without being considered + // misbehaviour. + string diversifier = 2; + uint64 timestamp = 3; +} + +// Header defines a solo machine consensus header +message Header { + option (gogoproto.goproto_getters) = false; + // sequence to update solo machine public key at + uint64 sequence = 1; + uint64 timestamp = 2; + bytes signature = 3; + google.protobuf.Any new_public_key = 4 [(gogoproto.moretags) = "yaml:\"new_public_key\""]; + string new_diversifier = 5 [(gogoproto.moretags) = "yaml:\"new_diversifier\""]; +} + +// Misbehaviour defines misbehaviour for a solo machine which consists +// of a sequence and two signatures over different messages at that sequence. +message Misbehaviour { + option (gogoproto.goproto_getters) = false; + + // ClientID is deprecated + string client_id = 1 [deprecated = true, (gogoproto.moretags) = "yaml:\"client_id\""]; + uint64 sequence = 2; + SignatureAndData signature_one = 3 [(gogoproto.moretags) = "yaml:\"signature_one\""]; + SignatureAndData signature_two = 4 [(gogoproto.moretags) = "yaml:\"signature_two\""]; +} + +// SignatureAndData contains a signature and the data signed over to create that +// signature. +message SignatureAndData { + option (gogoproto.goproto_getters) = false; + bytes signature = 1; + bytes path = 2; + bytes data = 3; + uint64 timestamp = 4; +} + +// TimestampedSignatureData contains the signature data and the timestamp of the +// signature. +message TimestampedSignatureData { + option (gogoproto.goproto_getters) = false; + bytes signature_data = 1 [(gogoproto.moretags) = "yaml:\"signature_data\""]; + uint64 timestamp = 2; +} + +// SignBytes defines the signed bytes used for signature verification. +message SignBytes { + option (gogoproto.goproto_getters) = false; + + // the sequence number + uint64 sequence = 1; + // the proof timestamp + uint64 timestamp = 2; + // the public key diversifier + string diversifier = 3; + // the standardised path bytes + bytes path = 4; + // the marshaled data bytes + bytes data = 5; +} + +// HeaderData returns the SignBytes data for update verification. +message HeaderData { + option (gogoproto.goproto_getters) = false; + + // header public key + google.protobuf.Any new_pub_key = 1 [(gogoproto.moretags) = "yaml:\"new_pub_key\""]; + // header diversifier + string new_diversifier = 2 [(gogoproto.moretags) = "yaml:\"new_diversifier\""]; +} diff --git a/testing/sdk_test.go b/testing/sdk_test.go deleted file mode 100644 index 415c61eee11..00000000000 --- a/testing/sdk_test.go +++ /dev/null @@ -1,279 +0,0 @@ -package ibctesting_test - -import ( - "fmt" - "testing" - "time" - - "github.com/cosmos/cosmos-sdk/baseapp" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/crypto/hd" - "github.com/cosmos/cosmos-sdk/crypto/keyring" - kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - servertypes "github.com/cosmos/cosmos-sdk/server/types" - storetypes "github.com/cosmos/cosmos-sdk/store/types" - "github.com/cosmos/cosmos-sdk/testutil" - clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" - "github.com/cosmos/cosmos-sdk/testutil/network" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/rest" - txtypes "github.com/cosmos/cosmos-sdk/types/tx" - authcli "github.com/cosmos/cosmos-sdk/x/auth/client/cli" - authrest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - "github.com/spf13/cobra" - "github.com/stretchr/testify/suite" - tmrand "github.com/tendermint/tendermint/libs/rand" - dbm "github.com/tendermint/tm-db" - - ibcclientcli "github.com/cosmos/ibc-go/v3/modules/core/02-client/client/cli" - "github.com/cosmos/ibc-go/v3/testing/simapp" - "github.com/cosmos/ibc-go/v3/testing/simapp/params" -) - -/* - This file contains tests from the SDK which had to deleted during the migration of - the IBC module from the SDK into this repository. https://github.com/cosmos/cosmos-sdk/pull/8735 - - They can be removed once the SDK deprecates amino. -*/ - -type IntegrationTestSuite struct { - suite.Suite - - cfg network.Config - network *network.Network -} - -func (s *IntegrationTestSuite) SetupSuite() { - s.T().Log("setting up integration test suite") - - cfg := DefaultConfig() - - cfg.NumValidators = 2 - - s.cfg = cfg - s.network = network.New(s.T(), cfg) - - kb := s.network.Validators[0].ClientCtx.Keyring - _, _, err := kb.NewMnemonic("newAccount", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) - s.Require().NoError(err) - - account1, _, err := kb.NewMnemonic("newAccount1", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) - s.Require().NoError(err) - - account2, _, err := kb.NewMnemonic("newAccount2", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) - s.Require().NoError(err) - - multi := kmultisig.NewLegacyAminoPubKey(2, []cryptotypes.PubKey{account1.GetPubKey(), account2.GetPubKey()}) - _, err = kb.SaveMultisig("multi", multi) - s.Require().NoError(err) - - _, err = s.network.WaitForHeight(1) - s.Require().NoError(err) - - s.Require().NoError(s.network.WaitForNextBlock()) -} - -func TestIntegrationTestSuite(t *testing.T) { - suite.Run(t, new(IntegrationTestSuite)) -} - -// NewAppConstructor returns a new simapp AppConstructor -func NewAppConstructor(encodingCfg params.EncodingConfig) network.AppConstructor { - return func(val network.Validator) servertypes.Application { - return simapp.NewSimApp( - val.Ctx.Logger, dbm.NewMemDB(), nil, true, make(map[int64]bool), val.Ctx.Config.RootDir, 0, - encodingCfg, - simapp.EmptyAppOptions{}, - baseapp.SetPruning(storetypes.NewPruningOptionsFromString(val.AppConfig.Pruning)), - baseapp.SetMinGasPrices(val.AppConfig.MinGasPrices), - ) - } -} - -// DefaultConfig returns a sane default configuration suitable for nearly all -// testing requirements. -func DefaultConfig() network.Config { - encCfg := simapp.MakeTestEncodingConfig() - - return network.Config{ - Codec: encCfg.Marshaler, - TxConfig: encCfg.TxConfig, - LegacyAmino: encCfg.Amino, - InterfaceRegistry: encCfg.InterfaceRegistry, - AccountRetriever: authtypes.AccountRetriever{}, - AppConstructor: NewAppConstructor(encCfg), - GenesisState: simapp.ModuleBasics.DefaultGenesis(encCfg.Marshaler), - TimeoutCommit: 2 * time.Second, - ChainID: "chain-" + tmrand.NewRand().Str(6), - NumValidators: 4, - BondDenom: sdk.DefaultBondDenom, - MinGasPrices: fmt.Sprintf("0.000006%s", sdk.DefaultBondDenom), - AccountTokens: sdk.TokensFromConsensusPower(1000, sdk.DefaultPowerReduction), - StakingTokens: sdk.TokensFromConsensusPower(500, sdk.DefaultPowerReduction), - BondedTokens: sdk.TokensFromConsensusPower(100, sdk.DefaultPowerReduction), - PruningStrategy: storetypes.PruningOptionNothing, - CleanupDir: true, - SigningAlgo: string(hd.Secp256k1Type), - KeyringOptions: []keyring.Option{}, - } -} - -func (s *IntegrationTestSuite) TearDownSuite() { - s.T().Log("tearing down integration test suite") - s.network.Cleanup() -} - -// TestLegacyRestErrMessages creates two IBC txs, one that fails, one that -// succeeds, and make sure we cannot query any of them (with pretty error msg). -// Our intension is to test the error message of querying a message which is -// signed with proto, since IBC won't support legacy amino at all we are -// considering a message from IBC module. -func (s *IntegrationTestSuite) TestLegacyRestErrMessages() { - val := s.network.Validators[0] - - // Write client state json to temp file, used for an IBC message. - // Generated by printing the result of cdc.MarshalIntefaceJSON on - // a solo machine client state - clientStateJSON := testutil.WriteToNewTempFile( - s.T(), - `{"@type":"/ibc.lightclients.solomachine.v2.ClientState","sequence":"1","is_frozen":false,"consensus_state":{"public_key":{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"AtK50+5pJOoaa04qqAqrnyAqsYrwrR/INnA6UPIaYZlp"},"diversifier":"testing","timestamp":"10"},"allow_update_after_proposal":false}`, - ) - - badClientStateJSON := testutil.WriteToNewTempFile( - s.T(), - `{"@type":"/ibc.lightclients.solomachine.v2.ClientState","sequence":"1","is_frozen":false,"consensus_state":{"public_key":{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"AtK50+5pJOoaa04qqAqrnyAqsYrwrR/INnA6UPIaYZlp"},"diversifier":"DIFFERENT","timestamp":"10"},"allow_update_after_proposal":false}`, - ) - - // Write consensus json to temp file, used for an IBC message. - // Generated by printing the result of cdc.MarshalIntefaceJSON on - // a solo machine consensus state - consensusJSON := testutil.WriteToNewTempFile( - s.T(), - `{"@type":"/ibc.lightclients.solomachine.v2.ConsensusState","public_key":{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"AtK50+5pJOoaa04qqAqrnyAqsYrwrR/INnA6UPIaYZlp"},"diversifier":"testing","timestamp":"10"}`, - ) - - testCases := []struct { - desc string - cmd *cobra.Command - args []string - code uint32 - }{ - { - "Failing IBC message", - ibcclientcli.NewCreateClientCmd(), - []string{ - badClientStateJSON.Name(), // path to client state json - consensusJSON.Name(), // path to consensus json, - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), - fmt.Sprintf("--gas=%d", flags.DefaultGasLimit), - fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), - fmt.Sprintf("--%s=foobar", flags.FlagNote), - }, - uint32(8), - }, - { - "Successful IBC message", - ibcclientcli.NewCreateClientCmd(), - []string{ - clientStateJSON.Name(), // path to client state json - consensusJSON.Name(), // path to consensus json, - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), - fmt.Sprintf("--gas=%d", flags.DefaultGasLimit), - fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), - fmt.Sprintf("--%s=foobar", flags.FlagNote), - }, - uint32(0), - }, - } - - for _, tc := range testCases { - s.Run(fmt.Sprintf("Case %s", tc.desc), func() { - out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, tc.cmd, tc.args) - s.Require().NoError(err) - var txRes sdk.TxResponse - s.Require().NoError(val.ClientCtx.JSONCodec.UnmarshalJSON(out.Bytes(), &txRes)) - s.Require().Equal(tc.code, txRes.Code) - - s.Require().NoError(s.network.WaitForNextBlock()) - - s.testQueryIBCTx(txRes, tc.cmd, tc.args) - }) - } -} - -// testQueryIBCTx is a helper function to test querying txs which: -// - show an error message on legacy REST endpoints -// - succeed using gRPC -// In practice, we call this function on IBC txs. -func (s *IntegrationTestSuite) testQueryIBCTx(txRes sdk.TxResponse, cmd *cobra.Command, args []string) { - val := s.network.Validators[0] - - errMsg := "this transaction cannot be displayed via legacy REST endpoints, because it does not support" + - " Amino serialization. Please either use CLI, gRPC, gRPC-gateway, or directly query the Tendermint RPC" + - " endpoint to query this transaction. The new REST endpoint (via gRPC-gateway) is " - - // Test that legacy endpoint return the above error message on IBC txs. - testCases := []struct { - desc string - url string - }{ - { - "Query by hash", - fmt.Sprintf("%s/txs/%s", val.APIAddress, txRes.TxHash), - }, - { - "Query by height", - fmt.Sprintf("%s/txs?tx.height=%d", val.APIAddress, txRes.Height), - }, - } - - for _, tc := range testCases { - s.Run(fmt.Sprintf("Case %s", tc.desc), func() { - txJSON, err := rest.GetRequest(tc.url) - s.Require().NoError(err) - - var errResp rest.ErrorResponse - s.Require().NoError(val.ClientCtx.LegacyAmino.UnmarshalJSON(txJSON, &errResp)) - - s.Require().Contains(errResp.Error, errMsg) - }) - } - - // try fetching the txn using gRPC req, it will fetch info since it has proto codec. - grpcJSON, err := rest.GetRequest(fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/%s", val.APIAddress, txRes.TxHash)) - s.Require().NoError(err) - - var getTxRes txtypes.GetTxResponse - s.Require().NoError(val.ClientCtx.JSONCodec.UnmarshalJSON(grpcJSON, &getTxRes)) - s.Require().Equal(getTxRes.Tx.Body.Memo, "foobar") - - // generate broadcast only txn. - args = append(args, fmt.Sprintf("--%s=true", flags.FlagGenerateOnly)) - out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, args) - s.Require().NoError(err) - - txFile := testutil.WriteToNewTempFile(s.T(), string(out.Bytes())) - txFileName := txFile.Name() - - // encode the generated txn. - out, err = clitestutil.ExecTestCLICmd(val.ClientCtx, authcli.GetEncodeCommand(), []string{txFileName}) - s.Require().NoError(err) - - bz, err := val.ClientCtx.LegacyAmino.MarshalJSON(authrest.DecodeReq{Tx: string(out.Bytes())}) - s.Require().NoError(err) - - // try to decode the txn using legacy rest, it fails. - res, err := rest.PostRequest(fmt.Sprintf("%s/txs/decode", val.APIAddress), "application/json", bz) - s.Require().NoError(err) - - var errResp rest.ErrorResponse - s.Require().NoError(val.ClientCtx.LegacyAmino.UnmarshalJSON(res, &errResp)) - s.Require().Contains(errResp.Error, errMsg) -} diff --git a/testing/solomachine.go b/testing/solomachine.go index 5eea01fbb19..f0cd445d1db 100644 --- a/testing/solomachine.go +++ b/testing/solomachine.go @@ -107,7 +107,8 @@ func (solo *Solomachine) GetHeight() exported.Height { // CreateHeader generates a new private/public key pair and creates the // necessary signature to construct a valid solo machine header. -func (solo *Solomachine) CreateHeader() *solomachinetypes.Header { +// A new diversifier will be used as well +func (solo *Solomachine) CreateHeader(newDiversifier string) *solomachinetypes.Header { // generate new private keys and signature for header newPrivKeys, newPubKeys, newPubKey := GenerateKeys(solo.t, uint64(len(solo.PrivateKeys))) @@ -116,7 +117,7 @@ func (solo *Solomachine) CreateHeader() *solomachinetypes.Header { data := &solomachinetypes.HeaderData{ NewPubKey: publicKey, - NewDiversifier: solo.Diversifier, + NewDiversifier: newDiversifier, } dataBz, err := solo.cdc.Marshal(data) @@ -126,7 +127,7 @@ func (solo *Solomachine) CreateHeader() *solomachinetypes.Header { Sequence: solo.Sequence, Timestamp: solo.Time, Diversifier: solo.Diversifier, - DataType: solomachinetypes.HEADER, + Path: []byte{}, Data: dataBz, } @@ -140,7 +141,7 @@ func (solo *Solomachine) CreateHeader() *solomachinetypes.Header { Timestamp: solo.Time, Signature: sig, NewPublicKey: publicKey, - NewDiversifier: solo.Diversifier, + NewDiversifier: newDiversifier, } // assumes successful header update @@ -148,6 +149,7 @@ func (solo *Solomachine) CreateHeader() *solomachinetypes.Header { solo.PrivateKeys = newPrivKeys solo.PublicKeys = newPubKeys solo.PublicKey = newPubKey + solo.Diversifier = newDiversifier return header } @@ -155,20 +157,19 @@ func (solo *Solomachine) CreateHeader() *solomachinetypes.Header { // CreateMisbehaviour constructs testing misbehaviour for the solo machine client // by signing over two different data bytes at the same sequence. func (solo *Solomachine) CreateMisbehaviour() *solomachinetypes.Misbehaviour { - path := solo.GetClientStatePath("counterparty") - dataOne, err := solomachinetypes.ClientStateDataBytes(solo.cdc, path, solo.ClientState()) + merklePath := solo.GetClientStatePath("counterparty") + path, err := solo.cdc.Marshal(&merklePath) require.NoError(solo.t, err) - path = solo.GetConsensusStatePath("counterparty", clienttypes.NewHeight(0, 1)) - dataTwo, err := solomachinetypes.ConsensusStateDataBytes(solo.cdc, path, solo.ConsensusState()) + data, err := solo.cdc.Marshal(solo.ClientState()) require.NoError(solo.t, err) signBytes := &solomachinetypes.SignBytes{ Sequence: solo.Sequence, Timestamp: solo.Time, Diversifier: solo.Diversifier, - DataType: solomachinetypes.CLIENT, - Data: dataOne, + Path: path, + Data: data, } bz, err := solo.cdc.Marshal(signBytes) @@ -177,20 +178,27 @@ func (solo *Solomachine) CreateMisbehaviour() *solomachinetypes.Misbehaviour { sig := solo.GenerateSignature(bz) signatureOne := solomachinetypes.SignatureAndData{ Signature: sig, - DataType: solomachinetypes.CLIENT, - Data: dataOne, + Path: path, + Data: data, Timestamp: solo.Time, } // misbehaviour signaturess can have different timestamps solo.Time++ + merklePath = solo.GetConsensusStatePath("counterparty", clienttypes.NewHeight(0, 1)) + path, err = solo.cdc.Marshal(&merklePath) + require.NoError(solo.t, err) + + data, err = solo.cdc.Marshal(solo.ConsensusState()) + require.NoError(solo.t, err) + signBytes = &solomachinetypes.SignBytes{ Sequence: solo.Sequence, Timestamp: solo.Time, Diversifier: solo.Diversifier, - DataType: solomachinetypes.CONSENSUS, - Data: dataTwo, + Path: path, + Data: data, } bz, err = solo.cdc.Marshal(signBytes) @@ -199,8 +207,8 @@ func (solo *Solomachine) CreateMisbehaviour() *solomachinetypes.Misbehaviour { sig = solo.GenerateSignature(bz) signatureTwo := solomachinetypes.SignatureAndData{ Signature: sig, - DataType: solomachinetypes.CONSENSUS, - Data: dataTwo, + Path: path, + Data: data, Timestamp: solo.Time, } From 7237c434a37f8a02ac805f17c23926d54b1874de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Colin=20Axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Tue, 2 Aug 2022 17:36:43 +0200 Subject: [PATCH 54/71] bump version from 3 to 5 --- .../adr-002-go-module-versioning.md | 6 +-- docs/migrations/v2-to-v3.md | 2 +- docs/migrations/v3-to-v4.md | 2 +- go.mod | 2 +- .../27-interchain-accounts/client/cli/cli.go | 4 +- .../controller/client/cli/query.go | 2 +- .../controller/ibc_module.go | 12 ++--- .../controller/ibc_module_test.go | 14 +++--- .../controller/keeper/account.go | 6 +-- .../controller/keeper/account_test.go | 8 ++-- .../controller/keeper/genesis.go | 4 +- .../controller/keeper/genesis_test.go | 8 ++-- .../controller/keeper/grpc_query.go | 2 +- .../controller/keeper/grpc_query_test.go | 2 +- .../controller/keeper/handshake.go | 4 +- .../controller/keeper/handshake_test.go | 8 ++-- .../controller/keeper/keeper.go | 8 ++-- .../controller/keeper/keeper_test.go | 6 +-- .../controller/keeper/params.go | 2 +- .../controller/keeper/params_test.go | 2 +- .../controller/keeper/relay.go | 6 +-- .../controller/keeper/relay_test.go | 10 ++-- .../controller/types/params_test.go | 2 +- .../host/client/cli/query.go | 8 ++-- .../27-interchain-accounts/host/ibc_module.go | 10 ++-- .../host/ibc_module_test.go | 14 +++--- .../host/keeper/account.go | 2 +- .../host/keeper/account_test.go | 4 +- .../host/keeper/events.go | 4 +- .../host/keeper/genesis.go | 4 +- .../host/keeper/genesis_test.go | 8 ++-- .../host/keeper/grpc_query.go | 2 +- .../host/keeper/grpc_query_test.go | 2 +- .../host/keeper/handshake.go | 6 +-- .../host/keeper/handshake_test.go | 8 ++-- .../host/keeper/keeper.go | 8 ++-- .../host/keeper/keeper_test.go | 6 +-- .../host/keeper/params.go | 2 +- .../host/keeper/params_test.go | 2 +- .../host/keeper/relay.go | 6 +-- .../host/keeper/relay_test.go | 12 ++--- .../27-interchain-accounts/host/types/ack.go | 2 +- .../host/types/ack_test.go | 4 +- .../host/types/params_test.go | 2 +- modules/apps/27-interchain-accounts/module.go | 20 ++++---- .../27-interchain-accounts/module_test.go | 12 ++--- .../types/account_test.go | 4 +- .../types/codec_test.go | 4 +- .../types/expected_keepers.go | 4 +- .../27-interchain-accounts/types/genesis.go | 6 +-- .../types/genesis.pb.go | 4 +- .../types/genesis_test.go | 8 ++-- .../27-interchain-accounts/types/keys_test.go | 2 +- .../27-interchain-accounts/types/metadata.go | 2 +- .../types/metadata_test.go | 4 +- .../types/packet_test.go | 2 +- .../27-interchain-accounts/types/port_test.go | 4 +- modules/apps/29-fee/client/cli/query.go | 4 +- modules/apps/29-fee/client/cli/tx.go | 4 +- modules/apps/29-fee/fee_test.go | 10 ++-- modules/apps/29-fee/ibc_module.go | 10 ++-- modules/apps/29-fee/ibc_module_test.go | 16 +++---- modules/apps/29-fee/keeper/escrow.go | 4 +- modules/apps/29-fee/keeper/escrow_test.go | 6 +-- modules/apps/29-fee/keeper/events.go | 4 +- modules/apps/29-fee/keeper/genesis.go | 2 +- modules/apps/29-fee/keeper/genesis_test.go | 6 +-- modules/apps/29-fee/keeper/grpc_query.go | 2 +- modules/apps/29-fee/keeper/grpc_query_test.go | 6 +-- modules/apps/29-fee/keeper/keeper.go | 6 +-- modules/apps/29-fee/keeper/keeper_test.go | 8 ++-- modules/apps/29-fee/keeper/msg_server.go | 4 +- modules/apps/29-fee/keeper/msg_server_test.go | 6 +-- modules/apps/29-fee/keeper/relay.go | 6 +-- modules/apps/29-fee/keeper/relay_test.go | 10 ++-- modules/apps/29-fee/module.go | 8 ++-- modules/apps/29-fee/transfer_test.go | 8 ++-- modules/apps/29-fee/types/expected_keepers.go | 4 +- modules/apps/29-fee/types/fee.go | 2 +- modules/apps/29-fee/types/fee.pb.go | 2 +- modules/apps/29-fee/types/fee_test.go | 2 +- modules/apps/29-fee/types/genesis.go | 2 +- modules/apps/29-fee/types/genesis.pb.go | 2 +- modules/apps/29-fee/types/genesis_test.go | 8 ++-- modules/apps/29-fee/types/keys.go | 2 +- modules/apps/29-fee/types/keys_test.go | 6 +-- modules/apps/29-fee/types/msgs.go | 4 +- modules/apps/29-fee/types/msgs_test.go | 6 +-- modules/apps/29-fee/types/query.pb.go | 2 +- modules/apps/29-fee/types/tx.pb.go | 2 +- modules/apps/transfer/client/cli/query.go | 2 +- modules/apps/transfer/client/cli/tx.go | 6 +-- modules/apps/transfer/ibc_module.go | 12 ++--- modules/apps/transfer/ibc_module_test.go | 8 ++-- modules/apps/transfer/keeper/encoding.go | 2 +- modules/apps/transfer/keeper/genesis.go | 2 +- modules/apps/transfer/keeper/genesis_test.go | 2 +- modules/apps/transfer/keeper/grpc_query.go | 2 +- .../apps/transfer/keeper/grpc_query_test.go | 2 +- modules/apps/transfer/keeper/keeper.go | 4 +- modules/apps/transfer/keeper/keeper_test.go | 4 +- .../apps/transfer/keeper/mbt_relay_test.go | 8 ++-- modules/apps/transfer/keeper/msg_server.go | 2 +- modules/apps/transfer/keeper/params.go | 2 +- modules/apps/transfer/keeper/params_test.go | 2 +- modules/apps/transfer/keeper/relay.go | 10 ++-- modules/apps/transfer/keeper/relay_test.go | 12 ++--- modules/apps/transfer/module.go | 10 ++-- modules/apps/transfer/simulation/decoder.go | 2 +- .../apps/transfer/simulation/decoder_test.go | 6 +-- modules/apps/transfer/simulation/genesis.go | 2 +- .../apps/transfer/simulation/genesis_test.go | 4 +- modules/apps/transfer/simulation/params.go | 2 +- .../apps/transfer/simulation/params_test.go | 2 +- modules/apps/transfer/transfer_test.go | 6 +-- modules/apps/transfer/types/ack.go | 2 +- modules/apps/transfer/types/ack_test.go | 4 +- .../apps/transfer/types/expected_keepers.go | 6 +-- modules/apps/transfer/types/genesis.go | 2 +- modules/apps/transfer/types/genesis_test.go | 2 +- modules/apps/transfer/types/keys_test.go | 2 +- modules/apps/transfer/types/msgs.go | 4 +- modules/apps/transfer/types/msgs_test.go | 2 +- modules/apps/transfer/types/trace.go | 2 +- modules/apps/transfer/types/tx.pb.go | 2 +- modules/core/02-client/abci.go | 4 +- modules/core/02-client/abci_test.go | 8 ++-- modules/core/02-client/client/cli/cli.go | 2 +- modules/core/02-client/client/cli/query.go | 6 +-- modules/core/02-client/client/cli/tx.go | 4 +- .../core/02-client/client/proposal_handler.go | 2 +- modules/core/02-client/client/utils/utils.go | 12 ++--- modules/core/02-client/genesis.go | 6 +-- modules/core/02-client/keeper/client.go | 4 +- modules/core/02-client/keeper/client_test.go | 14 +++--- modules/core/02-client/keeper/encoding.go | 4 +- modules/core/02-client/keeper/events.go | 4 +- modules/core/02-client/keeper/grpc_query.go | 6 +-- .../core/02-client/keeper/grpc_query_test.go | 10 ++-- modules/core/02-client/keeper/keeper.go | 10 ++-- modules/core/02-client/keeper/keeper_test.go | 18 ++++---- modules/core/02-client/keeper/migrations.go | 2 +- modules/core/02-client/keeper/params.go | 2 +- modules/core/02-client/keeper/params_test.go | 2 +- modules/core/02-client/keeper/proposal.go | 4 +- .../core/02-client/keeper/proposal_test.go | 8 ++-- modules/core/02-client/legacy/v100/genesis.go | 8 ++-- .../02-client/legacy/v100/genesis_test.go | 16 +++---- .../core/02-client/legacy/v100/solomachine.go | 2 +- .../02-client/legacy/v100/solomachine.pb.go | 4 +- modules/core/02-client/legacy/v100/store.go | 12 ++--- .../core/02-client/legacy/v100/store_test.go | 12 ++--- .../02-client/migrations/v6/solomachine.pb.go | 4 +- modules/core/02-client/module.go | 4 +- modules/core/02-client/proposal_handler.go | 4 +- .../core/02-client/proposal_handler_test.go | 8 ++-- modules/core/02-client/simulation/decoder.go | 6 +-- .../core/02-client/simulation/decoder_test.go | 10 ++-- modules/core/02-client/simulation/genesis.go | 2 +- modules/core/02-client/types/client.go | 4 +- modules/core/02-client/types/client_test.go | 4 +- modules/core/02-client/types/codec.go | 2 +- modules/core/02-client/types/codec_test.go | 10 ++-- modules/core/02-client/types/encoding.go | 2 +- modules/core/02-client/types/encoding_test.go | 4 +- modules/core/02-client/types/events.go | 2 +- modules/core/02-client/types/genesis.go | 4 +- modules/core/02-client/types/genesis_test.go | 16 +++---- modules/core/02-client/types/height.go | 2 +- modules/core/02-client/types/height_test.go | 2 +- modules/core/02-client/types/keys.go | 2 +- modules/core/02-client/types/keys_test.go | 2 +- modules/core/02-client/types/msgs.go | 4 +- modules/core/02-client/types/msgs_test.go | 10 ++-- modules/core/02-client/types/params.go | 2 +- modules/core/02-client/types/params_test.go | 2 +- modules/core/02-client/types/proposal.go | 2 +- modules/core/02-client/types/proposal_test.go | 6 +-- modules/core/02-client/types/query.go | 2 +- modules/core/03-connection/client/cli/cli.go | 2 +- .../core/03-connection/client/cli/query.go | 6 +-- .../core/03-connection/client/utils/utils.go | 14 +++--- modules/core/03-connection/genesis.go | 4 +- modules/core/03-connection/keeper/events.go | 2 +- .../core/03-connection/keeper/grpc_query.go | 6 +-- .../03-connection/keeper/grpc_query_test.go | 8 ++-- .../core/03-connection/keeper/handshake.go | 8 ++-- .../03-connection/keeper/handshake_test.go | 12 ++--- modules/core/03-connection/keeper/keeper.go | 10 ++-- .../core/03-connection/keeper/keeper_test.go | 6 +-- modules/core/03-connection/keeper/params.go | 2 +- .../core/03-connection/keeper/params_test.go | 2 +- modules/core/03-connection/keeper/verify.go | 12 ++--- .../core/03-connection/keeper/verify_test.go | 16 +++---- modules/core/03-connection/module.go | 4 +- .../core/03-connection/simulation/decoder.go | 4 +- .../03-connection/simulation/decoder_test.go | 8 ++-- .../core/03-connection/simulation/genesis.go | 2 +- modules/core/03-connection/types/codec.go | 2 +- .../core/03-connection/types/connection.go | 6 +-- .../core/03-connection/types/connection.pb.go | 2 +- .../03-connection/types/connection_test.go | 8 ++-- modules/core/03-connection/types/events.go | 2 +- .../03-connection/types/expected_keepers.go | 2 +- modules/core/03-connection/types/genesis.go | 2 +- .../core/03-connection/types/genesis_test.go | 6 +-- modules/core/03-connection/types/keys.go | 2 +- modules/core/03-connection/types/keys_test.go | 2 +- modules/core/03-connection/types/msgs.go | 8 ++-- modules/core/03-connection/types/msgs_test.go | 12 ++--- .../core/03-connection/types/params_test.go | 2 +- modules/core/03-connection/types/query.go | 4 +- modules/core/03-connection/types/query.pb.go | 2 +- modules/core/03-connection/types/tx.pb.go | 2 +- modules/core/03-connection/types/version.go | 2 +- .../core/03-connection/types/version_test.go | 6 +-- modules/core/04-channel/client/cli/cli.go | 2 +- modules/core/04-channel/client/cli/query.go | 6 +-- modules/core/04-channel/client/utils/utils.go | 12 ++--- modules/core/04-channel/genesis.go | 4 +- modules/core/04-channel/keeper/events.go | 4 +- modules/core/04-channel/keeper/grpc_query.go | 8 ++-- .../core/04-channel/keeper/grpc_query_test.go | 10 ++-- modules/core/04-channel/keeper/handshake.go | 10 ++-- .../core/04-channel/keeper/handshake_test.go | 12 ++--- modules/core/04-channel/keeper/keeper.go | 12 ++--- modules/core/04-channel/keeper/keeper_test.go | 6 +-- modules/core/04-channel/keeper/packet.go | 10 ++-- modules/core/04-channel/keeper/packet_test.go | 16 +++---- modules/core/04-channel/keeper/timeout.go | 8 ++-- .../core/04-channel/keeper/timeout_test.go | 12 ++--- modules/core/04-channel/module.go | 4 +- modules/core/04-channel/simulation/decoder.go | 4 +- .../04-channel/simulation/decoder_test.go | 8 ++-- modules/core/04-channel/simulation/genesis.go | 2 +- .../04-channel/types/acknowledgement_test.go | 2 +- modules/core/04-channel/types/channel.go | 4 +- modules/core/04-channel/types/channel.pb.go | 2 +- modules/core/04-channel/types/channel_test.go | 2 +- modules/core/04-channel/types/codec.go | 2 +- modules/core/04-channel/types/events.go | 2 +- .../core/04-channel/types/expected_keepers.go | 4 +- modules/core/04-channel/types/genesis.go | 2 +- modules/core/04-channel/types/genesis_test.go | 2 +- modules/core/04-channel/types/keys.go | 2 +- modules/core/04-channel/types/keys_test.go | 2 +- modules/core/04-channel/types/msgs.go | 6 +-- modules/core/04-channel/types/msgs_test.go | 10 ++-- modules/core/04-channel/types/packet.go | 6 +-- modules/core/04-channel/types/packet_test.go | 4 +- modules/core/04-channel/types/query.go | 4 +- modules/core/04-channel/types/query.pb.go | 2 +- modules/core/04-channel/types/tx.pb.go | 2 +- modules/core/04-channel/types/version_test.go | 2 +- modules/core/05-port/keeper/keeper.go | 4 +- modules/core/05-port/keeper/keeper_test.go | 4 +- modules/core/05-port/module.go | 4 +- modules/core/05-port/types/module.go | 4 +- modules/core/05-port/types/query.pb.go | 2 +- modules/core/23-commitment/types/codec.go | 2 +- modules/core/23-commitment/types/merkle.go | 2 +- .../core/23-commitment/types/merkle_test.go | 2 +- .../core/23-commitment/types/utils_test.go | 2 +- modules/core/24-host/keys.go | 2 +- modules/core/24-host/parse_test.go | 4 +- modules/core/ante/ante.go | 6 +-- modules/core/ante/ante_test.go | 12 ++--- modules/core/client/cli/cli.go | 8 ++-- modules/core/client/query.go | 6 +-- modules/core/genesis.go | 10 ++-- modules/core/genesis_test.go | 20 ++++---- modules/core/keeper/grpc_query.go | 6 +-- modules/core/keeper/keeper.go | 16 +++---- modules/core/keeper/migrations.go | 2 +- modules/core/keeper/msg_server.go | 10 ++-- modules/core/keeper/msg_server_test.go | 18 ++++---- modules/core/legacy/v100/genesis.go | 10 ++-- modules/core/legacy/v100/genesis_test.go | 18 ++++---- modules/core/migrations/v5/migrations.go | 8 ++-- modules/core/migrations/v5/migrations_test.go | 10 ++-- modules/core/module.go | 20 ++++---- modules/core/simulation/decoder.go | 10 ++-- modules/core/simulation/decoder_test.go | 14 +++--- modules/core/simulation/genesis.go | 16 +++---- modules/core/simulation/genesis_test.go | 6 +-- modules/core/types/codec.go | 12 ++--- modules/core/types/genesis.go | 6 +-- modules/core/types/genesis.pb.go | 6 +-- modules/core/types/query.go | 12 ++--- .../06-solomachine/client_state.go | 8 ++-- .../06-solomachine/client_state_test.go | 14 +++--- modules/light-clients/06-solomachine/codec.go | 2 +- .../06-solomachine/consensus_state.go | 4 +- .../06-solomachine/consensus_state_test.go | 6 +-- .../light-clients/06-solomachine/header.go | 4 +- .../06-solomachine/header_test.go | 6 +-- .../06-solomachine/misbehaviour.go | 6 +-- .../06-solomachine/misbehaviour_handle.go | 2 +- .../06-solomachine/misbehaviour_test.go | 6 +-- .../06-solomachine/proof_test.go | 2 +- .../06-solomachine/proposal_handle.go | 4 +- .../06-solomachine/proposal_handle_test.go | 12 ++--- .../06-solomachine/solomachine_test.go | 8 ++-- .../light-clients/06-solomachine/update.go | 6 +-- .../06-solomachine/update_test.go | 12 ++--- .../07-tendermint/client_state.go | 6 +-- .../07-tendermint/client_state_test.go | 18 ++++---- modules/light-clients/07-tendermint/codec.go | 2 +- .../07-tendermint/consensus_state.go | 6 +-- .../07-tendermint/consensus_state_test.go | 6 +-- .../light-clients/07-tendermint/genesis.go | 4 +- .../07-tendermint/genesis_test.go | 6 +-- modules/light-clients/07-tendermint/header.go | 6 +-- .../07-tendermint/header_test.go | 6 +-- .../07-tendermint/misbehaviour.go | 6 +-- .../07-tendermint/misbehaviour_handle.go | 2 +- .../07-tendermint/misbehaviour_handle_test.go | 12 ++--- .../07-tendermint/misbehaviour_test.go | 10 ++-- .../07-tendermint/proposal_handle.go | 4 +- .../07-tendermint/proposal_handle_test.go | 10 ++-- modules/light-clients/07-tendermint/store.go | 6 +-- .../light-clients/07-tendermint/store_test.go | 14 +++--- .../07-tendermint/tendermint.pb.go | 4 +- .../07-tendermint/tendermint_test.go | 10 ++-- modules/light-clients/07-tendermint/update.go | 8 ++-- .../07-tendermint/update_test.go | 14 +++--- .../light-clients/07-tendermint/upgrade.go | 6 +-- .../07-tendermint/upgrade_test.go | 10 ++-- proto/ibc/applications/fee/v1/ack.proto | 2 +- proto/ibc/applications/fee/v1/fee.proto | 2 +- proto/ibc/applications/fee/v1/genesis.proto | 2 +- proto/ibc/applications/fee/v1/metadata.proto | 2 +- proto/ibc/applications/fee/v1/query.proto | 2 +- proto/ibc/applications/fee/v1/tx.proto | 2 +- .../controller/v1/controller.proto | 2 +- .../controller/v1/query.proto | 2 +- .../interchain_accounts/host/v1/host.proto | 2 +- .../interchain_accounts/host/v1/query.proto | 2 +- .../interchain_accounts/v1/account.proto | 2 +- .../interchain_accounts/v1/genesis.proto | 2 +- .../interchain_accounts/v1/metadata.proto | 2 +- .../interchain_accounts/v1/packet.proto | 2 +- .../applications/transfer/v1/genesis.proto | 2 +- .../ibc/applications/transfer/v1/query.proto | 2 +- .../applications/transfer/v1/transfer.proto | 2 +- proto/ibc/applications/transfer/v1/tx.proto | 2 +- .../ibc/applications/transfer/v2/packet.proto | 2 +- proto/ibc/core/channel/v1/channel.proto | 2 +- proto/ibc/core/channel/v1/genesis.proto | 2 +- proto/ibc/core/channel/v1/query.proto | 2 +- proto/ibc/core/channel/v1/tx.proto | 2 +- proto/ibc/core/client/v1/client.proto | 2 +- proto/ibc/core/client/v1/genesis.proto | 2 +- proto/ibc/core/client/v1/query.proto | 2 +- proto/ibc/core/client/v1/tx.proto | 2 +- proto/ibc/core/commitment/v1/commitment.proto | 2 +- proto/ibc/core/connection/v1/connection.proto | 2 +- proto/ibc/core/connection/v1/genesis.proto | 2 +- proto/ibc/core/connection/v1/query.proto | 2 +- proto/ibc/core/connection/v1/tx.proto | 2 +- proto/ibc/core/types/v1/genesis.proto | 2 +- .../solomachine/v1/solomachine.proto | 2 +- .../solomachine/v2/solomachine.proto | 2 +- .../solomachine/v3/solomachine.proto | 2 +- .../tendermint/v1/tendermint.proto | 2 +- testing/README.md | 4 +- testing/app.go | 4 +- testing/chain.go | 16 +++---- testing/chain_test.go | 2 +- testing/config.go | 10 ++-- testing/endpoint.go | 14 +++--- testing/events.go | 6 +-- testing/mock/ibc_app.go | 4 +- testing/mock/ibc_module.go | 6 +-- testing/mock/ibc_module_test.go | 6 +-- testing/mock/mock.go | 6 +-- testing/mock/privval_test.go | 2 +- testing/path.go | 2 +- testing/simapp/ante_handler.go | 4 +- testing/simapp/app.go | 46 +++++++++---------- testing/simapp/encoding.go | 2 +- testing/simapp/genesis_account_test.go | 2 +- testing/simapp/sim_bench_test.go | 2 +- testing/simapp/sim_test.go | 6 +-- testing/simapp/simd/cmd/cmd_test.go | 4 +- testing/simapp/simd/cmd/genaccounts_test.go | 4 +- testing/simapp/simd/cmd/root.go | 4 +- testing/simapp/simd/main.go | 4 +- testing/simapp/state.go | 2 +- testing/simapp/test_helpers.go | 2 +- testing/simapp/utils.go | 2 +- testing/solomachine.go | 10 ++-- testing/values.go | 12 ++--- 393 files changed, 1093 insertions(+), 1093 deletions(-) diff --git a/docs/architecture/adr-002-go-module-versioning.md b/docs/architecture/adr-002-go-module-versioning.md index 28207d5474d..c5f01a9112e 100644 --- a/docs/architecture/adr-002-go-module-versioning.md +++ b/docs/architecture/adr-002-go-module-versioning.md @@ -23,7 +23,7 @@ v1.0.0 was decided to be used instead of v0.1.0 primarily for the following reas When a Go module is released under v1.0.0, all following releases must follow Go semantic versioning. Thus when the go API is broken, the Go module major version **must** be incremented. -For example, changing the go package version from `v2` to `v3` bumps the import from `github.com/cosmos/ibc-go/v2` to `github.com/cosmos/ibc-go/v3`. +For example, changing the go package version from `v2` to `v3` bumps the import from `github.com/cosmos/ibc-go/v2` to `github.com/cosmos/ibc-go/v5`. If the Go module version is not incremented then attempting to go get a module @v3.0.0 without the suffix results in: `invalid version: module contains a go.mod file, so major version must be compatible: should be v0 or v1, not v3` @@ -33,7 +33,7 @@ Not including a go.mod in our release is not a viable option. #### Attempting to import multiple go module versions for ibc-go -Attempting to import two versions of ibc-go, such as `github.com/cosmos/ibc-go/v2` and `github.com/cosmos/ibc-go/v3`, will result in multiple issues. +Attempting to import two versions of ibc-go, such as `github.com/cosmos/ibc-go/v2` and `github.com/cosmos/ibc-go/v5`, will result in multiple issues. The Cosmos SDK does global registration of error and governance proposal types. The errors and proposals used in ibc-go would need to now register their naming based on the go module version. @@ -76,7 +76,7 @@ For example, lets say this solution is implmented in v3. Then `github.com/cosmos/ibc-go/v2` cannot be imported with any other ibc-go version -`github.com/cosmos/ibc-go/v3` cannot be imported with any previous ibc-go versions +`github.com/cosmos/ibc-go/v5` cannot be imported with any previous ibc-go versions `github.com/cosmos/ibc-go/v4` may be imported with ibc-go versions v3+ diff --git a/docs/migrations/v2-to-v3.md b/docs/migrations/v2-to-v3.md index a37f74cf420..e44254c4c28 100644 --- a/docs/migrations/v2-to-v3.md +++ b/docs/migrations/v2-to-v3.md @@ -11,7 +11,7 @@ There are four sections based on the four potential user groups of this document **Note:** ibc-go supports golang semantic versioning and therefore all imports must be updated to bump the version number on major releases. ```go -github.com/cosmos/ibc-go/v2 -> github.com/cosmos/ibc-go/v3 +github.com/cosmos/ibc-go/v2 -> github.com/cosmos/ibc-go/v5 ``` No genesis or in-place migrations are required when upgrading from v1 or v2 of ibc-go. diff --git a/docs/migrations/v3-to-v4.md b/docs/migrations/v3-to-v4.md index df7dedabbae..8d6e6b18d3c 100644 --- a/docs/migrations/v3-to-v4.md +++ b/docs/migrations/v3-to-v4.md @@ -11,7 +11,7 @@ There are four sections based on the four potential user groups of this document **Note:** ibc-go supports golang semantic versioning and therefore all imports must be updated to bump the version number on major releases. ```go -github.com/cosmos/ibc-go/v3 -> github.com/cosmos/ibc-go/v4 +github.com/cosmos/ibc-go/v5 -> github.com/cosmos/ibc-go/v4 ``` No genesis or in-place migrations required when upgrading from v1 or v2 of ibc-go. diff --git a/go.mod b/go.mod index 225f608d60f..28be42dc253 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ go 1.17 -module github.com/cosmos/ibc-go/v3 +module github.com/cosmos/ibc-go/v5 replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 diff --git a/modules/apps/27-interchain-accounts/client/cli/cli.go b/modules/apps/27-interchain-accounts/client/cli/cli.go index 95b49f29e2a..ef15f82c301 100644 --- a/modules/apps/27-interchain-accounts/client/cli/cli.go +++ b/modules/apps/27-interchain-accounts/client/cli/cli.go @@ -3,8 +3,8 @@ package cli import ( "github.com/spf13/cobra" - controllercli "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller/client/cli" - hostcli "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/client/cli" + controllercli "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/client/cli" + hostcli "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/host/client/cli" ) // GetQueryCmd returns the query commands for the interchain-accounts submodule diff --git a/modules/apps/27-interchain-accounts/controller/client/cli/query.go b/modules/apps/27-interchain-accounts/controller/client/cli/query.go index 9a939906b90..4b53a21cae4 100644 --- a/modules/apps/27-interchain-accounts/controller/client/cli/query.go +++ b/modules/apps/27-interchain-accounts/controller/client/cli/query.go @@ -8,7 +8,7 @@ import ( "github.com/cosmos/cosmos-sdk/version" "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller/types" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/types" ) // GetCmdParams returns the command handler for the controller submodule parameter querying. diff --git a/modules/apps/27-interchain-accounts/controller/ibc_module.go b/modules/apps/27-interchain-accounts/controller/ibc_module.go index 325ec70959b..02a6f1d184d 100644 --- a/modules/apps/27-interchain-accounts/controller/ibc_module.go +++ b/modules/apps/27-interchain-accounts/controller/ibc_module.go @@ -5,12 +5,12 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller/keeper" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller/types" - icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - porttypes "github.com/cosmos/ibc-go/v3/modules/core/05-port/types" - ibcexported "github.com/cosmos/ibc-go/v3/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/keeper" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/types" + icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + porttypes "github.com/cosmos/ibc-go/v5/modules/core/05-port/types" + ibcexported "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // IBCModule implements the ICS26 interface for interchain accounts controller chains diff --git a/modules/apps/27-interchain-accounts/controller/ibc_module_test.go b/modules/apps/27-interchain-accounts/controller/ibc_module_test.go index 608996515c1..087cc55702d 100644 --- a/modules/apps/27-interchain-accounts/controller/ibc_module_test.go +++ b/modules/apps/27-interchain-accounts/controller/ibc_module_test.go @@ -9,13 +9,13 @@ import ( "github.com/stretchr/testify/suite" "github.com/tendermint/tendermint/crypto" - icacontroller "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller/types" - icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + icacontroller "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/types" + icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) var ( diff --git a/modules/apps/27-interchain-accounts/controller/keeper/account.go b/modules/apps/27-interchain-accounts/controller/keeper/account.go index 03eeef69f1f..828cbc8d3de 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/account.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/account.go @@ -4,9 +4,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) // RegisterInterchainAccount is the entry point to registering an interchain account. diff --git a/modules/apps/27-interchain-accounts/controller/keeper/account_test.go b/modules/apps/27-interchain-accounts/controller/keeper/account_test.go index 9387baa3936..0dcdba201e3 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/account_test.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/account_test.go @@ -1,10 +1,10 @@ package keeper_test import ( - icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func (suite *KeeperTestSuite) TestRegisterInterchainAccount() { diff --git a/modules/apps/27-interchain-accounts/controller/keeper/genesis.go b/modules/apps/27-interchain-accounts/controller/keeper/genesis.go index eab7687236e..25acfc74a8a 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/genesis.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/genesis.go @@ -5,8 +5,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) // InitGenesis initializes the interchain accounts controller application state from a provided genesis state diff --git a/modules/apps/27-interchain-accounts/controller/keeper/genesis_test.go b/modules/apps/27-interchain-accounts/controller/keeper/genesis_test.go index 7a41608d38f..38349d1dd11 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/genesis_test.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/genesis_test.go @@ -1,10 +1,10 @@ package keeper_test import ( - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller/keeper" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller/types" - icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/keeper" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/types" + icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func (suite *KeeperTestSuite) TestInitGenesis() { diff --git a/modules/apps/27-interchain-accounts/controller/keeper/grpc_query.go b/modules/apps/27-interchain-accounts/controller/keeper/grpc_query.go index dd1d04a96b3..c44e30018e4 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/grpc_query.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/grpc_query.go @@ -5,7 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller/types" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/types" ) var _ types.QueryServer = Keeper{} diff --git a/modules/apps/27-interchain-accounts/controller/keeper/grpc_query_test.go b/modules/apps/27-interchain-accounts/controller/keeper/grpc_query_test.go index e76cdac1fb4..f60d348bc34 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/grpc_query_test.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/grpc_query_test.go @@ -3,7 +3,7 @@ package keeper_test import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller/types" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/types" ) func (suite *KeeperTestSuite) TestQueryParams() { diff --git a/modules/apps/27-interchain-accounts/controller/keeper/handshake.go b/modules/apps/27-interchain-accounts/controller/keeper/handshake.go index 26a90878abf..2d9c93fde6e 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/handshake.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/handshake.go @@ -8,8 +8,8 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" ) // OnChanOpenInit performs basic validation of channel initialization. diff --git a/modules/apps/27-interchain-accounts/controller/keeper/handshake_test.go b/modules/apps/27-interchain-accounts/controller/keeper/handshake_test.go index 61c8a092a81..5bf49f535e8 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/handshake_test.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/handshake_test.go @@ -3,10 +3,10 @@ package keeper_test import ( capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func (suite *KeeperTestSuite) TestOnChanOpenInit() { diff --git a/modules/apps/27-interchain-accounts/controller/keeper/keeper.go b/modules/apps/27-interchain-accounts/controller/keeper/keeper.go index 86099b954bd..a681ea9c8cd 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/keeper.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/keeper.go @@ -12,10 +12,10 @@ import ( paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" "github.com/tendermint/tendermint/libs/log" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller/types" - icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/types" + icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) // Keeper defines the IBC interchain accounts controller keeper diff --git a/modules/apps/27-interchain-accounts/controller/keeper/keeper_test.go b/modules/apps/27-interchain-accounts/controller/keeper/keeper_test.go index a0772f1e648..15c6b8428f5 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/keeper_test.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/keeper_test.go @@ -7,9 +7,9 @@ import ( "github.com/stretchr/testify/suite" "github.com/tendermint/tendermint/crypto" - icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) var ( diff --git a/modules/apps/27-interchain-accounts/controller/keeper/params.go b/modules/apps/27-interchain-accounts/controller/keeper/params.go index dce72c0c491..c44687543cb 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/params.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/params.go @@ -3,7 +3,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller/types" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/types" ) // IsControllerEnabled retrieves the controller enabled boolean from the paramstore. diff --git a/modules/apps/27-interchain-accounts/controller/keeper/params_test.go b/modules/apps/27-interchain-accounts/controller/keeper/params_test.go index ffd71f49268..6b74c4ef667 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/params_test.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/params_test.go @@ -1,6 +1,6 @@ package keeper_test -import "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller/types" +import "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/types" func (suite *KeeperTestSuite) TestParams() { expParams := types.DefaultParams() diff --git a/modules/apps/27-interchain-accounts/controller/keeper/relay.go b/modules/apps/27-interchain-accounts/controller/keeper/relay.go index 4eaf26c52a6..57d92ebef99 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/relay.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/relay.go @@ -5,9 +5,9 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" ) // SendTx takes pre-built packet data containing messages to be executed on the host chain from an authentication module and attempts to send the packet. diff --git a/modules/apps/27-interchain-accounts/controller/keeper/relay_test.go b/modules/apps/27-interchain-accounts/controller/keeper/relay_test.go index bb4d17b8232..c180e1283f2 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/relay_test.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/relay_test.go @@ -5,11 +5,11 @@ import ( banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func (suite *KeeperTestSuite) TestSendTx() { diff --git a/modules/apps/27-interchain-accounts/controller/types/params_test.go b/modules/apps/27-interchain-accounts/controller/types/params_test.go index 1b2b296c7d4..d399c37fb02 100644 --- a/modules/apps/27-interchain-accounts/controller/types/params_test.go +++ b/modules/apps/27-interchain-accounts/controller/types/params_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller/types" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/types" ) func TestValidateParams(t *testing.T) { diff --git a/modules/apps/27-interchain-accounts/host/client/cli/query.go b/modules/apps/27-interchain-accounts/host/client/cli/query.go index c35e03baa16..c2b6489e683 100644 --- a/modules/apps/27-interchain-accounts/host/client/cli/query.go +++ b/modules/apps/27-interchain-accounts/host/client/cli/query.go @@ -12,10 +12,10 @@ import ( "github.com/spf13/cobra" abci "github.com/tendermint/tendermint/abci/types" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/types" - icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/host/types" + icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) // GetCmdParams returns the command handler for the host submodule parameter querying. diff --git a/modules/apps/27-interchain-accounts/host/ibc_module.go b/modules/apps/27-interchain-accounts/host/ibc_module.go index fb403c71937..0f2a097f2cc 100644 --- a/modules/apps/27-interchain-accounts/host/ibc_module.go +++ b/modules/apps/27-interchain-accounts/host/ibc_module.go @@ -5,11 +5,11 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/keeper" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/types" - icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - ibcexported "github.com/cosmos/ibc-go/v3/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/host/keeper" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/host/types" + icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + ibcexported "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // IBCModule implements the ICS26 interface for interchain accounts host chains diff --git a/modules/apps/27-interchain-accounts/host/ibc_module_test.go b/modules/apps/27-interchain-accounts/host/ibc_module_test.go index 3fa45384104..a7a6f68d903 100644 --- a/modules/apps/27-interchain-accounts/host/ibc_module_test.go +++ b/modules/apps/27-interchain-accounts/host/ibc_module_test.go @@ -14,13 +14,13 @@ import ( tmprotostate "github.com/tendermint/tendermint/proto/tendermint/state" tmstate "github.com/tendermint/tendermint/state" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/types" - icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/host/types" + icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) var ( diff --git a/modules/apps/27-interchain-accounts/host/keeper/account.go b/modules/apps/27-interchain-accounts/host/keeper/account.go index d37cc21f1c2..01ccb0f0883 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/account.go +++ b/modules/apps/27-interchain-accounts/host/keeper/account.go @@ -4,7 +4,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" + icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" ) // RegisterInterchainAccount attempts to create a new account using the provided address and diff --git a/modules/apps/27-interchain-accounts/host/keeper/account_test.go b/modules/apps/27-interchain-accounts/host/keeper/account_test.go index df1966277e4..cc9bb0e0719 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/account_test.go +++ b/modules/apps/27-interchain-accounts/host/keeper/account_test.go @@ -3,8 +3,8 @@ package keeper_test import ( sdk "github.com/cosmos/cosmos-sdk/types" - icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func (suite *KeeperTestSuite) TestRegisterInterchainAccount() { diff --git a/modules/apps/27-interchain-accounts/host/keeper/events.go b/modules/apps/27-interchain-accounts/host/keeper/events.go index 5926781d5c2..043f8f359fd 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/events.go +++ b/modules/apps/27-interchain-accounts/host/keeper/events.go @@ -3,8 +3,8 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" ) // EmitWriteErrorAcknowledgementEvent emits an event signalling an error acknowledgement and including the error details diff --git a/modules/apps/27-interchain-accounts/host/keeper/genesis.go b/modules/apps/27-interchain-accounts/host/keeper/genesis.go index 2a9caa3948b..c4c66e88414 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/genesis.go +++ b/modules/apps/27-interchain-accounts/host/keeper/genesis.go @@ -5,8 +5,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) // InitGenesis initializes the interchain accounts host application state from a provided genesis state diff --git a/modules/apps/27-interchain-accounts/host/keeper/genesis_test.go b/modules/apps/27-interchain-accounts/host/keeper/genesis_test.go index 138d713cf67..9a39befbaee 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/genesis_test.go +++ b/modules/apps/27-interchain-accounts/host/keeper/genesis_test.go @@ -1,10 +1,10 @@ package keeper_test import ( - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/keeper" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/types" - icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/host/keeper" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/host/types" + icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func (suite *KeeperTestSuite) TestInitGenesis() { diff --git a/modules/apps/27-interchain-accounts/host/keeper/grpc_query.go b/modules/apps/27-interchain-accounts/host/keeper/grpc_query.go index c2727eab535..4e278afa7d9 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/grpc_query.go +++ b/modules/apps/27-interchain-accounts/host/keeper/grpc_query.go @@ -5,7 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/types" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/host/types" ) var _ types.QueryServer = Keeper{} diff --git a/modules/apps/27-interchain-accounts/host/keeper/grpc_query_test.go b/modules/apps/27-interchain-accounts/host/keeper/grpc_query_test.go index e0ca62e8c56..a54f238db5b 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/grpc_query_test.go +++ b/modules/apps/27-interchain-accounts/host/keeper/grpc_query_test.go @@ -3,7 +3,7 @@ package keeper_test import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/types" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/host/types" ) func (suite *KeeperTestSuite) TestQueryParams() { diff --git a/modules/apps/27-interchain-accounts/host/keeper/handshake.go b/modules/apps/27-interchain-accounts/host/keeper/handshake.go index 11a7c7a378e..080d79e25b6 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/handshake.go +++ b/modules/apps/27-interchain-accounts/host/keeper/handshake.go @@ -8,9 +8,9 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) // OnChanOpenTry performs basic validation of the ICA channel diff --git a/modules/apps/27-interchain-accounts/host/keeper/handshake_test.go b/modules/apps/27-interchain-accounts/host/keeper/handshake_test.go index 0cac2912ccb..f6c8294638c 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/handshake_test.go +++ b/modules/apps/27-interchain-accounts/host/keeper/handshake_test.go @@ -3,10 +3,10 @@ package keeper_test import ( capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func (suite *KeeperTestSuite) TestOnChanOpenTry() { diff --git a/modules/apps/27-interchain-accounts/host/keeper/keeper.go b/modules/apps/27-interchain-accounts/host/keeper/keeper.go index ea3f8205c87..506442dfc64 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/keeper.go +++ b/modules/apps/27-interchain-accounts/host/keeper/keeper.go @@ -12,10 +12,10 @@ import ( paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" "github.com/tendermint/tendermint/libs/log" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/types" - icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/host/types" + icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) // Keeper defines the IBC interchain accounts host keeper diff --git a/modules/apps/27-interchain-accounts/host/keeper/keeper_test.go b/modules/apps/27-interchain-accounts/host/keeper/keeper_test.go index 609fb06884d..7a9ece7d1bc 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/keeper_test.go +++ b/modules/apps/27-interchain-accounts/host/keeper/keeper_test.go @@ -7,9 +7,9 @@ import ( "github.com/stretchr/testify/suite" "github.com/tendermint/tendermint/crypto" - icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) var ( diff --git a/modules/apps/27-interchain-accounts/host/keeper/params.go b/modules/apps/27-interchain-accounts/host/keeper/params.go index 1ecbd5b05f8..97f68fdb5de 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/params.go +++ b/modules/apps/27-interchain-accounts/host/keeper/params.go @@ -3,7 +3,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/types" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/host/types" ) // IsHostEnabled retrieves the host enabled boolean from the paramstore. diff --git a/modules/apps/27-interchain-accounts/host/keeper/params_test.go b/modules/apps/27-interchain-accounts/host/keeper/params_test.go index 529afecaecf..cc71a3a95c5 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/params_test.go +++ b/modules/apps/27-interchain-accounts/host/keeper/params_test.go @@ -1,6 +1,6 @@ package keeper_test -import "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/types" +import "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/host/types" func (suite *KeeperTestSuite) TestParams() { expParams := types.DefaultParams() diff --git a/modules/apps/27-interchain-accounts/host/keeper/relay.go b/modules/apps/27-interchain-accounts/host/keeper/relay.go index 8610a50f5e5..825a52e9f41 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/relay.go +++ b/modules/apps/27-interchain-accounts/host/keeper/relay.go @@ -5,9 +5,9 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/gogo/protobuf/proto" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/types" - icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/host/types" + icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" ) // OnRecvPacket handles a given interchain accounts packet on a destination host chain. diff --git a/modules/apps/27-interchain-accounts/host/keeper/relay_test.go b/modules/apps/27-interchain-accounts/host/keeper/relay_test.go index 0b4e8161317..2046520cf21 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/relay_test.go +++ b/modules/apps/27-interchain-accounts/host/keeper/relay_test.go @@ -10,12 +10,12 @@ import ( govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/types" - icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - transfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/host/types" + icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + transfertypes "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func (suite *KeeperTestSuite) TestOnRecvPacket() { diff --git a/modules/apps/27-interchain-accounts/host/types/ack.go b/modules/apps/27-interchain-accounts/host/types/ack.go index 202404fff3a..4a355c6c870 100644 --- a/modules/apps/27-interchain-accounts/host/types/ack.go +++ b/modules/apps/27-interchain-accounts/host/types/ack.go @@ -5,7 +5,7 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" ) const ( diff --git a/modules/apps/27-interchain-accounts/host/types/ack_test.go b/modules/apps/27-interchain-accounts/host/types/ack_test.go index bc4e2d07afc..50428c62150 100644 --- a/modules/apps/27-interchain-accounts/host/types/ack_test.go +++ b/modules/apps/27-interchain-accounts/host/types/ack_test.go @@ -9,8 +9,8 @@ import ( tmprotostate "github.com/tendermint/tendermint/proto/tendermint/state" tmstate "github.com/tendermint/tendermint/state" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/host/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) const ( diff --git a/modules/apps/27-interchain-accounts/host/types/params_test.go b/modules/apps/27-interchain-accounts/host/types/params_test.go index 2606f3fd2f5..7945097a975 100644 --- a/modules/apps/27-interchain-accounts/host/types/params_test.go +++ b/modules/apps/27-interchain-accounts/host/types/params_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/types" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/host/types" ) func TestValidateParams(t *testing.T) { diff --git a/modules/apps/27-interchain-accounts/module.go b/modules/apps/27-interchain-accounts/module.go index 1bb870fca5d..7fc36feaeac 100644 --- a/modules/apps/27-interchain-accounts/module.go +++ b/modules/apps/27-interchain-accounts/module.go @@ -15,16 +15,16 @@ import ( "github.com/spf13/cobra" abci "github.com/tendermint/tendermint/abci/types" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/client/cli" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller" - controllerkeeper "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller/keeper" - controllertypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller/types" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host" - hostkeeper "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/keeper" - hosttypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/types" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - porttypes "github.com/cosmos/ibc-go/v3/modules/core/05-port/types" - ibchost "github.com/cosmos/ibc-go/v3/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/client/cli" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller" + controllerkeeper "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/keeper" + controllertypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/types" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/host" + hostkeeper "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/host/keeper" + hosttypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/host/types" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + porttypes "github.com/cosmos/ibc-go/v5/modules/core/05-port/types" + ibchost "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) var ( diff --git a/modules/apps/27-interchain-accounts/module_test.go b/modules/apps/27-interchain-accounts/module_test.go index f030eac9075..c1820f50b02 100644 --- a/modules/apps/27-interchain-accounts/module_test.go +++ b/modules/apps/27-interchain-accounts/module_test.go @@ -8,12 +8,12 @@ import ( tmproto "github.com/tendermint/tendermint/proto/tendermint/types" dbm "github.com/tendermint/tm-db" - ica "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts" - controllertypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller/types" - hosttypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/types" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" - "github.com/cosmos/ibc-go/v3/testing/simapp" + ica "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts" + controllertypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/types" + hosttypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/host/types" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" + "github.com/cosmos/ibc-go/v5/testing/simapp" ) type InterchainAccountsTestSuite struct { diff --git a/modules/apps/27-interchain-accounts/types/account_test.go b/modules/apps/27-interchain-accounts/types/account_test.go index 13acc610152..d33efe7def1 100644 --- a/modules/apps/27-interchain-accounts/types/account_test.go +++ b/modules/apps/27-interchain-accounts/types/account_test.go @@ -10,8 +10,8 @@ import ( authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/stretchr/testify/suite" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) var ( diff --git a/modules/apps/27-interchain-accounts/types/codec_test.go b/modules/apps/27-interchain-accounts/types/codec_test.go index e027fda9346..b52b8728cc8 100644 --- a/modules/apps/27-interchain-accounts/types/codec_test.go +++ b/modules/apps/27-interchain-accounts/types/codec_test.go @@ -6,8 +6,8 @@ import ( banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - "github.com/cosmos/ibc-go/v3/testing/simapp" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + "github.com/cosmos/ibc-go/v5/testing/simapp" ) // caseRawBytes defines a helper struct, used for testing codec operations diff --git a/modules/apps/27-interchain-accounts/types/expected_keepers.go b/modules/apps/27-interchain-accounts/types/expected_keepers.go index 8fb0b743cda..00110df91cb 100644 --- a/modules/apps/27-interchain-accounts/types/expected_keepers.go +++ b/modules/apps/27-interchain-accounts/types/expected_keepers.go @@ -5,8 +5,8 @@ import ( authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - ibcexported "github.com/cosmos/ibc-go/v3/modules/core/exported" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + ibcexported "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // AccountKeeper defines the expected account keeper diff --git a/modules/apps/27-interchain-accounts/types/genesis.go b/modules/apps/27-interchain-accounts/types/genesis.go index f4c5b15b6d2..38929c664b0 100644 --- a/modules/apps/27-interchain-accounts/types/genesis.go +++ b/modules/apps/27-interchain-accounts/types/genesis.go @@ -1,9 +1,9 @@ package types import ( - controllertypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller/types" - hosttypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + controllertypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/types" + hosttypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/host/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) // DefaultGenesis creates and returns the interchain accounts GenesisState diff --git a/modules/apps/27-interchain-accounts/types/genesis.pb.go b/modules/apps/27-interchain-accounts/types/genesis.pb.go index c73f5c395f0..6998502dd48 100644 --- a/modules/apps/27-interchain-accounts/types/genesis.pb.go +++ b/modules/apps/27-interchain-accounts/types/genesis.pb.go @@ -5,8 +5,8 @@ package types import ( fmt "fmt" - types "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller/types" - types1 "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/types" + types "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/types" + types1 "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/host/types" _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" io "io" diff --git a/modules/apps/27-interchain-accounts/types/genesis_test.go b/modules/apps/27-interchain-accounts/types/genesis_test.go index 7e6ffdc280b..c57fa0c1698 100644 --- a/modules/apps/27-interchain-accounts/types/genesis_test.go +++ b/modules/apps/27-interchain-accounts/types/genesis_test.go @@ -1,10 +1,10 @@ package types_test import ( - controllertypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller/types" - hosttypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/types" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + controllertypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/types" + hosttypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/host/types" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func (suite *TypesTestSuite) TestValidateGenesisState() { diff --git a/modules/apps/27-interchain-accounts/types/keys_test.go b/modules/apps/27-interchain-accounts/types/keys_test.go index 94c7a3bed0d..45800573a12 100644 --- a/modules/apps/27-interchain-accounts/types/keys_test.go +++ b/modules/apps/27-interchain-accounts/types/keys_test.go @@ -1,7 +1,7 @@ package types_test import ( - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" ) func (suite *TypesTestSuite) TestKeyActiveChannel() { diff --git a/modules/apps/27-interchain-accounts/types/metadata.go b/modules/apps/27-interchain-accounts/types/metadata.go index 3a7eae51cdf..4f2486ecb30 100644 --- a/modules/apps/27-interchain-accounts/types/metadata.go +++ b/modules/apps/27-interchain-accounts/types/metadata.go @@ -4,7 +4,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" + connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" ) const ( diff --git a/modules/apps/27-interchain-accounts/types/metadata_test.go b/modules/apps/27-interchain-accounts/types/metadata_test.go index 05a1b457c38..22671924495 100644 --- a/modules/apps/27-interchain-accounts/types/metadata_test.go +++ b/modules/apps/27-interchain-accounts/types/metadata_test.go @@ -1,8 +1,8 @@ package types_test import ( - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) // use TestVersion as metadata being compared against diff --git a/modules/apps/27-interchain-accounts/types/packet_test.go b/modules/apps/27-interchain-accounts/types/packet_test.go index 840ca529851..f822290c1a6 100644 --- a/modules/apps/27-interchain-accounts/types/packet_test.go +++ b/modules/apps/27-interchain-accounts/types/packet_test.go @@ -1,7 +1,7 @@ package types_test import ( - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" ) var largeMemo = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum" diff --git a/modules/apps/27-interchain-accounts/types/port_test.go b/modules/apps/27-interchain-accounts/types/port_test.go index bdef740cd98..fa24b3eac1a 100644 --- a/modules/apps/27-interchain-accounts/types/port_test.go +++ b/modules/apps/27-interchain-accounts/types/port_test.go @@ -3,8 +3,8 @@ package types_test import ( "fmt" - "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func (suite *TypesTestSuite) TestNewControllerPortID() { diff --git a/modules/apps/29-fee/client/cli/query.go b/modules/apps/29-fee/client/cli/query.go index a183c8f89d3..bb59dfff41d 100644 --- a/modules/apps/29-fee/client/cli/query.go +++ b/modules/apps/29-fee/client/cli/query.go @@ -8,8 +8,8 @@ import ( "github.com/cosmos/cosmos-sdk/client/flags" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" - "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" "github.com/spf13/cobra" ) diff --git a/modules/apps/29-fee/client/cli/tx.go b/modules/apps/29-fee/client/cli/tx.go index f1f858b60cd..438a745cf31 100644 --- a/modules/apps/29-fee/client/cli/tx.go +++ b/modules/apps/29-fee/client/cli/tx.go @@ -12,8 +12,8 @@ import ( "github.com/cosmos/cosmos-sdk/version" "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" ) const ( diff --git a/modules/apps/29-fee/fee_test.go b/modules/apps/29-fee/fee_test.go index d2445adef22..3c84f74e52c 100644 --- a/modules/apps/29-fee/fee_test.go +++ b/modules/apps/29-fee/fee_test.go @@ -5,11 +5,11 @@ import ( "github.com/stretchr/testify/suite" - "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" - ibcmock "github.com/cosmos/ibc-go/v3/testing/mock" + "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" + ibcmock "github.com/cosmos/ibc-go/v5/testing/mock" ) type FeeTestSuite struct { diff --git a/modules/apps/29-fee/ibc_module.go b/modules/apps/29-fee/ibc_module.go index 6ed9faa1f34..37c6a32165e 100644 --- a/modules/apps/29-fee/ibc_module.go +++ b/modules/apps/29-fee/ibc_module.go @@ -5,11 +5,11 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/keeper" - "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - porttypes "github.com/cosmos/ibc-go/v3/modules/core/05-port/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/keeper" + "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + porttypes "github.com/cosmos/ibc-go/v5/modules/core/05-port/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // IBCModule implements the ICS26 callbacks for the fee middleware given the fee keeper and the underlying application. diff --git a/modules/apps/29-fee/ibc_module_test.go b/modules/apps/29-fee/ibc_module_test.go index 3f5cf64635a..3c61599a780 100644 --- a/modules/apps/29-fee/ibc_module_test.go +++ b/modules/apps/29-fee/ibc_module_test.go @@ -6,14 +6,14 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - fee "github.com/cosmos/ibc-go/v3/modules/apps/29-fee" - "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" - transfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctesting "github.com/cosmos/ibc-go/v3/testing" - ibcmock "github.com/cosmos/ibc-go/v3/testing/mock" + fee "github.com/cosmos/ibc-go/v5/modules/apps/29-fee" + "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types" + transfertypes "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + ibctesting "github.com/cosmos/ibc-go/v5/testing" + ibcmock "github.com/cosmos/ibc-go/v5/testing/mock" ) var ( diff --git a/modules/apps/29-fee/keeper/escrow.go b/modules/apps/29-fee/keeper/escrow.go index 0ca84684440..eff5685ad3b 100644 --- a/modules/apps/29-fee/keeper/escrow.go +++ b/modules/apps/29-fee/keeper/escrow.go @@ -7,8 +7,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" ) // escrowPacketFee sends the packet fee to the 29-fee module account to hold in escrow diff --git a/modules/apps/29-fee/keeper/escrow_test.go b/modules/apps/29-fee/keeper/escrow_test.go index e2a43afe586..31a1a6056bd 100644 --- a/modules/apps/29-fee/keeper/escrow_test.go +++ b/modules/apps/29-fee/keeper/escrow_test.go @@ -4,9 +4,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/tendermint/tendermint/crypto/secp256k1" - "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" - transfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types" + transfertypes "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" ) func (suite *KeeperTestSuite) TestDistributeFee() { diff --git a/modules/apps/29-fee/keeper/events.go b/modules/apps/29-fee/keeper/events.go index 9ff6f320ffc..1ccb3ee4ec7 100644 --- a/modules/apps/29-fee/keeper/events.go +++ b/modules/apps/29-fee/keeper/events.go @@ -5,8 +5,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" ) // EmitIncentivizedPacket emits an event so that relayers know an incentivized packet is ready to be relayed diff --git a/modules/apps/29-fee/keeper/genesis.go b/modules/apps/29-fee/keeper/genesis.go index 70b6a5012a2..8d818ecc458 100644 --- a/modules/apps/29-fee/keeper/genesis.go +++ b/modules/apps/29-fee/keeper/genesis.go @@ -3,7 +3,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" + "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types" ) // InitGenesis initializes the fee middleware application state from a provided genesis state diff --git a/modules/apps/29-fee/keeper/genesis_test.go b/modules/apps/29-fee/keeper/genesis_test.go index 8aa30385e58..9e2da7d2a54 100644 --- a/modules/apps/29-fee/keeper/genesis_test.go +++ b/modules/apps/29-fee/keeper/genesis_test.go @@ -1,9 +1,9 @@ package keeper_test import ( - "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func (suite *KeeperTestSuite) TestInitGenesis() { diff --git a/modules/apps/29-fee/keeper/grpc_query.go b/modules/apps/29-fee/keeper/grpc_query.go index 10da5bc5595..1446cd1fa79 100644 --- a/modules/apps/29-fee/keeper/grpc_query.go +++ b/modules/apps/29-fee/keeper/grpc_query.go @@ -10,7 +10,7 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" + "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types" ) var _ types.QueryServer = Keeper{} diff --git a/modules/apps/29-fee/keeper/grpc_query_test.go b/modules/apps/29-fee/keeper/grpc_query_test.go index d665c655ccf..fafbc5c5a66 100644 --- a/modules/apps/29-fee/keeper/grpc_query_test.go +++ b/modules/apps/29-fee/keeper/grpc_query_test.go @@ -7,9 +7,9 @@ import ( "github.com/cosmos/cosmos-sdk/types/query" "github.com/tendermint/tendermint/crypto/secp256k1" - "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func (suite *KeeperTestSuite) TestQueryIncentivizedPackets() { diff --git a/modules/apps/29-fee/keeper/keeper.go b/modules/apps/29-fee/keeper/keeper.go index e0317d3660a..99fc577c0e1 100644 --- a/modules/apps/29-fee/keeper/keeper.go +++ b/modules/apps/29-fee/keeper/keeper.go @@ -7,9 +7,9 @@ import ( paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" "github.com/tendermint/tendermint/libs/log" - "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) // Middleware must implement types.ChannelKeeper and types.PortKeeper expected interfaces diff --git a/modules/apps/29-fee/keeper/keeper_test.go b/modules/apps/29-fee/keeper/keeper_test.go index 7446ecbab64..2fc352024b2 100644 --- a/modules/apps/29-fee/keeper/keeper_test.go +++ b/modules/apps/29-fee/keeper/keeper_test.go @@ -8,10 +8,10 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/suite" - "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" - ibcmock "github.com/cosmos/ibc-go/v3/testing/mock" + "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" + ibcmock "github.com/cosmos/ibc-go/v5/testing/mock" ) var ( diff --git a/modules/apps/29-fee/keeper/msg_server.go b/modules/apps/29-fee/keeper/msg_server.go index 69f6520c759..84100737f92 100644 --- a/modules/apps/29-fee/keeper/msg_server.go +++ b/modules/apps/29-fee/keeper/msg_server.go @@ -5,8 +5,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" ) var _ types.MsgServer = Keeper{} diff --git a/modules/apps/29-fee/keeper/msg_server_test.go b/modules/apps/29-fee/keeper/msg_server_test.go index 8a927559a51..8eb1a78b548 100644 --- a/modules/apps/29-fee/keeper/msg_server_test.go +++ b/modules/apps/29-fee/keeper/msg_server_test.go @@ -3,9 +3,9 @@ package keeper_test import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func (suite *KeeperTestSuite) TestRegisterCounterpartyAddress() { diff --git a/modules/apps/29-fee/keeper/relay.go b/modules/apps/29-fee/keeper/relay.go index 476497bfbbb..f528c8ce30c 100644 --- a/modules/apps/29-fee/keeper/relay.go +++ b/modules/apps/29-fee/keeper/relay.go @@ -7,9 +7,9 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - ibcexported "github.com/cosmos/ibc-go/v3/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + ibcexported "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // SendPacket wraps IBC ChannelKeeper's SendPacket function diff --git a/modules/apps/29-fee/keeper/relay_test.go b/modules/apps/29-fee/keeper/relay_test.go index d0a9e620f9d..0bd90c0a0df 100644 --- a/modules/apps/29-fee/keeper/relay_test.go +++ b/modules/apps/29-fee/keeper/relay_test.go @@ -1,11 +1,11 @@ package keeper_test import ( - "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" - ibcmock "github.com/cosmos/ibc-go/v3/testing/mock" + "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" + ibcmock "github.com/cosmos/ibc-go/v5/testing/mock" ) func (suite *KeeperTestSuite) TestWriteAcknowledgementAsync() { diff --git a/modules/apps/29-fee/module.go b/modules/apps/29-fee/module.go index e493e047837..7659626125d 100644 --- a/modules/apps/29-fee/module.go +++ b/modules/apps/29-fee/module.go @@ -17,10 +17,10 @@ import ( "github.com/spf13/cobra" abci "github.com/tendermint/tendermint/abci/types" - "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/client/cli" - "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/keeper" - "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" - porttypes "github.com/cosmos/ibc-go/v3/modules/core/05-port/types" + "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/client/cli" + "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/keeper" + "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types" + porttypes "github.com/cosmos/ibc-go/v5/modules/core/05-port/types" ) var ( diff --git a/modules/apps/29-fee/transfer_test.go b/modules/apps/29-fee/transfer_test.go index f26183797ed..26437683f27 100644 --- a/modules/apps/29-fee/transfer_test.go +++ b/modules/apps/29-fee/transfer_test.go @@ -3,10 +3,10 @@ package fee_test import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" - transfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types" + transfertypes "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) // Integration test to ensure ics29 works with ics20 diff --git a/modules/apps/29-fee/types/expected_keepers.go b/modules/apps/29-fee/types/expected_keepers.go index 1d9d3439b07..81089fcaf9e 100644 --- a/modules/apps/29-fee/types/expected_keepers.go +++ b/modules/apps/29-fee/types/expected_keepers.go @@ -4,8 +4,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - ibcexported "github.com/cosmos/ibc-go/v3/modules/core/exported" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + ibcexported "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // AccountKeeper defines the contract required for account APIs. diff --git a/modules/apps/29-fee/types/fee.go b/modules/apps/29-fee/types/fee.go index 1f979c732d8..85f95874fae 100644 --- a/modules/apps/29-fee/types/fee.go +++ b/modules/apps/29-fee/types/fee.go @@ -6,7 +6,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" ) // NewPacketFee creates and returns a new PacketFee struct including the incentivization fees, refund addres and relayers diff --git a/modules/apps/29-fee/types/fee.pb.go b/modules/apps/29-fee/types/fee.pb.go index 1867eb351e5..e74686537b5 100644 --- a/modules/apps/29-fee/types/fee.pb.go +++ b/modules/apps/29-fee/types/fee.pb.go @@ -7,7 +7,7 @@ import ( fmt "fmt" github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" types "github.com/cosmos/cosmos-sdk/types" - types1 "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + types1 "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" io "io" diff --git a/modules/apps/29-fee/types/fee_test.go b/modules/apps/29-fee/types/fee_test.go index 2b02e5b857b..53d8b14980a 100644 --- a/modules/apps/29-fee/types/fee_test.go +++ b/modules/apps/29-fee/types/fee_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/crypto/secp256k1" - "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" + "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types" ) var ( diff --git a/modules/apps/29-fee/types/genesis.go b/modules/apps/29-fee/types/genesis.go index be9299fcd94..f77587a6a41 100644 --- a/modules/apps/29-fee/types/genesis.go +++ b/modules/apps/29-fee/types/genesis.go @@ -6,7 +6,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) // NewGenesisState creates a 29-fee GenesisState instance. diff --git a/modules/apps/29-fee/types/genesis.pb.go b/modules/apps/29-fee/types/genesis.pb.go index af78add463e..fb753aba1bd 100644 --- a/modules/apps/29-fee/types/genesis.pb.go +++ b/modules/apps/29-fee/types/genesis.pb.go @@ -5,7 +5,7 @@ package types import ( fmt "fmt" - types "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + types "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" io "io" diff --git a/modules/apps/29-fee/types/genesis_test.go b/modules/apps/29-fee/types/genesis_test.go index d574257e706..521c4a6cc4b 100644 --- a/modules/apps/29-fee/types/genesis_test.go +++ b/modules/apps/29-fee/types/genesis_test.go @@ -7,10 +7,10 @@ import ( "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/crypto/secp256k1" - "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" - transfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types" + transfertypes "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) var ( diff --git a/modules/apps/29-fee/types/keys.go b/modules/apps/29-fee/types/keys.go index 188a75e7ac3..d060673f33e 100644 --- a/modules/apps/29-fee/types/keys.go +++ b/modules/apps/29-fee/types/keys.go @@ -7,7 +7,7 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" ) const ( diff --git a/modules/apps/29-fee/types/keys_test.go b/modules/apps/29-fee/types/keys_test.go index 22dc91b6a57..faa1ecb4f13 100644 --- a/modules/apps/29-fee/types/keys_test.go +++ b/modules/apps/29-fee/types/keys_test.go @@ -6,9 +6,9 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) var ( diff --git a/modules/apps/29-fee/types/msgs.go b/modules/apps/29-fee/types/msgs.go index d2fec3e542f..c5a64025aad 100644 --- a/modules/apps/29-fee/types/msgs.go +++ b/modules/apps/29-fee/types/msgs.go @@ -6,8 +6,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) // msg types diff --git a/modules/apps/29-fee/types/msgs_test.go b/modules/apps/29-fee/types/msgs_test.go index d314a4f4193..6820b7dc99d 100644 --- a/modules/apps/29-fee/types/msgs_test.go +++ b/modules/apps/29-fee/types/msgs_test.go @@ -4,9 +4,9 @@ import ( "testing" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/crypto/secp256k1" diff --git a/modules/apps/29-fee/types/query.pb.go b/modules/apps/29-fee/types/query.pb.go index 32677378fe3..253089316f9 100644 --- a/modules/apps/29-fee/types/query.pb.go +++ b/modules/apps/29-fee/types/query.pb.go @@ -9,7 +9,7 @@ import ( github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" types1 "github.com/cosmos/cosmos-sdk/types" query "github.com/cosmos/cosmos-sdk/types/query" - types "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + types "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" _ "github.com/gogo/protobuf/gogoproto" grpc1 "github.com/gogo/protobuf/grpc" proto "github.com/gogo/protobuf/proto" diff --git a/modules/apps/29-fee/types/tx.pb.go b/modules/apps/29-fee/types/tx.pb.go index 1cecc607dc9..9f85cf8da51 100644 --- a/modules/apps/29-fee/types/tx.pb.go +++ b/modules/apps/29-fee/types/tx.pb.go @@ -6,7 +6,7 @@ package types import ( context "context" fmt "fmt" - types "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + types "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" _ "github.com/gogo/protobuf/gogoproto" grpc1 "github.com/gogo/protobuf/grpc" proto "github.com/gogo/protobuf/proto" diff --git a/modules/apps/transfer/client/cli/query.go b/modules/apps/transfer/client/cli/query.go index 3239b154377..78a275f16f0 100644 --- a/modules/apps/transfer/client/cli/query.go +++ b/modules/apps/transfer/client/cli/query.go @@ -8,7 +8,7 @@ import ( "github.com/cosmos/cosmos-sdk/version" "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" ) // GetCmdQueryDenomTrace defines the command to query a a denomination trace from a given hash. diff --git a/modules/apps/transfer/client/cli/tx.go b/modules/apps/transfer/client/cli/tx.go index 02e006be5eb..5d985cb94e7 100644 --- a/modules/apps/transfer/client/cli/tx.go +++ b/modules/apps/transfer/client/cli/tx.go @@ -13,9 +13,9 @@ import ( "github.com/cosmos/cosmos-sdk/version" "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - channelutils "github.com/cosmos/ibc-go/v3/modules/core/04-channel/client/utils" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + channelutils "github.com/cosmos/ibc-go/v5/modules/core/04-channel/client/utils" ) const ( diff --git a/modules/apps/transfer/ibc_module.go b/modules/apps/transfer/ibc_module.go index f5ed807d8b2..f94ce9ff0c8 100644 --- a/modules/apps/transfer/ibc_module.go +++ b/modules/apps/transfer/ibc_module.go @@ -8,12 +8,12 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/keeper" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - porttypes "github.com/cosmos/ibc-go/v3/modules/core/05-port/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - ibcexported "github.com/cosmos/ibc-go/v3/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/keeper" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + porttypes "github.com/cosmos/ibc-go/v5/modules/core/05-port/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + ibcexported "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // IBCModule implements the ICS26 interface for transfer given the transfer keeper. diff --git a/modules/apps/transfer/ibc_module_test.go b/modules/apps/transfer/ibc_module_test.go index 3dcb5518cbb..8629103d4e8 100644 --- a/modules/apps/transfer/ibc_module_test.go +++ b/modules/apps/transfer/ibc_module_test.go @@ -5,10 +5,10 @@ import ( capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func (suite *TransferTestSuite) TestOnChanOpenInit() { diff --git a/modules/apps/transfer/keeper/encoding.go b/modules/apps/transfer/keeper/encoding.go index 7e0333849d4..8e4bdf15c6c 100644 --- a/modules/apps/transfer/keeper/encoding.go +++ b/modules/apps/transfer/keeper/encoding.go @@ -1,7 +1,7 @@ package keeper import ( - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" ) // UnmarshalDenomTrace attempts to decode and return an DenomTrace object from diff --git a/modules/apps/transfer/keeper/genesis.go b/modules/apps/transfer/keeper/genesis.go index d4020508cc9..8a01fb45fb2 100644 --- a/modules/apps/transfer/keeper/genesis.go +++ b/modules/apps/transfer/keeper/genesis.go @@ -5,7 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" ) // InitGenesis initializes the ibc-transfer state and binds to PortID. diff --git a/modules/apps/transfer/keeper/genesis_test.go b/modules/apps/transfer/keeper/genesis_test.go index a8ded3f902d..f3a030a8ba4 100644 --- a/modules/apps/transfer/keeper/genesis_test.go +++ b/modules/apps/transfer/keeper/genesis_test.go @@ -3,7 +3,7 @@ package keeper_test import ( "fmt" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" ) func (suite *KeeperTestSuite) TestGenesis() { diff --git a/modules/apps/transfer/keeper/grpc_query.go b/modules/apps/transfer/keeper/grpc_query.go index e0b16c6a761..23bdad52eae 100644 --- a/modules/apps/transfer/keeper/grpc_query.go +++ b/modules/apps/transfer/keeper/grpc_query.go @@ -11,7 +11,7 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" ) var _ types.QueryServer = Keeper{} diff --git a/modules/apps/transfer/keeper/grpc_query_test.go b/modules/apps/transfer/keeper/grpc_query_test.go index 34563447b5f..1dfd43e6964 100644 --- a/modules/apps/transfer/keeper/grpc_query_test.go +++ b/modules/apps/transfer/keeper/grpc_query_test.go @@ -6,7 +6,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" ) func (suite *KeeperTestSuite) TestQueryDenomTrace() { diff --git a/modules/apps/transfer/keeper/keeper.go b/modules/apps/transfer/keeper/keeper.go index d3f32d95df7..9ec3be97062 100644 --- a/modules/apps/transfer/keeper/keeper.go +++ b/modules/apps/transfer/keeper/keeper.go @@ -10,8 +10,8 @@ import ( tmbytes "github.com/tendermint/tendermint/libs/bytes" "github.com/tendermint/tendermint/libs/log" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) // Keeper defines the IBC fungible transfer keeper diff --git a/modules/apps/transfer/keeper/keeper_test.go b/modules/apps/transfer/keeper/keeper_test.go index 40ceb6d782f..82d6b511855 100644 --- a/modules/apps/transfer/keeper/keeper_test.go +++ b/modules/apps/transfer/keeper/keeper_test.go @@ -6,8 +6,8 @@ import ( "github.com/cosmos/cosmos-sdk/baseapp" "github.com/stretchr/testify/suite" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) type KeeperTestSuite struct { diff --git a/modules/apps/transfer/keeper/mbt_relay_test.go b/modules/apps/transfer/keeper/mbt_relay_test.go index 3caef9aa5f9..1f993eefa13 100644 --- a/modules/apps/transfer/keeper/mbt_relay_test.go +++ b/modules/apps/transfer/keeper/mbt_relay_test.go @@ -15,10 +15,10 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/tendermint/tendermint/crypto" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) type TlaBalance struct { diff --git a/modules/apps/transfer/keeper/msg_server.go b/modules/apps/transfer/keeper/msg_server.go index 5d8e5682200..219990c957d 100644 --- a/modules/apps/transfer/keeper/msg_server.go +++ b/modules/apps/transfer/keeper/msg_server.go @@ -5,7 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" ) var _ types.MsgServer = Keeper{} diff --git a/modules/apps/transfer/keeper/params.go b/modules/apps/transfer/keeper/params.go index b88a1b93b69..e618316cfb1 100644 --- a/modules/apps/transfer/keeper/params.go +++ b/modules/apps/transfer/keeper/params.go @@ -3,7 +3,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" ) // GetSendEnabled retrieves the send enabled boolean from the paramstore diff --git a/modules/apps/transfer/keeper/params_test.go b/modules/apps/transfer/keeper/params_test.go index c0c7ff62af0..6c26b606305 100644 --- a/modules/apps/transfer/keeper/params_test.go +++ b/modules/apps/transfer/keeper/params_test.go @@ -1,6 +1,6 @@ package keeper_test -import "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" +import "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" func (suite *KeeperTestSuite) TestParams() { expParams := types.DefaultParams() diff --git a/modules/apps/transfer/keeper/relay.go b/modules/apps/transfer/keeper/relay.go index 3c3a5aa6690..188b5578202 100644 --- a/modules/apps/transfer/keeper/relay.go +++ b/modules/apps/transfer/keeper/relay.go @@ -9,11 +9,11 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - coretypes "github.com/cosmos/ibc-go/v3/modules/core/types" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + coretypes "github.com/cosmos/ibc-go/v5/modules/core/types" ) // SendTransfer handles transfer sending logic. There are 2 possible cases: diff --git a/modules/apps/transfer/keeper/relay_test.go b/modules/apps/transfer/keeper/relay_test.go index e76c1c1fb29..526bfd0fec8 100644 --- a/modules/apps/transfer/keeper/relay_test.go +++ b/modules/apps/transfer/keeper/relay_test.go @@ -5,12 +5,12 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - ibctesting "github.com/cosmos/ibc-go/v3/testing" - "github.com/cosmos/ibc-go/v3/testing/simapp" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + ibctesting "github.com/cosmos/ibc-go/v5/testing" + "github.com/cosmos/ibc-go/v5/testing/simapp" ) // test sending from chainA to chainB using both coin that orignate on diff --git a/modules/apps/transfer/module.go b/modules/apps/transfer/module.go index 0daa6dc3bf0..96c2d654926 100644 --- a/modules/apps/transfer/module.go +++ b/modules/apps/transfer/module.go @@ -17,11 +17,11 @@ import ( "github.com/spf13/cobra" abci "github.com/tendermint/tendermint/abci/types" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/client/cli" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/keeper" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/simulation" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" - porttypes "github.com/cosmos/ibc-go/v3/modules/core/05-port/types" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/client/cli" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/keeper" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/simulation" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" + porttypes "github.com/cosmos/ibc-go/v5/modules/core/05-port/types" ) var ( diff --git a/modules/apps/transfer/simulation/decoder.go b/modules/apps/transfer/simulation/decoder.go index 8758d904544..7ad36527885 100644 --- a/modules/apps/transfer/simulation/decoder.go +++ b/modules/apps/transfer/simulation/decoder.go @@ -6,7 +6,7 @@ import ( "github.com/cosmos/cosmos-sdk/types/kv" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" ) // TransferUnmarshaler defines the expected encoding store functions. diff --git a/modules/apps/transfer/simulation/decoder_test.go b/modules/apps/transfer/simulation/decoder_test.go index a505dffb15d..e8598ffa611 100644 --- a/modules/apps/transfer/simulation/decoder_test.go +++ b/modules/apps/transfer/simulation/decoder_test.go @@ -7,9 +7,9 @@ import ( "github.com/cosmos/cosmos-sdk/types/kv" "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/simulation" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" - "github.com/cosmos/ibc-go/v3/testing/simapp" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/simulation" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v5/testing/simapp" ) func TestDecodeStore(t *testing.T) { diff --git a/modules/apps/transfer/simulation/genesis.go b/modules/apps/transfer/simulation/genesis.go index a74277d1671..bd097eff49d 100644 --- a/modules/apps/transfer/simulation/genesis.go +++ b/modules/apps/transfer/simulation/genesis.go @@ -9,7 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/types/module" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" ) // Simulation parameter constants diff --git a/modules/apps/transfer/simulation/genesis_test.go b/modules/apps/transfer/simulation/genesis_test.go index c869d1c7b25..8a638bf4d43 100644 --- a/modules/apps/transfer/simulation/genesis_test.go +++ b/modules/apps/transfer/simulation/genesis_test.go @@ -11,8 +11,8 @@ import ( simtypes "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/simulation" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/simulation" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" ) // TestRandomizedGenState tests the normal scenario of applying RandomizedGenState. diff --git a/modules/apps/transfer/simulation/params.go b/modules/apps/transfer/simulation/params.go index c7193b47193..354574a6151 100644 --- a/modules/apps/transfer/simulation/params.go +++ b/modules/apps/transfer/simulation/params.go @@ -8,7 +8,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/simulation" gogotypes "github.com/gogo/protobuf/types" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" ) // ParamChanges defines the parameters that can be modified by param change proposals diff --git a/modules/apps/transfer/simulation/params_test.go b/modules/apps/transfer/simulation/params_test.go index 491fee0d3f3..b50c297e750 100644 --- a/modules/apps/transfer/simulation/params_test.go +++ b/modules/apps/transfer/simulation/params_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/simulation" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/simulation" ) func TestParamChanges(t *testing.T) { diff --git a/modules/apps/transfer/transfer_test.go b/modules/apps/transfer/transfer_test.go index 5402f0573bb..b6a88f7e651 100644 --- a/modules/apps/transfer/transfer_test.go +++ b/modules/apps/transfer/transfer_test.go @@ -6,9 +6,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/suite" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) type TransferTestSuite struct { diff --git a/modules/apps/transfer/types/ack.go b/modules/apps/transfer/types/ack.go index 6512f2e8371..9d6fe9dba98 100644 --- a/modules/apps/transfer/types/ack.go +++ b/modules/apps/transfer/types/ack.go @@ -5,7 +5,7 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" ) const ( diff --git a/modules/apps/transfer/types/ack_test.go b/modules/apps/transfer/types/ack_test.go index 4f4c3a874d7..44738110b3a 100644 --- a/modules/apps/transfer/types/ack_test.go +++ b/modules/apps/transfer/types/ack_test.go @@ -9,8 +9,8 @@ import ( tmprotostate "github.com/tendermint/tendermint/proto/tendermint/state" tmstate "github.com/tendermint/tendermint/state" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) const ( diff --git a/modules/apps/transfer/types/expected_keepers.go b/modules/apps/transfer/types/expected_keepers.go index 22ad54b9e62..bf73bf2c66c 100644 --- a/modules/apps/transfer/types/expected_keepers.go +++ b/modules/apps/transfer/types/expected_keepers.go @@ -5,9 +5,9 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - ibcexported "github.com/cosmos/ibc-go/v3/modules/core/exported" + connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + ibcexported "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // AccountKeeper defines the contract required for account APIs. diff --git a/modules/apps/transfer/types/genesis.go b/modules/apps/transfer/types/genesis.go index 73dbe111a2e..21aed7d1f47 100644 --- a/modules/apps/transfer/types/genesis.go +++ b/modules/apps/transfer/types/genesis.go @@ -1,7 +1,7 @@ package types import ( - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) // NewGenesisState creates a new ibc-transfer GenesisState instance. diff --git a/modules/apps/transfer/types/genesis_test.go b/modules/apps/transfer/types/genesis_test.go index 534eefba7ab..6ba8de4502d 100644 --- a/modules/apps/transfer/types/genesis_test.go +++ b/modules/apps/transfer/types/genesis_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" ) func TestValidateGenesis(t *testing.T) { diff --git a/modules/apps/transfer/types/keys_test.go b/modules/apps/transfer/types/keys_test.go index 3270dba55be..40bc07868bd 100644 --- a/modules/apps/transfer/types/keys_test.go +++ b/modules/apps/transfer/types/keys_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" ) // Test that there is domain separation between the port id and the channel id otherwise an diff --git a/modules/apps/transfer/types/msgs.go b/modules/apps/transfer/types/msgs.go index dab9fb21d46..50177d4b378 100644 --- a/modules/apps/transfer/types/msgs.go +++ b/modules/apps/transfer/types/msgs.go @@ -6,8 +6,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) // msg types diff --git a/modules/apps/transfer/types/msgs_test.go b/modules/apps/transfer/types/msgs_test.go index 00570ac15ed..939de03a1cc 100644 --- a/modules/apps/transfer/types/msgs_test.go +++ b/modules/apps/transfer/types/msgs_test.go @@ -8,7 +8,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" ) // define constants used for testing diff --git a/modules/apps/transfer/types/trace.go b/modules/apps/transfer/types/trace.go index 303ddd3769e..bdee7d700a1 100644 --- a/modules/apps/transfer/types/trace.go +++ b/modules/apps/transfer/types/trace.go @@ -12,7 +12,7 @@ import ( tmbytes "github.com/tendermint/tendermint/libs/bytes" tmtypes "github.com/tendermint/tendermint/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) // ParseDenomTrace parses a string with the ibc prefix (denom trace) and the base denomination diff --git a/modules/apps/transfer/types/tx.pb.go b/modules/apps/transfer/types/tx.pb.go index f240ba7368e..9e6594c68f4 100644 --- a/modules/apps/transfer/types/tx.pb.go +++ b/modules/apps/transfer/types/tx.pb.go @@ -7,7 +7,7 @@ import ( context "context" fmt "fmt" types "github.com/cosmos/cosmos-sdk/types" - types1 "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + types1 "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" _ "github.com/gogo/protobuf/gogoproto" grpc1 "github.com/gogo/protobuf/grpc" proto "github.com/gogo/protobuf/proto" diff --git a/modules/core/02-client/abci.go b/modules/core/02-client/abci.go index 0fb1cb5233a..74aa5f3b7f6 100644 --- a/modules/core/02-client/abci.go +++ b/modules/core/02-client/abci.go @@ -3,8 +3,8 @@ package client import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/keeper" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/keeper" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ) // BeginBlocker is used to perform IBC client upgrades diff --git a/modules/core/02-client/abci_test.go b/modules/core/02-client/abci_test.go index 72e9fbe44dd..1fb212497bb 100644 --- a/modules/core/02-client/abci_test.go +++ b/modules/core/02-client/abci_test.go @@ -10,10 +10,10 @@ import ( abci "github.com/tendermint/tendermint/abci/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - client "github.com/cosmos/ibc-go/v3/modules/core/02-client" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + client "github.com/cosmos/ibc-go/v5/modules/core/02-client" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) type ClientTestSuite struct { diff --git a/modules/core/02-client/client/cli/cli.go b/modules/core/02-client/client/cli/cli.go index 2a2546f1ab1..8917b9373e3 100644 --- a/modules/core/02-client/client/cli/cli.go +++ b/modules/core/02-client/client/cli/cli.go @@ -4,7 +4,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" ) // GetQueryCmd returns the query commands for IBC clients diff --git a/modules/core/02-client/client/cli/query.go b/modules/core/02-client/client/cli/query.go index d5e18679a28..47653b16238 100644 --- a/modules/core/02-client/client/cli/query.go +++ b/modules/core/02-client/client/cli/query.go @@ -9,9 +9,9 @@ import ( "github.com/cosmos/cosmos-sdk/version" "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/client/utils" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/client/utils" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) const ( diff --git a/modules/core/02-client/client/cli/tx.go b/modules/core/02-client/client/cli/tx.go index 00cee6e5d2a..2ae383d578d 100644 --- a/modules/core/02-client/client/cli/tx.go +++ b/modules/core/02-client/client/cli/tx.go @@ -16,8 +16,8 @@ import ( upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // NewCreateClientCmd defines the command to create a new IBC light client. diff --git a/modules/core/02-client/client/proposal_handler.go b/modules/core/02-client/client/proposal_handler.go index 93e98dbc8b2..37f80aebddd 100644 --- a/modules/core/02-client/client/proposal_handler.go +++ b/modules/core/02-client/client/proposal_handler.go @@ -8,7 +8,7 @@ import ( govclient "github.com/cosmos/cosmos-sdk/x/gov/client" govrest "github.com/cosmos/cosmos-sdk/x/gov/client/rest" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/client/cli" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/client/cli" ) var ( diff --git a/modules/core/02-client/client/utils/utils.go b/modules/core/02-client/client/utils/utils.go index 0ea44d7523b..a8493da38ad 100644 --- a/modules/core/02-client/client/utils/utils.go +++ b/modules/core/02-client/client/utils/utils.go @@ -8,12 +8,12 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" tmtypes "github.com/tendermint/tendermint/types" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - ibcclient "github.com/cosmos/ibc-go/v3/modules/core/client" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + ibcclient "github.com/cosmos/ibc-go/v5/modules/core/client" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ) // QueryClientState returns a client state. If prove is true, it performs an ABCI store query diff --git a/modules/core/02-client/genesis.go b/modules/core/02-client/genesis.go index 87deebba5d0..18dc745620d 100644 --- a/modules/core/02-client/genesis.go +++ b/modules/core/02-client/genesis.go @@ -5,9 +5,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/keeper" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/keeper" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // InitGenesis initializes the ibc client submodule's state from a provided genesis diff --git a/modules/core/02-client/keeper/client.go b/modules/core/02-client/keeper/client.go index 9fc10fe3d1b..329afadf55a 100644 --- a/modules/core/02-client/keeper/client.go +++ b/modules/core/02-client/keeper/client.go @@ -6,8 +6,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // CreateClient creates a new client state and populates it with a given consensus diff --git a/modules/core/02-client/keeper/client_test.go b/modules/core/02-client/keeper/client_test.go index 504eca3f726..c461b43fd76 100644 --- a/modules/core/02-client/keeper/client_test.go +++ b/modules/core/02-client/keeper/client_test.go @@ -7,13 +7,13 @@ import ( upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - solomachinetypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + solomachinetypes "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func (suite *KeeperTestSuite) TestCreateClient() { diff --git a/modules/core/02-client/keeper/encoding.go b/modules/core/02-client/keeper/encoding.go index 9a8156df37e..d8efad641c1 100644 --- a/modules/core/02-client/keeper/encoding.go +++ b/modules/core/02-client/keeper/encoding.go @@ -1,8 +1,8 @@ package keeper import ( - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // UnmarshalClientState attempts to decode and return an ClientState object from diff --git a/modules/core/02-client/keeper/events.go b/modules/core/02-client/keeper/events.go index 56c537b224b..a6aadb133fd 100644 --- a/modules/core/02-client/keeper/events.go +++ b/modules/core/02-client/keeper/events.go @@ -9,8 +9,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // EmitCreateClientEvent emits a create client event diff --git a/modules/core/02-client/keeper/grpc_query.go b/modules/core/02-client/keeper/grpc_query.go index d4a2eca5a4f..cf9ccce6ff0 100644 --- a/modules/core/02-client/keeper/grpc_query.go +++ b/modules/core/02-client/keeper/grpc_query.go @@ -14,9 +14,9 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) var _ types.QueryServer = Keeper{} diff --git a/modules/core/02-client/keeper/grpc_query_test.go b/modules/core/02-client/keeper/grpc_query_test.go index af8183bc5de..92d8165301f 100644 --- a/modules/core/02-client/keeper/grpc_query_test.go +++ b/modules/core/02-client/keeper/grpc_query_test.go @@ -10,11 +10,11 @@ import ( "github.com/cosmos/cosmos-sdk/types/query" "google.golang.org/grpc/metadata" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func (suite *KeeperTestSuite) TestQueryClientState() { diff --git a/modules/core/02-client/keeper/keeper.go b/modules/core/02-client/keeper/keeper.go index 1d32549654a..1d675f2e775 100644 --- a/modules/core/02-client/keeper/keeper.go +++ b/modules/core/02-client/keeper/keeper.go @@ -14,11 +14,11 @@ import ( "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/light" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ) // Keeper represents a type that grants read and write permissions to any client diff --git a/modules/core/02-client/keeper/keeper_test.go b/modules/core/02-client/keeper/keeper_test.go index 00bcfda178e..eb546ea02b6 100644 --- a/modules/core/02-client/keeper/keeper_test.go +++ b/modules/core/02-client/keeper/keeper_test.go @@ -15,15 +15,15 @@ import ( tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmtypes "github.com/tendermint/tendermint/types" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/keeper" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - solomachinetypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v3/testing" - ibctestingmock "github.com/cosmos/ibc-go/v3/testing/mock" - "github.com/cosmos/ibc-go/v3/testing/simapp" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/keeper" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + solomachinetypes "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" + ibctestingmock "github.com/cosmos/ibc-go/v5/testing/mock" + "github.com/cosmos/ibc-go/v5/testing/simapp" ) const ( diff --git a/modules/core/02-client/keeper/migrations.go b/modules/core/02-client/keeper/migrations.go index 53ff8a657e7..e4ba66760df 100644 --- a/modules/core/02-client/keeper/migrations.go +++ b/modules/core/02-client/keeper/migrations.go @@ -3,7 +3,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - v100 "github.com/cosmos/ibc-go/v3/modules/core/02-client/legacy/v100" + v100 "github.com/cosmos/ibc-go/v5/modules/core/02-client/legacy/v100" ) // Migrator is a struct for handling in-place store migrations. diff --git a/modules/core/02-client/keeper/params.go b/modules/core/02-client/keeper/params.go index 84bd2845ba3..78fe9b6d5de 100644 --- a/modules/core/02-client/keeper/params.go +++ b/modules/core/02-client/keeper/params.go @@ -3,7 +3,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" ) // GetAllowedClients retrieves the allowed clients from the paramstore diff --git a/modules/core/02-client/keeper/params_test.go b/modules/core/02-client/keeper/params_test.go index c293f7ec195..9e9c7f9574f 100644 --- a/modules/core/02-client/keeper/params_test.go +++ b/modules/core/02-client/keeper/params_test.go @@ -1,7 +1,7 @@ package keeper_test import ( - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" ) func (suite *KeeperTestSuite) TestParams() { diff --git a/modules/core/02-client/keeper/proposal.go b/modules/core/02-client/keeper/proposal.go index 115707777e5..475be2f0823 100644 --- a/modules/core/02-client/keeper/proposal.go +++ b/modules/core/02-client/keeper/proposal.go @@ -6,8 +6,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // ClientUpdateProposal will retrieve the subject and substitute client. diff --git a/modules/core/02-client/keeper/proposal_test.go b/modules/core/02-client/keeper/proposal_test.go index 0dcd123f06e..7a3b02e463a 100644 --- a/modules/core/02-client/keeper/proposal_test.go +++ b/modules/core/02-client/keeper/proposal_test.go @@ -4,10 +4,10 @@ import ( govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func (suite *KeeperTestSuite) TestClientUpdateProposal() { diff --git a/modules/core/02-client/legacy/v100/genesis.go b/modules/core/02-client/legacy/v100/genesis.go index 507443382a5..04fb4b188cb 100644 --- a/modules/core/02-client/legacy/v100/genesis.go +++ b/modules/core/02-client/legacy/v100/genesis.go @@ -7,10 +7,10 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ) // MigrateGenesis accepts exported v1.0.0 IBC client genesis file and migrates it to: diff --git a/modules/core/02-client/legacy/v100/genesis_test.go b/modules/core/02-client/legacy/v100/genesis_test.go index 0c64722bc59..a3611b14162 100644 --- a/modules/core/02-client/legacy/v100/genesis_test.go +++ b/modules/core/02-client/legacy/v100/genesis_test.go @@ -9,14 +9,14 @@ import ( "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" - ibcclient "github.com/cosmos/ibc-go/v3/modules/core/02-client" - v100 "github.com/cosmos/ibc-go/v3/modules/core/02-client/legacy/v100" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v3/testing" - "github.com/cosmos/ibc-go/v3/testing/simapp" + ibcclient "github.com/cosmos/ibc-go/v5/modules/core/02-client" + v100 "github.com/cosmos/ibc-go/v5/modules/core/02-client/legacy/v100" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" + "github.com/cosmos/ibc-go/v5/testing/simapp" ) func (suite *LegacyTestSuite) TestMigrateGenesisSolomachine() { diff --git a/modules/core/02-client/legacy/v100/solomachine.go b/modules/core/02-client/legacy/v100/solomachine.go index d41d6c31dbc..9df81bee22a 100644 --- a/modules/core/02-client/legacy/v100/solomachine.go +++ b/modules/core/02-client/legacy/v100/solomachine.go @@ -7,7 +7,7 @@ import ( cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // NOTE: this is a mock implmentation for exported.ClientState. This implementation diff --git a/modules/core/02-client/legacy/v100/solomachine.pb.go b/modules/core/02-client/legacy/v100/solomachine.pb.go index 1c87a4d9d2f..881f3483b1b 100644 --- a/modules/core/02-client/legacy/v100/solomachine.pb.go +++ b/modules/core/02-client/legacy/v100/solomachine.pb.go @@ -6,8 +6,8 @@ package v100 import ( fmt "fmt" types "github.com/cosmos/cosmos-sdk/codec/types" - types1 "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - types2 "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + types1 "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + types2 "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" io "io" diff --git a/modules/core/02-client/legacy/v100/store.go b/modules/core/02-client/legacy/v100/store.go index 08910ea419a..cc0f4766dfc 100644 --- a/modules/core/02-client/legacy/v100/store.go +++ b/modules/core/02-client/legacy/v100/store.go @@ -10,12 +10,12 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - smtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + smtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ) // MigrateStore performs in-place store migrations from SDK v0.40 of the IBC module to v1.0.0 of ibc-go. diff --git a/modules/core/02-client/legacy/v100/store_test.go b/modules/core/02-client/legacy/v100/store_test.go index db06d7d4cbd..c8fea0156ec 100644 --- a/modules/core/02-client/legacy/v100/store_test.go +++ b/modules/core/02-client/legacy/v100/store_test.go @@ -6,12 +6,12 @@ import ( "github.com/stretchr/testify/suite" - v100 "github.com/cosmos/ibc-go/v3/modules/core/02-client/legacy/v100" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + v100 "github.com/cosmos/ibc-go/v5/modules/core/02-client/legacy/v100" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) type LegacyTestSuite struct { diff --git a/modules/core/02-client/migrations/v6/solomachine.pb.go b/modules/core/02-client/migrations/v6/solomachine.pb.go index c92f04029f2..53d40ef8d92 100644 --- a/modules/core/02-client/migrations/v6/solomachine.pb.go +++ b/modules/core/02-client/migrations/v6/solomachine.pb.go @@ -6,8 +6,8 @@ package v6 import ( fmt "fmt" types "github.com/cosmos/cosmos-sdk/codec/types" - types1 "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - types2 "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + types1 "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + types2 "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" io "io" diff --git a/modules/core/02-client/module.go b/modules/core/02-client/module.go index c15bef6bf97..34ebfc34ec5 100644 --- a/modules/core/02-client/module.go +++ b/modules/core/02-client/module.go @@ -4,8 +4,8 @@ import ( "github.com/gogo/protobuf/grpc" "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/client/cli" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/client/cli" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" ) // Name returns the IBC client name diff --git a/modules/core/02-client/proposal_handler.go b/modules/core/02-client/proposal_handler.go index d1b15ce2dad..f90f0d31ee7 100644 --- a/modules/core/02-client/proposal_handler.go +++ b/modules/core/02-client/proposal_handler.go @@ -5,8 +5,8 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/keeper" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/keeper" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" ) // NewClientProposalHandler defines the 02-client proposal handler diff --git a/modules/core/02-client/proposal_handler_test.go b/modules/core/02-client/proposal_handler_test.go index 2e510f8777e..6b499728e67 100644 --- a/modules/core/02-client/proposal_handler_test.go +++ b/modules/core/02-client/proposal_handler_test.go @@ -5,10 +5,10 @@ import ( distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - client "github.com/cosmos/ibc-go/v3/modules/core/02-client" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + client "github.com/cosmos/ibc-go/v5/modules/core/02-client" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func (suite *ClientTestSuite) TestNewClientUpdateProposalHandler() { diff --git a/modules/core/02-client/simulation/decoder.go b/modules/core/02-client/simulation/decoder.go index 8aa19dd7c9c..010b2b75d0e 100644 --- a/modules/core/02-client/simulation/decoder.go +++ b/modules/core/02-client/simulation/decoder.go @@ -6,9 +6,9 @@ import ( "github.com/cosmos/cosmos-sdk/types/kv" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/keeper" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/keeper" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) var _ ClientUnmarshaler = (*keeper.Keeper)(nil) diff --git a/modules/core/02-client/simulation/decoder_test.go b/modules/core/02-client/simulation/decoder_test.go index 962435a7e25..65b4a287818 100644 --- a/modules/core/02-client/simulation/decoder_test.go +++ b/modules/core/02-client/simulation/decoder_test.go @@ -8,11 +8,11 @@ import ( "github.com/cosmos/cosmos-sdk/types/kv" "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/simulation" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - "github.com/cosmos/ibc-go/v3/testing/simapp" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/simulation" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + "github.com/cosmos/ibc-go/v5/testing/simapp" ) func TestDecodeStore(t *testing.T) { diff --git a/modules/core/02-client/simulation/genesis.go b/modules/core/02-client/simulation/genesis.go index ddf592dfad2..09ff36f8659 100644 --- a/modules/core/02-client/simulation/genesis.go +++ b/modules/core/02-client/simulation/genesis.go @@ -5,7 +5,7 @@ import ( simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" ) // GenClientGenesis returns the default client genesis state. diff --git a/modules/core/02-client/types/client.go b/modules/core/02-client/types/client.go index bb3d5c4f3d3..4649c3073fa 100644 --- a/modules/core/02-client/types/client.go +++ b/modules/core/02-client/types/client.go @@ -10,8 +10,8 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" proto "github.com/gogo/protobuf/proto" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) var ( diff --git a/modules/core/02-client/types/client_test.go b/modules/core/02-client/types/client_test.go index 9f41843c064..eee43a9ebfd 100644 --- a/modules/core/02-client/types/client_test.go +++ b/modules/core/02-client/types/client_test.go @@ -5,8 +5,8 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func (suite *TypesTestSuite) TestMarshalConsensusStateWithHeight() { diff --git a/modules/core/02-client/types/codec.go b/modules/core/02-client/types/codec.go index defdf4c3480..9374f4df375 100644 --- a/modules/core/02-client/types/codec.go +++ b/modules/core/02-client/types/codec.go @@ -8,7 +8,7 @@ import ( govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" proto "github.com/gogo/protobuf/proto" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // RegisterInterfaces registers the client interfaces to protobuf Any. diff --git a/modules/core/02-client/types/codec_test.go b/modules/core/02-client/types/codec_test.go index 5dd9b99620c..3630d9af20c 100644 --- a/modules/core/02-client/types/codec_test.go +++ b/modules/core/02-client/types/codec_test.go @@ -3,11 +3,11 @@ package types_test import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) type caseAny struct { diff --git a/modules/core/02-client/types/encoding.go b/modules/core/02-client/types/encoding.go index 0ffff6971c5..b974eb8b942 100644 --- a/modules/core/02-client/types/encoding.go +++ b/modules/core/02-client/types/encoding.go @@ -5,7 +5,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // MustUnmarshalClientState attempts to decode and return an ClientState object from diff --git a/modules/core/02-client/types/encoding_test.go b/modules/core/02-client/types/encoding_test.go index e1846c39f25..cda54c481e3 100644 --- a/modules/core/02-client/types/encoding_test.go +++ b/modules/core/02-client/types/encoding_test.go @@ -1,8 +1,8 @@ package types_test import ( - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ) func (suite *TypesTestSuite) TestMarshalHeader() { diff --git a/modules/core/02-client/types/events.go b/modules/core/02-client/types/events.go index 6975bcd6e66..6fca5175b81 100644 --- a/modules/core/02-client/types/events.go +++ b/modules/core/02-client/types/events.go @@ -3,7 +3,7 @@ package types import ( "fmt" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) // IBC client events diff --git a/modules/core/02-client/types/genesis.go b/modules/core/02-client/types/genesis.go index fce8a3d1b71..97b37af626d 100644 --- a/modules/core/02-client/types/genesis.go +++ b/modules/core/02-client/types/genesis.go @@ -6,8 +6,8 @@ import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) var ( diff --git a/modules/core/02-client/types/genesis_test.go b/modules/core/02-client/types/genesis_test.go index 3774ff48800..778c0ffd30a 100644 --- a/modules/core/02-client/types/genesis_test.go +++ b/modules/core/02-client/types/genesis_test.go @@ -5,14 +5,14 @@ import ( tmtypes "github.com/tendermint/tendermint/types" - client "github.com/cosmos/ibc-go/v3/modules/core/02-client" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - solomachinetypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v3/testing" - ibctestingmock "github.com/cosmos/ibc-go/v3/testing/mock" + client "github.com/cosmos/ibc-go/v5/modules/core/02-client" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + solomachinetypes "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" + ibctestingmock "github.com/cosmos/ibc-go/v5/testing/mock" ) const ( diff --git a/modules/core/02-client/types/height.go b/modules/core/02-client/types/height.go index 40125b23f2e..292a519fe7c 100644 --- a/modules/core/02-client/types/height.go +++ b/modules/core/02-client/types/height.go @@ -10,7 +10,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) var _ exported.Height = (*Height)(nil) diff --git a/modules/core/02-client/types/height_test.go b/modules/core/02-client/types/height_test.go index c31bbaabf21..f86a423a583 100644 --- a/modules/core/02-client/types/height_test.go +++ b/modules/core/02-client/types/height_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" ) func TestZeroHeight(t *testing.T) { diff --git a/modules/core/02-client/types/keys.go b/modules/core/02-client/types/keys.go index 426747b0f6c..ea22b0744ef 100644 --- a/modules/core/02-client/types/keys.go +++ b/modules/core/02-client/types/keys.go @@ -8,7 +8,7 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) const ( diff --git a/modules/core/02-client/types/keys_test.go b/modules/core/02-client/types/keys_test.go index da722040e1c..8a961a532e9 100644 --- a/modules/core/02-client/types/keys_test.go +++ b/modules/core/02-client/types/keys_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" ) // tests ParseClientIdentifier and IsValidClientID diff --git a/modules/core/02-client/types/msgs.go b/modules/core/02-client/types/msgs.go index 43d98a93493..1b18b56978f 100644 --- a/modules/core/02-client/types/msgs.go +++ b/modules/core/02-client/types/msgs.go @@ -5,8 +5,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // message types for the IBC client diff --git a/modules/core/02-client/types/msgs_test.go b/modules/core/02-client/types/msgs_test.go index 03148e73686..6f6cc33c1d6 100644 --- a/modules/core/02-client/types/msgs_test.go +++ b/modules/core/02-client/types/msgs_test.go @@ -7,11 +7,11 @@ import ( "github.com/golang/protobuf/proto" "github.com/stretchr/testify/suite" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - solomachinetypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + solomachinetypes "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) type TypesTestSuite struct { diff --git a/modules/core/02-client/types/params.go b/modules/core/02-client/types/params.go index 884fec2e5f4..b45659c6376 100644 --- a/modules/core/02-client/types/params.go +++ b/modules/core/02-client/types/params.go @@ -6,7 +6,7 @@ import ( paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) var ( diff --git a/modules/core/02-client/types/params_test.go b/modules/core/02-client/types/params_test.go index 87fed77622e..9ef52485141 100644 --- a/modules/core/02-client/types/params_test.go +++ b/modules/core/02-client/types/params_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) func TestValidateParams(t *testing.T) { diff --git a/modules/core/02-client/types/proposal.go b/modules/core/02-client/types/proposal.go index 02dc3c517ee..69745c7f680 100644 --- a/modules/core/02-client/types/proposal.go +++ b/modules/core/02-client/types/proposal.go @@ -9,7 +9,7 @@ import ( govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) const ( diff --git a/modules/core/02-client/types/proposal_test.go b/modules/core/02-client/types/proposal_test.go index 8ef103ee18a..128c0c6fb0f 100644 --- a/modules/core/02-client/types/proposal_test.go +++ b/modules/core/02-client/types/proposal_test.go @@ -8,9 +8,9 @@ import ( govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func (suite *TypesTestSuite) TestValidateBasic() { diff --git a/modules/core/02-client/types/query.go b/modules/core/02-client/types/query.go index ac9519b9298..23edf0f26d2 100644 --- a/modules/core/02-client/types/query.go +++ b/modules/core/02-client/types/query.go @@ -3,7 +3,7 @@ package types import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) var ( diff --git a/modules/core/03-connection/client/cli/cli.go b/modules/core/03-connection/client/cli/cli.go index d31649cf8ef..ccc2b56adae 100644 --- a/modules/core/03-connection/client/cli/cli.go +++ b/modules/core/03-connection/client/cli/cli.go @@ -3,7 +3,7 @@ package cli import ( "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" ) // GetQueryCmd returns the query commands for IBC connections diff --git a/modules/core/03-connection/client/cli/query.go b/modules/core/03-connection/client/cli/query.go index 6ef8fca5b1a..b2cdf51f890 100644 --- a/modules/core/03-connection/client/cli/query.go +++ b/modules/core/03-connection/client/cli/query.go @@ -8,9 +8,9 @@ import ( "github.com/cosmos/cosmos-sdk/version" "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v3/modules/core/03-connection/client/utils" - "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/03-connection/client/utils" + "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) // GetCmdQueryConnections defines the command to query all the connection ends diff --git a/modules/core/03-connection/client/utils/utils.go b/modules/core/03-connection/client/utils/utils.go index 3bd54a44541..86acbfe1786 100644 --- a/modules/core/03-connection/client/utils/utils.go +++ b/modules/core/03-connection/client/utils/utils.go @@ -10,13 +10,13 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - clientutils "github.com/cosmos/ibc-go/v3/modules/core/02-client/client/utils" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - ibcclient "github.com/cosmos/ibc-go/v3/modules/core/client" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + clientutils "github.com/cosmos/ibc-go/v5/modules/core/02-client/client/utils" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + ibcclient "github.com/cosmos/ibc-go/v5/modules/core/client" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // QueryConnection returns a connection end. diff --git a/modules/core/03-connection/genesis.go b/modules/core/03-connection/genesis.go index dca212469c3..00cba50de7d 100644 --- a/modules/core/03-connection/genesis.go +++ b/modules/core/03-connection/genesis.go @@ -3,8 +3,8 @@ package connection import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v3/modules/core/03-connection/keeper" - "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v5/modules/core/03-connection/keeper" + "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" ) // InitGenesis initializes the ibc connection submodule's state from a provided genesis diff --git a/modules/core/03-connection/keeper/events.go b/modules/core/03-connection/keeper/events.go index 6b1636ea71e..41946229e57 100644 --- a/modules/core/03-connection/keeper/events.go +++ b/modules/core/03-connection/keeper/events.go @@ -3,7 +3,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" ) // EmitConnectionOpenInitEvent emits a connection open init event diff --git a/modules/core/03-connection/keeper/grpc_query.go b/modules/core/03-connection/keeper/grpc_query.go index 77d8bc98c84..0f0f7395064 100644 --- a/modules/core/03-connection/keeper/grpc_query.go +++ b/modules/core/03-connection/keeper/grpc_query.go @@ -10,9 +10,9 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) var _ types.QueryServer = Keeper{} diff --git a/modules/core/03-connection/keeper/grpc_query_test.go b/modules/core/03-connection/keeper/grpc_query_test.go index a2542fe62b2..5f6ad8dfd17 100644 --- a/modules/core/03-connection/keeper/grpc_query_test.go +++ b/modules/core/03-connection/keeper/grpc_query_test.go @@ -6,10 +6,10 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func (suite *KeeperTestSuite) TestQueryConnection() { diff --git a/modules/core/03-connection/keeper/handshake.go b/modules/core/03-connection/keeper/handshake.go index 177324e3e17..8dcc412034f 100644 --- a/modules/core/03-connection/keeper/handshake.go +++ b/modules/core/03-connection/keeper/handshake.go @@ -8,10 +8,10 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/gogo/protobuf/proto" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // ConnOpenInit initialises a connection attempt on chain A. The generated connection identifier diff --git a/modules/core/03-connection/keeper/handshake_test.go b/modules/core/03-connection/keeper/handshake_test.go index 316c82023ee..fe2d60b8fdf 100644 --- a/modules/core/03-connection/keeper/handshake_test.go +++ b/modules/core/03-connection/keeper/handshake_test.go @@ -3,12 +3,12 @@ package keeper_test import ( "time" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) // TestConnOpenInit - chainA initializes (INIT state) a connection with diff --git a/modules/core/03-connection/keeper/keeper.go b/modules/core/03-connection/keeper/keeper.go index 8221dd597bc..d24ded37e92 100644 --- a/modules/core/03-connection/keeper/keeper.go +++ b/modules/core/03-connection/keeper/keeper.go @@ -8,11 +8,11 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // Keeper defines the IBC connection keeper diff --git a/modules/core/03-connection/keeper/keeper_test.go b/modules/core/03-connection/keeper/keeper_test.go index 0c25d51c452..cdd276068da 100644 --- a/modules/core/03-connection/keeper/keeper_test.go +++ b/modules/core/03-connection/keeper/keeper_test.go @@ -6,9 +6,9 @@ import ( "github.com/stretchr/testify/suite" - "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) type KeeperTestSuite struct { diff --git a/modules/core/03-connection/keeper/params.go b/modules/core/03-connection/keeper/params.go index 3d7c145ac77..9bf0dbefcd8 100644 --- a/modules/core/03-connection/keeper/params.go +++ b/modules/core/03-connection/keeper/params.go @@ -3,7 +3,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" ) // GetMaxExpectedTimePerBlock retrieves the maximum expected time per block from the paramstore diff --git a/modules/core/03-connection/keeper/params_test.go b/modules/core/03-connection/keeper/params_test.go index 21d628fa6fc..930364c410e 100644 --- a/modules/core/03-connection/keeper/params_test.go +++ b/modules/core/03-connection/keeper/params_test.go @@ -1,7 +1,7 @@ package keeper_test import ( - "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" ) func (suite *KeeperTestSuite) TestParams() { diff --git a/modules/core/03-connection/keeper/verify.go b/modules/core/03-connection/keeper/verify.go index d3807b1f104..3b27990e669 100644 --- a/modules/core/03-connection/keeper/verify.go +++ b/modules/core/03-connection/keeper/verify.go @@ -6,12 +6,12 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // VerifyClientState verifies a proof of a client state of the running machine diff --git a/modules/core/03-connection/keeper/verify_test.go b/modules/core/03-connection/keeper/verify_test.go index f6fc1f92c19..5b9280f0036 100644 --- a/modules/core/03-connection/keeper/verify_test.go +++ b/modules/core/03-connection/keeper/verify_test.go @@ -4,14 +4,14 @@ import ( "fmt" "time" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v3/testing" - ibcmock "github.com/cosmos/ibc-go/v3/testing/mock" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" + ibcmock "github.com/cosmos/ibc-go/v5/testing/mock" ) var defaultTimeoutHeight = clienttypes.NewHeight(1, 100000) diff --git a/modules/core/03-connection/module.go b/modules/core/03-connection/module.go index 8bfa8ed6017..a298d942446 100644 --- a/modules/core/03-connection/module.go +++ b/modules/core/03-connection/module.go @@ -4,8 +4,8 @@ import ( "github.com/gogo/protobuf/grpc" "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v3/modules/core/03-connection/client/cli" - "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v5/modules/core/03-connection/client/cli" + "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" ) // Name returns the IBC connection ICS name. diff --git a/modules/core/03-connection/simulation/decoder.go b/modules/core/03-connection/simulation/decoder.go index e01d2787e32..82be6c8d162 100644 --- a/modules/core/03-connection/simulation/decoder.go +++ b/modules/core/03-connection/simulation/decoder.go @@ -7,8 +7,8 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/types/kv" - "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) // NewDecodeStore returns a decoder function closure that unmarshals the KVPair's diff --git a/modules/core/03-connection/simulation/decoder_test.go b/modules/core/03-connection/simulation/decoder_test.go index 94de9fae92a..dadc31cf3dd 100644 --- a/modules/core/03-connection/simulation/decoder_test.go +++ b/modules/core/03-connection/simulation/decoder_test.go @@ -7,10 +7,10 @@ import ( "github.com/cosmos/cosmos-sdk/types/kv" "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v3/modules/core/03-connection/simulation" - "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/testing/simapp" + "github.com/cosmos/ibc-go/v5/modules/core/03-connection/simulation" + "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/testing/simapp" ) func TestDecodeStore(t *testing.T) { diff --git a/modules/core/03-connection/simulation/genesis.go b/modules/core/03-connection/simulation/genesis.go index 746daec042f..c8492473b79 100644 --- a/modules/core/03-connection/simulation/genesis.go +++ b/modules/core/03-connection/simulation/genesis.go @@ -5,7 +5,7 @@ import ( simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" ) // GenConnectionGenesis returns the default connection genesis state. diff --git a/modules/core/03-connection/types/codec.go b/modules/core/03-connection/types/codec.go index 8f65884aadf..8d8d6c36c0d 100644 --- a/modules/core/03-connection/types/codec.go +++ b/modules/core/03-connection/types/codec.go @@ -6,7 +6,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/msgservice" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // RegisterInterfaces register the ibc interfaces submodule implementations to protobuf diff --git a/modules/core/03-connection/types/connection.go b/modules/core/03-connection/types/connection.go index 3fa86a6a30f..0a0ef981361 100644 --- a/modules/core/03-connection/types/connection.go +++ b/modules/core/03-connection/types/connection.go @@ -3,9 +3,9 @@ package types import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) var _ exported.ConnectionI = (*ConnectionEnd)(nil) diff --git a/modules/core/03-connection/types/connection.pb.go b/modules/core/03-connection/types/connection.pb.go index d198ef2d8fe..209684ae402 100644 --- a/modules/core/03-connection/types/connection.pb.go +++ b/modules/core/03-connection/types/connection.pb.go @@ -5,7 +5,7 @@ package types import ( fmt "fmt" - types "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" + types "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" io "io" diff --git a/modules/core/03-connection/types/connection_test.go b/modules/core/03-connection/types/connection_test.go index 08c90be9951..0513b3f4331 100644 --- a/modules/core/03-connection/types/connection_test.go +++ b/modules/core/03-connection/types/connection_test.go @@ -5,10 +5,10 @@ import ( "github.com/stretchr/testify/require" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) var ( diff --git a/modules/core/03-connection/types/events.go b/modules/core/03-connection/types/events.go index 10fdbeff391..c517b08f11d 100644 --- a/modules/core/03-connection/types/events.go +++ b/modules/core/03-connection/types/events.go @@ -3,7 +3,7 @@ package types import ( "fmt" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) // IBC connection events diff --git a/modules/core/03-connection/types/expected_keepers.go b/modules/core/03-connection/types/expected_keepers.go index 05495181222..5570468ee6d 100644 --- a/modules/core/03-connection/types/expected_keepers.go +++ b/modules/core/03-connection/types/expected_keepers.go @@ -3,7 +3,7 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // ClientKeeper expected account IBC client keeper diff --git a/modules/core/03-connection/types/genesis.go b/modules/core/03-connection/types/genesis.go index 3502c13eb59..d0b01b4fda2 100644 --- a/modules/core/03-connection/types/genesis.go +++ b/modules/core/03-connection/types/genesis.go @@ -3,7 +3,7 @@ package types import ( "fmt" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) // NewConnectionPaths creates a ConnectionPaths instance. diff --git a/modules/core/03-connection/types/genesis_test.go b/modules/core/03-connection/types/genesis_test.go index db2192385b7..9f4ecd8dbad 100644 --- a/modules/core/03-connection/types/genesis_test.go +++ b/modules/core/03-connection/types/genesis_test.go @@ -5,9 +5,9 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func TestValidateGenesis(t *testing.T) { diff --git a/modules/core/03-connection/types/keys.go b/modules/core/03-connection/types/keys.go index e7ed7a943f3..b1c98de4fb6 100644 --- a/modules/core/03-connection/types/keys.go +++ b/modules/core/03-connection/types/keys.go @@ -6,7 +6,7 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) const ( diff --git a/modules/core/03-connection/types/keys_test.go b/modules/core/03-connection/types/keys_test.go index b0478d00703..04f7bebb69f 100644 --- a/modules/core/03-connection/types/keys_test.go +++ b/modules/core/03-connection/types/keys_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" ) // tests ParseConnectionSequence and IsValidConnectionID diff --git a/modules/core/03-connection/types/msgs.go b/modules/core/03-connection/types/msgs.go index 3ea63c84d37..0a2580af0ea 100644 --- a/modules/core/03-connection/types/msgs.go +++ b/modules/core/03-connection/types/msgs.go @@ -5,10 +5,10 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) var ( diff --git a/modules/core/03-connection/types/msgs_test.go b/modules/core/03-connection/types/msgs_test.go index 871da3c2b9c..9114f9bdba3 100644 --- a/modules/core/03-connection/types/msgs_test.go +++ b/modules/core/03-connection/types/msgs_test.go @@ -12,12 +12,12 @@ import ( abci "github.com/tendermint/tendermint/abci/types" dbm "github.com/tendermint/tm-db" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v3/testing" - "github.com/cosmos/ibc-go/v3/testing/simapp" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" + "github.com/cosmos/ibc-go/v5/testing/simapp" ) var ( diff --git a/modules/core/03-connection/types/params_test.go b/modules/core/03-connection/types/params_test.go index 28c4311ff94..a4abacf241a 100644 --- a/modules/core/03-connection/types/params_test.go +++ b/modules/core/03-connection/types/params_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" ) func TestValidateParams(t *testing.T) { diff --git a/modules/core/03-connection/types/query.go b/modules/core/03-connection/types/query.go index 2cae95934b6..2927fd36451 100644 --- a/modules/core/03-connection/types/query.go +++ b/modules/core/03-connection/types/query.go @@ -3,8 +3,8 @@ package types import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) var ( diff --git a/modules/core/03-connection/types/query.pb.go b/modules/core/03-connection/types/query.pb.go index a3d5cf40410..8fe71826eb7 100644 --- a/modules/core/03-connection/types/query.pb.go +++ b/modules/core/03-connection/types/query.pb.go @@ -8,7 +8,7 @@ import ( fmt "fmt" types1 "github.com/cosmos/cosmos-sdk/codec/types" query "github.com/cosmos/cosmos-sdk/types/query" - types "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + types "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" _ "github.com/gogo/protobuf/gogoproto" grpc1 "github.com/gogo/protobuf/grpc" proto "github.com/gogo/protobuf/proto" diff --git a/modules/core/03-connection/types/tx.pb.go b/modules/core/03-connection/types/tx.pb.go index 3e332e51c4b..a096e6465b1 100644 --- a/modules/core/03-connection/types/tx.pb.go +++ b/modules/core/03-connection/types/tx.pb.go @@ -7,7 +7,7 @@ import ( context "context" fmt "fmt" types "github.com/cosmos/cosmos-sdk/codec/types" - types1 "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + types1 "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" _ "github.com/gogo/protobuf/gogoproto" grpc1 "github.com/gogo/protobuf/grpc" proto "github.com/gogo/protobuf/proto" diff --git a/modules/core/03-connection/types/version.go b/modules/core/03-connection/types/version.go index f88164aff69..95ede1f38ea 100644 --- a/modules/core/03-connection/types/version.go +++ b/modules/core/03-connection/types/version.go @@ -5,7 +5,7 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) var ( diff --git a/modules/core/03-connection/types/version_test.go b/modules/core/03-connection/types/version_test.go index 3fa637cc4de..4bc42bb9e87 100644 --- a/modules/core/03-connection/types/version_test.go +++ b/modules/core/03-connection/types/version_test.go @@ -5,9 +5,9 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func TestValidateVersion(t *testing.T) { diff --git a/modules/core/04-channel/client/cli/cli.go b/modules/core/04-channel/client/cli/cli.go index d2dc4e661f8..96ea619249f 100644 --- a/modules/core/04-channel/client/cli/cli.go +++ b/modules/core/04-channel/client/cli/cli.go @@ -4,7 +4,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" ) // GetQueryCmd returns the query commands for IBC channels diff --git a/modules/core/04-channel/client/cli/query.go b/modules/core/04-channel/client/cli/query.go index a92ea327d6d..08d540cafc0 100644 --- a/modules/core/04-channel/client/cli/query.go +++ b/modules/core/04-channel/client/cli/query.go @@ -9,9 +9,9 @@ import ( "github.com/cosmos/cosmos-sdk/version" "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v3/modules/core/04-channel/client/utils" - "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/04-channel/client/utils" + "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) const ( diff --git a/modules/core/04-channel/client/utils/utils.go b/modules/core/04-channel/client/utils/utils.go index de40903e6fb..489a8aa8a39 100644 --- a/modules/core/04-channel/client/utils/utils.go +++ b/modules/core/04-channel/client/utils/utils.go @@ -8,12 +8,12 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - clientutils "github.com/cosmos/ibc-go/v3/modules/core/02-client/client/utils" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - ibcclient "github.com/cosmos/ibc-go/v3/modules/core/client" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + clientutils "github.com/cosmos/ibc-go/v5/modules/core/02-client/client/utils" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + ibcclient "github.com/cosmos/ibc-go/v5/modules/core/client" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // QueryChannel returns a channel end. diff --git a/modules/core/04-channel/genesis.go b/modules/core/04-channel/genesis.go index 56f81ca9f72..740d8b81b8d 100644 --- a/modules/core/04-channel/genesis.go +++ b/modules/core/04-channel/genesis.go @@ -3,8 +3,8 @@ package channel import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v3/modules/core/04-channel/keeper" - "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v5/modules/core/04-channel/keeper" + "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" ) // InitGenesis initializes the ibc channel submodule's state from a provided genesis diff --git a/modules/core/04-channel/keeper/events.go b/modules/core/04-channel/keeper/events.go index 731d298a2ae..4757fd9e797 100644 --- a/modules/core/04-channel/keeper/events.go +++ b/modules/core/04-channel/keeper/events.go @@ -6,8 +6,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // EmitChannelOpenInitEvent emits a channel open init event diff --git a/modules/core/04-channel/keeper/grpc_query.go b/modules/core/04-channel/keeper/grpc_query.go index 6bd8418a564..1e6db96c7ed 100644 --- a/modules/core/04-channel/keeper/grpc_query.go +++ b/modules/core/04-channel/keeper/grpc_query.go @@ -12,10 +12,10 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) var _ types.QueryServer = (*Keeper)(nil) diff --git a/modules/core/04-channel/keeper/grpc_query_test.go b/modules/core/04-channel/keeper/grpc_query_test.go index cc899e99444..f0a142212e2 100644 --- a/modules/core/04-channel/keeper/grpc_query_test.go +++ b/modules/core/04-channel/keeper/grpc_query_test.go @@ -6,11 +6,11 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func (suite *KeeperTestSuite) TestQueryChannel() { diff --git a/modules/core/04-channel/keeper/handshake.go b/modules/core/04-channel/keeper/handshake.go index 1d650bb4d7b..5545d29b106 100644 --- a/modules/core/04-channel/keeper/handshake.go +++ b/modules/core/04-channel/keeper/handshake.go @@ -8,11 +8,11 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - porttypes "github.com/cosmos/ibc-go/v3/modules/core/05-port/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + porttypes "github.com/cosmos/ibc-go/v5/modules/core/05-port/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // ChanOpenInit is called by a module to initiate a channel opening handshake with diff --git a/modules/core/04-channel/keeper/handshake_test.go b/modules/core/04-channel/keeper/handshake_test.go index aa554fe9fd8..01a7f0ed93c 100644 --- a/modules/core/04-channel/keeper/handshake_test.go +++ b/modules/core/04-channel/keeper/handshake_test.go @@ -5,12 +5,12 @@ import ( capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) type testCase = struct { diff --git a/modules/core/04-channel/keeper/keeper.go b/modules/core/04-channel/keeper/keeper.go index ca2f824ad48..9235a3b8493 100644 --- a/modules/core/04-channel/keeper/keeper.go +++ b/modules/core/04-channel/keeper/keeper.go @@ -12,12 +12,12 @@ import ( "github.com/tendermint/tendermint/libs/log" db "github.com/tendermint/tm-db" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - porttypes "github.com/cosmos/ibc-go/v3/modules/core/05-port/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + porttypes "github.com/cosmos/ibc-go/v5/modules/core/05-port/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) var _ porttypes.ICS4Wrapper = Keeper{} diff --git a/modules/core/04-channel/keeper/keeper_test.go b/modules/core/04-channel/keeper/keeper_test.go index f04664d71f4..af7c365609d 100644 --- a/modules/core/04-channel/keeper/keeper_test.go +++ b/modules/core/04-channel/keeper/keeper_test.go @@ -5,9 +5,9 @@ import ( "github.com/stretchr/testify/suite" - "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" - ibcmock "github.com/cosmos/ibc-go/v3/testing/mock" + "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" + ibcmock "github.com/cosmos/ibc-go/v5/testing/mock" ) // KeeperTestSuite is a testing suite to test keeper functions. diff --git a/modules/core/04-channel/keeper/packet.go b/modules/core/04-channel/keeper/packet.go index 05be506077a..41b13de7c16 100644 --- a/modules/core/04-channel/keeper/packet.go +++ b/modules/core/04-channel/keeper/packet.go @@ -9,11 +9,11 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // SendPacket is called by a module in order to send an IBC packet on a channel diff --git a/modules/core/04-channel/keeper/packet_test.go b/modules/core/04-channel/keeper/packet_test.go index 949b45a2e2f..2aa95ba8838 100644 --- a/modules/core/04-channel/keeper/packet_test.go +++ b/modules/core/04-channel/keeper/packet_test.go @@ -7,14 +7,14 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v3/testing" - ibcmock "github.com/cosmos/ibc-go/v3/testing/mock" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" + ibcmock "github.com/cosmos/ibc-go/v5/testing/mock" ) var ( diff --git a/modules/core/04-channel/keeper/timeout.go b/modules/core/04-channel/keeper/timeout.go index 5a14ef85b6b..8e657d3f11b 100644 --- a/modules/core/04-channel/keeper/timeout.go +++ b/modules/core/04-channel/keeper/timeout.go @@ -8,10 +8,10 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // TimeoutPacket is called by a module which originally attempted to send a diff --git a/modules/core/04-channel/keeper/timeout_test.go b/modules/core/04-channel/keeper/timeout_test.go index a7151c4c0ff..b8a25a723ee 100644 --- a/modules/core/04-channel/keeper/timeout_test.go +++ b/modules/core/04-channel/keeper/timeout_test.go @@ -7,12 +7,12 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) // TestTimeoutPacket test the TimeoutPacket call on chainA by ensuring the timeout has passed diff --git a/modules/core/04-channel/module.go b/modules/core/04-channel/module.go index 00dfc6a808b..977867bac03 100644 --- a/modules/core/04-channel/module.go +++ b/modules/core/04-channel/module.go @@ -4,8 +4,8 @@ import ( "github.com/gogo/protobuf/grpc" "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v3/modules/core/04-channel/client/cli" - "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v5/modules/core/04-channel/client/cli" + "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" ) // Name returns the IBC channel ICS name. diff --git a/modules/core/04-channel/simulation/decoder.go b/modules/core/04-channel/simulation/decoder.go index d09834551e8..338e70e8834 100644 --- a/modules/core/04-channel/simulation/decoder.go +++ b/modules/core/04-channel/simulation/decoder.go @@ -8,8 +8,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/kv" - "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) // NewDecodeStore returns a decoder function closure that unmarshals the KVPair's diff --git a/modules/core/04-channel/simulation/decoder_test.go b/modules/core/04-channel/simulation/decoder_test.go index 5a78c03be56..fbafa56a9ab 100644 --- a/modules/core/04-channel/simulation/decoder_test.go +++ b/modules/core/04-channel/simulation/decoder_test.go @@ -8,10 +8,10 @@ import ( "github.com/cosmos/cosmos-sdk/types/kv" "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v3/modules/core/04-channel/simulation" - "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/testing/simapp" + "github.com/cosmos/ibc-go/v5/modules/core/04-channel/simulation" + "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/testing/simapp" ) func TestDecodeStore(t *testing.T) { diff --git a/modules/core/04-channel/simulation/genesis.go b/modules/core/04-channel/simulation/genesis.go index 7c11dd6cb55..5e24f86b2db 100644 --- a/modules/core/04-channel/simulation/genesis.go +++ b/modules/core/04-channel/simulation/genesis.go @@ -5,7 +5,7 @@ import ( simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" ) // GenChannelGenesis returns the default channel genesis state. diff --git a/modules/core/04-channel/types/acknowledgement_test.go b/modules/core/04-channel/types/acknowledgement_test.go index 658ff31a8b5..37a73a9e55f 100644 --- a/modules/core/04-channel/types/acknowledgement_test.go +++ b/modules/core/04-channel/types/acknowledgement_test.go @@ -1,6 +1,6 @@ package types_test -import "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" +import "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" // tests acknowledgement.ValidateBasic and acknowledgement.GetBytes func (suite TypesTestSuite) TestAcknowledgement() { diff --git a/modules/core/04-channel/types/channel.go b/modules/core/04-channel/types/channel.go index 4671000ab70..7bf6454d511 100644 --- a/modules/core/04-channel/types/channel.go +++ b/modules/core/04-channel/types/channel.go @@ -3,8 +3,8 @@ package types import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) var ( diff --git a/modules/core/04-channel/types/channel.pb.go b/modules/core/04-channel/types/channel.pb.go index 3ce5ce3a2e1..0f12d87cde4 100644 --- a/modules/core/04-channel/types/channel.pb.go +++ b/modules/core/04-channel/types/channel.pb.go @@ -5,7 +5,7 @@ package types import ( fmt "fmt" - types "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + types "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" io "io" diff --git a/modules/core/04-channel/types/channel_test.go b/modules/core/04-channel/types/channel_test.go index c6cc9a03a9c..8bf27306e86 100644 --- a/modules/core/04-channel/types/channel_test.go +++ b/modules/core/04-channel/types/channel_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" ) func TestChannelValidateBasic(t *testing.T) { diff --git a/modules/core/04-channel/types/codec.go b/modules/core/04-channel/types/codec.go index 8981417130b..28b87bef117 100644 --- a/modules/core/04-channel/types/codec.go +++ b/modules/core/04-channel/types/codec.go @@ -6,7 +6,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/msgservice" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // RegisterInterfaces register the ibc channel submodule interfaces to protobuf diff --git a/modules/core/04-channel/types/events.go b/modules/core/04-channel/types/events.go index e9f909a695d..f0bed9dedb3 100644 --- a/modules/core/04-channel/types/events.go +++ b/modules/core/04-channel/types/events.go @@ -3,7 +3,7 @@ package types import ( "fmt" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) // IBC channel events diff --git a/modules/core/04-channel/types/expected_keepers.go b/modules/core/04-channel/types/expected_keepers.go index 317cefd16c1..56031bdf528 100644 --- a/modules/core/04-channel/types/expected_keepers.go +++ b/modules/core/04-channel/types/expected_keepers.go @@ -4,8 +4,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // ClientKeeper expected account IBC client keeper diff --git a/modules/core/04-channel/types/genesis.go b/modules/core/04-channel/types/genesis.go index 5f6b9370942..05bcc20fb52 100644 --- a/modules/core/04-channel/types/genesis.go +++ b/modules/core/04-channel/types/genesis.go @@ -4,7 +4,7 @@ import ( "errors" "fmt" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) // NewPacketState creates a new PacketState instance. diff --git a/modules/core/04-channel/types/genesis_test.go b/modules/core/04-channel/types/genesis_test.go index 856c0833c88..d02bd534246 100644 --- a/modules/core/04-channel/types/genesis_test.go +++ b/modules/core/04-channel/types/genesis_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" ) const ( diff --git a/modules/core/04-channel/types/keys.go b/modules/core/04-channel/types/keys.go index 8b81da4cfbb..18b7389b173 100644 --- a/modules/core/04-channel/types/keys.go +++ b/modules/core/04-channel/types/keys.go @@ -6,7 +6,7 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) const ( diff --git a/modules/core/04-channel/types/keys_test.go b/modules/core/04-channel/types/keys_test.go index f5bef1eaf2d..ecc7adef30c 100644 --- a/modules/core/04-channel/types/keys_test.go +++ b/modules/core/04-channel/types/keys_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" ) // tests ParseChannelSequence and IsValidChannelID diff --git a/modules/core/04-channel/types/msgs.go b/modules/core/04-channel/types/msgs.go index ea94f4b18b4..cff930eeaa8 100644 --- a/modules/core/04-channel/types/msgs.go +++ b/modules/core/04-channel/types/msgs.go @@ -6,9 +6,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) var _ sdk.Msg = &MsgChannelOpenInit{} diff --git a/modules/core/04-channel/types/msgs_test.go b/modules/core/04-channel/types/msgs_test.go index 623ed992d22..1be8d5b0cc4 100644 --- a/modules/core/04-channel/types/msgs_test.go +++ b/modules/core/04-channel/types/msgs_test.go @@ -12,11 +12,11 @@ import ( abci "github.com/tendermint/tendermint/abci/types" dbm "github.com/tendermint/tm-db" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - "github.com/cosmos/ibc-go/v3/testing/simapp" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + "github.com/cosmos/ibc-go/v5/testing/simapp" ) const ( diff --git a/modules/core/04-channel/types/packet.go b/modules/core/04-channel/types/packet.go index dd59c8bfde9..47b70546c9c 100644 --- a/modules/core/04-channel/types/packet.go +++ b/modules/core/04-channel/types/packet.go @@ -7,9 +7,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // CommitPacket returns the packet commitment bytes. The commitment consists of: diff --git a/modules/core/04-channel/types/packet_test.go b/modules/core/04-channel/types/packet_test.go index 1020e763e50..cb1644f1dcc 100644 --- a/modules/core/04-channel/types/packet_test.go +++ b/modules/core/04-channel/types/packet_test.go @@ -7,8 +7,8 @@ import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/stretchr/testify/require" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" ) func TestCommitPacket(t *testing.T) { diff --git a/modules/core/04-channel/types/query.go b/modules/core/04-channel/types/query.go index 31d58e50632..b43718a0bcd 100644 --- a/modules/core/04-channel/types/query.go +++ b/modules/core/04-channel/types/query.go @@ -3,8 +3,8 @@ package types import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) var ( diff --git a/modules/core/04-channel/types/query.pb.go b/modules/core/04-channel/types/query.pb.go index 1345b0bcdea..c5c1f12ee20 100644 --- a/modules/core/04-channel/types/query.pb.go +++ b/modules/core/04-channel/types/query.pb.go @@ -8,7 +8,7 @@ import ( fmt "fmt" types1 "github.com/cosmos/cosmos-sdk/codec/types" query "github.com/cosmos/cosmos-sdk/types/query" - types "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + types "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" _ "github.com/gogo/protobuf/gogoproto" grpc1 "github.com/gogo/protobuf/grpc" proto "github.com/gogo/protobuf/proto" diff --git a/modules/core/04-channel/types/tx.pb.go b/modules/core/04-channel/types/tx.pb.go index fe6f344384d..07aa47cb3ee 100644 --- a/modules/core/04-channel/types/tx.pb.go +++ b/modules/core/04-channel/types/tx.pb.go @@ -6,7 +6,7 @@ package types import ( context "context" fmt "fmt" - types "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + types "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" _ "github.com/gogo/protobuf/gogoproto" grpc1 "github.com/gogo/protobuf/grpc" proto "github.com/gogo/protobuf/proto" diff --git a/modules/core/04-channel/types/version_test.go b/modules/core/04-channel/types/version_test.go index 1ccacf7d9f2..75a6fdb0a86 100644 --- a/modules/core/04-channel/types/version_test.go +++ b/modules/core/04-channel/types/version_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" ) func TestSplitVersions(t *testing.T) { diff --git a/modules/core/05-port/keeper/keeper.go b/modules/core/05-port/keeper/keeper.go index ef898664337..588c6311a92 100644 --- a/modules/core/05-port/keeper/keeper.go +++ b/modules/core/05-port/keeper/keeper.go @@ -8,8 +8,8 @@ import ( capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" "github.com/tendermint/tendermint/libs/log" - "github.com/cosmos/ibc-go/v3/modules/core/05-port/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/05-port/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) // Keeper defines the IBC connection keeper diff --git a/modules/core/05-port/keeper/keeper_test.go b/modules/core/05-port/keeper/keeper_test.go index 52fa4198730..71d4349edca 100644 --- a/modules/core/05-port/keeper/keeper_test.go +++ b/modules/core/05-port/keeper/keeper_test.go @@ -8,8 +8,8 @@ import ( "github.com/stretchr/testify/suite" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - "github.com/cosmos/ibc-go/v3/modules/core/05-port/keeper" - "github.com/cosmos/ibc-go/v3/testing/simapp" + "github.com/cosmos/ibc-go/v5/modules/core/05-port/keeper" + "github.com/cosmos/ibc-go/v5/testing/simapp" ) var ( diff --git a/modules/core/05-port/module.go b/modules/core/05-port/module.go index 8aab55b1fb8..770aef4c1f0 100644 --- a/modules/core/05-port/module.go +++ b/modules/core/05-port/module.go @@ -4,8 +4,8 @@ import ( "github.com/gogo/protobuf/grpc" "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v3/modules/core/05-port/types" - "github.com/cosmos/ibc-go/v3/modules/core/client/cli" + "github.com/cosmos/ibc-go/v5/modules/core/05-port/types" + "github.com/cosmos/ibc-go/v5/modules/core/client/cli" ) // Name returns the IBC port ICS name. diff --git a/modules/core/05-port/types/module.go b/modules/core/05-port/types/module.go index e6ba8f3449b..40345e7ae7e 100644 --- a/modules/core/05-port/types/module.go +++ b/modules/core/05-port/types/module.go @@ -4,8 +4,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // IBCModule defines an interface that implements all the callbacks diff --git a/modules/core/05-port/types/query.pb.go b/modules/core/05-port/types/query.pb.go index 17a6cfc65da..4aced9ebf1e 100644 --- a/modules/core/05-port/types/query.pb.go +++ b/modules/core/05-port/types/query.pb.go @@ -6,7 +6,7 @@ package types import ( context "context" fmt "fmt" - types "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + types "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" grpc1 "github.com/gogo/protobuf/grpc" proto "github.com/gogo/protobuf/proto" grpc "google.golang.org/grpc" diff --git a/modules/core/23-commitment/types/codec.go b/modules/core/23-commitment/types/codec.go index 886a6c14e5b..04dac5c2dd4 100644 --- a/modules/core/23-commitment/types/codec.go +++ b/modules/core/23-commitment/types/codec.go @@ -3,7 +3,7 @@ package types import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // RegisterInterfaces registers the commitment interfaces to protobuf Any. diff --git a/modules/core/23-commitment/types/merkle.go b/modules/core/23-commitment/types/merkle.go index 17f1487d982..234ad719ec7 100644 --- a/modules/core/23-commitment/types/merkle.go +++ b/modules/core/23-commitment/types/merkle.go @@ -10,7 +10,7 @@ import ( "github.com/gogo/protobuf/proto" tmcrypto "github.com/tendermint/tendermint/proto/tendermint/crypto" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // var representing the proofspecs for a SDK chain diff --git a/modules/core/23-commitment/types/merkle_test.go b/modules/core/23-commitment/types/merkle_test.go index 28b8b3cd0dd..86fd6f9db0f 100644 --- a/modules/core/23-commitment/types/merkle_test.go +++ b/modules/core/23-commitment/types/merkle_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" - "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" ) func (suite *MerkleTestSuite) TestVerifyMembership() { diff --git a/modules/core/23-commitment/types/utils_test.go b/modules/core/23-commitment/types/utils_test.go index ea2abea2026..7af4272e6dc 100644 --- a/modules/core/23-commitment/types/utils_test.go +++ b/modules/core/23-commitment/types/utils_test.go @@ -7,7 +7,7 @@ import ( abci "github.com/tendermint/tendermint/abci/types" crypto "github.com/tendermint/tendermint/proto/tendermint/crypto" - "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" ) func (suite *MerkleTestSuite) TestConvertProofs() { diff --git a/modules/core/24-host/keys.go b/modules/core/24-host/keys.go index c12449f182f..f67b92aba35 100644 --- a/modules/core/24-host/keys.go +++ b/modules/core/24-host/keys.go @@ -3,7 +3,7 @@ package host import ( "fmt" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) const ( diff --git a/modules/core/24-host/parse_test.go b/modules/core/24-host/parse_test.go index 60b33d8ce45..7700fafed18 100644 --- a/modules/core/24-host/parse_test.go +++ b/modules/core/24-host/parse_test.go @@ -6,8 +6,8 @@ import ( "github.com/stretchr/testify/require" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) func TestParseIdentifier(t *testing.T) { diff --git a/modules/core/ante/ante.go b/modules/core/ante/ante.go index e9218ea4b94..62b7d3e6cb6 100644 --- a/modules/core/ante/ante.go +++ b/modules/core/ante/ante.go @@ -3,9 +3,9 @@ package ante import ( sdk "github.com/cosmos/cosmos-sdk/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - "github.com/cosmos/ibc-go/v3/modules/core/keeper" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v5/modules/core/keeper" ) type AnteDecorator struct { diff --git a/modules/core/ante/ante_test.go b/modules/core/ante/ante_test.go index 43c17d9f382..e6800dc89ac 100644 --- a/modules/core/ante/ante_test.go +++ b/modules/core/ante/ante_test.go @@ -7,12 +7,12 @@ import ( "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/ante" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/ante" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) type AnteTestSuite struct { diff --git a/modules/core/client/cli/cli.go b/modules/core/client/cli/cli.go index 92a3756cdb5..074125a5288 100644 --- a/modules/core/client/cli/cli.go +++ b/modules/core/client/cli/cli.go @@ -5,10 +5,10 @@ import ( "github.com/cosmos/cosmos-sdk/client" - ibcclient "github.com/cosmos/ibc-go/v3/modules/core/02-client" - connection "github.com/cosmos/ibc-go/v3/modules/core/03-connection" - channel "github.com/cosmos/ibc-go/v3/modules/core/04-channel" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + ibcclient "github.com/cosmos/ibc-go/v5/modules/core/02-client" + connection "github.com/cosmos/ibc-go/v5/modules/core/03-connection" + channel "github.com/cosmos/ibc-go/v5/modules/core/04-channel" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) // GetTxCmd returns the transaction commands for this module diff --git a/modules/core/client/query.go b/modules/core/client/query.go index 30377a495c4..65a6a450d79 100644 --- a/modules/core/client/query.go +++ b/modules/core/client/query.go @@ -7,9 +7,9 @@ import ( "github.com/cosmos/cosmos-sdk/codec" abci "github.com/tendermint/tendermint/abci/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) // QueryTendermintProof performs an ABCI query with the given key and returns diff --git a/modules/core/genesis.go b/modules/core/genesis.go index 47797440b39..e7875770dac 100644 --- a/modules/core/genesis.go +++ b/modules/core/genesis.go @@ -3,11 +3,11 @@ package ibc import ( sdk "github.com/cosmos/cosmos-sdk/types" - client "github.com/cosmos/ibc-go/v3/modules/core/02-client" - connection "github.com/cosmos/ibc-go/v3/modules/core/03-connection" - channel "github.com/cosmos/ibc-go/v3/modules/core/04-channel" - "github.com/cosmos/ibc-go/v3/modules/core/keeper" - "github.com/cosmos/ibc-go/v3/modules/core/types" + client "github.com/cosmos/ibc-go/v5/modules/core/02-client" + connection "github.com/cosmos/ibc-go/v5/modules/core/03-connection" + channel "github.com/cosmos/ibc-go/v5/modules/core/04-channel" + "github.com/cosmos/ibc-go/v5/modules/core/keeper" + "github.com/cosmos/ibc-go/v5/modules/core/types" ) // InitGenesis initializes the ibc state from a provided genesis diff --git a/modules/core/genesis_test.go b/modules/core/genesis_test.go index e27f44cf5bd..d5c9b7abbd1 100644 --- a/modules/core/genesis_test.go +++ b/modules/core/genesis_test.go @@ -8,16 +8,16 @@ import ( "github.com/stretchr/testify/suite" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - ibc "github.com/cosmos/ibc-go/v3/modules/core" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - "github.com/cosmos/ibc-go/v3/modules/core/types" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v3/testing" - "github.com/cosmos/ibc-go/v3/testing/simapp" + ibc "github.com/cosmos/ibc-go/v5/modules/core" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/core/types" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" + "github.com/cosmos/ibc-go/v5/testing/simapp" ) const ( diff --git a/modules/core/keeper/grpc_query.go b/modules/core/keeper/grpc_query.go index 2fb171a9c02..9a28343b3a6 100644 --- a/modules/core/keeper/grpc_query.go +++ b/modules/core/keeper/grpc_query.go @@ -3,9 +3,9 @@ package keeper import ( "context" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" ) // ClientState implements the IBC QueryServer interface diff --git a/modules/core/keeper/keeper.go b/modules/core/keeper/keeper.go index 69044e9e4ea..02800080a1c 100644 --- a/modules/core/keeper/keeper.go +++ b/modules/core/keeper/keeper.go @@ -6,14 +6,14 @@ import ( capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" - clientkeeper "github.com/cosmos/ibc-go/v3/modules/core/02-client/keeper" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - connectionkeeper "github.com/cosmos/ibc-go/v3/modules/core/03-connection/keeper" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - channelkeeper "github.com/cosmos/ibc-go/v3/modules/core/04-channel/keeper" - portkeeper "github.com/cosmos/ibc-go/v3/modules/core/05-port/keeper" - porttypes "github.com/cosmos/ibc-go/v3/modules/core/05-port/types" - "github.com/cosmos/ibc-go/v3/modules/core/types" + clientkeeper "github.com/cosmos/ibc-go/v5/modules/core/02-client/keeper" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + connectionkeeper "github.com/cosmos/ibc-go/v5/modules/core/03-connection/keeper" + connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + channelkeeper "github.com/cosmos/ibc-go/v5/modules/core/04-channel/keeper" + portkeeper "github.com/cosmos/ibc-go/v5/modules/core/05-port/keeper" + porttypes "github.com/cosmos/ibc-go/v5/modules/core/05-port/types" + "github.com/cosmos/ibc-go/v5/modules/core/types" ) var _ types.QueryServer = (*Keeper)(nil) diff --git a/modules/core/keeper/migrations.go b/modules/core/keeper/migrations.go index 286ce5b2e97..ca56594b113 100644 --- a/modules/core/keeper/migrations.go +++ b/modules/core/keeper/migrations.go @@ -3,7 +3,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - clientkeeper "github.com/cosmos/ibc-go/v3/modules/core/02-client/keeper" + clientkeeper "github.com/cosmos/ibc-go/v5/modules/core/02-client/keeper" ) // Migrator is a struct for handling in-place store migrations. diff --git a/modules/core/keeper/msg_server.go b/modules/core/keeper/msg_server.go index fd7ccbdff64..ab65b0146b4 100644 --- a/modules/core/keeper/msg_server.go +++ b/modules/core/keeper/msg_server.go @@ -9,11 +9,11 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - porttypes "github.com/cosmos/ibc-go/v3/modules/core/05-port/types" - coretypes "github.com/cosmos/ibc-go/v3/modules/core/types" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + porttypes "github.com/cosmos/ibc-go/v5/modules/core/05-port/types" + coretypes "github.com/cosmos/ibc-go/v5/modules/core/types" ) var _ clienttypes.MsgServer = Keeper{} diff --git a/modules/core/keeper/msg_server_test.go b/modules/core/keeper/msg_server_test.go index f4d0a05282f..a234c751bc4 100644 --- a/modules/core/keeper/msg_server_test.go +++ b/modules/core/keeper/msg_server_test.go @@ -7,15 +7,15 @@ import ( upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" "github.com/stretchr/testify/suite" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - "github.com/cosmos/ibc-go/v3/modules/core/keeper" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v3/testing" - ibcmock "github.com/cosmos/ibc-go/v3/testing/mock" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/core/keeper" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" + ibcmock "github.com/cosmos/ibc-go/v5/testing/mock" ) const height = 10 diff --git a/modules/core/legacy/v100/genesis.go b/modules/core/legacy/v100/genesis.go index a1c85978ba2..697e3a4f856 100644 --- a/modules/core/legacy/v100/genesis.go +++ b/modules/core/legacy/v100/genesis.go @@ -6,11 +6,11 @@ import ( genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" tmtypes "github.com/tendermint/tendermint/types" - clientv100 "github.com/cosmos/ibc-go/v3/modules/core/02-client/legacy/v100" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/types" + clientv100 "github.com/cosmos/ibc-go/v5/modules/core/02-client/legacy/v100" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/types" ) // MigrateGenesis accepts exported v1.0.0 IBC client genesis file and migrates it to: diff --git a/modules/core/legacy/v100/genesis_test.go b/modules/core/legacy/v100/genesis_test.go index b0db2e4e1b3..cf84b4fb391 100644 --- a/modules/core/legacy/v100/genesis_test.go +++ b/modules/core/legacy/v100/genesis_test.go @@ -9,15 +9,15 @@ import ( "github.com/stretchr/testify/suite" tmtypes "github.com/tendermint/tendermint/types" - ibcclient "github.com/cosmos/ibc-go/v3/modules/core/02-client" - clientv100 "github.com/cosmos/ibc-go/v3/modules/core/02-client/legacy/v100" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/legacy/v100" - "github.com/cosmos/ibc-go/v3/modules/core/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" - "github.com/cosmos/ibc-go/v3/testing/simapp" + ibcclient "github.com/cosmos/ibc-go/v5/modules/core/02-client" + clientv100 "github.com/cosmos/ibc-go/v5/modules/core/02-client/legacy/v100" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/legacy/v100" + "github.com/cosmos/ibc-go/v5/modules/core/types" + ibctesting "github.com/cosmos/ibc-go/v5/testing" + "github.com/cosmos/ibc-go/v5/testing/simapp" ) type LegacyTestSuite struct { diff --git a/modules/core/migrations/v5/migrations.go b/modules/core/migrations/v5/migrations.go index c08992cf7d3..dec7eb79d80 100644 --- a/modules/core/migrations/v5/migrations.go +++ b/modules/core/migrations/v5/migrations.go @@ -5,10 +5,10 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - clientkeeper "github.com/cosmos/ibc-go/v3/modules/core/02-client/keeper" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + clientkeeper "github.com/cosmos/ibc-go/v5/modules/core/02-client/keeper" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // Localhost is the client type for a localhost client. It is also used as the clientID diff --git a/modules/core/migrations/v5/migrations_test.go b/modules/core/migrations/v5/migrations_test.go index 0c81e5c973d..2e70ede1cae 100644 --- a/modules/core/migrations/v5/migrations_test.go +++ b/modules/core/migrations/v5/migrations_test.go @@ -6,11 +6,11 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/suite" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - v5 "github.com/cosmos/ibc-go/v3/modules/core/migrations/v5" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + v5 "github.com/cosmos/ibc-go/v5/modules/core/migrations/v5" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) type MigrationsV5TestSuite struct { diff --git a/modules/core/module.go b/modules/core/module.go index cab9fc8ab2e..921fd83b028 100644 --- a/modules/core/module.go +++ b/modules/core/module.go @@ -17,16 +17,16 @@ import ( "github.com/spf13/cobra" abci "github.com/tendermint/tendermint/abci/types" - ibcclient "github.com/cosmos/ibc-go/v3/modules/core/02-client" - clientkeeper "github.com/cosmos/ibc-go/v3/modules/core/02-client/keeper" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/client/cli" - "github.com/cosmos/ibc-go/v3/modules/core/keeper" - "github.com/cosmos/ibc-go/v3/modules/core/simulation" - "github.com/cosmos/ibc-go/v3/modules/core/types" + ibcclient "github.com/cosmos/ibc-go/v5/modules/core/02-client" + clientkeeper "github.com/cosmos/ibc-go/v5/modules/core/02-client/keeper" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/client/cli" + "github.com/cosmos/ibc-go/v5/modules/core/keeper" + "github.com/cosmos/ibc-go/v5/modules/core/simulation" + "github.com/cosmos/ibc-go/v5/modules/core/types" ) var ( diff --git a/modules/core/simulation/decoder.go b/modules/core/simulation/decoder.go index 16869f1b061..27585b26ea4 100644 --- a/modules/core/simulation/decoder.go +++ b/modules/core/simulation/decoder.go @@ -5,11 +5,11 @@ import ( "github.com/cosmos/cosmos-sdk/types/kv" - clientsim "github.com/cosmos/ibc-go/v3/modules/core/02-client/simulation" - connectionsim "github.com/cosmos/ibc-go/v3/modules/core/03-connection/simulation" - channelsim "github.com/cosmos/ibc-go/v3/modules/core/04-channel/simulation" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/keeper" + clientsim "github.com/cosmos/ibc-go/v5/modules/core/02-client/simulation" + connectionsim "github.com/cosmos/ibc-go/v5/modules/core/03-connection/simulation" + channelsim "github.com/cosmos/ibc-go/v5/modules/core/04-channel/simulation" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/keeper" ) // NewDecodeStore returns a decoder function closure that unmarshals the KVPair's diff --git a/modules/core/simulation/decoder_test.go b/modules/core/simulation/decoder_test.go index 621375a024f..43f1d918e8c 100644 --- a/modules/core/simulation/decoder_test.go +++ b/modules/core/simulation/decoder_test.go @@ -7,13 +7,13 @@ import ( "github.com/cosmos/cosmos-sdk/types/kv" "github.com/stretchr/testify/require" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/simulation" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - "github.com/cosmos/ibc-go/v3/testing/simapp" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/simulation" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + "github.com/cosmos/ibc-go/v5/testing/simapp" ) func TestDecodeStore(t *testing.T) { diff --git a/modules/core/simulation/genesis.go b/modules/core/simulation/genesis.go index e6decacb26b..0705bfd22f4 100644 --- a/modules/core/simulation/genesis.go +++ b/modules/core/simulation/genesis.go @@ -9,14 +9,14 @@ import ( "github.com/cosmos/cosmos-sdk/types/module" - clientsims "github.com/cosmos/ibc-go/v3/modules/core/02-client/simulation" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - connectionsims "github.com/cosmos/ibc-go/v3/modules/core/03-connection/simulation" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - channelsims "github.com/cosmos/ibc-go/v3/modules/core/04-channel/simulation" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/types" + clientsims "github.com/cosmos/ibc-go/v5/modules/core/02-client/simulation" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + connectionsims "github.com/cosmos/ibc-go/v5/modules/core/03-connection/simulation" + connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + channelsims "github.com/cosmos/ibc-go/v5/modules/core/04-channel/simulation" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/types" ) // Simulation parameter constants diff --git a/modules/core/simulation/genesis_test.go b/modules/core/simulation/genesis_test.go index 6010f74c20f..a684813136f 100644 --- a/modules/core/simulation/genesis_test.go +++ b/modules/core/simulation/genesis_test.go @@ -11,9 +11,9 @@ import ( simtypes "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/stretchr/testify/require" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/simulation" - "github.com/cosmos/ibc-go/v3/modules/core/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/simulation" + "github.com/cosmos/ibc-go/v5/modules/core/types" ) // TestRandomizedGenState tests the normal scenario of applying RandomizedGenState. diff --git a/modules/core/types/codec.go b/modules/core/types/codec.go index 079fa341d25..30cb5ecd263 100644 --- a/modules/core/types/codec.go +++ b/modules/core/types/codec.go @@ -3,12 +3,12 @@ package types import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - solomachinetypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + solomachinetypes "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ) // RegisterInterfaces registers x/ibc interfaces into protobuf Any. diff --git a/modules/core/types/genesis.go b/modules/core/types/genesis.go index 6b9304d9b8e..6334b559548 100644 --- a/modules/core/types/genesis.go +++ b/modules/core/types/genesis.go @@ -3,9 +3,9 @@ package types import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" ) var _ codectypes.UnpackInterfacesMessage = GenesisState{} diff --git a/modules/core/types/genesis.pb.go b/modules/core/types/genesis.pb.go index 11fe53adab2..a5806af653f 100644 --- a/modules/core/types/genesis.pb.go +++ b/modules/core/types/genesis.pb.go @@ -5,9 +5,9 @@ package types import ( fmt "fmt" - types "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - types1 "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - types2 "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + types "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + types1 "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + types2 "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" io "io" diff --git a/modules/core/types/query.go b/modules/core/types/query.go index ef9d0589448..dda49b9a37a 100644 --- a/modules/core/types/query.go +++ b/modules/core/types/query.go @@ -3,12 +3,12 @@ package types import ( "github.com/gogo/protobuf/grpc" - client "github.com/cosmos/ibc-go/v3/modules/core/02-client" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - connection "github.com/cosmos/ibc-go/v3/modules/core/03-connection" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - channel "github.com/cosmos/ibc-go/v3/modules/core/04-channel" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + client "github.com/cosmos/ibc-go/v5/modules/core/02-client" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + connection "github.com/cosmos/ibc-go/v5/modules/core/03-connection" + connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + channel "github.com/cosmos/ibc-go/v5/modules/core/04-channel" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" ) // QueryServer defines the IBC interfaces that the gRPC query server must implement diff --git a/modules/light-clients/06-solomachine/client_state.go b/modules/light-clients/06-solomachine/client_state.go index 159ea5052f6..81976ef508d 100644 --- a/modules/light-clients/06-solomachine/client_state.go +++ b/modules/light-clients/06-solomachine/client_state.go @@ -9,10 +9,10 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/tx/signing" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) var _ exported.ClientState = (*ClientState)(nil) diff --git a/modules/light-clients/06-solomachine/client_state_test.go b/modules/light-clients/06-solomachine/client_state_test.go index 49fedc102a9..9f5c4ed02ff 100644 --- a/modules/light-clients/06-solomachine/client_state_test.go +++ b/modules/light-clients/06-solomachine/client_state_test.go @@ -3,13 +3,13 @@ package solomachine_test import ( sdk "github.com/cosmos/cosmos-sdk/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - solomachine "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + solomachine "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) const ( diff --git a/modules/light-clients/06-solomachine/codec.go b/modules/light-clients/06-solomachine/codec.go index ed040d7b2c2..2792e19c759 100644 --- a/modules/light-clients/06-solomachine/codec.go +++ b/modules/light-clients/06-solomachine/codec.go @@ -6,7 +6,7 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/tx/signing" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // RegisterInterfaces register the ibc channel submodule interfaces to protobuf diff --git a/modules/light-clients/06-solomachine/consensus_state.go b/modules/light-clients/06-solomachine/consensus_state.go index 35e11fa6064..1f63823663f 100644 --- a/modules/light-clients/06-solomachine/consensus_state.go +++ b/modules/light-clients/06-solomachine/consensus_state.go @@ -6,8 +6,8 @@ import ( cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) var _ exported.ConsensusState = &ConsensusState{} diff --git a/modules/light-clients/06-solomachine/consensus_state_test.go b/modules/light-clients/06-solomachine/consensus_state_test.go index 6b601a95cb6..f868d6c28d7 100644 --- a/modules/light-clients/06-solomachine/consensus_state_test.go +++ b/modules/light-clients/06-solomachine/consensus_state_test.go @@ -1,9 +1,9 @@ package solomachine_test import ( - "github.com/cosmos/ibc-go/v3/modules/core/exported" - solomachine "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + solomachine "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func (suite *SoloMachineTestSuite) TestConsensusState() { diff --git a/modules/light-clients/06-solomachine/header.go b/modules/light-clients/06-solomachine/header.go index f2a63ef4ddb..b5bfe11f28d 100644 --- a/modules/light-clients/06-solomachine/header.go +++ b/modules/light-clients/06-solomachine/header.go @@ -6,8 +6,8 @@ import ( cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) var _ exported.ClientMessage = &Header{} diff --git a/modules/light-clients/06-solomachine/header_test.go b/modules/light-clients/06-solomachine/header_test.go index a2b96ea7d62..1a0ce46ee33 100644 --- a/modules/light-clients/06-solomachine/header_test.go +++ b/modules/light-clients/06-solomachine/header_test.go @@ -1,9 +1,9 @@ package solomachine_test import ( - "github.com/cosmos/ibc-go/v3/modules/core/exported" - solomachine "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + solomachine "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func (suite *SoloMachineTestSuite) TestHeaderValidateBasic() { diff --git a/modules/light-clients/06-solomachine/misbehaviour.go b/modules/light-clients/06-solomachine/misbehaviour.go index 6e58a8c76f8..526fc76e839 100644 --- a/modules/light-clients/06-solomachine/misbehaviour.go +++ b/modules/light-clients/06-solomachine/misbehaviour.go @@ -5,9 +5,9 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) var _ exported.ClientMessage = &Misbehaviour{} diff --git a/modules/light-clients/06-solomachine/misbehaviour_handle.go b/modules/light-clients/06-solomachine/misbehaviour_handle.go index 80eb3f04097..dd86d49b6d5 100644 --- a/modules/light-clients/06-solomachine/misbehaviour_handle.go +++ b/modules/light-clients/06-solomachine/misbehaviour_handle.go @@ -3,7 +3,7 @@ package solomachine import ( "github.com/cosmos/cosmos-sdk/codec" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" ) // verifySignatureAndData verifies that the currently registered public key has signed diff --git a/modules/light-clients/06-solomachine/misbehaviour_test.go b/modules/light-clients/06-solomachine/misbehaviour_test.go index 552fc9c7e57..2e6044b67d1 100644 --- a/modules/light-clients/06-solomachine/misbehaviour_test.go +++ b/modules/light-clients/06-solomachine/misbehaviour_test.go @@ -1,9 +1,9 @@ package solomachine_test import ( - "github.com/cosmos/ibc-go/v3/modules/core/exported" - solomachine "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + solomachine "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func (suite *SoloMachineTestSuite) TestMisbehaviour() { diff --git a/modules/light-clients/06-solomachine/proof_test.go b/modules/light-clients/06-solomachine/proof_test.go index 0ea96ee4116..2fa853d46a5 100644 --- a/modules/light-clients/06-solomachine/proof_test.go +++ b/modules/light-clients/06-solomachine/proof_test.go @@ -4,7 +4,7 @@ import ( cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/types/tx/signing" - solomachine "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" + solomachine "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" ) func (suite *SoloMachineTestSuite) TestVerifySignature() { diff --git a/modules/light-clients/06-solomachine/proposal_handle.go b/modules/light-clients/06-solomachine/proposal_handle.go index 4a32e2af2e1..16b7f715f62 100644 --- a/modules/light-clients/06-solomachine/proposal_handle.go +++ b/modules/light-clients/06-solomachine/proposal_handle.go @@ -7,8 +7,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // CheckSubstituteAndUpdateState verifies that the subject is allowed to be updated by diff --git a/modules/light-clients/06-solomachine/proposal_handle_test.go b/modules/light-clients/06-solomachine/proposal_handle_test.go index 26f04b93326..cc099327d25 100644 --- a/modules/light-clients/06-solomachine/proposal_handle_test.go +++ b/modules/light-clients/06-solomachine/proposal_handle_test.go @@ -1,12 +1,12 @@ package solomachine_test import ( - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - solomachine "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + solomachine "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func (suite *SoloMachineTestSuite) TestCheckSubstituteAndUpdateState() { diff --git a/modules/light-clients/06-solomachine/solomachine_test.go b/modules/light-clients/06-solomachine/solomachine_test.go index c913d776fc2..c73a8ecebc4 100644 --- a/modules/light-clients/06-solomachine/solomachine_test.go +++ b/modules/light-clients/06-solomachine/solomachine_test.go @@ -11,10 +11,10 @@ import ( "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - solomachine "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + solomachine "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) type SoloMachineTestSuite struct { diff --git a/modules/light-clients/06-solomachine/update.go b/modules/light-clients/06-solomachine/update.go index d514603f784..5eb628876c1 100644 --- a/modules/light-clients/06-solomachine/update.go +++ b/modules/light-clients/06-solomachine/update.go @@ -7,9 +7,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // VerifyClientMessage introspects the provided ClientMessage and checks its validity diff --git a/modules/light-clients/06-solomachine/update_test.go b/modules/light-clients/06-solomachine/update_test.go index e205886bc25..5664eb264a2 100644 --- a/modules/light-clients/06-solomachine/update_test.go +++ b/modules/light-clients/06-solomachine/update_test.go @@ -4,12 +4,12 @@ import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - solomachine "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + solomachine "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { diff --git a/modules/light-clients/07-tendermint/client_state.go b/modules/light-clients/07-tendermint/client_state.go index 27a78768571..e5999bb17d4 100644 --- a/modules/light-clients/07-tendermint/client_state.go +++ b/modules/light-clients/07-tendermint/client_state.go @@ -11,9 +11,9 @@ import ( "github.com/tendermint/tendermint/light" tmtypes "github.com/tendermint/tendermint/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) var _ exported.ClientState = (*ClientState)(nil) diff --git a/modules/light-clients/07-tendermint/client_state_test.go b/modules/light-clients/07-tendermint/client_state_test.go index f69aecd969d..6ce557ec159 100644 --- a/modules/light-clients/07-tendermint/client_state_test.go +++ b/modules/light-clients/07-tendermint/client_state_test.go @@ -6,15 +6,15 @@ import ( ics23 "github.com/confio/ics23/go" sdk "github.com/cosmos/cosmos-sdk/types" - transfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - tendermint "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v3/testing" - ibcmock "github.com/cosmos/ibc-go/v3/testing/mock" + transfertypes "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + tendermint "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" + ibcmock "github.com/cosmos/ibc-go/v5/testing/mock" ) const ( diff --git a/modules/light-clients/07-tendermint/codec.go b/modules/light-clients/07-tendermint/codec.go index 931f66ca1b4..5e98991f24e 100644 --- a/modules/light-clients/07-tendermint/codec.go +++ b/modules/light-clients/07-tendermint/codec.go @@ -3,7 +3,7 @@ package tendermint import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // RegisterInterfaces registers the tendermint concrete client-related diff --git a/modules/light-clients/07-tendermint/consensus_state.go b/modules/light-clients/07-tendermint/consensus_state.go index 10a551f36b1..ce0e3006afe 100644 --- a/modules/light-clients/07-tendermint/consensus_state.go +++ b/modules/light-clients/07-tendermint/consensus_state.go @@ -7,9 +7,9 @@ import ( tmbytes "github.com/tendermint/tendermint/libs/bytes" tmtypes "github.com/tendermint/tendermint/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // SentinelRoot is used as a stand-in root value for the consensus state set at the upgrade height diff --git a/modules/light-clients/07-tendermint/consensus_state_test.go b/modules/light-clients/07-tendermint/consensus_state_test.go index b2b73ca4b4a..01853d5e11b 100644 --- a/modules/light-clients/07-tendermint/consensus_state_test.go +++ b/modules/light-clients/07-tendermint/consensus_state_test.go @@ -3,9 +3,9 @@ package tendermint_test import ( "time" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - tendermint "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + tendermint "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ) func (suite *TendermintTestSuite) TestConsensusStateValidateBasic() { diff --git a/modules/light-clients/07-tendermint/genesis.go b/modules/light-clients/07-tendermint/genesis.go index ea47828a075..ab9159a00a8 100644 --- a/modules/light-clients/07-tendermint/genesis.go +++ b/modules/light-clients/07-tendermint/genesis.go @@ -3,8 +3,8 @@ package tendermint import ( sdk "github.com/cosmos/cosmos-sdk/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // ExportMetadata exports all the consensus metadata in the client store so they can be included in clients genesis diff --git a/modules/light-clients/07-tendermint/genesis_test.go b/modules/light-clients/07-tendermint/genesis_test.go index 9feb66372cf..b7b7d85820a 100644 --- a/modules/light-clients/07-tendermint/genesis_test.go +++ b/modules/light-clients/07-tendermint/genesis_test.go @@ -3,9 +3,9 @@ package tendermint_test import ( sdk "github.com/cosmos/cosmos-sdk/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - tendermint "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + tendermint "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) // expected export ordering: diff --git a/modules/light-clients/07-tendermint/header.go b/modules/light-clients/07-tendermint/header.go index ee57b2c085f..9fc00198245 100644 --- a/modules/light-clients/07-tendermint/header.go +++ b/modules/light-clients/07-tendermint/header.go @@ -7,9 +7,9 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" tmtypes "github.com/tendermint/tendermint/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) var _ exported.ClientMessage = &Header{} diff --git a/modules/light-clients/07-tendermint/header_test.go b/modules/light-clients/07-tendermint/header_test.go index b1df2128a1f..ba5cb8956e3 100644 --- a/modules/light-clients/07-tendermint/header_test.go +++ b/modules/light-clients/07-tendermint/header_test.go @@ -5,9 +5,9 @@ import ( tmprotocrypto "github.com/tendermint/tendermint/proto/tendermint/crypto" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - tendermint "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + tendermint "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ) func (suite *TendermintTestSuite) TestGetHeight() { diff --git a/modules/light-clients/07-tendermint/misbehaviour.go b/modules/light-clients/07-tendermint/misbehaviour.go index 41eb6a11efd..6242d39feec 100644 --- a/modules/light-clients/07-tendermint/misbehaviour.go +++ b/modules/light-clients/07-tendermint/misbehaviour.go @@ -7,9 +7,9 @@ import ( tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmtypes "github.com/tendermint/tendermint/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) var _ exported.ClientMessage = &Misbehaviour{} diff --git a/modules/light-clients/07-tendermint/misbehaviour_handle.go b/modules/light-clients/07-tendermint/misbehaviour_handle.go index 08ddfcffecb..c3b629318f7 100644 --- a/modules/light-clients/07-tendermint/misbehaviour_handle.go +++ b/modules/light-clients/07-tendermint/misbehaviour_handle.go @@ -9,7 +9,7 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" tmtypes "github.com/tendermint/tendermint/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" ) // verifyMisbehaviour determines whether or not two conflicting diff --git a/modules/light-clients/07-tendermint/misbehaviour_handle_test.go b/modules/light-clients/07-tendermint/misbehaviour_handle_test.go index a8bfa024862..52cb17efa0b 100644 --- a/modules/light-clients/07-tendermint/misbehaviour_handle_test.go +++ b/modules/light-clients/07-tendermint/misbehaviour_handle_test.go @@ -7,12 +7,12 @@ import ( tmtypes "github.com/tendermint/tendermint/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - smtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" - tendermint "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v3/testing" - ibctestingmock "github.com/cosmos/ibc-go/v3/testing/mock" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + smtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" + tendermint "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" + ibctestingmock "github.com/cosmos/ibc-go/v5/testing/mock" ) func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { diff --git a/modules/light-clients/07-tendermint/misbehaviour_test.go b/modules/light-clients/07-tendermint/misbehaviour_test.go index 41aa89cbdec..8a433a4d0e0 100644 --- a/modules/light-clients/07-tendermint/misbehaviour_test.go +++ b/modules/light-clients/07-tendermint/misbehaviour_test.go @@ -7,11 +7,11 @@ import ( tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmtypes "github.com/tendermint/tendermint/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - tendermint "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v3/testing" - ibctestingmock "github.com/cosmos/ibc-go/v3/testing/mock" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + tendermint "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" + ibctestingmock "github.com/cosmos/ibc-go/v5/testing/mock" ) func (suite *TendermintTestSuite) TestMisbehaviour() { diff --git a/modules/light-clients/07-tendermint/proposal_handle.go b/modules/light-clients/07-tendermint/proposal_handle.go index 62457e67876..cf4b8f1178d 100644 --- a/modules/light-clients/07-tendermint/proposal_handle.go +++ b/modules/light-clients/07-tendermint/proposal_handle.go @@ -7,8 +7,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // CheckSubstituteAndUpdateState will try to update the client with the state of the diff --git a/modules/light-clients/07-tendermint/proposal_handle_test.go b/modules/light-clients/07-tendermint/proposal_handle_test.go index a14e80bae7b..ea5193b605f 100644 --- a/modules/light-clients/07-tendermint/proposal_handle_test.go +++ b/modules/light-clients/07-tendermint/proposal_handle_test.go @@ -3,11 +3,11 @@ package tendermint_test import ( "time" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - tendermint "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + tendermint "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) var ( diff --git a/modules/light-clients/07-tendermint/store.go b/modules/light-clients/07-tendermint/store.go index 2be5e94c97e..479b734cb6d 100644 --- a/modules/light-clients/07-tendermint/store.go +++ b/modules/light-clients/07-tendermint/store.go @@ -9,9 +9,9 @@ import ( "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) /* diff --git a/modules/light-clients/07-tendermint/store_test.go b/modules/light-clients/07-tendermint/store_test.go index f5d57ded2cd..851a39c1945 100644 --- a/modules/light-clients/07-tendermint/store_test.go +++ b/modules/light-clients/07-tendermint/store_test.go @@ -4,13 +4,13 @@ import ( "math" "time" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - solomachine "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" - tendermint "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + solomachine "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" + tendermint "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func (suite *TendermintTestSuite) TestGetConsensusState() { diff --git a/modules/light-clients/07-tendermint/tendermint.pb.go b/modules/light-clients/07-tendermint/tendermint.pb.go index 7bac5c42672..737dd0aea4c 100644 --- a/modules/light-clients/07-tendermint/tendermint.pb.go +++ b/modules/light-clients/07-tendermint/tendermint.pb.go @@ -6,8 +6,8 @@ package tendermint import ( fmt "fmt" _go "github.com/confio/ics23/go" - types "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - types1 "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" + types "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + types1 "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" diff --git a/modules/light-clients/07-tendermint/tendermint_test.go b/modules/light-clients/07-tendermint/tendermint_test.go index 843e1f71c57..a3610f8c86a 100644 --- a/modules/light-clients/07-tendermint/tendermint_test.go +++ b/modules/light-clients/07-tendermint/tendermint_test.go @@ -11,11 +11,11 @@ import ( tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmtypes "github.com/tendermint/tendermint/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - tendermint "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v3/testing" - ibctestingmock "github.com/cosmos/ibc-go/v3/testing/mock" - "github.com/cosmos/ibc-go/v3/testing/simapp" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + tendermint "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" + ibctestingmock "github.com/cosmos/ibc-go/v5/testing/mock" + "github.com/cosmos/ibc-go/v5/testing/simapp" ) const ( diff --git a/modules/light-clients/07-tendermint/update.go b/modules/light-clients/07-tendermint/update.go index f1606748b07..30b1c1fa20c 100644 --- a/modules/light-clients/07-tendermint/update.go +++ b/modules/light-clients/07-tendermint/update.go @@ -11,10 +11,10 @@ import ( "github.com/tendermint/tendermint/light" tmtypes "github.com/tendermint/tendermint/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // VerifyClientMessage checks if the clientMessage is of type Header or Misbehaviour and verifies the message diff --git a/modules/light-clients/07-tendermint/update_test.go b/modules/light-clients/07-tendermint/update_test.go index dec1e384a86..d79698a5ae5 100644 --- a/modules/light-clients/07-tendermint/update_test.go +++ b/modules/light-clients/07-tendermint/update_test.go @@ -4,13 +4,13 @@ import ( "time" sdk "github.com/cosmos/cosmos-sdk/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - tendermint "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v3/testing" - ibctestingmock "github.com/cosmos/ibc-go/v3/testing/mock" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + tendermint "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" + ibctestingmock "github.com/cosmos/ibc-go/v5/testing/mock" tmtypes "github.com/tendermint/tendermint/types" ) diff --git a/modules/light-clients/07-tendermint/upgrade.go b/modules/light-clients/07-tendermint/upgrade.go index 9d3e25ae5fa..f70996eaa08 100644 --- a/modules/light-clients/07-tendermint/upgrade.go +++ b/modules/light-clients/07-tendermint/upgrade.go @@ -8,9 +8,9 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // VerifyUpgradeAndUpdateState checks if the upgraded client has been committed by the current client diff --git a/modules/light-clients/07-tendermint/upgrade_test.go b/modules/light-clients/07-tendermint/upgrade_test.go index 601ab53d046..0506e4b6478 100644 --- a/modules/light-clients/07-tendermint/upgrade_test.go +++ b/modules/light-clients/07-tendermint/upgrade_test.go @@ -3,11 +3,11 @@ package tendermint_test import ( upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - tendermint "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + tendermint "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func (suite *TendermintTestSuite) TestVerifyUpgrade() { diff --git a/proto/ibc/applications/fee/v1/ack.proto b/proto/ibc/applications/fee/v1/ack.proto index 728c7536c6b..8b435adab0c 100644 --- a/proto/ibc/applications/fee/v1/ack.proto +++ b/proto/ibc/applications/fee/v1/ack.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.fee.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types"; import "gogoproto/gogo.proto"; diff --git a/proto/ibc/applications/fee/v1/fee.proto b/proto/ibc/applications/fee/v1/fee.proto index e7a1fa438df..e39d1e7b716 100644 --- a/proto/ibc/applications/fee/v1/fee.proto +++ b/proto/ibc/applications/fee/v1/fee.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.fee.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types"; import "cosmos/base/v1beta1/coin.proto"; import "gogoproto/gogo.proto"; diff --git a/proto/ibc/applications/fee/v1/genesis.proto b/proto/ibc/applications/fee/v1/genesis.proto index cae132239d6..3a988616eea 100644 --- a/proto/ibc/applications/fee/v1/genesis.proto +++ b/proto/ibc/applications/fee/v1/genesis.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.fee.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types"; import "gogoproto/gogo.proto"; import "ibc/applications/fee/v1/fee.proto"; diff --git a/proto/ibc/applications/fee/v1/metadata.proto b/proto/ibc/applications/fee/v1/metadata.proto index 0afb3e09b2e..ba47374fd4b 100644 --- a/proto/ibc/applications/fee/v1/metadata.proto +++ b/proto/ibc/applications/fee/v1/metadata.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.fee.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types"; import "gogoproto/gogo.proto"; diff --git a/proto/ibc/applications/fee/v1/query.proto b/proto/ibc/applications/fee/v1/query.proto index acab12f5161..7a789cdb7da 100644 --- a/proto/ibc/applications/fee/v1/query.proto +++ b/proto/ibc/applications/fee/v1/query.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.fee.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types"; import "gogoproto/gogo.proto"; import "google/api/annotations.proto"; diff --git a/proto/ibc/applications/fee/v1/tx.proto b/proto/ibc/applications/fee/v1/tx.proto index 7a0044cd363..e4877e7a8a6 100644 --- a/proto/ibc/applications/fee/v1/tx.proto +++ b/proto/ibc/applications/fee/v1/tx.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.fee.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types"; import "gogoproto/gogo.proto"; import "ibc/applications/fee/v1/fee.proto"; diff --git a/proto/ibc/applications/interchain_accounts/controller/v1/controller.proto b/proto/ibc/applications/interchain_accounts/controller/v1/controller.proto index 291f3e4fd90..99f007e0873 100644 --- a/proto/ibc/applications/interchain_accounts/controller/v1/controller.proto +++ b/proto/ibc/applications/interchain_accounts/controller/v1/controller.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.interchain_accounts.controller.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/types"; import "gogoproto/gogo.proto"; diff --git a/proto/ibc/applications/interchain_accounts/controller/v1/query.proto b/proto/ibc/applications/interchain_accounts/controller/v1/query.proto index d3960e9d14c..5b0d0b407c5 100644 --- a/proto/ibc/applications/interchain_accounts/controller/v1/query.proto +++ b/proto/ibc/applications/interchain_accounts/controller/v1/query.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.interchain_accounts.controller.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/types"; import "ibc/applications/interchain_accounts/controller/v1/controller.proto"; import "google/api/annotations.proto"; diff --git a/proto/ibc/applications/interchain_accounts/host/v1/host.proto b/proto/ibc/applications/interchain_accounts/host/v1/host.proto index a9d951cef82..3e0316680e6 100644 --- a/proto/ibc/applications/interchain_accounts/host/v1/host.proto +++ b/proto/ibc/applications/interchain_accounts/host/v1/host.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.interchain_accounts.host.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/host/types"; import "gogoproto/gogo.proto"; diff --git a/proto/ibc/applications/interchain_accounts/host/v1/query.proto b/proto/ibc/applications/interchain_accounts/host/v1/query.proto index 5512d7b4fa5..8e3c22cf63a 100644 --- a/proto/ibc/applications/interchain_accounts/host/v1/query.proto +++ b/proto/ibc/applications/interchain_accounts/host/v1/query.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.interchain_accounts.host.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/host/types"; import "google/api/annotations.proto"; import "ibc/applications/interchain_accounts/host/v1/host.proto"; diff --git a/proto/ibc/applications/interchain_accounts/v1/account.proto b/proto/ibc/applications/interchain_accounts/v1/account.proto index 75d2fbd89ed..ae98b9cfac9 100644 --- a/proto/ibc/applications/interchain_accounts/v1/account.proto +++ b/proto/ibc/applications/interchain_accounts/v1/account.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.interchain_accounts.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types"; import "cosmos_proto/cosmos.proto"; import "gogoproto/gogo.proto"; diff --git a/proto/ibc/applications/interchain_accounts/v1/genesis.proto b/proto/ibc/applications/interchain_accounts/v1/genesis.proto index 3902f890708..0687003195f 100644 --- a/proto/ibc/applications/interchain_accounts/v1/genesis.proto +++ b/proto/ibc/applications/interchain_accounts/v1/genesis.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.interchain_accounts.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types"; import "gogoproto/gogo.proto"; import "ibc/applications/interchain_accounts/controller/v1/controller.proto"; diff --git a/proto/ibc/applications/interchain_accounts/v1/metadata.proto b/proto/ibc/applications/interchain_accounts/v1/metadata.proto index 3eab1d04a69..fc8722289a6 100644 --- a/proto/ibc/applications/interchain_accounts/v1/metadata.proto +++ b/proto/ibc/applications/interchain_accounts/v1/metadata.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.interchain_accounts.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types"; import "gogoproto/gogo.proto"; diff --git a/proto/ibc/applications/interchain_accounts/v1/packet.proto b/proto/ibc/applications/interchain_accounts/v1/packet.proto index 51ff4279aeb..f66130f24b1 100644 --- a/proto/ibc/applications/interchain_accounts/v1/packet.proto +++ b/proto/ibc/applications/interchain_accounts/v1/packet.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.interchain_accounts.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types"; import "google/protobuf/any.proto"; import "gogoproto/gogo.proto"; diff --git a/proto/ibc/applications/transfer/v1/genesis.proto b/proto/ibc/applications/transfer/v1/genesis.proto index 0b5c0e0d122..e1b653bb782 100644 --- a/proto/ibc/applications/transfer/v1/genesis.proto +++ b/proto/ibc/applications/transfer/v1/genesis.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.transfer.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types"; import "ibc/applications/transfer/v1/transfer.proto"; import "gogoproto/gogo.proto"; diff --git a/proto/ibc/applications/transfer/v1/query.proto b/proto/ibc/applications/transfer/v1/query.proto index 2ed28049fd7..e37c215dca0 100644 --- a/proto/ibc/applications/transfer/v1/query.proto +++ b/proto/ibc/applications/transfer/v1/query.proto @@ -7,7 +7,7 @@ import "cosmos/base/query/v1beta1/pagination.proto"; import "ibc/applications/transfer/v1/transfer.proto"; import "google/api/annotations.proto"; -option go_package = "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types"; // Query provides defines the gRPC querier service. service Query { diff --git a/proto/ibc/applications/transfer/v1/transfer.proto b/proto/ibc/applications/transfer/v1/transfer.proto index 7a99485c589..4c51c2b9e1c 100644 --- a/proto/ibc/applications/transfer/v1/transfer.proto +++ b/proto/ibc/applications/transfer/v1/transfer.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.transfer.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types"; import "gogoproto/gogo.proto"; diff --git a/proto/ibc/applications/transfer/v1/tx.proto b/proto/ibc/applications/transfer/v1/tx.proto index 8f1392b0cf3..3747f9e5fa1 100644 --- a/proto/ibc/applications/transfer/v1/tx.proto +++ b/proto/ibc/applications/transfer/v1/tx.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.transfer.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types"; import "gogoproto/gogo.proto"; import "cosmos/base/v1beta1/coin.proto"; diff --git a/proto/ibc/applications/transfer/v2/packet.proto b/proto/ibc/applications/transfer/v2/packet.proto index 850320df340..3860046525c 100644 --- a/proto/ibc/applications/transfer/v2/packet.proto +++ b/proto/ibc/applications/transfer/v2/packet.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.applications.transfer.v2; -option go_package = "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types"; // FungibleTokenPacketData defines a struct for the packet payload // See FungibleTokenPacketData spec: diff --git a/proto/ibc/core/channel/v1/channel.proto b/proto/ibc/core/channel/v1/channel.proto index 177f2c85fd1..27547d6291e 100644 --- a/proto/ibc/core/channel/v1/channel.proto +++ b/proto/ibc/core/channel/v1/channel.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.core.channel.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types"; import "gogoproto/gogo.proto"; import "ibc/core/client/v1/client.proto"; diff --git a/proto/ibc/core/channel/v1/genesis.proto b/proto/ibc/core/channel/v1/genesis.proto index d95c891b6c8..cf106a42504 100644 --- a/proto/ibc/core/channel/v1/genesis.proto +++ b/proto/ibc/core/channel/v1/genesis.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.core.channel.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types"; import "gogoproto/gogo.proto"; import "ibc/core/channel/v1/channel.proto"; diff --git a/proto/ibc/core/channel/v1/query.proto b/proto/ibc/core/channel/v1/query.proto index ceb13d00091..2165df8c7ea 100644 --- a/proto/ibc/core/channel/v1/query.proto +++ b/proto/ibc/core/channel/v1/query.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.core.channel.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types"; import "ibc/core/client/v1/client.proto"; import "cosmos/base/query/v1beta1/pagination.proto"; diff --git a/proto/ibc/core/channel/v1/tx.proto b/proto/ibc/core/channel/v1/tx.proto index d34b00e9124..e79cacee74e 100644 --- a/proto/ibc/core/channel/v1/tx.proto +++ b/proto/ibc/core/channel/v1/tx.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.core.channel.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types"; import "gogoproto/gogo.proto"; import "ibc/core/client/v1/client.proto"; diff --git a/proto/ibc/core/client/v1/client.proto b/proto/ibc/core/client/v1/client.proto index 657d99ed204..d9209f9acc6 100644 --- a/proto/ibc/core/client/v1/client.proto +++ b/proto/ibc/core/client/v1/client.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.core.client.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/core/02-client/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/core/02-client/types"; import "gogoproto/gogo.proto"; import "google/protobuf/any.proto"; diff --git a/proto/ibc/core/client/v1/genesis.proto b/proto/ibc/core/client/v1/genesis.proto index 0ca29d224b0..da92918df65 100644 --- a/proto/ibc/core/client/v1/genesis.proto +++ b/proto/ibc/core/client/v1/genesis.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.core.client.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/core/02-client/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/core/02-client/types"; import "ibc/core/client/v1/client.proto"; import "gogoproto/gogo.proto"; diff --git a/proto/ibc/core/client/v1/query.proto b/proto/ibc/core/client/v1/query.proto index 91a906fe54b..51eae4841d4 100644 --- a/proto/ibc/core/client/v1/query.proto +++ b/proto/ibc/core/client/v1/query.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.core.client.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/core/02-client/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/core/02-client/types"; import "cosmos/base/query/v1beta1/pagination.proto"; import "ibc/core/client/v1/client.proto"; diff --git a/proto/ibc/core/client/v1/tx.proto b/proto/ibc/core/client/v1/tx.proto index 386bf9a51ee..27f81917e4c 100644 --- a/proto/ibc/core/client/v1/tx.proto +++ b/proto/ibc/core/client/v1/tx.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.core.client.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/core/02-client/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/core/02-client/types"; import "gogoproto/gogo.proto"; import "google/protobuf/any.proto"; diff --git a/proto/ibc/core/commitment/v1/commitment.proto b/proto/ibc/core/commitment/v1/commitment.proto index b0afed2273b..5667f20de24 100644 --- a/proto/ibc/core/commitment/v1/commitment.proto +++ b/proto/ibc/core/commitment/v1/commitment.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.core.commitment.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types"; import "gogoproto/gogo.proto"; import "proofs.proto"; diff --git a/proto/ibc/core/connection/v1/connection.proto b/proto/ibc/core/connection/v1/connection.proto index 7fd2a6909ad..9defdcb8b86 100644 --- a/proto/ibc/core/connection/v1/connection.proto +++ b/proto/ibc/core/connection/v1/connection.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.core.connection.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types"; import "gogoproto/gogo.proto"; import "ibc/core/commitment/v1/commitment.proto"; diff --git a/proto/ibc/core/connection/v1/genesis.proto b/proto/ibc/core/connection/v1/genesis.proto index 1a53422c94a..b0860737a75 100644 --- a/proto/ibc/core/connection/v1/genesis.proto +++ b/proto/ibc/core/connection/v1/genesis.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.core.connection.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types"; import "gogoproto/gogo.proto"; import "ibc/core/connection/v1/connection.proto"; diff --git a/proto/ibc/core/connection/v1/query.proto b/proto/ibc/core/connection/v1/query.proto index f28578f5fc5..30bae901aef 100644 --- a/proto/ibc/core/connection/v1/query.proto +++ b/proto/ibc/core/connection/v1/query.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.core.connection.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types"; import "gogoproto/gogo.proto"; import "cosmos/base/query/v1beta1/pagination.proto"; diff --git a/proto/ibc/core/connection/v1/tx.proto b/proto/ibc/core/connection/v1/tx.proto index e7e09c84cff..fc3adede6a9 100644 --- a/proto/ibc/core/connection/v1/tx.proto +++ b/proto/ibc/core/connection/v1/tx.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.core.connection.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types"; import "gogoproto/gogo.proto"; import "google/protobuf/any.proto"; diff --git a/proto/ibc/core/types/v1/genesis.proto b/proto/ibc/core/types/v1/genesis.proto index fbddbf3035f..09deaf6184b 100644 --- a/proto/ibc/core/types/v1/genesis.proto +++ b/proto/ibc/core/types/v1/genesis.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.core.types.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/core/types"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/core/types"; import "gogoproto/gogo.proto"; import "ibc/core/client/v1/genesis.proto"; diff --git a/proto/ibc/lightclients/solomachine/v1/solomachine.proto b/proto/ibc/lightclients/solomachine/v1/solomachine.proto index c279f5e728e..8be12dab8b7 100644 --- a/proto/ibc/lightclients/solomachine/v1/solomachine.proto +++ b/proto/ibc/lightclients/solomachine/v1/solomachine.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.lightclients.solomachine.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/core/02-client/legacy/v100"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/core/02-client/legacy/v100"; import "ibc/core/connection/v1/connection.proto"; import "ibc/core/channel/v1/channel.proto"; diff --git a/proto/ibc/lightclients/solomachine/v2/solomachine.proto b/proto/ibc/lightclients/solomachine/v2/solomachine.proto index 5b828483bfc..24b75dbf2d0 100644 --- a/proto/ibc/lightclients/solomachine/v2/solomachine.proto +++ b/proto/ibc/lightclients/solomachine/v2/solomachine.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.lightclients.solomachine.v2; -option go_package = "github.com/cosmos/ibc-go/v3/modules/core/02-client/migrations/v6"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/core/02-client/migrations/v6"; import "ibc/core/connection/v1/connection.proto"; import "ibc/core/channel/v1/channel.proto"; diff --git a/proto/ibc/lightclients/solomachine/v3/solomachine.proto b/proto/ibc/lightclients/solomachine/v3/solomachine.proto index 63ce566782d..5475a16bd93 100644 --- a/proto/ibc/lightclients/solomachine/v3/solomachine.proto +++ b/proto/ibc/lightclients/solomachine/v3/solomachine.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.lightclients.solomachine.v3; -option go_package = "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine;solomachine"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine;solomachine"; import "gogoproto/gogo.proto"; import "google/protobuf/any.proto"; diff --git a/proto/ibc/lightclients/tendermint/v1/tendermint.proto b/proto/ibc/lightclients/tendermint/v1/tendermint.proto index 89ab3595684..27c5c909460 100644 --- a/proto/ibc/lightclients/tendermint/v1/tendermint.proto +++ b/proto/ibc/lightclients/tendermint/v1/tendermint.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ibc.lightclients.tendermint.v1; -option go_package = "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint;tendermint"; +option go_package = "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint;tendermint"; import "tendermint/types/validator.proto"; import "tendermint/types/types.proto"; diff --git a/testing/README.md b/testing/README.md index ad02a80e640..a488ffa42f1 100644 --- a/testing/README.md +++ b/testing/README.md @@ -255,8 +255,8 @@ import ( "github.com/tendermint/tendermint/libs/log" dbm "github.com/tendermint/tm-db" - "github.com/cosmos/ibc-go/v3/modules/apps/transfer/simapp" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + "github.com/cosmos/ibc-go/v5/modules/apps/transfer/simapp" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func SetupTransferTestingApp() (ibctesting.TestingApp, map[string]json.RawMessage) { diff --git a/testing/app.go b/testing/app.go index 1cde8614b6d..a07ce9c564f 100644 --- a/testing/app.go +++ b/testing/app.go @@ -23,8 +23,8 @@ import ( tmtypes "github.com/tendermint/tendermint/types" dbm "github.com/tendermint/tm-db" - "github.com/cosmos/ibc-go/v3/modules/core/keeper" - "github.com/cosmos/ibc-go/v3/testing/simapp" + "github.com/cosmos/ibc-go/v5/modules/core/keeper" + "github.com/cosmos/ibc-go/v5/testing/simapp" ) var DefaultTestingAppInit func() (TestingApp, map[string]json.RawMessage) = SetupTestingApp diff --git a/testing/chain.go b/testing/chain.go index 9b6dbf40590..7d6f62e2e54 100644 --- a/testing/chain.go +++ b/testing/chain.go @@ -25,14 +25,14 @@ import ( tmtypes "github.com/tendermint/tendermint/types" tmversion "github.com/tendermint/tendermint/version" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - "github.com/cosmos/ibc-go/v3/modules/core/types" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - "github.com/cosmos/ibc-go/v3/testing/mock" - "github.com/cosmos/ibc-go/v3/testing/simapp" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + "github.com/cosmos/ibc-go/v5/modules/core/types" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + "github.com/cosmos/ibc-go/v5/testing/mock" + "github.com/cosmos/ibc-go/v5/testing/simapp" ) var MaxAccounts = 10 diff --git a/testing/chain_test.go b/testing/chain_test.go index 64ddc6c751e..65be25d2326 100644 --- a/testing/chain_test.go +++ b/testing/chain_test.go @@ -7,7 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/staking/types" - ibctesting "github.com/cosmos/ibc-go/v3/testing" + ibctesting "github.com/cosmos/ibc-go/v5/testing" ) func TestChangeValSet(t *testing.T) { diff --git a/testing/config.go b/testing/config.go index 673e374b5ba..15c3cc6fde1 100644 --- a/testing/config.go +++ b/testing/config.go @@ -3,11 +3,11 @@ package ibctesting import ( "time" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - "github.com/cosmos/ibc-go/v3/testing/mock" + connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + "github.com/cosmos/ibc-go/v5/testing/mock" ) type ClientConfig interface { diff --git a/testing/endpoint.go b/testing/endpoint.go index d332ae97a6e..434df6d2cf2 100644 --- a/testing/endpoint.go +++ b/testing/endpoint.go @@ -7,13 +7,13 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ) // Endpoint is a which represents a channel endpoint and its associated diff --git a/testing/events.go b/testing/events.go index 7828b42619f..4f45d1e7118 100644 --- a/testing/events.go +++ b/testing/events.go @@ -6,9 +6,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" ) // ParseClientIDFromEvents parses events emitted from a MsgCreateClient and returns the diff --git a/testing/mock/ibc_app.go b/testing/mock/ibc_app.go index 77eb17b8c6f..cb768ab84cb 100644 --- a/testing/mock/ibc_app.go +++ b/testing/mock/ibc_app.go @@ -5,8 +5,8 @@ import ( capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // MockIBCApp contains IBC application module callbacks as defined in 05-port. diff --git a/testing/mock/ibc_module.go b/testing/mock/ibc_module.go index e58f6ae7156..fe4a69a1321 100644 --- a/testing/mock/ibc_module.go +++ b/testing/mock/ibc_module.go @@ -8,9 +8,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ) // IBCModule implements the ICS26 callbacks for testing/mock. diff --git a/testing/mock/ibc_module_test.go b/testing/mock/ibc_module_test.go index d3efe9f142c..8188c9d1f68 100644 --- a/testing/mock/ibc_module_test.go +++ b/testing/mock/ibc_module_test.go @@ -5,9 +5,9 @@ import ( "github.com/stretchr/testify/require" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - "github.com/cosmos/ibc-go/v3/testing/mock" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v5/testing/mock" ) func TestCreateCapabilityName(t *testing.T) { diff --git a/testing/mock/mock.go b/testing/mock/mock.go index b621a05e9f7..43b10d85166 100644 --- a/testing/mock/mock.go +++ b/testing/mock/mock.go @@ -14,9 +14,9 @@ import ( "github.com/spf13/cobra" abci "github.com/tendermint/tendermint/abci/types" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" - porttypes "github.com/cosmos/ibc-go/v3/modules/core/05-port/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + porttypes "github.com/cosmos/ibc-go/v5/modules/core/05-port/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ) const ( diff --git a/testing/mock/privval_test.go b/testing/mock/privval_test.go index 452fa667824..c4ce5a8f77f 100644 --- a/testing/mock/privval_test.go +++ b/testing/mock/privval_test.go @@ -7,7 +7,7 @@ import ( tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmtypes "github.com/tendermint/tendermint/types" - "github.com/cosmos/ibc-go/v3/testing/mock" + "github.com/cosmos/ibc-go/v5/testing/mock" ) const chainID = "testChain" diff --git a/testing/path.go b/testing/path.go index 731d3cd5e1b..bb581b8f62f 100644 --- a/testing/path.go +++ b/testing/path.go @@ -4,7 +4,7 @@ import ( "bytes" "fmt" - channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" ) // Path contains two endpoints representing two chains connected over IBC diff --git a/testing/simapp/ante_handler.go b/testing/simapp/ante_handler.go index 04ffad13e2a..fd540c8bd5d 100644 --- a/testing/simapp/ante_handler.go +++ b/testing/simapp/ante_handler.go @@ -5,8 +5,8 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/auth/ante" - ibcante "github.com/cosmos/ibc-go/v3/modules/core/ante" - "github.com/cosmos/ibc-go/v3/modules/core/keeper" + ibcante "github.com/cosmos/ibc-go/v5/modules/core/ante" + "github.com/cosmos/ibc-go/v5/modules/core/keeper" ) // HandlerOptions extend the SDK's AnteHandler options by requiring the IBC keeper. diff --git a/testing/simapp/app.go b/testing/simapp/app.go index 738e29cdd59..cc859a5252c 100644 --- a/testing/simapp/app.go +++ b/testing/simapp/app.go @@ -43,7 +43,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/capability" capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - simappparams "github.com/cosmos/ibc-go/v3/testing/simapp/params" + simappparams "github.com/cosmos/ibc-go/v5/testing/simapp/params" "github.com/cosmos/cosmos-sdk/x/crisis" crisiskeeper "github.com/cosmos/cosmos-sdk/x/crisis/keeper" @@ -81,28 +81,28 @@ import ( upgradeclient "github.com/cosmos/cosmos-sdk/x/upgrade/client" upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - ica "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts" - icacontroller "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller" - icacontrollerkeeper "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller/keeper" - icacontrollertypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller/types" - icahost "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host" - icahostkeeper "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/keeper" - icahosttypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/types" - icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - ibcfee "github.com/cosmos/ibc-go/v3/modules/apps/29-fee" - ibcfeekeeper "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/keeper" - ibcfeetypes "github.com/cosmos/ibc-go/v3/modules/apps/29-fee/types" - transfer "github.com/cosmos/ibc-go/v3/modules/apps/transfer" - ibctransferkeeper "github.com/cosmos/ibc-go/v3/modules/apps/transfer/keeper" - ibctransfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" - ibc "github.com/cosmos/ibc-go/v3/modules/core" - ibcclient "github.com/cosmos/ibc-go/v3/modules/core/02-client" - ibcclientclient "github.com/cosmos/ibc-go/v3/modules/core/02-client/client" - ibcclienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - porttypes "github.com/cosmos/ibc-go/v3/modules/core/05-port/types" - ibchost "github.com/cosmos/ibc-go/v3/modules/core/24-host" - ibckeeper "github.com/cosmos/ibc-go/v3/modules/core/keeper" - ibcmock "github.com/cosmos/ibc-go/v3/testing/mock" + ica "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts" + icacontroller "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller" + icacontrollerkeeper "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/keeper" + icacontrollertypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/types" + icahost "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/host" + icahostkeeper "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/host/keeper" + icahosttypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/host/types" + icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + ibcfee "github.com/cosmos/ibc-go/v5/modules/apps/29-fee" + ibcfeekeeper "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/keeper" + ibcfeetypes "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types" + transfer "github.com/cosmos/ibc-go/v5/modules/apps/transfer" + ibctransferkeeper "github.com/cosmos/ibc-go/v5/modules/apps/transfer/keeper" + ibctransfertypes "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" + ibc "github.com/cosmos/ibc-go/v5/modules/core" + ibcclient "github.com/cosmos/ibc-go/v5/modules/core/02-client" + ibcclientclient "github.com/cosmos/ibc-go/v5/modules/core/02-client/client" + ibcclienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + porttypes "github.com/cosmos/ibc-go/v5/modules/core/05-port/types" + ibchost "github.com/cosmos/ibc-go/v5/modules/core/24-host" + ibckeeper "github.com/cosmos/ibc-go/v5/modules/core/keeper" + ibcmock "github.com/cosmos/ibc-go/v5/testing/mock" authz "github.com/cosmos/cosmos-sdk/x/authz" authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" diff --git a/testing/simapp/encoding.go b/testing/simapp/encoding.go index f308cadc23d..63eb410f547 100644 --- a/testing/simapp/encoding.go +++ b/testing/simapp/encoding.go @@ -3,7 +3,7 @@ package simapp import ( "github.com/cosmos/cosmos-sdk/std" - simappparams "github.com/cosmos/ibc-go/v3/testing/simapp/params" + simappparams "github.com/cosmos/ibc-go/v5/testing/simapp/params" ) // MakeTestEncodingConfig creates an EncodingConfig for testing. This function diff --git a/testing/simapp/genesis_account_test.go b/testing/simapp/genesis_account_test.go index 8a223148955..44e6cd25a20 100644 --- a/testing/simapp/genesis_account_test.go +++ b/testing/simapp/genesis_account_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/crypto" - "github.com/cosmos/ibc-go/v3/testing/simapp" + "github.com/cosmos/ibc-go/v5/testing/simapp" ) func TestSimGenesisAccountValidate(t *testing.T) { diff --git a/testing/simapp/sim_bench_test.go b/testing/simapp/sim_bench_test.go index 6f8b020a043..8e2e17ef518 100644 --- a/testing/simapp/sim_bench_test.go +++ b/testing/simapp/sim_bench_test.go @@ -11,7 +11,7 @@ import ( ) // Profile with: -// /usr/local/go/bin/go test -benchmem -run=^$ github.com/cosmos/ibc-go/v3/testing/simapp -bench ^BenchmarkFullAppSimulation$ -Commit=true -cpuprofile cpu.out +// /usr/local/go/bin/go test -benchmem -run=^$ github.com/cosmos/ibc-go/v5/testing/simapp -bench ^BenchmarkFullAppSimulation$ -Commit=true -cpuprofile cpu.out func BenchmarkFullAppSimulation(b *testing.B) { b.ReportAllocs() config, db, dir, logger, _, err := SetupSimulation("goleveldb-app-sim", "Simulation") diff --git a/testing/simapp/sim_test.go b/testing/simapp/sim_test.go index bbc8f4a8c0c..bbae0a798bc 100644 --- a/testing/simapp/sim_test.go +++ b/testing/simapp/sim_test.go @@ -29,9 +29,9 @@ import ( tmproto "github.com/tendermint/tendermint/proto/tendermint/types" dbm "github.com/tendermint/tm-db" - ibctransfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" - ibchost "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/testing/simapp/helpers" + ibctransfertypes "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" + ibchost "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/testing/simapp/helpers" ) // Get flags every time the simulator is run diff --git a/testing/simapp/simd/cmd/cmd_test.go b/testing/simapp/simd/cmd/cmd_test.go index 446639c5bf0..d7893e0492b 100644 --- a/testing/simapp/simd/cmd/cmd_test.go +++ b/testing/simapp/simd/cmd/cmd_test.go @@ -8,8 +8,8 @@ import ( "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" "github.com/stretchr/testify/require" - "github.com/cosmos/ibc-go/v3/testing/simapp" - "github.com/cosmos/ibc-go/v3/testing/simapp/simd/cmd" + "github.com/cosmos/ibc-go/v5/testing/simapp" + "github.com/cosmos/ibc-go/v5/testing/simapp/simd/cmd" ) func TestInitCmd(t *testing.T) { diff --git a/testing/simapp/simd/cmd/genaccounts_test.go b/testing/simapp/simd/cmd/genaccounts_test.go index 0cdf365bf93..e9fb96e616e 100644 --- a/testing/simapp/simd/cmd/genaccounts_test.go +++ b/testing/simapp/simd/cmd/genaccounts_test.go @@ -16,8 +16,8 @@ import ( "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/libs/log" - "github.com/cosmos/ibc-go/v3/testing/simapp" - simcmd "github.com/cosmos/ibc-go/v3/testing/simapp/simd/cmd" + "github.com/cosmos/ibc-go/v5/testing/simapp" + simcmd "github.com/cosmos/ibc-go/v5/testing/simapp/simd/cmd" ) var testMbm = module.NewBasicManager(genutil.AppModuleBasic{}) diff --git a/testing/simapp/simd/cmd/root.go b/testing/simapp/simd/cmd/root.go index e690093709e..fd5de4e3762 100644 --- a/testing/simapp/simd/cmd/root.go +++ b/testing/simapp/simd/cmd/root.go @@ -30,8 +30,8 @@ import ( "github.com/tendermint/tendermint/libs/log" dbm "github.com/tendermint/tm-db" - "github.com/cosmos/ibc-go/v3/testing/simapp" - "github.com/cosmos/ibc-go/v3/testing/simapp/params" + "github.com/cosmos/ibc-go/v5/testing/simapp" + "github.com/cosmos/ibc-go/v5/testing/simapp/params" ) // NewRootCmd creates a new root command for simd. It is called once in the diff --git a/testing/simapp/simd/main.go b/testing/simapp/simd/main.go index 79a7da2ac28..b0cfda9957b 100644 --- a/testing/simapp/simd/main.go +++ b/testing/simapp/simd/main.go @@ -6,8 +6,8 @@ import ( "github.com/cosmos/cosmos-sdk/server" svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" - "github.com/cosmos/ibc-go/v3/testing/simapp" - "github.com/cosmos/ibc-go/v3/testing/simapp/simd/cmd" + "github.com/cosmos/ibc-go/v5/testing/simapp" + "github.com/cosmos/ibc-go/v5/testing/simapp/simd/cmd" ) func main() { diff --git a/testing/simapp/state.go b/testing/simapp/state.go index 86f20e2b115..eac80963b0e 100644 --- a/testing/simapp/state.go +++ b/testing/simapp/state.go @@ -19,7 +19,7 @@ import ( tmjson "github.com/tendermint/tendermint/libs/json" tmtypes "github.com/tendermint/tendermint/types" - simappparams "github.com/cosmos/ibc-go/v3/testing/simapp/params" + simappparams "github.com/cosmos/ibc-go/v5/testing/simapp/params" ) // AppStateFn returns the initial application state using a genesis or the simulation parameters. diff --git a/testing/simapp/test_helpers.go b/testing/simapp/test_helpers.go index 1df1714d8a7..9fb43bfa4e3 100644 --- a/testing/simapp/test_helpers.go +++ b/testing/simapp/test_helpers.go @@ -25,7 +25,7 @@ import ( tmtypes "github.com/tendermint/tendermint/types" dbm "github.com/tendermint/tm-db" - "github.com/cosmos/ibc-go/v3/testing/simapp/helpers" + "github.com/cosmos/ibc-go/v5/testing/simapp/helpers" ) // DefaultConsensusParams defines the default Tendermint consensus params used in diff --git a/testing/simapp/utils.go b/testing/simapp/utils.go index 9abfc060b94..ae225974e8a 100644 --- a/testing/simapp/utils.go +++ b/testing/simapp/utils.go @@ -13,7 +13,7 @@ import ( "github.com/tendermint/tendermint/libs/log" dbm "github.com/tendermint/tm-db" - "github.com/cosmos/ibc-go/v3/testing/simapp/helpers" + "github.com/cosmos/ibc-go/v5/testing/simapp/helpers" ) // SetupSimulation creates the config, db (levelDB), temporary directory and logger for diff --git a/testing/solomachine.go b/testing/solomachine.go index f0cd445d1db..d22e67afd24 100644 --- a/testing/solomachine.go +++ b/testing/solomachine.go @@ -12,11 +12,11 @@ import ( "github.com/cosmos/cosmos-sdk/types/tx/signing" "github.com/stretchr/testify/require" - clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" - "github.com/cosmos/ibc-go/v3/modules/core/exported" - solomachinetypes "github.com/cosmos/ibc-go/v3/modules/light-clients/06-solomachine" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + solomachinetypes "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" ) // Solomachine is a testing helper used to simulate a counterparty diff --git a/testing/values.go b/testing/values.go index 3591711e24a..4b99430db14 100644 --- a/testing/values.go +++ b/testing/values.go @@ -9,12 +9,12 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - ibctransfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" - connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint" - "github.com/cosmos/ibc-go/v3/testing/mock" - "github.com/cosmos/ibc-go/v3/testing/simapp" + ibctransfertypes "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" + connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" + commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" + ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + "github.com/cosmos/ibc-go/v5/testing/mock" + "github.com/cosmos/ibc-go/v5/testing/simapp" ) const ( From 0d8f4089bc7ba08ec058745af85f64b8d258a66a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Colin=20Axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Tue, 2 Aug 2022 18:59:53 +0200 Subject: [PATCH 55/71] fix merge conflicts --- modules/core/05-port/types/query.pb.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/modules/core/05-port/types/query.pb.go b/modules/core/05-port/types/query.pb.go index 2fd3163810b..68efeeb04ea 100644 --- a/modules/core/05-port/types/query.pb.go +++ b/modules/core/05-port/types/query.pb.go @@ -6,13 +6,10 @@ package types import ( context "context" fmt "fmt" -<<<<<<< HEAD -======= io "io" math "math" math_bits "math/bits" ->>>>>>> 59693049dfbad0a30c626827ce079fd5d6b0d576 types "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" grpc1 "github.com/gogo/protobuf/grpc" proto "github.com/gogo/protobuf/proto" From a7d23fd28d4414ce6a51e2138e937c9da6a4ef74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Colin=20Axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Wed, 3 Aug 2022 15:24:13 +0200 Subject: [PATCH 56/71] fix build --- modules/apps/29-fee/ibc_module.go | 274 ------ modules/apps/29-fee/ibc_module_test.go | 912 ------------------ modules/apps/29-fee/ica_test.go | 2 +- modules/apps/29-fee/transfer_test.go | 2 +- modules/apps/transfer/keeper/relay_test.go | 2 +- modules/core/02-client/keeper/events.go | 1 + .../core/02-client/keeper/grpc_query_test.go | 1 - modules/core/02-client/keeper/keeper_test.go | 2 - modules/core/02-client/legacy/v100/store.go | 1 - .../07-tendermint/proposal_handle_test.go | 8 +- 10 files changed, 7 insertions(+), 1198 deletions(-) delete mode 100644 modules/apps/29-fee/ibc_module.go delete mode 100644 modules/apps/29-fee/ibc_module_test.go diff --git a/modules/apps/29-fee/ibc_module.go b/modules/apps/29-fee/ibc_module.go deleted file mode 100644 index 37c6a32165e..00000000000 --- a/modules/apps/29-fee/ibc_module.go +++ /dev/null @@ -1,274 +0,0 @@ -package fee - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - - "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/keeper" - "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types" - channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" - porttypes "github.com/cosmos/ibc-go/v5/modules/core/05-port/types" - "github.com/cosmos/ibc-go/v5/modules/core/exported" -) - -// IBCModule implements the ICS26 callbacks for the fee middleware given the fee keeper and the underlying application. -type IBCModule struct { - keeper keeper.Keeper - app porttypes.IBCModule -} - -// NewIBCModule creates a new IBCModule given the keeper and underlying application -func NewIBCModule(k keeper.Keeper, app porttypes.IBCModule) IBCModule { - return IBCModule{ - keeper: k, - app: app, - } -} - -// OnChanOpenInit implements the IBCModule interface -func (im IBCModule) OnChanOpenInit( - ctx sdk.Context, - order channeltypes.Order, - connectionHops []string, - portID string, - channelID string, - chanCap *capabilitytypes.Capability, - counterparty channeltypes.Counterparty, - version string, -) error { - 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 middleware version may be for a middleware - // lower 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.OnChanOpenInit(ctx, order, connectionHops, portID, channelID, - chanCap, counterparty, version) - } - - if versionMetadata.FeeVersion != types.Version { - return sdkerrors.Wrapf(types.ErrInvalidVersion, "expected %s, got %s", types.Version, versionMetadata.FeeVersion) - } - - im.keeper.SetFeeEnabled(ctx, portID, channelID) - - // call underlying app's OnChanOpenInit callback with the appVersion - return im.app.OnChanOpenInit(ctx, order, connectionHops, portID, channelID, - chanCap, counterparty, versionMetadata.AppVersion) -} - -// OnChanOpenTry implements the IBCModule interface -// If the channel is not fee enabled the underlying application version will be returned -// If the channel is fee enabled we merge the underlying application version with the ics29 version -func (im IBCModule) OnChanOpenTry( - ctx sdk.Context, - order channeltypes.Order, - connectionHops []string, - portID, - channelID string, - chanCap *capabilitytypes.Capability, - counterparty channeltypes.Counterparty, - counterpartyVersion string, -) (string, error) { - 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 middleware version may be for a middleware - // lower 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.OnChanOpenTry(ctx, order, connectionHops, portID, channelID, chanCap, counterparty, counterpartyVersion) - } - - if versionMetadata.FeeVersion != types.Version { - return "", sdkerrors.Wrapf(types.ErrInvalidVersion, "expected %s, got %s", types.Version, versionMetadata.FeeVersion) - } - - im.keeper.SetFeeEnabled(ctx, portID, channelID) - - // call underlying app's OnChanOpenTry callback with the app versions - appVersion, err := im.app.OnChanOpenTry(ctx, order, connectionHops, portID, channelID, chanCap, counterparty, versionMetadata.AppVersion) - if err != nil { - return "", err - } - - versionMetadata.AppVersion = appVersion - - versionBytes, err := types.ModuleCdc.MarshalJSON(&versionMetadata) - if err != nil { - return "", err - } - - return string(versionBytes), nil -} - -// OnChanOpenAck implements the IBCModule interface -func (im IBCModule) OnChanOpenAck( - ctx sdk.Context, - portID, - channelID string, - counterpartyChannelID string, - counterpartyVersion string, -) error { - // If handshake was initialized with fee enabled it must complete with fee enabled. - // If 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 sdkerrors.Wrap(types.ErrInvalidVersion, "failed to unmarshal ICS29 counterparty version metadata") - } - - if versionMetadata.FeeVersion != types.Version { - return sdkerrors.Wrapf(types.ErrInvalidVersion, "expected counterparty fee version: %s, got: %s", types.Version, versionMetadata.FeeVersion) - } - - // call underlying app's OnChanOpenAck callback with the counterparty app version. - return im.app.OnChanOpenAck(ctx, portID, channelID, counterpartyChannelID, versionMetadata.AppVersion) - } - - // call underlying app's OnChanOpenAck callback with the counterparty app version. - return im.app.OnChanOpenAck(ctx, portID, channelID, counterpartyChannelID, counterpartyVersion) -} - -// OnChanOpenConfirm implements the IBCModule interface -func (im IBCModule) OnChanOpenConfirm( - ctx sdk.Context, - portID, - channelID string, -) error { - // call underlying app's OnChanOpenConfirm callback. - return im.app.OnChanOpenConfirm(ctx, portID, channelID) -} - -// OnChanCloseInit implements the IBCModule interface -func (im IBCModule) OnChanCloseInit( - ctx sdk.Context, - portID, - channelID string, -) error { - if err := im.app.OnChanCloseInit(ctx, portID, channelID); err != nil { - return err - } - - if err := im.keeper.RefundFeesOnChannelClosure(ctx, portID, channelID); err != nil { - return err - } - - return nil -} - -// OnChanCloseConfirm implements the IBCModule interface -func (im IBCModule) OnChanCloseConfirm( - ctx sdk.Context, - portID, - channelID string, -) error { - if err := im.app.OnChanCloseConfirm(ctx, portID, channelID); err != nil { - return err - } - - if err := im.keeper.RefundFeesOnChannelClosure(ctx, portID, channelID); err != nil { - return err - } - - return nil -} - -// OnRecvPacket implements the IBCModule interface. -// If fees are not enabled, this callback will default to the ibc-core packet callback -func (im IBCModule) OnRecvPacket( - ctx sdk.Context, - packet channeltypes.Packet, - relayer sdk.AccAddress, -) exported.Acknowledgement { - if !im.keeper.IsFeeEnabled(ctx, packet.DestinationPort, packet.DestinationChannel) { - return im.app.OnRecvPacket(ctx, packet, relayer) - } - - ack := im.app.OnRecvPacket(ctx, packet, relayer) - - // incase of async aknowledgement (ack == nil) store the relayer address for use later during async WriteAcknowledgement - if ack == nil { - im.keeper.SetRelayerAddressForAsyncAck(ctx, channeltypes.NewPacketId(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()), relayer.String()) - return nil - } - - // if forwardRelayer is not found we refund recv_fee - forwardRelayer, _ := im.keeper.GetCounterpartyAddress(ctx, relayer.String(), packet.GetDestChannel()) - - return types.NewIncentivizedAcknowledgement(forwardRelayer, ack.Acknowledgement(), ack.Success()) -} - -// OnAcknowledgementPacket implements the IBCModule interface -// If fees are not enabled, this callback will default to the ibc-core packet callback -func (im IBCModule) OnAcknowledgementPacket( - ctx sdk.Context, - packet channeltypes.Packet, - acknowledgement []byte, - relayer sdk.AccAddress, -) error { - if !im.keeper.IsFeeEnabled(ctx, packet.SourcePort, packet.SourceChannel) { - return im.app.OnAcknowledgementPacket(ctx, packet, acknowledgement, relayer) - } - - ack := new(types.IncentivizedAcknowledgement) - if err := types.ModuleCdc.UnmarshalJSON(acknowledgement, ack); err != nil { - return sdkerrors.Wrapf(err, "cannot unmarshal ICS-29 incentivized packet acknowledgement: %v", ack) - } - - if im.keeper.IsLocked(ctx) { - // if the fee keeper is locked then fee logic should be skipped - // this may occur in the presence of a severe bug which leads to invalid state - // the fee keeper will be unlocked after manual intervention - // the acknowledgement has been unmarshalled into an ics29 acknowledgement - // since the counterparty is still sending incentivized acknowledgements - // for fee enabled channels - // - // Please see ADR 004 for more information. - return im.app.OnAcknowledgementPacket(ctx, packet, ack.Result, relayer) - } - - packetID := channeltypes.NewPacketId(packet.SourcePort, packet.SourceChannel, packet.Sequence) - feesInEscrow, found := im.keeper.GetFeesInEscrow(ctx, packetID) - if found { - im.keeper.DistributePacketFeesOnAcknowledgement(ctx, ack.ForwardRelayerAddress, relayer, feesInEscrow.PacketFees) - - // removes the fees from the store as fees are now paid - im.keeper.DeleteFeesInEscrow(ctx, packetID) - } - - // call underlying callback - return im.app.OnAcknowledgementPacket(ctx, packet, ack.Result, relayer) -} - -// OnTimeoutPacket implements the IBCModule interface -// If fees are not enabled, this callback will default to the ibc-core packet callback -func (im IBCModule) OnTimeoutPacket( - ctx sdk.Context, - packet channeltypes.Packet, - relayer sdk.AccAddress, -) error { - // if the fee keeper is locked then fee logic should be skipped - // this may occur in the presence of a severe bug which leads to invalid state - // the fee keeper will be unlocked after manual intervention - // - // Please see ADR 004 for more information. - if !im.keeper.IsFeeEnabled(ctx, packet.SourcePort, packet.SourceChannel) || im.keeper.IsLocked(ctx) { - return im.app.OnTimeoutPacket(ctx, packet, relayer) - } - - packetID := channeltypes.NewPacketId(packet.SourcePort, packet.SourceChannel, packet.Sequence) - feesInEscrow, found := im.keeper.GetFeesInEscrow(ctx, packetID) - if found { - im.keeper.DistributePacketFeesOnTimeout(ctx, relayer, feesInEscrow.PacketFees) - - // removes the fee from the store as fee is now paid - im.keeper.DeleteFeesInEscrow(ctx, packetID) - } - - // call underlying callback - return im.app.OnTimeoutPacket(ctx, packet, relayer) -} - -// GetAppVersion returns the application version of the underlying application -func (im IBCModule) GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) { - return im.keeper.GetAppVersion(ctx, portID, channelID) -} diff --git a/modules/apps/29-fee/ibc_module_test.go b/modules/apps/29-fee/ibc_module_test.go deleted file mode 100644 index 3c61599a780..00000000000 --- a/modules/apps/29-fee/ibc_module_test.go +++ /dev/null @@ -1,912 +0,0 @@ -package fee_test - -import ( - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" - capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - - fee "github.com/cosmos/ibc-go/v5/modules/apps/29-fee" - "github.com/cosmos/ibc-go/v5/modules/apps/29-fee/types" - transfertypes "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" - channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" - host "github.com/cosmos/ibc-go/v5/modules/core/24-host" - "github.com/cosmos/ibc-go/v5/modules/core/exported" - ibctesting "github.com/cosmos/ibc-go/v5/testing" - ibcmock "github.com/cosmos/ibc-go/v5/testing/mock" -) - -var ( - defaultRecvFee = sdk.Coins{sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: sdk.NewInt(100)}} - defaultAckFee = sdk.Coins{sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: sdk.NewInt(200)}} - defaultTimeoutFee = sdk.Coins{sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: sdk.NewInt(300)}} -) - -// Tests OnChanOpenInit on ChainA -func (suite *FeeTestSuite) TestOnChanOpenInit() { - testCases := []struct { - name string - version string - expPass bool - }{ - { - "success - valid fee middleware and mock version", - string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: ibcmock.Version})), - true, - }, - { - "success - fee version not included, only perform mock logic", - ibcmock.Version, - true, - }, - { - "invalid fee middleware version", - string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: "invalid-ics29-1", AppVersion: ibcmock.Version})), - false, - }, - { - "invalid mock version", - string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: "invalid-mock-version"})), - false, - }, - { - "mock version not wrapped", - types.Version, - false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - // reset suite - suite.SetupTest() - suite.coordinator.SetupConnections(suite.path) - - // setup mock callback - suite.chainA.GetSimApp().FeeMockModule.IBCApp.OnChanOpenInit = func(ctx sdk.Context, order channeltypes.Order, connectionHops []string, - portID, channelID string, chanCap *capabilitytypes.Capability, - counterparty channeltypes.Counterparty, version string, - ) error { - if version != ibcmock.Version { - return fmt.Errorf("incorrect mock version") - } - return nil - } - - suite.path.EndpointA.ChannelID = ibctesting.FirstChannelID - - counterparty := channeltypes.NewCounterparty(suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID) - channel := &channeltypes.Channel{ - State: channeltypes.INIT, - Ordering: channeltypes.UNORDERED, - Counterparty: counterparty, - ConnectionHops: []string{suite.path.EndpointA.ConnectionID}, - Version: tc.version, - } - - module, _, err := suite.chainA.App.GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), ibctesting.MockFeePort) - suite.Require().NoError(err) - - chanCap, err := suite.chainA.App.GetScopedIBCKeeper().NewCapability(suite.chainA.GetContext(), host.ChannelCapabilityPath(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID)) - suite.Require().NoError(err) - - cbs, ok := suite.chainA.App.GetIBCKeeper().Router.GetRoute(module) - suite.Require().True(ok) - - err = cbs.OnChanOpenInit(suite.chainA.GetContext(), channel.Ordering, channel.GetConnectionHops(), - suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, chanCap, counterparty, channel.Version) - - if tc.expPass { - suite.Require().NoError(err, "unexpected error from version: %s", tc.version) - } else { - suite.Require().Error(err, "error not returned for version: %s", tc.version) - } - }) - } -} - -// Tests OnChanOpenTry on ChainA -func (suite *FeeTestSuite) TestOnChanOpenTry() { - testCases := []struct { - name string - cpVersion string - crossing bool - expPass bool - }{ - { - "success - valid fee middleware version", - string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: ibcmock.Version})), - false, - true, - }, - { - "success - valid mock version", - ibcmock.Version, - false, - true, - }, - { - "success - crossing hellos: valid fee middleware", - string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: ibcmock.Version})), - true, - true, - }, - { - "invalid fee middleware version", - string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: "invalid-ics29-1", AppVersion: ibcmock.Version})), - false, - false, - }, - { - "invalid mock version", - string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: "invalid-mock-version"})), - false, - false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - // reset suite - suite.SetupTest() - suite.coordinator.SetupConnections(suite.path) - suite.path.EndpointB.ChanOpenInit() - - // setup mock callback - suite.chainA.GetSimApp().FeeMockModule.IBCApp.OnChanOpenTry = func(ctx sdk.Context, order channeltypes.Order, connectionHops []string, - portID, channelID string, chanCap *capabilitytypes.Capability, - counterparty channeltypes.Counterparty, counterpartyVersion string, - ) (string, error) { - if counterpartyVersion != ibcmock.Version { - return "", fmt.Errorf("incorrect mock version") - } - return ibcmock.Version, nil - } - - var ( - chanCap *capabilitytypes.Capability - ok bool - err error - ) - if tc.crossing { - suite.path.EndpointA.ChanOpenInit() - chanCap, ok = suite.chainA.GetSimApp().ScopedFeeMockKeeper.GetCapability(suite.chainA.GetContext(), host.ChannelCapabilityPath(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID)) - suite.Require().True(ok) - } else { - chanCap, err = suite.chainA.App.GetScopedIBCKeeper().NewCapability(suite.chainA.GetContext(), host.ChannelCapabilityPath(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID)) - suite.Require().NoError(err) - } - - suite.path.EndpointA.ChannelID = ibctesting.FirstChannelID - - counterparty := channeltypes.NewCounterparty(suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID) - channel := &channeltypes.Channel{ - State: channeltypes.INIT, - Ordering: channeltypes.UNORDERED, - Counterparty: counterparty, - ConnectionHops: []string{suite.path.EndpointA.ConnectionID}, - Version: tc.cpVersion, - } - - module, _, err := suite.chainA.App.GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), ibctesting.MockFeePort) - suite.Require().NoError(err) - - cbs, ok := suite.chainA.App.GetIBCKeeper().Router.GetRoute(module) - suite.Require().True(ok) - - _, err = cbs.OnChanOpenTry(suite.chainA.GetContext(), channel.Ordering, channel.GetConnectionHops(), - suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, chanCap, counterparty, tc.cpVersion) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} - -// Tests OnChanOpenAck on ChainA -func (suite *FeeTestSuite) TestOnChanOpenAck() { - testCases := []struct { - name string - cpVersion string - malleate func(suite *FeeTestSuite) - expPass bool - }{ - { - "success", - string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: ibcmock.Version})), - func(suite *FeeTestSuite) {}, - true, - }, - { - "invalid fee version", - string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: "invalid-ics29-1", AppVersion: ibcmock.Version})), - func(suite *FeeTestSuite) {}, - false, - }, - { - "invalid mock version", - string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: "invalid-mock-version"})), - func(suite *FeeTestSuite) {}, - false, - }, - { - "invalid version fails to unmarshal metadata", - "invalid-version", - func(suite *FeeTestSuite) {}, - false, - }, - { - "previous INIT set without fee, however counterparty set fee version", // note this can only happen with incompetent or malicious counterparty chain - string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: ibcmock.Version})), - func(suite *FeeTestSuite) { - // do the first steps without fee version, then pass the fee version as counterparty version in ChanOpenACK - suite.path.EndpointA.ChannelConfig.Version = ibcmock.Version - suite.path.EndpointB.ChannelConfig.Version = ibcmock.Version - }, - false, - }, - } - - for _, tc := range testCases { - tc := tc - suite.Run(tc.name, func() { - suite.SetupTest() - suite.coordinator.SetupConnections(suite.path) - - // setup mock callback - suite.chainA.GetSimApp().FeeMockModule.IBCApp.OnChanOpenAck = func( - ctx sdk.Context, portID, channelID string, counterpartyChannelID string, counterpartyVersion string, - ) error { - if counterpartyVersion != ibcmock.Version { - return fmt.Errorf("incorrect mock version") - } - return nil - } - - // malleate test case - tc.malleate(suite) - - suite.path.EndpointA.ChanOpenInit() - suite.path.EndpointB.ChanOpenTry() - - module, _, err := suite.chainA.App.GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), ibctesting.MockFeePort) - suite.Require().NoError(err) - - cbs, ok := suite.chainA.App.GetIBCKeeper().Router.GetRoute(module) - suite.Require().True(ok) - - err = cbs.OnChanOpenAck(suite.chainA.GetContext(), suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, suite.path.EndpointA.Counterparty.ChannelID, tc.cpVersion) - if tc.expPass { - suite.Require().NoError(err, "unexpected error for case: %s", tc.name) - } else { - suite.Require().Error(err, "%s expected error but returned none", tc.name) - } - }) - } -} - -func (suite *FeeTestSuite) TestOnChanCloseInit() { - var ( - refundAcc sdk.AccAddress - fee types.Fee - ) - - testCases := []struct { - name string - malleate func() - expPass bool - }{ - { - "success", func() {}, true, - }, - { - "application callback fails", func() { - suite.chainA.GetSimApp().FeeMockModule.IBCApp.OnChanCloseInit = func( - ctx sdk.Context, portID, channelID string, - ) error { - return fmt.Errorf("application callback fails") - } - }, false, - }, - { - "RefundFeesOnChannelClosure fails - invalid refund address", func() { - // store the fee in state & update escrow account balance - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(1)) - packetFees := types.NewPacketFees([]types.PacketFee{types.NewPacketFee(fee, "invalid refund address", nil)}) - - suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, packetFees) - err := suite.chainA.GetSimApp().BankKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), refundAcc, types.ModuleName, fee.Total()) - suite.Require().NoError(err) - }, - false, - }, - } - - for _, tc := range testCases { - tc := tc - suite.Run(tc.name, func() { - suite.SetupTest() - suite.coordinator.Setup(suite.path) // setup channel - - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) - fee = types.Fee{ - RecvFee: defaultRecvFee, - AckFee: defaultAckFee, - TimeoutFee: defaultTimeoutFee, - } - - refundAcc = suite.chainA.SenderAccount.GetAddress() - packetFee := types.NewPacketFee(fee, refundAcc.String(), []string{}) - - suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, types.NewPacketFees([]types.PacketFee{packetFee})) - err := suite.chainA.GetSimApp().BankKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), refundAcc, types.ModuleName, fee.Total()) - suite.Require().NoError(err) - - tc.malleate() - - module, _, err := suite.chainA.App.GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), ibctesting.MockFeePort) - suite.Require().NoError(err) - - cbs, ok := suite.chainA.App.GetIBCKeeper().Router.GetRoute(module) - suite.Require().True(ok) - - err = cbs.OnChanCloseInit(suite.chainA.GetContext(), suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} - -// Tests OnChanCloseConfirm on chainA -func (suite *FeeTestSuite) TestOnChanCloseConfirm() { - var ( - refundAcc sdk.AccAddress - fee types.Fee - ) - - testCases := []struct { - name string - malleate func() - expPass bool - }{ - { - "success", func() {}, true, - }, - { - "application callback fails", func() { - suite.chainA.GetSimApp().FeeMockModule.IBCApp.OnChanCloseConfirm = func( - ctx sdk.Context, portID, channelID string, - ) error { - return fmt.Errorf("application callback fails") - } - }, false, - }, - { - "RefundChannelFeesOnClosure fails - refund address is invalid", func() { - // store the fee in state & update escrow account balance - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, uint64(1)) - packetFees := types.NewPacketFees([]types.PacketFee{types.NewPacketFee(fee, "invalid refund address", nil)}) - - suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, packetFees) - err := suite.chainA.GetSimApp().BankKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), refundAcc, types.ModuleName, fee.Total()) - suite.Require().NoError(err) - }, - false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() - suite.coordinator.Setup(suite.path) // setup channel - - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, 1) - fee = types.Fee{ - RecvFee: defaultRecvFee, - AckFee: defaultAckFee, - TimeoutFee: defaultTimeoutFee, - } - - refundAcc = suite.chainA.SenderAccount.GetAddress() - packetFee := types.NewPacketFee(fee, refundAcc.String(), []string{}) - - suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, types.NewPacketFees([]types.PacketFee{packetFee})) - err := suite.chainA.GetSimApp().BankKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), refundAcc, types.ModuleName, fee.Total()) - suite.Require().NoError(err) - - tc.malleate() - - module, _, err := suite.chainA.App.GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), ibctesting.MockFeePort) - suite.Require().NoError(err) - - cbs, ok := suite.chainA.App.GetIBCKeeper().Router.GetRoute(module) - suite.Require().True(ok) - - err = cbs.OnChanCloseConfirm(suite.chainA.GetContext(), suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - - }) - } -} - -func (suite *FeeTestSuite) TestOnRecvPacket() { - testCases := []struct { - name string - malleate func() - // forwardRelayer bool indicates if there is a forwardRelayer address set - forwardRelayer bool - feeEnabled bool - }{ - { - "success", - func() {}, - true, - true, - }, - { - "async write acknowledgement: ack is nil", - func() { - // setup mock callback - suite.chainB.GetSimApp().FeeMockModule.IBCApp.OnRecvPacket = func( - ctx sdk.Context, - packet channeltypes.Packet, - relayer sdk.AccAddress, - ) exported.Acknowledgement { - return nil - } - }, - true, - true, - }, - { - "fee not enabled", - func() { - suite.chainB.GetSimApp().IBCFeeKeeper.DeleteFeeEnabled(suite.chainB.GetContext(), suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID) - }, - true, - false, - }, - { - "forward address is not found", - func() { - suite.chainB.GetSimApp().IBCFeeKeeper.SetCounterpartyAddress(suite.chainB.GetContext(), suite.chainA.SenderAccount.GetAddress().String(), "", suite.path.EndpointB.ChannelID) - }, - false, - true, - }, - } - - for _, tc := range testCases { - tc := tc - suite.Run(tc.name, func() { - suite.SetupTest() - // setup pathAToC (chainA -> chainC) first in order to have different channel IDs for chainA & chainB - suite.coordinator.Setup(suite.pathAToC) - // setup path for chainA -> chainB - suite.coordinator.Setup(suite.path) - - suite.chainB.GetSimApp().IBCFeeKeeper.SetFeeEnabled(suite.chainB.GetContext(), suite.path.EndpointB.ChannelConfig.PortID, suite.path.EndpointB.ChannelID) - - packet := suite.CreateMockPacket() - - // set up module and callbacks - module, _, err := suite.chainB.App.GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainB.GetContext(), ibctesting.MockFeePort) - suite.Require().NoError(err) - - cbs, ok := suite.chainB.App.GetIBCKeeper().Router.GetRoute(module) - suite.Require().True(ok) - - suite.chainB.GetSimApp().IBCFeeKeeper.SetCounterpartyAddress(suite.chainB.GetContext(), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), suite.path.EndpointB.ChannelID) - - // malleate test case - tc.malleate() - - result := cbs.OnRecvPacket(suite.chainB.GetContext(), packet, suite.chainA.SenderAccount.GetAddress()) - - switch { - case tc.name == "success": - forwardAddr, _ := suite.chainB.GetSimApp().IBCFeeKeeper.GetCounterpartyAddress(suite.chainB.GetContext(), suite.chainA.SenderAccount.GetAddress().String(), suite.path.EndpointB.ChannelID) - - expectedAck := types.IncentivizedAcknowledgement{ - Result: ibcmock.MockAcknowledgement.Acknowledgement(), - ForwardRelayerAddress: forwardAddr, - UnderlyingAppSuccess: true, - } - suite.Require().Equal(expectedAck, result) - - case !tc.feeEnabled: - suite.Require().Equal(ibcmock.MockAcknowledgement, result) - - case tc.forwardRelayer && result == nil: - suite.Require().Equal(nil, result) - packetID := channeltypes.NewPacketId(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) - - // retrieve the forward relayer that was stored in `onRecvPacket` - relayer, _ := suite.chainB.GetSimApp().IBCFeeKeeper.GetRelayerAddressForAsyncAck(suite.chainB.GetContext(), packetID) - suite.Require().Equal(relayer, suite.chainA.SenderAccount.GetAddress().String()) - - case !tc.forwardRelayer: - expectedAck := types.IncentivizedAcknowledgement{ - Result: ibcmock.MockAcknowledgement.Acknowledgement(), - ForwardRelayerAddress: "", - UnderlyingAppSuccess: true, - } - suite.Require().Equal(expectedAck, result) - } - }) - } -} - -// different channel than sending chain -func (suite *FeeTestSuite) TestOnAcknowledgementPacket() { - var ( - ack []byte - packetFee types.PacketFee - originalBalance sdk.Coins - expectedBalance sdk.Coins - expectedRelayerBalance sdk.Coins - ) - - testCases := []struct { - name string - malleate func() - expPass bool - }{ - { - "success", - func() { - expectedRelayerBalance = packetFee.Fee.RecvFee.Add(packetFee.Fee.AckFee[0]) - }, - true, - }, - { - "no op success without a packet fee", - func() { - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, suite.chainA.SenderAccount.GetSequence()) - suite.chainA.GetSimApp().IBCFeeKeeper.DeleteFeesInEscrow(suite.chainA.GetContext(), packetID) - - ack = types.IncentivizedAcknowledgement{ - Result: ibcmock.MockAcknowledgement.Acknowledgement(), - ForwardRelayerAddress: suite.chainA.SenderAccount.GetAddress().String(), - }.Acknowledgement() - - expectedBalance = originalBalance - }, - true, - }, - { - "ack wrong format", - func() { - ack = []byte("unsupported acknowledgement format") - - expectedBalance = originalBalance - }, - false, - }, - { - "channel is not fee not enabled, success", - func() { - suite.chainA.GetSimApp().IBCFeeKeeper.DeleteFeeEnabled(suite.chainA.GetContext(), suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID) - ack = ibcmock.MockAcknowledgement.Acknowledgement() - - expectedBalance = originalBalance - }, - true, - }, - { - "success: fee module is disabled, skip fee logic", - func() { - lockFeeModule(suite.chainA) - - expectedBalance = originalBalance - }, - true, - }, - { - "fail on distribute receive fee (blocked address)", - func() { - blockedAddr := suite.chainA.GetSimApp().AccountKeeper.GetModuleAccount(suite.chainA.GetContext(), transfertypes.ModuleName).GetAddress() - - ack = types.IncentivizedAcknowledgement{ - Result: ibcmock.MockAcknowledgement.Acknowledgement(), - ForwardRelayerAddress: blockedAddr.String(), - }.Acknowledgement() - - expectedRelayerBalance = packetFee.Fee.AckFee - expectedBalance = expectedBalance.Add(packetFee.Fee.RecvFee...) - }, - true, - }, - } - - for _, tc := range testCases { - tc := tc - suite.Run(tc.name, func() { - suite.SetupTest() - suite.coordinator.Setup(suite.path) - packet := suite.CreateMockPacket() - - expectedRelayerBalance = sdk.Coins{} // reset - - // set up module and callbacks - module, _, err := suite.chainA.App.GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), ibctesting.MockFeePort) - suite.Require().NoError(err) - - cbs, ok := suite.chainA.App.GetIBCKeeper().Router.GetRoute(module) - suite.Require().True(ok) - - // escrow the packet fee - packetID := channeltypes.NewPacketId(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) - packetFee = types.NewPacketFee( - types.Fee{ - RecvFee: defaultRecvFee, - AckFee: defaultAckFee, - TimeoutFee: defaultTimeoutFee, - }, - suite.chainA.SenderAccount.GetAddress().String(), - []string{}, - ) - - suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, types.NewPacketFees([]types.PacketFee{packetFee})) - err = suite.chainA.GetSimApp().BankKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), types.ModuleName, packetFee.Fee.Total()) - suite.Require().NoError(err) - - relayerAddr := suite.chainB.SenderAccount.GetAddress() - - // must be changed explicitly - ack = types.IncentivizedAcknowledgement{ - Result: ibcmock.MockAcknowledgement.Acknowledgement(), - ForwardRelayerAddress: relayerAddr.String(), - }.Acknowledgement() - - // log original sender balance - // NOTE: balance is logged after escrowing tokens - originalBalance = sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), ibctesting.TestCoin.Denom)) - - // default to success case - expectedBalance = originalBalance.Add(packetFee.Fee.TimeoutFee[0]) - - // malleate test case - tc.malleate() - - err = cbs.OnAcknowledgementPacket(suite.chainA.GetContext(), packet, ack, relayerAddr) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - - suite.Require().Equal( - expectedBalance, - sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), ibctesting.TestCoin.Denom)), - ) - - relayerBalance := sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), relayerAddr, ibctesting.TestCoin.Denom)) - suite.Require().Equal( - expectedRelayerBalance, - relayerBalance, - ) - }) - } -} - -func (suite *FeeTestSuite) TestOnTimeoutPacket() { - var ( - relayerAddr sdk.AccAddress - packetFee types.PacketFee - originalBalance sdk.Coins - expectedBalance sdk.Coins - ) - testCases := []struct { - name string - malleate func() - expFeeDistributed bool - }{ - { - "success", - func() {}, - true, - }, - { - "fee not enabled", - func() { - suite.chainA.GetSimApp().IBCFeeKeeper.DeleteFeeEnabled(suite.chainA.GetContext(), suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID) - - expectedBalance = originalBalance - }, - false, - }, - { - "fee module is disabled, skip fee logic", - func() { - lockFeeModule(suite.chainA) - - expectedBalance = originalBalance - }, - false, - }, - { - "no op if identified packet fee doesn't exist", - func() { - // delete packet fee - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, suite.chainA.SenderAccount.GetSequence()) - suite.chainA.GetSimApp().IBCFeeKeeper.DeleteFeesInEscrow(suite.chainA.GetContext(), packetID) - - expectedBalance = originalBalance - }, - false, - }, - { - "distribute fee fails for timeout fee (blocked address)", - func() { - relayerAddr = suite.chainA.GetSimApp().AccountKeeper.GetModuleAccount(suite.chainA.GetContext(), transfertypes.ModuleName).GetAddress() - - expectedBalance = originalBalance. - Add(packetFee.Fee.RecvFee...). - Add(packetFee.Fee.AckFee...). - Add(packetFee.Fee.TimeoutFee...) - }, - false, - }, - } - - for _, tc := range testCases { - tc := tc - suite.Run(tc.name, func() { - suite.SetupTest() - suite.coordinator.Setup(suite.path) - packet := suite.CreateMockPacket() - - // set up module and callbacks - module, _, err := suite.chainA.App.GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), ibctesting.MockFeePort) - suite.Require().NoError(err) - - cbs, ok := suite.chainA.App.GetIBCKeeper().Router.GetRoute(module) - suite.Require().True(ok) - - packetID := channeltypes.NewPacketId(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) - - // must be explicitly changed - relayerAddr = suite.chainB.SenderAccount.GetAddress() - - packetFee = types.NewPacketFee( - types.Fee{ - RecvFee: defaultRecvFee, - AckFee: defaultAckFee, - TimeoutFee: defaultTimeoutFee, - }, - suite.chainA.SenderAccount.GetAddress().String(), - []string{}, - ) - - suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, types.NewPacketFees([]types.PacketFee{packetFee})) - err = suite.chainA.GetSimApp().BankKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), types.ModuleName, packetFee.Fee.Total()) - suite.Require().NoError(err) - - // log original sender balance - // NOTE: balance is logged after escrowing tokens - originalBalance = sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), ibctesting.TestCoin.Denom)) - - // default to success case - expectedBalance = originalBalance. - Add(packetFee.Fee.RecvFee[0]). - Add(packetFee.Fee.AckFee[0]) - - // malleate test case - tc.malleate() - - err = cbs.OnTimeoutPacket(suite.chainA.GetContext(), packet, relayerAddr) - suite.Require().NoError(err) - - suite.Require().Equal( - expectedBalance, - sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), ibctesting.TestCoin.Denom)), - ) - - relayerBalance := sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), relayerAddr, ibctesting.TestCoin.Denom)) - if tc.expFeeDistributed { - // there should no longer be a fee in escrow for this packet - found := suite.chainA.GetSimApp().IBCFeeKeeper.HasFeesInEscrow(suite.chainA.GetContext(), packetID) - suite.Require().False(found) - - suite.Require().Equal(packetFee.Fee.TimeoutFee, relayerBalance) - } else { - suite.Require().Empty(relayerBalance) - } - }) - } -} - -func (suite *FeeTestSuite) TestGetAppVersion() { - var ( - portID string - channelID string - expAppVersion string - ) - testCases := []struct { - name string - malleate func() - expFound bool - }{ - { - "success for fee enabled channel", - func() { - expAppVersion = ibcmock.Version - }, - true, - }, - { - "success for non fee enabled channel", - func() { - path := ibctesting.NewPath(suite.chainA, suite.chainB) - path.EndpointA.ChannelConfig.PortID = ibctesting.MockFeePort - path.EndpointB.ChannelConfig.PortID = ibctesting.MockFeePort - // by default a new path uses a non fee channel - suite.coordinator.Setup(path) - portID = path.EndpointA.ChannelConfig.PortID - channelID = path.EndpointA.ChannelID - - expAppVersion = ibcmock.Version - }, - true, - }, - { - "channel does not exist", - func() { - channelID = "does not exist" - }, - false, - }, - } - - for _, tc := range testCases { - tc := tc - suite.Run(tc.name, func() { - suite.SetupTest() - suite.coordinator.Setup(suite.path) - - portID = suite.path.EndpointA.ChannelConfig.PortID - channelID = suite.path.EndpointA.ChannelID - - // malleate test case - tc.malleate() - - module, _, err := suite.chainA.App.GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), ibctesting.MockFeePort) - suite.Require().NoError(err) - - cbs, ok := suite.chainA.App.GetIBCKeeper().Router.GetRoute(module) - suite.Require().True(ok) - - feeModule := cbs.(fee.IBCModule) - - appVersion, found := feeModule.GetAppVersion(suite.chainA.GetContext(), portID, channelID) - - if tc.expFound { - suite.Require().True(found) - suite.Require().Equal(expAppVersion, appVersion) - } else { - suite.Require().False(found) - suite.Require().Empty(appVersion) - } - }) - } -} diff --git a/modules/apps/29-fee/ica_test.go b/modules/apps/29-fee/ica_test.go index bcc8098d0dc..a1ab7ef6d03 100644 --- a/modules/apps/29-fee/ica_test.go +++ b/modules/apps/29-fee/ica_test.go @@ -189,7 +189,7 @@ func buildInterchainAccountsPacket(path *ibctesting.Path, data []byte, seq uint6 path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, - clienttypes.NewHeight(0, 100), + clienttypes.NewHeight(1, 100), 0, ) diff --git a/modules/apps/29-fee/transfer_test.go b/modules/apps/29-fee/transfer_test.go index 74d7089faf7..8aeb6ffeac0 100644 --- a/modules/apps/29-fee/transfer_test.go +++ b/modules/apps/29-fee/transfer_test.go @@ -30,7 +30,7 @@ func (suite *FeeTestSuite) TestFeeTransfer() { msgs := []sdk.Msg{ types.NewMsgPayPacketFee(fee, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, suite.chainA.SenderAccount.GetAddress().String(), nil), - transfertypes.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, coin, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), clienttypes.NewHeight(0, 100), 0), + transfertypes.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, coin, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), clienttypes.NewHeight(1, 100), 0), } res, err := suite.chainA.SendMsgs(msgs...) suite.Require().NoError(err) // message committed diff --git a/modules/apps/transfer/keeper/relay_test.go b/modules/apps/transfer/keeper/relay_test.go index 4843c89cbe4..1e57e3f2236 100644 --- a/modules/apps/transfer/keeper/relay_test.go +++ b/modules/apps/transfer/keeper/relay_test.go @@ -210,7 +210,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { if tc.recvIsSource { // send coin from chainB to chainA, receive them, acknowledge them, and send back to chainB coinFromBToA := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) - transferMsg := types.NewMsgTransfer(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, coinFromBToA, suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), clienttypes.NewHeight(0, 110), 0) + transferMsg := types.NewMsgTransfer(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, coinFromBToA, suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), clienttypes.NewHeight(1, 110), 0) res, err := suite.chainB.SendMsgs(transferMsg) suite.Require().NoError(err) // message committed diff --git a/modules/core/02-client/keeper/events.go b/modules/core/02-client/keeper/events.go index e332ddec846..0399d7b5392 100644 --- a/modules/core/02-client/keeper/events.go +++ b/modules/core/02-client/keeper/events.go @@ -2,6 +2,7 @@ package keeper import ( "encoding/hex" + "fmt" "strconv" "strings" diff --git a/modules/core/02-client/keeper/grpc_query_test.go b/modules/core/02-client/keeper/grpc_query_test.go index 5dde3b2b609..f79e838ab48 100644 --- a/modules/core/02-client/keeper/grpc_query_test.go +++ b/modules/core/02-client/keeper/grpc_query_test.go @@ -10,7 +10,6 @@ import ( "google.golang.org/grpc/metadata" "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v5/modules/core/exported" ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" diff --git a/modules/core/02-client/keeper/keeper_test.go b/modules/core/02-client/keeper/keeper_test.go index 560ae9db5de..dcc8460023e 100644 --- a/modules/core/02-client/keeper/keeper_test.go +++ b/modules/core/02-client/keeper/keeper_test.go @@ -68,8 +68,6 @@ type KeeperTestSuite struct { signers map[string]tmtypes.PrivValidator - signers map[string]tmtypes.PrivValidator - // TODO: deprecate queryClient types.QueryClient } diff --git a/modules/core/02-client/legacy/v100/store.go b/modules/core/02-client/legacy/v100/store.go index df085fdfeeb..1d22547cf26 100644 --- a/modules/core/02-client/legacy/v100/store.go +++ b/modules/core/02-client/legacy/v100/store.go @@ -11,7 +11,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" host "github.com/cosmos/ibc-go/v5/modules/core/24-host" "github.com/cosmos/ibc-go/v5/modules/core/exported" diff --git a/modules/light-clients/07-tendermint/proposal_handle_test.go b/modules/light-clients/07-tendermint/proposal_handle_test.go index 376b2d2f036..22703f4b01d 100644 --- a/modules/light-clients/07-tendermint/proposal_handle_test.go +++ b/modules/light-clients/07-tendermint/proposal_handle_test.go @@ -4,7 +4,6 @@ import ( "time" clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v5/modules/core/24-host" "github.com/cosmos/ibc-go/v5/modules/core/exported" tendermint "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" @@ -29,8 +28,7 @@ func (suite *TendermintTestSuite) TestCheckSubstituteUpdateStateBasic() { { "non-matching substitute", func() { suite.coordinator.SetupClients(substitutePath) - substituteClientState = suite.chainA.GetClientState(substitutePath.EndpointA.ClientID).(*tendermint.ClientState) - tmClientState, ok := substituteClientState.(*tendermint.ClientState) + substituteClientState, ok := suite.chainA.GetClientState(substitutePath.EndpointA.ClientID).(*tendermint.ClientState) suite.Require().True(ok) // change trusting period so that test should fail substituteClientState.TrustingPeriod = time.Hour * 24 * 7 @@ -247,8 +245,8 @@ func (suite *TendermintTestSuite) TestIsMatchingClientState() { }, { "not matching, trust level is different", func() { - subjectClientState.TrustLevel = types.Fraction{2, 3} - substituteClientState.TrustLevel = types.Fraction{1, 3} + subjectClientState.TrustLevel = tendermint.Fraction{2, 3} + substituteClientState.TrustLevel = tendermint.Fraction{1, 3} }, false, }, } From f861e0e39556bf8c9d43c431322f37539041b149 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Colin=20Axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Wed, 3 Aug 2022 15:28:15 +0200 Subject: [PATCH 57/71] go imports --- modules/light-clients/07-tendermint/update_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/light-clients/07-tendermint/update_test.go b/modules/light-clients/07-tendermint/update_test.go index d79698a5ae5..41406e35dd7 100644 --- a/modules/light-clients/07-tendermint/update_test.go +++ b/modules/light-clients/07-tendermint/update_test.go @@ -4,6 +4,8 @@ import ( "time" sdk "github.com/cosmos/cosmos-sdk/types" + tmtypes "github.com/tendermint/tendermint/types" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" host "github.com/cosmos/ibc-go/v5/modules/core/24-host" @@ -11,7 +13,6 @@ import ( tendermint "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ibctestingmock "github.com/cosmos/ibc-go/v5/testing/mock" - tmtypes "github.com/tendermint/tendermint/types" ) func (suite *TendermintTestSuite) TestVerifyHeader() { From 607458abc010438cda9ff45a5f82aea30fe6ef06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Colin=20Axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Wed, 3 Aug 2022 15:29:45 +0200 Subject: [PATCH 58/71] make format --- modules/core/ante/ante_test.go | 2 +- modules/core/exported/client.go | 2 +- modules/light-clients/06-solomachine/client_state.go | 2 +- modules/light-clients/07-tendermint/client_state.go | 2 +- modules/light-clients/07-tendermint/client_state_test.go | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/core/ante/ante_test.go b/modules/core/ante/ante_test.go index 5349897991e..363ea28e398 100644 --- a/modules/core/ante/ante_test.go +++ b/modules/core/ante/ante_test.go @@ -430,7 +430,7 @@ func (suite *AnteTestSuite) TestAnteDecorator() { msg := suite.createRecvPacketMessage(uint64(1), false) // We want to be able to run check tx with the non-redundant message without - // commiting it to a block, so that the when check tx runs with the redundant + // committing it to a block, so that the when check tx runs with the redundant // message they are both in the same block k := suite.chainB.App.GetIBCKeeper() decorator := ante.NewRedundantRelayDecorator(k) diff --git a/modules/core/exported/client.go b/modules/core/exported/client.go index 9d54a66c71b..1254e89ab10 100644 --- a/modules/core/exported/client.go +++ b/modules/core/exported/client.go @@ -109,7 +109,7 @@ type ClientState interface { value []byte, ) error - // VerifyNonMembership is a generic proof verification method which verifies the absense of a given CommitmentPath at a specified height. + // VerifyNonMembership is a generic proof verification method which verifies the absence of a given CommitmentPath at a specified height. // The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). VerifyNonMembership( ctx sdk.Context, diff --git a/modules/light-clients/06-solomachine/client_state.go b/modules/light-clients/06-solomachine/client_state.go index 81976ef508d..fd660c3785a 100644 --- a/modules/light-clients/06-solomachine/client_state.go +++ b/modules/light-clients/06-solomachine/client_state.go @@ -152,7 +152,7 @@ func (cs *ClientState) VerifyMembership( return nil } -// VerifyNonMembership is a generic proof verification method which verifies the absense of a given CommitmentPath at a specified height. +// VerifyNonMembership is a generic proof verification method which verifies the absence of a given CommitmentPath at a specified height. // The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). func (cs *ClientState) VerifyNonMembership( ctx sdk.Context, diff --git a/modules/light-clients/07-tendermint/client_state.go b/modules/light-clients/07-tendermint/client_state.go index e5999bb17d4..0c3715e4db1 100644 --- a/modules/light-clients/07-tendermint/client_state.go +++ b/modules/light-clients/07-tendermint/client_state.go @@ -248,7 +248,7 @@ func (cs ClientState) VerifyMembership( return nil } -// VerifyNonMembership is a generic proof verification method which verifies the absense of a given CommitmentPath at a specified height. +// VerifyNonMembership is a generic proof verification method which verifies the absence of a given CommitmentPath at a specified height. // The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). func (cs ClientState) VerifyNonMembership( ctx sdk.Context, diff --git a/modules/light-clients/07-tendermint/client_state_test.go b/modules/light-clients/07-tendermint/client_state_test.go index 6ce557ec159..38144e0a3a3 100644 --- a/modules/light-clients/07-tendermint/client_state_test.go +++ b/modules/light-clients/07-tendermint/client_state_test.go @@ -411,7 +411,7 @@ func (suite *TendermintTestSuite) TestVerifyMembership() { }, { "proof verification failed", func() { - // change the value being prooved + // change the value being proved value = []byte("invalid value") }, false, }, @@ -638,7 +638,7 @@ func (suite *TendermintTestSuite) TestVerifyNonMembership() { }, { "verify non membership fails as path exists", func() { - // change the value being prooved + // change the value being proved key := host.FullClientStateKey(testingpath.EndpointB.ClientID) merklePath := commitmenttypes.NewMerklePath(string(key)) merklePath, err := commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) From e9a7fac40d7a90f1b81c821946c924abe31a4ed8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Colin=20Axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Wed, 3 Aug 2022 15:41:33 +0200 Subject: [PATCH 59/71] fix linter --- modules/core/02-client/keeper/events.go | 7 +++---- .../light-clients/06-solomachine/misbehaviour_handle.go | 1 - modules/light-clients/07-tendermint/misbehaviour_handle.go | 1 - modules/light-clients/07-tendermint/update.go | 6 ++++-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/modules/core/02-client/keeper/events.go b/modules/core/02-client/keeper/events.go index 0399d7b5392..8936442b8eb 100644 --- a/modules/core/02-client/keeper/events.go +++ b/modules/core/02-client/keeper/events.go @@ -32,7 +32,6 @@ func EmitCreateClientEvent(ctx sdk.Context, clientID string, clientState exporte // EmitUpdateClientEvent emits an update client event func EmitUpdateClientEvent(ctx sdk.Context, clientID string, clientType string, consensusHeights []exported.Height, cdc codec.BinaryCodec, clientMsg exported.ClientMessage) { - // Marshal the ClientMessage as an Any and encode the resulting bytes to hex. // This prevents the event value from containing invalid UTF-8 characters // which may cause data to be lost when JSON encoding/decoding. @@ -43,9 +42,9 @@ func EmitUpdateClientEvent(ctx sdk.Context, clientID string, clientType string, consensusHeightAttr = consensusHeights[0].String() } - var consensusHeightsAttr []string - for _, height := range consensusHeights { - consensusHeightsAttr = append(consensusHeightsAttr, height.String()) + consensusHeightsAttr := make([]string, len(consensusHeights)) + for i, height := range consensusHeights { + consensusHeightsAttr[i] = height.String() } ctx.EventManager().EmitEvents(sdk.Events{ diff --git a/modules/light-clients/06-solomachine/misbehaviour_handle.go b/modules/light-clients/06-solomachine/misbehaviour_handle.go index dd86d49b6d5..e108931b7ea 100644 --- a/modules/light-clients/06-solomachine/misbehaviour_handle.go +++ b/modules/light-clients/06-solomachine/misbehaviour_handle.go @@ -43,5 +43,4 @@ func (cs ClientState) verifySignatureAndData(cdc codec.BinaryCodec, misbehaviour } return nil - } diff --git a/modules/light-clients/07-tendermint/misbehaviour_handle.go b/modules/light-clients/07-tendermint/misbehaviour_handle.go index 3c205a14da2..0417dd4da17 100644 --- a/modules/light-clients/07-tendermint/misbehaviour_handle.go +++ b/modules/light-clients/07-tendermint/misbehaviour_handle.go @@ -21,7 +21,6 @@ import ( // to misbehaviour.Header2 // Misbehaviour sets frozen height to {0, 1} since it is only used as a boolean value (zero or non-zero). func (cs *ClientState) verifyMisbehaviour(ctx sdk.Context, clientStore sdk.KVStore, cdc codec.BinaryCodec, misbehaviour *Misbehaviour) error { - // if heights are equal check that this is valid misbehaviour of a fork // otherwise if heights are unequal check that this is valid misbehavior of BFT time violation if misbehaviour.Header1.GetHeight().EQ(misbehaviour.Header2.GetHeight()) { diff --git a/modules/light-clients/07-tendermint/update.go b/modules/light-clients/07-tendermint/update.go index 30b1c1fa20c..39481df125a 100644 --- a/modules/light-clients/07-tendermint/update.go +++ b/modules/light-clients/07-tendermint/update.go @@ -186,7 +186,9 @@ func (cs ClientState) pruneOldestConsensusState(ctx sdk.Context, cdc codec.Binar return true } - IterateConsensusStateAscending(clientStore, pruneCb) + if err := IterateConsensusStateAscending(clientStore, pruneCb); err != nil { + panic(err) + } // if pruneHeight is set, delete consensus state and metadata if pruneHeight != nil { @@ -209,7 +211,7 @@ func (cs ClientState) CheckForMisbehaviour(ctx sdk.Context, cdc codec.BinaryCode if existingConsState != nil { // This header has already been submitted and the necessary state is already stored // in client store, thus we can return early without further validation. - if reflect.DeepEqual(existingConsState, tmHeader.ConsensusState()) { + if reflect.DeepEqual(existingConsState, tmHeader.ConsensusState()) { //nolint:gosimple return false } From 18eee1ab4ba8ab71dfacaa0d29a1a72e72704a90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Colin=20Axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Wed, 3 Aug 2022 16:30:03 +0200 Subject: [PATCH 60/71] apply review suggestions --- CHANGELOG.md | 7 ++----- docs/migrations/v2-to-v3.md | 2 +- docs/migrations/v3-to-v4.md | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12e5f00d967..a493409e68c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,8 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Dependencies +* [\#1615](https://github.com/cosmos/ibc-go/pull/1615) Bump SDK version to v0.45.6 +* [\#1627](https://github.com/cosmos/ibc-go/pull/1627) Bump Go version to 1.18 ### API Breaking @@ -45,11 +47,6 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (07-tendermint) [\#1677](https://github.com/cosmos/ibc-go/pull/1677) Remove `types` sub-package from `07-tendermint` lightclient directory. * (06-solomachine) [\#1687](https://github.com/cosmos/ibc-go/pull/1687) Bump `06-solomachine` protobuf version from `v2` to `v3`. * (06-solomachine) [\#1687](https://github.com/cosmos/ibc-go/pull/1687) Removed `DataType` enum and associated message types from `06-solomachine`. `DataType` has been removed from `SignBytes` and `SignatureAndData` in favour of `path`. -* [\#1615](https://github.com/cosmos/ibc-go/pull/1615) Bump SDK version to v0.45.6 -* [\#1627](https://github.com/cosmos/ibc-go/pull/1627) Bump Go version to 1.18 - -### API Breaking - * (core/03-connection) [\#1797](https://github.com/cosmos/ibc-go/pull/1797) Remove `PreviousConnectionID` from `NewMsgConnectionOpenTry` arguments. `MsgConnectionOpenTry.ValidateBasic()` returns error if the deprecated `PreviousConnectionID` is not empty. * (core/04-channel) [\#1792](https://github.com/cosmos/ibc-go/pull/1792) Remove `PreviousChannelID` from `NewMsgChannelOpenTry` arguments. `MsgChannelOpenTry.ValidateBasic()` returns error if the deprecated `PreviousChannelID` is not empty. * (core/04-channel) [\#1418](https://github.com/cosmos/ibc-go/pull/1418) `NewPacketId` has been renamed to `NewPacketID` to comply with go linting rules. diff --git a/docs/migrations/v2-to-v3.md b/docs/migrations/v2-to-v3.md index b30ad6f935c..b274c85fd05 100644 --- a/docs/migrations/v2-to-v3.md +++ b/docs/migrations/v2-to-v3.md @@ -11,7 +11,7 @@ There are four sections based on the four potential user groups of this document **Note:** ibc-go supports golang semantic versioning and therefore all imports must be updated to bump the version number on major releases. ```go -github.com/cosmos/ibc-go/v2 -> github.com/cosmos/ibc-go/v5 +github.com/cosmos/ibc-go/v2 -> github.com/cosmos/ibc-go/v3 ``` No genesis or in-place migrations are required when upgrading from v1 or v2 of ibc-go. diff --git a/docs/migrations/v3-to-v4.md b/docs/migrations/v3-to-v4.md index 4129cce4221..1526b1642e0 100644 --- a/docs/migrations/v3-to-v4.md +++ b/docs/migrations/v3-to-v4.md @@ -11,7 +11,7 @@ There are four sections based on the four potential user groups of this document **Note:** ibc-go supports golang semantic versioning and therefore all imports must be updated to bump the version number on major releases. ```go -github.com/cosmos/ibc-go/v5 -> github.com/cosmos/ibc-go/v4 +github.com/cosmos/ibc-go/v3 -> github.com/cosmos/ibc-go/v4 ``` No genesis or in-place migrations required when upgrading from v1 or v2 of ibc-go. From 2da9e65811283131a3aa11acfa227b1930e58422 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?colin=20axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Thu, 4 Aug 2022 14:02:32 +0200 Subject: [PATCH 61/71] fix: import changes for 02-client-refactor (#1875) * ibctmtypes -> ibctm * tendermint -> ibctmtypes * remove space * make goimports --- modules/core/02-client/abci.go | 4 +- modules/core/02-client/abci_test.go | 4 +- modules/core/02-client/client/utils/utils.go | 28 ++++---- modules/core/02-client/keeper/client_test.go | 46 ++++++------- .../core/02-client/keeper/grpc_query_test.go | 6 +- modules/core/02-client/keeper/keeper.go | 8 +-- modules/core/02-client/keeper/keeper_test.go | 56 +++++++-------- .../core/02-client/keeper/proposal_test.go | 24 +++---- modules/core/02-client/legacy/v100/genesis.go | 12 ++-- .../02-client/legacy/v100/genesis_test.go | 18 ++--- modules/core/02-client/legacy/v100/store.go | 10 +-- .../core/02-client/legacy/v100/store_test.go | 24 +++---- .../core/02-client/proposal_handler_test.go | 6 +- .../core/02-client/simulation/decoder_test.go | 6 +- modules/core/02-client/types/codec_test.go | 4 +- modules/core/02-client/types/encoding_test.go | 4 +- modules/core/02-client/types/genesis_test.go | 56 +++++++-------- modules/core/02-client/types/msgs_test.go | 28 ++++---- modules/core/02-client/types/proposal_test.go | 14 ++-- .../03-connection/keeper/handshake_test.go | 14 ++-- .../core/03-connection/keeper/verify_test.go | 24 +++---- modules/core/03-connection/types/msgs_test.go | 22 +++--- modules/core/04-channel/keeper/packet_test.go | 4 +- modules/core/genesis_test.go | 12 ++-- modules/core/keeper/msg_server_test.go | 12 ++-- modules/core/simulation/decoder_test.go | 4 +- modules/core/types/codec.go | 4 +- .../06-solomachine/client_state_test.go | 4 +- .../06-solomachine/proposal_handle_test.go | 4 +- .../06-solomachine/update_test.go | 6 +- .../07-tendermint/client_state_test.go | 46 ++++++------- .../07-tendermint/consensus_state_test.go | 18 ++--- .../07-tendermint/genesis_test.go | 32 ++++----- .../07-tendermint/header_test.go | 4 +- .../07-tendermint/misbehaviour_handle_test.go | 68 +++++++++---------- .../07-tendermint/misbehaviour_test.go | 68 +++++++++---------- .../07-tendermint/proposal_handle_test.go | 42 ++++++------ .../07-tendermint/tendermint_test.go | 4 +- .../07-tendermint/update_test.go | 62 ++++++++--------- .../07-tendermint/upgrade_test.go | 22 +++--- testing/chain.go | 18 ++--- testing/config.go | 4 +- testing/endpoint.go | 8 +-- testing/simapp/app.go | 1 - testing/values.go | 4 +- 45 files changed, 434 insertions(+), 435 deletions(-) diff --git a/modules/core/02-client/abci.go b/modules/core/02-client/abci.go index 09e2e366c84..c4fcc91ec37 100644 --- a/modules/core/02-client/abci.go +++ b/modules/core/02-client/abci.go @@ -4,7 +4,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/ibc-go/v5/modules/core/02-client/keeper" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ) // BeginBlocker is used to perform IBC client upgrades @@ -19,7 +19,7 @@ func BeginBlocker(ctx sdk.Context, k keeper.Keeper) { // within the trusting period of the last block time on this chain. _, exists := k.GetUpgradedClient(ctx, plan.Height) if exists && ctx.BlockHeight() == plan.Height-1 { - upgradedConsState := &ibctmtypes.ConsensusState{ + upgradedConsState := &ibctm.ConsensusState{ Timestamp: ctx.BlockTime(), NextValidatorsHash: ctx.BlockHeader().NextValidatorsHash, } diff --git a/modules/core/02-client/abci_test.go b/modules/core/02-client/abci_test.go index 1fb212497bb..acdfd32f389 100644 --- a/modules/core/02-client/abci_test.go +++ b/modules/core/02-client/abci_test.go @@ -12,7 +12,7 @@ import ( client "github.com/cosmos/ibc-go/v5/modules/core/02-client" "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ) @@ -72,7 +72,7 @@ func (suite *ClientTestSuite) TestBeginBlockerConsensusState() { // plan Height is at ctx.BlockHeight+1 consState, found := suite.chainA.GetSimApp().UpgradeKeeper.GetUpgradedConsensusState(newCtx, plan.Height) suite.Require().True(found) - bz, err = types.MarshalConsensusState(suite.chainA.App.AppCodec(), &ibctmtypes.ConsensusState{Timestamp: newCtx.BlockTime(), NextValidatorsHash: nextValsHash}) + bz, err = types.MarshalConsensusState(suite.chainA.App.AppCodec(), &ibctm.ConsensusState{Timestamp: newCtx.BlockTime(), NextValidatorsHash: nextValsHash}) suite.Require().NoError(err) suite.Require().Equal(bz, consState) } diff --git a/modules/core/02-client/client/utils/utils.go b/modules/core/02-client/client/utils/utils.go index a8493da38ad..61e49ae2b3a 100644 --- a/modules/core/02-client/client/utils/utils.go +++ b/modules/core/02-client/client/utils/utils.go @@ -13,7 +13,7 @@ import ( host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ibcclient "github.com/cosmos/ibc-go/v5/modules/core/client" "github.com/cosmos/ibc-go/v5/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ) // QueryClientState returns a client state. If prove is true, it performs an ABCI store query @@ -119,15 +119,15 @@ func QueryConsensusStateABCI( // QueryTendermintHeader takes a client context and returns the appropriate // tendermint header -func QueryTendermintHeader(clientCtx client.Context) (ibctmtypes.Header, int64, error) { +func QueryTendermintHeader(clientCtx client.Context) (ibctm.Header, int64, error) { node, err := clientCtx.GetNode() if err != nil { - return ibctmtypes.Header{}, 0, err + return ibctm.Header{}, 0, err } info, err := node.ABCIInfo(context.Background()) if err != nil { - return ibctmtypes.Header{}, 0, err + return ibctm.Header{}, 0, err } var height int64 @@ -139,7 +139,7 @@ func QueryTendermintHeader(clientCtx client.Context) (ibctmtypes.Header, int64, commit, err := node.Commit(context.Background(), &height) if err != nil { - return ibctmtypes.Header{}, 0, err + return ibctm.Header{}, 0, err } page := 1 @@ -147,16 +147,16 @@ func QueryTendermintHeader(clientCtx client.Context) (ibctmtypes.Header, int64, validators, err := node.Validators(context.Background(), &height, &page, &count) if err != nil { - return ibctmtypes.Header{}, 0, err + return ibctm.Header{}, 0, err } protoCommit := commit.SignedHeader.ToProto() protoValset, err := tmtypes.NewValidatorSet(validators.Validators).ToProto() if err != nil { - return ibctmtypes.Header{}, 0, err + return ibctm.Header{}, 0, err } - header := ibctmtypes.Header{ + header := ibctm.Header{ SignedHeader: protoCommit, ValidatorSet: protoValset, } @@ -166,15 +166,15 @@ func QueryTendermintHeader(clientCtx client.Context) (ibctmtypes.Header, int64, // QuerySelfConsensusState takes a client context and returns the appropriate // tendermint consensus state -func QuerySelfConsensusState(clientCtx client.Context) (*ibctmtypes.ConsensusState, int64, error) { +func QuerySelfConsensusState(clientCtx client.Context) (*ibctm.ConsensusState, int64, error) { node, err := clientCtx.GetNode() if err != nil { - return &ibctmtypes.ConsensusState{}, 0, err + return &ibctm.ConsensusState{}, 0, err } info, err := node.ABCIInfo(context.Background()) if err != nil { - return &ibctmtypes.ConsensusState{}, 0, err + return &ibctm.ConsensusState{}, 0, err } var height int64 @@ -186,7 +186,7 @@ func QuerySelfConsensusState(clientCtx client.Context) (*ibctmtypes.ConsensusSta commit, err := node.Commit(context.Background(), &height) if err != nil { - return &ibctmtypes.ConsensusState{}, 0, err + return &ibctm.ConsensusState{}, 0, err } page := 1 @@ -195,10 +195,10 @@ func QuerySelfConsensusState(clientCtx client.Context) (*ibctmtypes.ConsensusSta nextHeight := height + 1 nextVals, err := node.Validators(context.Background(), &nextHeight, &page, &count) if err != nil { - return &ibctmtypes.ConsensusState{}, 0, err + return &ibctm.ConsensusState{}, 0, err } - state := &ibctmtypes.ConsensusState{ + state := &ibctm.ConsensusState{ Timestamp: commit.Time, Root: commitmenttypes.NewMerkleRoot(commit.AppHash), NextValidatorsHash: tmtypes.NewValidatorSet(nextVals.Validators).Hash(), diff --git a/modules/core/02-client/keeper/client_test.go b/modules/core/02-client/keeper/client_test.go index 57f26d305bc..22b3c3556fd 100644 --- a/modules/core/02-client/keeper/client_test.go +++ b/modules/core/02-client/keeper/client_test.go @@ -12,7 +12,7 @@ import ( commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v5/modules/core/exported" solomachinetypes "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ) @@ -22,7 +22,7 @@ func (suite *KeeperTestSuite) TestCreateClient() { clientState exported.ClientState expPass bool }{ - {"success", ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), true}, + {"success", ibctm.NewClientState(testChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), true}, {"client type not supported", solomachinetypes.NewClientState(0, &solomachinetypes.ConsensusState{suite.solomachine.ConsensusState().PublicKey, suite.solomachine.Diversifier, suite.solomachine.Time}, false), false}, } @@ -42,20 +42,20 @@ func (suite *KeeperTestSuite) TestCreateClient() { func (suite *KeeperTestSuite) TestUpdateClientTendermint() { var ( path *ibctesting.Path - updateHeader *ibctmtypes.Header + updateHeader *ibctm.Header ) // Must create header creation functions since suite.header gets recreated on each test case - createFutureUpdateFn := func(trustedHeight clienttypes.Height) *ibctmtypes.Header { + createFutureUpdateFn := func(trustedHeight clienttypes.Height) *ibctm.Header { header, err := suite.chainA.ConstructUpdateTMClientHeaderWithTrustedHeight(path.EndpointB.Chain, path.EndpointA.ClientID, trustedHeight) suite.Require().NoError(err) return header } - createPastUpdateFn := func(fillHeight, trustedHeight clienttypes.Height) *ibctmtypes.Header { + createPastUpdateFn := func(fillHeight, trustedHeight clienttypes.Height) *ibctm.Header { consState, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientConsensusState(suite.chainA.GetContext(), path.EndpointA.ClientID, trustedHeight) suite.Require().True(found) - return suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(fillHeight.RevisionHeight), trustedHeight, consState.(*ibctmtypes.ConsensusState).Timestamp.Add(time.Second*5), + return suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(fillHeight.RevisionHeight), trustedHeight, consState.(*ibctm.ConsensusState).Timestamp.Add(time.Second*5), suite.chainB.Vals, suite.chainB.Vals, suite.chainB.Vals, suite.chainB.Signers) } @@ -66,7 +66,7 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { expFreeze bool }{ {"valid update", func() { - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) trustHeight := clientState.GetLatestHeight().(types.Height) // store intermediate consensus state to check that trustedHeight does not need to be highest consensus state before header height @@ -103,7 +103,7 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { height1 := types.NewHeight(1, 1) // store previous consensus state - prevConsState := &ibctmtypes.ConsensusState{ + prevConsState := &ibctm.ConsensusState{ Timestamp: suite.past, NextValidatorsHash: suite.chainB.Vals.Hash(), } @@ -111,7 +111,7 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { height5 := types.NewHeight(1, 5) // store next consensus state to check that trustedHeight does not need to be hightest consensus state before header height - nextConsState := &ibctmtypes.ConsensusState{ + nextConsState := &ibctm.ConsensusState{ Timestamp: suite.past.Add(time.Minute), NextValidatorsHash: suite.chainB.Vals.Hash(), } @@ -129,7 +129,7 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { height1 := types.NewHeight(1, 1) // store previous consensus state - prevConsState := &ibctmtypes.ConsensusState{ + prevConsState := &ibctm.ConsensusState{ Timestamp: suite.past, NextValidatorsHash: suite.chainB.Vals.Hash(), } @@ -137,7 +137,7 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { height5 := types.NewHeight(1, 5) // store next consensus state to check that trustedHeight does not need to be hightest consensus state before header height - nextConsState := &ibctmtypes.ConsensusState{ + nextConsState := &ibctm.ConsensusState{ Timestamp: suite.past.Add(time.Minute), NextValidatorsHash: suite.chainB.Vals.Hash(), } @@ -153,21 +153,21 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), clientID, updateHeader.GetHeight(), conflictConsState) }, true, true}, {"misbehaviour detection: monotonic time violation", func() { - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) clientID := path.EndpointA.ClientID trustedHeight := clientState.GetLatestHeight().(types.Height) // store intermediate consensus state at a time greater than updateHeader time // this will break time monotonicity incrementedClientHeight := clientState.GetLatestHeight().Increment().(types.Height) - intermediateConsState := &ibctmtypes.ConsensusState{ + intermediateConsState := &ibctm.ConsensusState{ Timestamp: suite.coordinator.CurrentTime.Add(2 * time.Hour), NextValidatorsHash: suite.chainB.Vals.Hash(), } suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), clientID, incrementedClientHeight, intermediateConsState) // set iteration key clientStore := suite.keeper.ClientStore(suite.ctx, clientID) - ibctmtypes.SetIterationKey(clientStore, incrementedClientHeight) + ibctm.SetIterationKey(clientStore, incrementedClientHeight) clientState.LatestHeight = incrementedClientHeight suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, clientState) @@ -181,7 +181,7 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { }, false, false}, {"consensus state not found", func() { clientState := path.EndpointA.GetClientState() - tmClient, ok := clientState.(*ibctmtypes.ClientState) + tmClient, ok := clientState.(*ibctm.ClientState) suite.Require().True(ok) tmClient.LatestHeight = tmClient.LatestHeight.Increment().(types.Height) @@ -189,7 +189,7 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { updateHeader = createFutureUpdateFn(clientState.GetLatestHeight().(types.Height)) }, false, false}, {"client is not active", func() { - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) clientState.FrozenHeight = types.NewHeight(1, 1) suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID, clientState) updateHeader = createFutureUpdateFn(clientState.GetLatestHeight().(types.Height)) @@ -222,9 +222,9 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { newClientState := path.EndpointA.GetClientState() if tc.expFreeze { - suite.Require().True(!newClientState.(*ibctmtypes.ClientState).FrozenHeight.IsZero(), "client did not freeze after conflicting header was submitted to UpdateClient") + suite.Require().True(!newClientState.(*ibctm.ClientState).FrozenHeight.IsZero(), "client did not freeze after conflicting header was submitted to UpdateClient") } else { - expConsensusState := &ibctmtypes.ConsensusState{ + expConsensusState := &ibctm.ConsensusState{ Timestamp: updateHeader.GetTime(), Root: commitmenttypes.NewMerkleRoot(updateHeader.Header.GetAppHash()), NextValidatorsHash: updateHeader.Header.NextValidatorsHash, @@ -347,7 +347,7 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { proofUpgradedConsState, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) // set frozen client in store - tmClient, ok := cs.(*ibctmtypes.ClientState) + tmClient, ok := cs.(*ibctm.ClientState) suite.Require().True(ok) tmClient.FrozenHeight = types.NewHeight(1, 1) suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID, tmClient) @@ -367,7 +367,7 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { suite.Require().NoError(err) // change upgradedClient client-specified parameters - tmClient := upgradedClient.(*ibctmtypes.ClientState) + tmClient := upgradedClient.(*ibctm.ClientState) tmClient.ChainId = "wrongchainID" upgradedClient = tmClient @@ -390,18 +390,18 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { path = ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupClients(path) - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) revisionNumber := clienttypes.ParseChainID(clientState.ChainId) newChainID, err := clienttypes.SetRevisionNumber(clientState.ChainId, revisionNumber+1) suite.Require().NoError(err) - upgradedClient = ibctmtypes.NewClientState(newChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, clienttypes.NewHeight(revisionNumber+1, clientState.GetLatestHeight().GetRevisionHeight()+1), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + upgradedClient = ibctm.NewClientState(newChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, clienttypes.NewHeight(revisionNumber+1, clientState.GetLatestHeight().GetRevisionHeight()+1), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) upgradedClient = upgradedClient.ZeroCustomFields() upgradedClientBz, err = types.MarshalClientState(suite.chainA.App.AppCodec(), upgradedClient) suite.Require().NoError(err) - upgradedConsState = &ibctmtypes.ConsensusState{ + upgradedConsState = &ibctm.ConsensusState{ NextValidatorsHash: []byte("nextValsHash"), } upgradedConsStateBz, err = types.MarshalConsensusState(suite.chainA.App.AppCodec(), upgradedConsState) diff --git a/modules/core/02-client/keeper/grpc_query_test.go b/modules/core/02-client/keeper/grpc_query_test.go index f79e838ab48..e66badac616 100644 --- a/modules/core/02-client/keeper/grpc_query_test.go +++ b/modules/core/02-client/keeper/grpc_query_test.go @@ -11,7 +11,7 @@ import ( "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" "github.com/cosmos/ibc-go/v5/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ) @@ -528,7 +528,7 @@ func (suite *KeeperTestSuite) TestQueryClientStatus() { func() { path := ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupClients(path) - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) // increment latest height so no consensus state is stored clientState.LatestHeight = clientState.LatestHeight.Increment().(types.Height) @@ -545,7 +545,7 @@ func (suite *KeeperTestSuite) TestQueryClientStatus() { func() { path := ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupClients(path) - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) clientState.FrozenHeight = types.NewHeight(0, 1) path.EndpointA.SetClientState(clientState) diff --git a/modules/core/02-client/keeper/keeper.go b/modules/core/02-client/keeper/keeper.go index 1ff74c43be0..33b6a39ac14 100644 --- a/modules/core/02-client/keeper/keeper.go +++ b/modules/core/02-client/keeper/keeper.go @@ -19,7 +19,7 @@ import ( commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" host "github.com/cosmos/ibc-go/v5/modules/core/24-host" "github.com/cosmos/ibc-go/v5/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ) // Keeper represents a type that grants read and write permissions to any client @@ -257,7 +257,7 @@ func (k Keeper) GetSelfConsensusState(ctx sdk.Context, height exported.Height) ( return nil, sdkerrors.Wrapf(sdkerrors.ErrNotFound, "no historical info found at height %d", selfHeight.RevisionHeight) } - consensusState := &ibctmtypes.ConsensusState{ + consensusState := &ibctm.ConsensusState{ Timestamp: histInfo.Header.Time, Root: commitmenttypes.NewMerkleRoot(histInfo.Header.GetAppHash()), NextValidatorsHash: histInfo.Header.NextValidatorsHash, @@ -269,10 +269,10 @@ func (k Keeper) GetSelfConsensusState(ctx sdk.Context, height exported.Height) ( // This function is only used to validate the client state the counterparty stores for this chain // Client must be in same revision as the executing chain func (k Keeper) ValidateSelfClient(ctx sdk.Context, clientState exported.ClientState) error { - tmClient, ok := clientState.(*ibctmtypes.ClientState) + tmClient, ok := clientState.(*ibctm.ClientState) if !ok { return sdkerrors.Wrapf(types.ErrInvalidClient, "client must be a Tendermint client, expected: %T, got: %T", - &ibctmtypes.ClientState{}, tmClient) + &ibctm.ClientState{}, tmClient) } if !tmClient.FrozenHeight.IsZero() { diff --git a/modules/core/02-client/keeper/keeper_test.go b/modules/core/02-client/keeper/keeper_test.go index dcc8460023e..e38f7a0d430 100644 --- a/modules/core/02-client/keeper/keeper_test.go +++ b/modules/core/02-client/keeper/keeper_test.go @@ -20,7 +20,7 @@ import ( commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v5/modules/core/exported" solomachinetypes "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ibctestingmock "github.com/cosmos/ibc-go/v5/testing/mock" "github.com/cosmos/ibc-go/v5/testing/simapp" @@ -57,8 +57,8 @@ type KeeperTestSuite struct { cdc codec.Codec ctx sdk.Context keeper *keeper.Keeper - consensusState *ibctmtypes.ConsensusState - header *ibctmtypes.Header + consensusState *ibctm.ConsensusState + header *ibctm.Header valSet *tmtypes.ValidatorSet valSetHash tmbytes.HexBytes privVal tmtypes.PrivValidator @@ -102,7 +102,7 @@ func (suite *KeeperTestSuite) SetupTest() { suite.signers[validator.Address.String()] = suite.privVal suite.header = suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight), testClientHeightMinus1, now2, suite.valSet, suite.valSet, suite.valSet, suite.signers) - suite.consensusState = ibctmtypes.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot([]byte("hash")), suite.valSetHash) + suite.consensusState = ibctm.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot([]byte("hash")), suite.valSetHash) var validators stakingtypes.Validators for i := 1; i < 11; i++ { @@ -135,7 +135,7 @@ func TestKeeperTestSuite(t *testing.T) { } func (suite *KeeperTestSuite) TestSetClientState() { - clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + clientState := ibctm.NewClientState(testChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) suite.keeper.SetClientState(suite.ctx, testClientID, clientState) retrievedState, found := suite.keeper.GetClientState(suite.ctx, testClientID) @@ -149,7 +149,7 @@ func (suite *KeeperTestSuite) TestSetClientConsensusState() { retrievedConsState, found := suite.keeper.GetClientConsensusState(suite.ctx, testClientID, testClientHeight) suite.Require().True(found, "GetConsensusState failed") - tmConsState, ok := retrievedConsState.(*ibctmtypes.ConsensusState) + tmConsState, ok := retrievedConsState.(*ibctm.ConsensusState) suite.Require().True(ok) suite.Require().Equal(suite.consensusState, tmConsState, "ConsensusState not stored correctly") } @@ -165,27 +165,27 @@ func (suite *KeeperTestSuite) TestValidateSelfClient() { }{ { "success", - ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), true, }, { "success with nil UpgradePath", - ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), nil, false, false), + ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), nil, false, false), true, }, { "frozen client", - &ibctmtypes.ClientState{ChainId: suite.chainA.ChainID, TrustLevel: ibctmtypes.DefaultTrustLevel, TrustingPeriod: trustingPeriod, UnbondingPeriod: ubdPeriod, MaxClockDrift: maxClockDrift, FrozenHeight: testClientHeight, LatestHeight: testClientHeight, ProofSpecs: commitmenttypes.GetSDKSpecs(), UpgradePath: ibctesting.UpgradePath, AllowUpdateAfterExpiry: false, AllowUpdateAfterMisbehaviour: false}, + &ibctm.ClientState{ChainId: suite.chainA.ChainID, TrustLevel: ibctm.DefaultTrustLevel, TrustingPeriod: trustingPeriod, UnbondingPeriod: ubdPeriod, MaxClockDrift: maxClockDrift, FrozenHeight: testClientHeight, LatestHeight: testClientHeight, ProofSpecs: commitmenttypes.GetSDKSpecs(), UpgradePath: ibctesting.UpgradePath, AllowUpdateAfterExpiry: false, AllowUpdateAfterMisbehaviour: false}, false, }, { "incorrect chainID", - ibctmtypes.NewClientState("gaiatestnet", ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ibctm.NewClientState("gaiatestnet", ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), false, }, { "invalid client height", - ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.GetSelfHeight(suite.chainA.GetContext()).Increment().(types.Height), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.GetSelfHeight(suite.chainA.GetContext()).Increment().(types.Height), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), false, }, { @@ -195,32 +195,32 @@ func (suite *KeeperTestSuite) TestValidateSelfClient() { }, { "invalid client revision", - ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeightRevision1, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeightRevision1, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), false, }, { "invalid proof specs", - ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, nil, ibctesting.UpgradePath, false, false), + ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, nil, ibctesting.UpgradePath, false, false), false, }, { "invalid trust level", - ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.Fraction{Numerator: 0, Denominator: 1}, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ibctm.NewClientState(suite.chainA.ChainID, ibctm.Fraction{Numerator: 0, Denominator: 1}, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), false, }, { "invalid unbonding period", - ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod+10, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod+10, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), false, }, { "invalid trusting period", - ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, ubdPeriod+10, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, ubdPeriod+10, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), false, }, { "invalid upgrade path", - ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), []string{"bad", "upgrade", "path"}, false, false), + ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), []string{"bad", "upgrade", "path"}, false, false), false, }, } @@ -240,9 +240,9 @@ func (suite KeeperTestSuite) TestGetAllGenesisClients() { testClientID2, testClientID3, testClientID, } expClients := []exported.ClientState{ - ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), - ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), - ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ibctm.NewClientState(testChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ibctm.NewClientState(testChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ibctm.NewClientState(testChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), } expGenClients := make(types.IdentifiedClientStates, len(expClients)) @@ -262,22 +262,22 @@ func (suite KeeperTestSuite) TestGetAllGenesisMetadata() { types.NewIdentifiedGenesisMetadata( "07-tendermint-1", []types.GenesisMetadata{ - types.NewGenesisMetadata(ibctmtypes.ProcessedTimeKey(types.NewHeight(0, 1)), []byte("foo")), - types.NewGenesisMetadata(ibctmtypes.ProcessedTimeKey(types.NewHeight(0, 2)), []byte("bar")), - types.NewGenesisMetadata(ibctmtypes.ProcessedTimeKey(types.NewHeight(0, 3)), []byte("baz")), + types.NewGenesisMetadata(ibctm.ProcessedTimeKey(types.NewHeight(0, 1)), []byte("foo")), + types.NewGenesisMetadata(ibctm.ProcessedTimeKey(types.NewHeight(0, 2)), []byte("bar")), + types.NewGenesisMetadata(ibctm.ProcessedTimeKey(types.NewHeight(0, 3)), []byte("baz")), }, ), types.NewIdentifiedGenesisMetadata( "clientB", []types.GenesisMetadata{ - types.NewGenesisMetadata(ibctmtypes.ProcessedTimeKey(types.NewHeight(1, 100)), []byte("val1")), - types.NewGenesisMetadata(ibctmtypes.ProcessedTimeKey(types.NewHeight(2, 300)), []byte("val2")), + types.NewGenesisMetadata(ibctm.ProcessedTimeKey(types.NewHeight(1, 100)), []byte("val1")), + types.NewGenesisMetadata(ibctm.ProcessedTimeKey(types.NewHeight(2, 300)), []byte("val2")), }, ), } genClients := []types.IdentifiedClientState{ - types.NewIdentifiedClientState("07-tendermint-1", &ibctmtypes.ClientState{}), types.NewIdentifiedClientState("clientB", &ibctmtypes.ClientState{}), + types.NewIdentifiedClientState("07-tendermint-1", &ibctm.ClientState{}), types.NewIdentifiedClientState("clientB", &ibctm.ClientState{}), } suite.chainA.App.GetIBCKeeper().ClientKeeper.SetAllClientMetadata(suite.chainA.GetContext(), expectedGenMetadata) @@ -315,12 +315,12 @@ func (suite KeeperTestSuite) TestGetConsensusState() { func (suite KeeperTestSuite) TestConsensusStateHelpers() { // initial setup - clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + clientState := ibctm.NewClientState(testChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) suite.keeper.SetClientState(suite.ctx, testClientID, clientState) suite.keeper.SetClientConsensusState(suite.ctx, testClientID, testClientHeight, suite.consensusState) - nextState := ibctmtypes.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot([]byte("next")), suite.valSetHash) + nextState := ibctm.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot([]byte("next")), suite.valSetHash) testClientHeightPlus5 := types.NewHeight(0, height+5) diff --git a/modules/core/02-client/keeper/proposal_test.go b/modules/core/02-client/keeper/proposal_test.go index ec11074a5fe..762d41a41ec 100644 --- a/modules/core/02-client/keeper/proposal_test.go +++ b/modules/core/02-client/keeper/proposal_test.go @@ -6,7 +6,7 @@ import ( "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" "github.com/cosmos/ibc-go/v5/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ) @@ -30,7 +30,7 @@ func (suite *KeeperTestSuite) TestClientUpdateProposal() { }, { "subject and substitute use different revision numbers", func() { - tmClientState, ok := substituteClientState.(*ibctmtypes.ClientState) + tmClientState, ok := substituteClientState.(*ibctm.ClientState) suite.Require().True(ok) consState, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientConsensusState(suite.chainA.GetContext(), substitute, tmClientState.LatestHeight) suite.Require().True(found) @@ -40,8 +40,8 @@ func (suite *KeeperTestSuite) TestClientUpdateProposal() { suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), substitute, tmClientState.LatestHeight, consState) clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), substitute) - ibctmtypes.SetProcessedTime(clientStore, tmClientState.LatestHeight, 100) - ibctmtypes.SetProcessedHeight(clientStore, tmClientState.LatestHeight, types.NewHeight(0, 1)) + ibctm.SetProcessedTime(clientStore, tmClientState.LatestHeight, 100) + ibctm.SetProcessedHeight(clientStore, tmClientState.LatestHeight, types.NewHeight(0, 1)) suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), substitute, tmClientState) content = types.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, subject, substitute) @@ -68,7 +68,7 @@ func (suite *KeeperTestSuite) TestClientUpdateProposal() { }, { "subject and substitute have equal latest height", func() { - tmClientState, ok := subjectClientState.(*ibctmtypes.ClientState) + tmClientState, ok := subjectClientState.(*ibctm.ClientState) suite.Require().True(ok) tmClientState.LatestHeight = substituteClientState.GetLatestHeight().(types.Height) suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), subject, tmClientState) @@ -78,7 +78,7 @@ func (suite *KeeperTestSuite) TestClientUpdateProposal() { }, { "update fails, client is not frozen or expired", func() { - tmClientState, ok := subjectClientState.(*ibctmtypes.ClientState) + tmClientState, ok := subjectClientState.(*ibctm.ClientState) suite.Require().True(ok) tmClientState.FrozenHeight = types.ZeroHeight() suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), subject, tmClientState) @@ -88,7 +88,7 @@ func (suite *KeeperTestSuite) TestClientUpdateProposal() { }, { "substitute is frozen", func() { - tmClientState, ok := substituteClientState.(*ibctmtypes.ClientState) + tmClientState, ok := substituteClientState.(*ibctm.ClientState) suite.Require().True(ok) tmClientState.FrozenHeight = types.NewHeight(0, 1) suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), substitute, tmClientState) @@ -118,14 +118,14 @@ func (suite *KeeperTestSuite) TestClientUpdateProposal() { substitutePath.EndpointA.UpdateClient() substituteClientState = suite.chainA.GetClientState(substitute) - tmClientState, ok := subjectClientState.(*ibctmtypes.ClientState) + tmClientState, ok := subjectClientState.(*ibctm.ClientState) suite.Require().True(ok) tmClientState.AllowUpdateAfterMisbehaviour = true tmClientState.AllowUpdateAfterExpiry = true tmClientState.FrozenHeight = tmClientState.LatestHeight suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), subject, tmClientState) - tmClientState, ok = substituteClientState.(*ibctmtypes.ClientState) + tmClientState, ok = substituteClientState.(*ibctm.ClientState) suite.Require().True(ok) tmClientState.AllowUpdateAfterMisbehaviour = true tmClientState.AllowUpdateAfterExpiry = true @@ -148,7 +148,7 @@ func (suite *KeeperTestSuite) TestClientUpdateProposal() { func (suite *KeeperTestSuite) TestHandleUpgradeProposal() { var ( - upgradedClientState *ibctmtypes.ClientState + upgradedClientState *ibctm.ClientState oldPlan, plan upgradetypes.Plan content govtypes.Content err error @@ -178,7 +178,7 @@ func (suite *KeeperTestSuite) TestHandleUpgradeProposal() { }, { "cannot unpack client state", func() { - any, err := types.PackConsensusState(&ibctmtypes.ConsensusState{}) + any, err := types.PackConsensusState(&ibctm.ConsensusState{}) suite.Require().NoError(err) content = &types.UpgradeProposal{ Title: ibctesting.Title, @@ -199,7 +199,7 @@ func (suite *KeeperTestSuite) TestHandleUpgradeProposal() { path := ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupClients(path) - upgradedClientState = suite.chainA.GetClientState(path.EndpointA.ClientID).ZeroCustomFields().(*ibctmtypes.ClientState) + upgradedClientState = suite.chainA.GetClientState(path.EndpointA.ClientID).ZeroCustomFields().(*ibctm.ClientState) // use height 1000 to distinguish from old plan plan = upgradetypes.Plan{ diff --git a/modules/core/02-client/legacy/v100/genesis.go b/modules/core/02-client/legacy/v100/genesis.go index 4126cb18af6..3a4018ff621 100644 --- a/modules/core/02-client/legacy/v100/genesis.go +++ b/modules/core/02-client/legacy/v100/genesis.go @@ -10,7 +10,7 @@ import ( "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" host "github.com/cosmos/ibc-go/v5/modules/core/24-host" "github.com/cosmos/ibc-go/v5/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ) // MigrateGenesis accepts exported v1.0.0 IBC client genesis file and migrates it to: @@ -67,7 +67,7 @@ func MigrateGenesis(cdc codec.BinaryCodec, clientGenState *types.GenesisState, g case exported.Tendermint: // only add non expired consensus states to new clientsConsensus - tmClientState, ok := client.ClientState.GetCachedValue().(*ibctmtypes.ClientState) + tmClientState, ok := client.ClientState.GetCachedValue().(*ibctm.ClientState) if !ok { return nil, types.ErrInvalidClient } @@ -75,7 +75,7 @@ func MigrateGenesis(cdc codec.BinaryCodec, clientGenState *types.GenesisState, g // collect unexpired consensus states var unexpiredConsensusStates []types.ConsensusStateWithHeight for _, consState := range clientConsensusStates.ConsensusStates { - tmConsState := consState.ConsensusState.GetCachedValue().(*ibctmtypes.ConsensusState) + tmConsState := consState.ConsensusState.GetCachedValue().(*ibctm.ConsensusState) if !tmClientState.IsExpired(tmConsState.Timestamp, genesisBlockTime) { unexpiredConsensusStates = append(unexpiredConsensusStates, consState) } @@ -109,18 +109,18 @@ func MigrateGenesis(cdc codec.BinaryCodec, clientGenState *types.GenesisState, g // the previous version of IBC only contained the processed time metadata // if we find the processed time metadata for an unexpired height, add the // iteration key and processed height keys. - if bytes.Equal(metadata.Key, ibctmtypes.ProcessedTimeKey(height)) { + if bytes.Equal(metadata.Key, ibctm.ProcessedTimeKey(height)) { clientMetadata = append(clientMetadata, // set the processed height using the current self height // this is safe, it may cause delays in packet processing if there // is a non zero connection delay time types.GenesisMetadata{ - Key: ibctmtypes.ProcessedHeightKey(height), + Key: ibctm.ProcessedHeightKey(height), Value: []byte(selfHeight.String()), }, metadata, // processed time types.GenesisMetadata{ - Key: ibctmtypes.IterationKey(height), + Key: ibctm.IterationKey(height), Value: host.ConsensusStateKey(height), }) } diff --git a/modules/core/02-client/legacy/v100/genesis_test.go b/modules/core/02-client/legacy/v100/genesis_test.go index 7d779a250be..953643e4ecc 100644 --- a/modules/core/02-client/legacy/v100/genesis_test.go +++ b/modules/core/02-client/legacy/v100/genesis_test.go @@ -14,7 +14,7 @@ import ( "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" host "github.com/cosmos/ibc-go/v5/modules/core/24-host" "github.com/cosmos/ibc-go/v5/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" "github.com/cosmos/ibc-go/v5/testing/simapp" ) @@ -134,7 +134,7 @@ func (suite *LegacyTestSuite) TestMigrateGenesisSolomachine() { var updatedMetadata []types.GenesisMetadata var iterationKeys []types.GenesisMetadata for _, metadata := range clientMetadata.ClientMetadata { - if bytes.HasPrefix(metadata.Key, []byte(ibctmtypes.KeyIterateConsensusStatePrefix)) { + if bytes.HasPrefix(metadata.Key, []byte(ibctm.KeyIterateConsensusStatePrefix)) { iterationKeys = append(iterationKeys, metadata) } else { updatedMetadata = append(updatedMetadata, metadata) @@ -233,7 +233,7 @@ func (suite *LegacyTestSuite) TestMigrateGenesisTendermint() { var updatedMetadata []types.GenesisMetadata var iterationKeys []types.GenesisMetadata for _, metadata := range clientMetadata.ClientMetadata { - if bytes.HasPrefix(metadata.Key, []byte(ibctmtypes.KeyIterateConsensusStatePrefix)) { + if bytes.HasPrefix(metadata.Key, []byte(ibctm.KeyIterateConsensusStatePrefix)) { iterationKeys = append(iterationKeys, metadata) } else { updatedMetadata = append(updatedMetadata, metadata) @@ -258,9 +258,9 @@ func (suite *LegacyTestSuite) TestMigrateGenesisTendermint() { for _, client := range migrated.ClientsMetadata { if client.ClientId == path1.EndpointA.ClientID { for _, metadata := range client.ClientMetadata { - suite.Require().NotEqual(ibctmtypes.ProcessedTimeKey(height), metadata.Key) - suite.Require().NotEqual(ibctmtypes.ProcessedHeightKey(height), metadata.Key) - suite.Require().NotEqual(ibctmtypes.IterationKey(height), metadata.Key) + suite.Require().NotEqual(ibctm.ProcessedTimeKey(height), metadata.Key) + suite.Require().NotEqual(ibctm.ProcessedHeightKey(height), metadata.Key) + suite.Require().NotEqual(ibctm.IterationKey(height), metadata.Key) } } } @@ -278,9 +278,9 @@ func (suite *LegacyTestSuite) TestMigrateGenesisTendermint() { for _, client := range migrated.ClientsMetadata { if client.ClientId == path2.EndpointA.ClientID { for _, metadata := range client.ClientMetadata { - suite.Require().NotEqual(ibctmtypes.ProcessedTimeKey(height), metadata.Key) - suite.Require().NotEqual(ibctmtypes.ProcessedHeightKey(height), metadata.Key) - suite.Require().NotEqual(ibctmtypes.IterationKey(height), metadata.Key) + suite.Require().NotEqual(ibctm.ProcessedTimeKey(height), metadata.Key) + suite.Require().NotEqual(ibctm.ProcessedHeightKey(height), metadata.Key) + suite.Require().NotEqual(ibctm.IterationKey(height), metadata.Key) } } } diff --git a/modules/core/02-client/legacy/v100/store.go b/modules/core/02-client/legacy/v100/store.go index 1d22547cf26..b59f512f92d 100644 --- a/modules/core/02-client/legacy/v100/store.go +++ b/modules/core/02-client/legacy/v100/store.go @@ -15,7 +15,7 @@ import ( host "github.com/cosmos/ibc-go/v5/modules/core/24-host" "github.com/cosmos/ibc-go/v5/modules/core/exported" smtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ) // MigrateStore performs in-place store migrations from SDK v0.40 of the IBC module to v1.0.0 of ibc-go. @@ -88,7 +88,7 @@ func MigrateStore(ctx sdk.Context, storeKey storetypes.StoreKey, cdc codec.Binar return sdkerrors.Wrap(err, "failed to unmarshal client state bytes into tendermint client state") } - tmClientState, ok := clientState.(*ibctmtypes.ClientState) + tmClientState, ok := clientState.(*ibctm.ClientState) if !ok { return sdkerrors.Wrap(clienttypes.ErrInvalidClient, "client state is not tendermint even though client id contains 07-tendermint") } @@ -96,7 +96,7 @@ func MigrateStore(ctx sdk.Context, storeKey storetypes.StoreKey, cdc codec.Binar // add iteration keys so pruning will be successful addConsensusMetadata(ctx, clientStore) - if err = ibctmtypes.PruneAllExpiredConsensusStates(ctx, clientStore, cdc, tmClientState); err != nil { + if err = ibctm.PruneAllExpiredConsensusStates(ctx, clientStore, cdc, tmClientState); err != nil { return err } @@ -170,7 +170,7 @@ func addConsensusMetadata(ctx sdk.Context, clientStore sdk.KVStore) { for _, height := range heights { // set the iteration key and processed height // these keys were not included in the SDK v0.42.0 release - ibctmtypes.SetProcessedHeight(clientStore, height, clienttypes.GetSelfHeight(ctx)) - ibctmtypes.SetIterationKey(clientStore, height) + ibctm.SetProcessedHeight(clientStore, height, clienttypes.GetSelfHeight(ctx)) + ibctm.SetIterationKey(clientStore, height) } } diff --git a/modules/core/02-client/legacy/v100/store_test.go b/modules/core/02-client/legacy/v100/store_test.go index 0d07cfc91e0..69c431fe15f 100644 --- a/modules/core/02-client/legacy/v100/store_test.go +++ b/modules/core/02-client/legacy/v100/store_test.go @@ -10,7 +10,7 @@ import ( "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" host "github.com/cosmos/ibc-go/v5/modules/core/24-host" "github.com/cosmos/ibc-go/v5/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ) @@ -146,15 +146,15 @@ func (suite *LegacyTestSuite) TestMigrateStoreTendermint() { ctx := path.EndpointA.Chain.GetContext() clientStore := path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) - processedTime, ok := ibctmtypes.GetProcessedTime(clientStore, pruneHeight) + processedTime, ok := ibctm.GetProcessedTime(clientStore, pruneHeight) suite.Require().True(ok) suite.Require().NotNil(processedTime) - processedHeight, ok := ibctmtypes.GetProcessedHeight(clientStore, pruneHeight) + processedHeight, ok := ibctm.GetProcessedHeight(clientStore, pruneHeight) suite.Require().True(ok) suite.Require().NotNil(processedHeight) - expectedConsKey := ibctmtypes.GetIterationKey(clientStore, pruneHeight) + expectedConsKey := ibctm.GetIterationKey(clientStore, pruneHeight) suite.Require().NotNil(expectedConsKey) } pruneHeightMap[path] = pruneHeights @@ -174,8 +174,8 @@ func (suite *LegacyTestSuite) TestMigrateStoreTendermint() { // remove processed height and iteration keys since these were missing from previous version of ibc module clientStore := path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.ClientStore(path.EndpointA.Chain.GetContext(), path.EndpointA.ClientID) for _, height := range unexpiredHeights { - clientStore.Delete(ibctmtypes.ProcessedHeightKey(height)) - clientStore.Delete(ibctmtypes.IterationKey(height)) + clientStore.Delete(ibctm.ProcessedHeightKey(height)) + clientStore.Delete(ibctm.IterationKey(height)) } unexpiredHeightMap[path] = unexpiredHeights @@ -198,15 +198,15 @@ func (suite *LegacyTestSuite) TestMigrateStoreTendermint() { suite.Require().False(ok, i) suite.Require().Nil(consState, i) - processedTime, ok := ibctmtypes.GetProcessedTime(clientStore, pruneHeight) + processedTime, ok := ibctm.GetProcessedTime(clientStore, pruneHeight) suite.Require().False(ok, i) suite.Require().Equal(uint64(0), processedTime, i) - processedHeight, ok := ibctmtypes.GetProcessedHeight(clientStore, pruneHeight) + processedHeight, ok := ibctm.GetProcessedHeight(clientStore, pruneHeight) suite.Require().False(ok, i) suite.Require().Nil(processedHeight, i) - expectedConsKey := ibctmtypes.GetIterationKey(clientStore, pruneHeight) + expectedConsKey := ibctm.GetIterationKey(clientStore, pruneHeight) suite.Require().Nil(expectedConsKey, i) } @@ -216,15 +216,15 @@ func (suite *LegacyTestSuite) TestMigrateStoreTendermint() { suite.Require().True(ok) suite.Require().NotNil(consState) - processedTime, ok := ibctmtypes.GetProcessedTime(clientStore, height) + processedTime, ok := ibctm.GetProcessedTime(clientStore, height) suite.Require().True(ok) suite.Require().NotEqual(uint64(0), processedTime) - processedHeight, ok := ibctmtypes.GetProcessedHeight(clientStore, height) + processedHeight, ok := ibctm.GetProcessedHeight(clientStore, height) suite.Require().True(ok) suite.Require().Equal(types.GetSelfHeight(path.EndpointA.Chain.GetContext()), processedHeight) - consKey := ibctmtypes.GetIterationKey(clientStore, height) + consKey := ibctm.GetIterationKey(clientStore, height) suite.Require().Equal(host.ConsensusStateKey(height), consKey) } } diff --git a/modules/core/02-client/proposal_handler_test.go b/modules/core/02-client/proposal_handler_test.go index c128eb0dee0..9c13a1c56f6 100644 --- a/modules/core/02-client/proposal_handler_test.go +++ b/modules/core/02-client/proposal_handler_test.go @@ -7,7 +7,7 @@ import ( client "github.com/cosmos/ibc-go/v5/modules/core/02-client" clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ) @@ -38,14 +38,14 @@ func (suite *ClientTestSuite) TestNewClientUpdateProposalHandler() { suite.Require().NoError(err) substituteClientState := suite.chainA.GetClientState(substitutePath.EndpointA.ClientID) - tmClientState, ok := subjectClientState.(*ibctmtypes.ClientState) + tmClientState, ok := subjectClientState.(*ibctm.ClientState) suite.Require().True(ok) tmClientState.AllowUpdateAfterMisbehaviour = true tmClientState.FrozenHeight = tmClientState.LatestHeight suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), subjectPath.EndpointA.ClientID, tmClientState) // replicate changes to substitute (they must match) - tmClientState, ok = substituteClientState.(*ibctmtypes.ClientState) + tmClientState, ok = substituteClientState.(*ibctm.ClientState) suite.Require().True(ok) tmClientState.AllowUpdateAfterMisbehaviour = true suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), substitutePath.EndpointA.ClientID, tmClientState) diff --git a/modules/core/02-client/simulation/decoder_test.go b/modules/core/02-client/simulation/decoder_test.go index 65b4a287818..40a7c8eea5e 100644 --- a/modules/core/02-client/simulation/decoder_test.go +++ b/modules/core/02-client/simulation/decoder_test.go @@ -11,7 +11,7 @@ import ( "github.com/cosmos/ibc-go/v5/modules/core/02-client/simulation" "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" host "github.com/cosmos/ibc-go/v5/modules/core/24-host" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" "github.com/cosmos/ibc-go/v5/testing/simapp" ) @@ -21,11 +21,11 @@ func TestDecodeStore(t *testing.T) { height := types.NewHeight(0, 10) - clientState := &ibctmtypes.ClientState{ + clientState := &ibctm.ClientState{ FrozenHeight: height, } - consState := &ibctmtypes.ConsensusState{ + consState := &ibctm.ConsensusState{ Timestamp: time.Now().UTC(), } diff --git a/modules/core/02-client/types/codec_test.go b/modules/core/02-client/types/codec_test.go index 8a0abb79d7e..cfcd395b0c9 100644 --- a/modules/core/02-client/types/codec_test.go +++ b/modules/core/02-client/types/codec_test.go @@ -6,7 +6,7 @@ import ( "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v5/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ) @@ -29,7 +29,7 @@ func (suite *TypesTestSuite) TestPackClientState() { }, { "tendermint client", - ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), true, }, { diff --git a/modules/core/02-client/types/encoding_test.go b/modules/core/02-client/types/encoding_test.go index 6da50574848..7142153ff7e 100644 --- a/modules/core/02-client/types/encoding_test.go +++ b/modules/core/02-client/types/encoding_test.go @@ -2,12 +2,12 @@ package types_test import ( "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ) func (suite *TypesTestSuite) TestMarshalHeader() { cdc := suite.chainA.App.AppCodec() - h := &ibctmtypes.Header{ + h := &ibctm.Header{ TrustedHeight: types.NewHeight(4, 100), } diff --git a/modules/core/02-client/types/genesis_test.go b/modules/core/02-client/types/genesis_test.go index 778c0ffd30a..08d55606c61 100644 --- a/modules/core/02-client/types/genesis_test.go +++ b/modules/core/02-client/types/genesis_test.go @@ -10,7 +10,7 @@ import ( commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v5/modules/core/exported" solomachinetypes "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ibctestingmock "github.com/cosmos/ibc-go/v5/testing/mock" ) @@ -76,7 +76,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - tmClientID0, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + tmClientID0, ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -85,7 +85,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { []types.ConsensusStateWithHeight{ types.NewConsensusStateWithHeight( header.GetHeight().(types.Height), - ibctmtypes.NewConsensusState( + ibctm.NewConsensusState( header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, ), ), @@ -112,7 +112,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - soloMachineClientID, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + soloMachineClientID, ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), types.NewIdentifiedClientState(tmClientID0, solomachinetypes.NewClientState(0, &solomachinetypes.ConsensusState{suite.solomachine.ConsensusState().PublicKey, suite.solomachine.Diversifier, suite.solomachine.Time}, false)), }, @@ -129,7 +129,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - invalidClientID, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + invalidClientID, ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -138,7 +138,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { []types.ConsensusStateWithHeight{ types.NewConsensusStateWithHeight( header.GetHeight().(types.Height), - ibctmtypes.NewConsensusState( + ibctm.NewConsensusState( header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, ), ), @@ -157,7 +157,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - tmClientID0, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + tmClientID0, ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -166,7 +166,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { []types.ConsensusStateWithHeight{ types.NewConsensusStateWithHeight( types.NewHeight(1, 1), - ibctmtypes.NewConsensusState( + ibctm.NewConsensusState( header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, ), ), @@ -185,7 +185,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - tmClientID0, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + tmClientID0, ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -194,7 +194,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { []types.ConsensusStateWithHeight{ types.NewConsensusStateWithHeight( types.ZeroHeight(), - ibctmtypes.NewConsensusState( + ibctm.NewConsensusState( header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, ), ), @@ -213,7 +213,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - tmClientID0, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + tmClientID0, ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -222,7 +222,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { []types.ConsensusStateWithHeight{ types.NewConsensusStateWithHeight( types.NewHeight(1, 1), - ibctmtypes.NewConsensusState( + ibctm.NewConsensusState( time.Time{}, commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, ), ), @@ -241,7 +241,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - tmClientID0, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + tmClientID0, ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -250,7 +250,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { []types.ConsensusStateWithHeight{ types.NewConsensusStateWithHeight( header.GetHeight().(types.Height), - ibctmtypes.NewConsensusState( + ibctm.NewConsensusState( header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, ), ), @@ -269,7 +269,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - clientID, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + clientID, ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -278,7 +278,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { []types.ConsensusStateWithHeight{ types.NewConsensusStateWithHeight( header.GetHeight().(types.Height), - ibctmtypes.NewConsensusState( + ibctm.NewConsensusState( header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, ), ), @@ -305,7 +305,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - clientID, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + clientID, ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -314,7 +314,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { []types.ConsensusStateWithHeight{ types.NewConsensusStateWithHeight( header.GetHeight().(types.Height), - ibctmtypes.NewConsensusState( + ibctm.NewConsensusState( header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, ), ), @@ -340,7 +340,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - tmClientID0, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + tmClientID0, ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -349,7 +349,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { []types.ConsensusStateWithHeight{ types.NewConsensusStateWithHeight( header.GetHeight().(types.Height), - ibctmtypes.NewConsensusState( + ibctm.NewConsensusState( header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, ), ), @@ -368,7 +368,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - tmClientID0, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + tmClientID0, ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -377,7 +377,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { []types.ConsensusStateWithHeight{ types.NewConsensusStateWithHeight( header.GetHeight().(types.Height), - ibctmtypes.NewConsensusState( + ibctm.NewConsensusState( header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, ), ), @@ -396,10 +396,10 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - tmClientID0, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + tmClientID0, ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), types.NewIdentifiedClientState( - tmClientID1, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + tmClientID1, ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -408,7 +408,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { []types.ConsensusStateWithHeight{ types.NewConsensusStateWithHeight( header.GetHeight().(types.Height), - ibctmtypes.NewConsensusState( + ibctm.NewConsensusState( header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, ), ), @@ -427,7 +427,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { genState: types.NewGenesisState( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - "my-client", ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + "my-client", ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -436,7 +436,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { []types.ConsensusStateWithHeight{ types.NewConsensusStateWithHeight( header.GetHeight().(types.Height), - ibctmtypes.NewConsensusState( + ibctm.NewConsensusState( header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, ), ), @@ -460,7 +460,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { []types.ConsensusStateWithHeight{ types.NewConsensusStateWithHeight( header.GetHeight().(types.Height), - ibctmtypes.NewConsensusState( + ibctm.NewConsensusState( header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, ), ), diff --git a/modules/core/02-client/types/msgs_test.go b/modules/core/02-client/types/msgs_test.go index 34622f67051..b42445c030c 100644 --- a/modules/core/02-client/types/msgs_test.go +++ b/modules/core/02-client/types/msgs_test.go @@ -10,7 +10,7 @@ import ( "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" solomachinetypes "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ) @@ -56,7 +56,7 @@ func (suite *TypesTestSuite) TestMarshalMsgCreateClient() { }, { "tendermint client", func() { - tendermintClient := ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + tendermintClient := ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) msg, err = types.NewMsgCreateClient(tendermintClient, suite.chainA.CurrentTMClientHeader().ConsensusState(), suite.chainA.SenderAccount.GetAddress().String()) suite.Require().NoError(err) }, @@ -101,7 +101,7 @@ func (suite *TypesTestSuite) TestMsgCreateClient_ValidateBasic() { { "valid - tendermint client", func() { - tendermintClient := ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + tendermintClient := ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) msg, err = types.NewMsgCreateClient(tendermintClient, suite.chainA.CurrentTMClientHeader().ConsensusState(), suite.chainA.SenderAccount.GetAddress().String()) suite.Require().NoError(err) }, @@ -110,7 +110,7 @@ func (suite *TypesTestSuite) TestMsgCreateClient_ValidateBasic() { { "invalid tendermint client", func() { - msg, err = types.NewMsgCreateClient(&ibctmtypes.ClientState{}, suite.chainA.CurrentTMClientHeader().ConsensusState(), suite.chainA.SenderAccount.GetAddress().String()) + msg, err = types.NewMsgCreateClient(&ibctm.ClientState{}, suite.chainA.CurrentTMClientHeader().ConsensusState(), suite.chainA.SenderAccount.GetAddress().String()) suite.Require().NoError(err) }, false, @@ -125,7 +125,7 @@ func (suite *TypesTestSuite) TestMsgCreateClient_ValidateBasic() { { "failed to unpack consensus state", func() { - tendermintClient := ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + tendermintClient := ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) msg, err = types.NewMsgCreateClient(tendermintClient, suite.chainA.CurrentTMClientHeader().ConsensusState(), suite.chainA.SenderAccount.GetAddress().String()) suite.Require().NoError(err) msg.ConsensusState = nil @@ -169,7 +169,7 @@ func (suite *TypesTestSuite) TestMsgCreateClient_ValidateBasic() { { "invalid - client state and consensus state client types do not match", func() { - tendermintClient := ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + tendermintClient := ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2) msg, err = types.NewMsgCreateClient(tendermintClient, soloMachine.ConsensusState(), suite.chainA.SenderAccount.GetAddress().String()) suite.Require().NoError(err) @@ -269,7 +269,7 @@ func (suite *TypesTestSuite) TestMsgUpdateClient_ValidateBasic() { { "invalid tendermint header", func() { - msg, err = types.NewMsgUpdateClient("tendermint", &ibctmtypes.Header{}, suite.chainA.SenderAccount.GetAddress().String()) + msg, err = types.NewMsgUpdateClient("tendermint", &ibctm.Header{}, suite.chainA.SenderAccount.GetAddress().String()) suite.Require().NoError(err) }, false, @@ -332,8 +332,8 @@ func (suite *TypesTestSuite) TestMarshalMsgUpgradeClient() { { "client upgrades to new tendermint client", func() { - tendermintClient := ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) - tendermintConsState := &ibctmtypes.ConsensusState{NextValidatorsHash: []byte("nextValsHash")} + tendermintClient := ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + tendermintConsState := &ibctm.ConsensusState{NextValidatorsHash: []byte("nextValsHash")} msg, err = types.NewMsgUpgradeClient("clientid", tendermintClient, tendermintConsState, []byte("proofUpgradeClient"), []byte("proofUpgradeConsState"), suite.chainA.SenderAccount.GetAddress().String()) suite.Require().NoError(err) }, @@ -445,8 +445,8 @@ func (suite *TypesTestSuite) TestMsgUpgradeClient_ValidateBasic() { for _, tc := range cases { tc := tc - clientState := ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) - consState := &ibctmtypes.ConsensusState{NextValidatorsHash: []byte("nextValsHash")} + clientState := ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + consState := &ibctm.ConsensusState{NextValidatorsHash: []byte("nextValsHash")} msg, err := types.NewMsgUpgradeClient("testclientid", clientState, consState, []byte("proofUpgradeClient"), []byte("proofUpgradeConsState"), suite.chainA.SenderAccount.GetAddress().String()) suite.Require().NoError(err) @@ -486,7 +486,7 @@ func (suite *TypesTestSuite) TestMarshalMsgSubmitMisbehaviour() { header1 := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, int64(height.RevisionHeight), heightMinus1, suite.chainA.CurrentHeader.Time, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) header2 := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, int64(height.RevisionHeight), heightMinus1, suite.chainA.CurrentHeader.Time.Add(time.Minute), suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) - misbehaviour := ibctmtypes.NewMisbehaviour("tendermint", header1, header2) + misbehaviour := ibctm.NewMisbehaviour("tendermint", header1, header2) msg, err = types.NewMsgSubmitMisbehaviour("tendermint", misbehaviour, suite.chainA.SenderAccount.GetAddress().String()) suite.Require().NoError(err) }, @@ -543,7 +543,7 @@ func (suite *TypesTestSuite) TestMsgSubmitMisbehaviour_ValidateBasic() { header1 := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, int64(height.RevisionHeight), heightMinus1, suite.chainA.CurrentHeader.Time, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) header2 := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, int64(height.RevisionHeight), heightMinus1, suite.chainA.CurrentHeader.Time.Add(time.Minute), suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) - misbehaviour := ibctmtypes.NewMisbehaviour("tendermint", header1, header2) + misbehaviour := ibctm.NewMisbehaviour("tendermint", header1, header2) msg, err = types.NewMsgSubmitMisbehaviour("tendermint", misbehaviour, suite.chainA.SenderAccount.GetAddress().String()) suite.Require().NoError(err) }, @@ -552,7 +552,7 @@ func (suite *TypesTestSuite) TestMsgSubmitMisbehaviour_ValidateBasic() { { "invalid tendermint misbehaviour", func() { - msg, err = types.NewMsgSubmitMisbehaviour("tendermint", &ibctmtypes.Misbehaviour{}, suite.chainA.SenderAccount.GetAddress().String()) + msg, err = types.NewMsgSubmitMisbehaviour("tendermint", &ibctm.Misbehaviour{}, suite.chainA.SenderAccount.GetAddress().String()) suite.Require().NoError(err) }, false, diff --git a/modules/core/02-client/types/proposal_test.go b/modules/core/02-client/types/proposal_test.go index 073056b289d..2b1725248e0 100644 --- a/modules/core/02-client/types/proposal_test.go +++ b/modules/core/02-client/types/proposal_test.go @@ -9,7 +9,7 @@ import ( upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ) @@ -122,7 +122,7 @@ func (suite *TypesTestSuite) TestUpgradeProposalValidateBasic() { }, { "non zeroed fields", func() { - proposal, err = types.NewUpgradeProposal(ibctesting.Title, ibctesting.Description, plan, &ibctmtypes.ClientState{ + proposal, err = types.NewUpgradeProposal(ibctesting.Title, ibctesting.Description, plan, &ibctm.ClientState{ FrozenHeight: types.Height{ RevisionHeight: 10, }, @@ -149,7 +149,7 @@ func (suite *TypesTestSuite) TestUpgradeProposalValidateBasic() { }, { "failed to unpack client state", func() { - any, err := types.PackConsensusState(&ibctmtypes.ConsensusState{}) + any, err := types.PackConsensusState(&ibctm.ConsensusState{}) suite.Require().NoError(err) proposal = &types.UpgradeProposal{ @@ -184,7 +184,7 @@ func (suite *TypesTestSuite) TestMarshalUpgradeProposal() { Name: "upgrade ibc", Height: 1000, } - content, err := types.NewUpgradeProposal("title", "description", plan, &ibctmtypes.ClientState{}) + content, err := types.NewUpgradeProposal("title", "description", plan, &ibctm.ClientState{}) suite.Require().NoError(err) up, ok := content.(*types.UpgradeProposal) @@ -194,7 +194,7 @@ func (suite *TypesTestSuite) TestMarshalUpgradeProposal() { ir := codectypes.NewInterfaceRegistry() types.RegisterInterfaces(ir) govtypes.RegisterInterfaces(ir) - ibctmtypes.RegisterInterfaces(ir) + ibctm.RegisterInterfaces(ir) cdc := codec.NewProtoCodec(ir) // marshal message @@ -218,10 +218,10 @@ func (suite *TypesTestSuite) TestUpgradeString() { Height: 1000, } - proposal, err := types.NewUpgradeProposal(ibctesting.Title, ibctesting.Description, plan, &ibctmtypes.ClientState{}) + proposal, err := types.NewUpgradeProposal(ibctesting.Title, ibctesting.Description, plan, &ibctm.ClientState{}) suite.Require().NoError(err) - expect := fmt.Sprintf("IBC Upgrade Proposal\n Title: title\n Description: description\n Upgrade Plan\n Name: ibc upgrade\n height: 1000\n Info: https://foo.bar/baz.\n Upgraded IBC Client: %s", &ibctmtypes.ClientState{}) + expect := fmt.Sprintf("IBC Upgrade Proposal\n Title: title\n Description: description\n Upgrade Plan\n Name: ibc upgrade\n height: 1000\n Info: https://foo.bar/baz.\n Upgraded IBC Client: %s", &ibctm.ClientState{}) suite.Require().Equal(expect, proposal.String()) } diff --git a/modules/core/03-connection/keeper/handshake_test.go b/modules/core/03-connection/keeper/handshake_test.go index 6ffb8f697dc..0dfafa5f911 100644 --- a/modules/core/03-connection/keeper/handshake_test.go +++ b/modules/core/03-connection/keeper/handshake_test.go @@ -7,7 +7,7 @@ import ( "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" host "github.com/cosmos/ibc-go/v5/modules/core/24-host" "github.com/cosmos/ibc-go/v5/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ) @@ -125,7 +125,7 @@ func (suite *KeeperTestSuite) TestConnOpenTry() { counterpartyClient = suite.chainA.GetClientState(path.EndpointA.ClientID) // Set an invalid client of chainA on chainB - tmClient, ok := counterpartyClient.(*ibctmtypes.ClientState) + tmClient, ok := counterpartyClient.(*ibctm.ClientState) suite.Require().True(ok) tmClient.ChainId = "wrongchainid" @@ -182,7 +182,7 @@ func (suite *KeeperTestSuite) TestConnOpenTry() { counterpartyClient = suite.chainA.GetClientState(path.EndpointA.ClientID) // modify counterparty client without setting in store so it still passes validate but fails proof verification - tmClient, ok := counterpartyClient.(*ibctmtypes.ClientState) + tmClient, ok := counterpartyClient.(*ibctm.ClientState) suite.Require().True(ok) tmClient.LatestHeight = tmClient.LatestHeight.Increment().(clienttypes.Height) }, false}, @@ -194,7 +194,7 @@ func (suite *KeeperTestSuite) TestConnOpenTry() { consState, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetLatestClientConsensusState(suite.chainA.GetContext(), path.EndpointA.ClientID) suite.Require().True(found) - tmConsState, ok := consState.(*ibctmtypes.ConsensusState) + tmConsState, ok := consState.(*ibctm.ConsensusState) suite.Require().True(ok) tmConsState.Timestamp = time.Now() @@ -291,7 +291,7 @@ func (suite *KeeperTestSuite) TestConnOpenAck() { counterpartyClient = suite.chainB.GetClientState(path.EndpointB.ClientID) // Set an invalid client of chainA on chainB - tmClient, ok := counterpartyClient.(*ibctmtypes.ClientState) + tmClient, ok := counterpartyClient.(*ibctm.ClientState) suite.Require().True(ok) tmClient.ChainId = "wrongchainid" @@ -431,7 +431,7 @@ func (suite *KeeperTestSuite) TestConnOpenAck() { counterpartyClient = suite.chainB.GetClientState(path.EndpointB.ClientID) // modify counterparty client without setting in store so it still passes validate but fails proof verification - tmClient, ok := counterpartyClient.(*ibctmtypes.ClientState) + tmClient, ok := counterpartyClient.(*ibctm.ClientState) suite.Require().True(ok) tmClient.LatestHeight = tmClient.LatestHeight.Increment().(clienttypes.Height) @@ -449,7 +449,7 @@ func (suite *KeeperTestSuite) TestConnOpenAck() { consState, found := suite.chainB.App.GetIBCKeeper().ClientKeeper.GetLatestClientConsensusState(suite.chainB.GetContext(), path.EndpointB.ClientID) suite.Require().True(found) - tmConsState, ok := consState.(*ibctmtypes.ConsensusState) + tmConsState, ok := consState.(*ibctm.ConsensusState) suite.Require().True(ok) tmConsState.Timestamp = tmConsState.Timestamp.Add(time.Second) diff --git a/modules/core/03-connection/keeper/verify_test.go b/modules/core/03-connection/keeper/verify_test.go index 5b9280f0036..3ad64e9b285 100644 --- a/modules/core/03-connection/keeper/verify_test.go +++ b/modules/core/03-connection/keeper/verify_test.go @@ -9,7 +9,7 @@ import ( channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" host "github.com/cosmos/ibc-go/v5/modules/core/24-host" "github.com/cosmos/ibc-go/v5/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ibcmock "github.com/cosmos/ibc-go/v5/testing/mock" ) @@ -38,12 +38,12 @@ func (suite *KeeperTestSuite) TestVerifyClientState() { heightDiff = 5 }, false}, {"verification failed", func() { - counterpartyClient := path.EndpointB.GetClientState().(*ibctmtypes.ClientState) + counterpartyClient := path.EndpointB.GetClientState().(*ibctm.ClientState) counterpartyClient.ChainId = "wrongChainID" path.EndpointB.SetClientState(counterpartyClient) }, false}, {"client status is not active - client is expired", func() { - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) clientState.FrozenHeight = clienttypes.NewHeight(0, 1) path.EndpointA.SetClientState(clientState) }, false}, @@ -109,7 +109,7 @@ func (suite *KeeperTestSuite) TestVerifyClientConsensusState() { consState, found := suite.chainB.App.GetIBCKeeper().ClientKeeper.GetLatestClientConsensusState(suite.chainB.GetContext(), path.EndpointB.ClientID) suite.Require().True(found) - tmConsState, ok := consState.(*ibctmtypes.ConsensusState) + tmConsState, ok := consState.(*ibctm.ConsensusState) suite.Require().True(ok) tmConsState.Timestamp = time.Now() @@ -118,7 +118,7 @@ func (suite *KeeperTestSuite) TestVerifyClientConsensusState() { suite.coordinator.CommitBlock(suite.chainB) }, false}, {"client status is not active - client is expired", func() { - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) clientState.FrozenHeight = clienttypes.NewHeight(0, 1) path.EndpointA.SetClientState(clientState) }, false}, @@ -183,7 +183,7 @@ func (suite *KeeperTestSuite) TestVerifyConnectionState() { path.EndpointA.SetConnection(connection) }, false}, {"client status is not active - client is expired", func() { - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) clientState.FrozenHeight = clienttypes.NewHeight(0, 1) path.EndpointA.SetClientState(clientState) }, false}, @@ -248,7 +248,7 @@ func (suite *KeeperTestSuite) TestVerifyChannelState() { path.EndpointA.SetChannel(channel) }, false}, {"client status is not active - client is expired", func() { - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) clientState.FrozenHeight = clienttypes.NewHeight(0, 1) path.EndpointA.SetClientState(clientState) }, false}, @@ -326,7 +326,7 @@ func (suite *KeeperTestSuite) TestVerifyPacketCommitment() { packet.Data = []byte(ibctesting.InvalidID) }, false}, {"client status is not active - client is expired", func() { - clientState := path.EndpointB.GetClientState().(*ibctmtypes.ClientState) + clientState := path.EndpointB.GetClientState().(*ibctm.ClientState) clientState.FrozenHeight = clienttypes.NewHeight(0, 1) path.EndpointB.SetClientState(clientState) }, false}, @@ -418,7 +418,7 @@ func (suite *KeeperTestSuite) TestVerifyPacketAcknowledgement() { ack = ibcmock.MockFailAcknowledgement }, false}, {"client status is not active - client is expired", func() { - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) clientState.FrozenHeight = clienttypes.NewHeight(0, 1) path.EndpointA.SetClientState(clientState) }, false}, @@ -524,7 +524,7 @@ func (suite *KeeperTestSuite) TestVerifyPacketReceiptAbsence() { suite.Require().NoError(err) }, false}, {"client status is not active - client is expired", func() { - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) clientState.FrozenHeight = clienttypes.NewHeight(0, 1) path.EndpointA.SetClientState(clientState) }, false}, @@ -553,7 +553,7 @@ func (suite *KeeperTestSuite) TestVerifyPacketReceiptAbsence() { connection := path.EndpointA.GetConnection() connection.DelayPeriod = delayTimePeriod - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) if clientState.FrozenHeight.IsZero() { // need to update height to prove absence or receipt suite.coordinator.CommitBlock(suite.chainA, suite.chainB) @@ -624,7 +624,7 @@ func (suite *KeeperTestSuite) TestVerifyNextSequenceRecv() { offsetSeq = 1 }, false}, {"client status is not active - client is expired", func() { - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) clientState.FrozenHeight = clienttypes.NewHeight(0, 1) path.EndpointA.SetClientState(clientState) }, false}, diff --git a/modules/core/03-connection/types/msgs_test.go b/modules/core/03-connection/types/msgs_test.go index 28b56cb6648..5d8d4bcc99c 100644 --- a/modules/core/03-connection/types/msgs_test.go +++ b/modules/core/03-connection/types/msgs_test.go @@ -16,7 +16,7 @@ import ( clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" "github.com/cosmos/ibc-go/v5/testing/simapp" ) @@ -109,22 +109,22 @@ func (suite *MsgTestSuite) TestNewMsgConnectionOpenInit() { func (suite *MsgTestSuite) TestNewMsgConnectionOpenTry() { prefix := commitmenttypes.NewMerklePrefix([]byte("storePrefixKey")) - clientState := ibctmtypes.NewClientState( - chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false, + clientState := ibctm.NewClientState( + chainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false, ) any, err := clienttypes.PackClientState(clientState) suite.Require().NoError(err) // Pack consensus state into any to test unpacking error - consState := ibctmtypes.NewConsensusState( + consState := ibctm.NewConsensusState( time.Now(), commitmenttypes.NewMerkleRoot([]byte("root")), []byte("nextValsHash"), ) invalidAny := clienttypes.MustPackConsensusState(consState) counterparty := types.NewCounterparty("connectiontotest", "clienttotest", prefix) // invalidClientState fails validateBasic - invalidClient := ibctmtypes.NewClientState( - chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clienttypes.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false, + invalidClient := ibctm.NewClientState( + chainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clienttypes.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false, ) testCases := []struct { @@ -162,19 +162,19 @@ func (suite *MsgTestSuite) TestNewMsgConnectionOpenTry() { } func (suite *MsgTestSuite) TestNewMsgConnectionOpenAck() { - clientState := ibctmtypes.NewClientState( - chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false, + clientState := ibctm.NewClientState( + chainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false, ) // Pack consensus state into any to test unpacking error - consState := ibctmtypes.NewConsensusState( + consState := ibctm.NewConsensusState( time.Now(), commitmenttypes.NewMerkleRoot([]byte("root")), []byte("nextValsHash"), ) invalidAny := clienttypes.MustPackConsensusState(consState) // invalidClientState fails validateBasic - invalidClient := ibctmtypes.NewClientState( - chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clienttypes.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false, + invalidClient := ibctm.NewClientState( + chainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clienttypes.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false, ) connectionID := "connection-0" diff --git a/modules/core/04-channel/keeper/packet_test.go b/modules/core/04-channel/keeper/packet_test.go index 29319bf7507..8614f369bbd 100644 --- a/modules/core/04-channel/keeper/packet_test.go +++ b/modules/core/04-channel/keeper/packet_test.go @@ -12,7 +12,7 @@ import ( "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" host "github.com/cosmos/ibc-go/v5/modules/core/24-host" "github.com/cosmos/ibc-go/v5/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ibcmock "github.com/cosmos/ibc-go/v5/testing/mock" ) @@ -148,7 +148,7 @@ func (suite *KeeperTestSuite) TestSendPacket() { connection := path.EndpointA.GetConnection() clientState := path.EndpointA.GetClientState() - cs, ok := clientState.(*ibctmtypes.ClientState) + cs, ok := clientState.(*ibctm.ClientState) suite.Require().True(ok) // freeze client diff --git a/modules/core/genesis_test.go b/modules/core/genesis_test.go index d5c9b7abbd1..786339fda2d 100644 --- a/modules/core/genesis_test.go +++ b/modules/core/genesis_test.go @@ -15,7 +15,7 @@ import ( commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v5/modules/core/exported" "github.com/cosmos/ibc-go/v5/modules/core/types" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" "github.com/cosmos/ibc-go/v5/testing/simapp" ) @@ -75,7 +75,7 @@ func (suite *IBCTestSuite) TestValidateGenesis() { ClientGenesis: clienttypes.NewGenesisState( []clienttypes.IdentifiedClientState{ clienttypes.NewIdentifiedClientState( - clientID, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + clientID, ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []clienttypes.ClientConsensusStates{ @@ -84,7 +84,7 @@ func (suite *IBCTestSuite) TestValidateGenesis() { []clienttypes.ConsensusStateWithHeight{ clienttypes.NewConsensusStateWithHeight( header.GetHeight().(clienttypes.Height), - ibctmtypes.NewConsensusState( + ibctm.NewConsensusState( header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.AppHash), header.Header.NextValidatorsHash, ), ), @@ -152,7 +152,7 @@ func (suite *IBCTestSuite) TestValidateGenesis() { ClientGenesis: clienttypes.NewGenesisState( []clienttypes.IdentifiedClientState{ clienttypes.NewIdentifiedClientState( - clientID, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + clientID, ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, nil, @@ -233,7 +233,7 @@ func (suite *IBCTestSuite) TestInitGenesis() { ClientGenesis: clienttypes.NewGenesisState( []clienttypes.IdentifiedClientState{ clienttypes.NewIdentifiedClientState( - clientID, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + clientID, ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []clienttypes.ClientConsensusStates{ @@ -242,7 +242,7 @@ func (suite *IBCTestSuite) TestInitGenesis() { []clienttypes.ConsensusStateWithHeight{ clienttypes.NewConsensusStateWithHeight( header.GetHeight().(clienttypes.Height), - ibctmtypes.NewConsensusState( + ibctm.NewConsensusState( header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.AppHash), header.Header.NextValidatorsHash, ), ), diff --git a/modules/core/keeper/msg_server_test.go b/modules/core/keeper/msg_server_test.go index a98e00327d7..f2ef2e5e89c 100644 --- a/modules/core/keeper/msg_server_test.go +++ b/modules/core/keeper/msg_server_test.go @@ -10,7 +10,7 @@ import ( host "github.com/cosmos/ibc-go/v5/modules/core/24-host" "github.com/cosmos/ibc-go/v5/modules/core/exported" "github.com/cosmos/ibc-go/v5/modules/core/keeper" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ibcmock "github.com/cosmos/ibc-go/v5/testing/mock" ) @@ -642,11 +642,11 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { { name: "successful upgrade", setup: func() { - upgradedClient = ibctmtypes.NewClientState(newChainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod+ibctesting.TrustingPeriod, ibctesting.MaxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + upgradedClient = ibctm.NewClientState(newChainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod+ibctesting.TrustingPeriod, ibctesting.MaxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) // Call ZeroCustomFields on upgraded clients to clear any client-chosen parameters in test-case upgradedClient upgradedClient = upgradedClient.ZeroCustomFields() - upgradedConsState = &ibctmtypes.ConsensusState{ + upgradedConsState = &ibctm.ConsensusState{ NextValidatorsHash: []byte("nextValsHash"), } @@ -682,11 +682,11 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { { name: "VerifyUpgrade fails", setup: func() { - upgradedClient = ibctmtypes.NewClientState(newChainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod+ibctesting.TrustingPeriod, ibctesting.MaxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + upgradedClient = ibctm.NewClientState(newChainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod+ibctesting.TrustingPeriod, ibctesting.MaxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) // Call ZeroCustomFields on upgraded clients to clear any client-chosen parameters in test-case upgradedClient upgradedClient = upgradedClient.ZeroCustomFields() - upgradedConsState = &ibctmtypes.ConsensusState{ + upgradedConsState = &ibctm.ConsensusState{ NextValidatorsHash: []byte("nextValsHash"), } @@ -720,7 +720,7 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { suite.coordinator.SetupClients(path) var err error - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) revisionNumber := clienttypes.ParseChainID(clientState.ChainId) newChainID, err = clienttypes.SetRevisionNumber(clientState.ChainId, revisionNumber+1) diff --git a/modules/core/simulation/decoder_test.go b/modules/core/simulation/decoder_test.go index 43f1d918e8c..6701dca6b6f 100644 --- a/modules/core/simulation/decoder_test.go +++ b/modules/core/simulation/decoder_test.go @@ -12,7 +12,7 @@ import ( channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" host "github.com/cosmos/ibc-go/v5/modules/core/24-host" "github.com/cosmos/ibc-go/v5/modules/core/simulation" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" "github.com/cosmos/ibc-go/v5/testing/simapp" ) @@ -25,7 +25,7 @@ func TestDecodeStore(t *testing.T) { channelID := "channelidone" portID := "portidone" - clientState := &ibctmtypes.ClientState{ + clientState := &ibctm.ClientState{ FrozenHeight: clienttypes.NewHeight(0, 10), } connection := connectiontypes.ConnectionEnd{ diff --git a/modules/core/types/codec.go b/modules/core/types/codec.go index 30cb5ecd263..5911d6d7215 100644 --- a/modules/core/types/codec.go +++ b/modules/core/types/codec.go @@ -8,7 +8,7 @@ import ( channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" solomachinetypes "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ) // RegisterInterfaces registers x/ibc interfaces into protobuf Any. @@ -17,6 +17,6 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) { connectiontypes.RegisterInterfaces(registry) channeltypes.RegisterInterfaces(registry) solomachinetypes.RegisterInterfaces(registry) - ibctmtypes.RegisterInterfaces(registry) + ibctm.RegisterInterfaces(registry) commitmenttypes.RegisterInterfaces(registry) } diff --git a/modules/light-clients/06-solomachine/client_state_test.go b/modules/light-clients/06-solomachine/client_state_test.go index 9f5c4ed02ff..89d41b7d18f 100644 --- a/modules/light-clients/06-solomachine/client_state_test.go +++ b/modules/light-clients/06-solomachine/client_state_test.go @@ -8,7 +8,7 @@ import ( commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v5/modules/core/exported" solomachine "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ) @@ -112,7 +112,7 @@ func (suite *SoloMachineTestSuite) TestInitialize() { }, { "invalid consensus state: Tendermint consensus state", - &ibctmtypes.ConsensusState{}, + &ibctm.ConsensusState{}, false, }, { diff --git a/modules/light-clients/06-solomachine/proposal_handle_test.go b/modules/light-clients/06-solomachine/proposal_handle_test.go index cc099327d25..8b8ee96c53a 100644 --- a/modules/light-clients/06-solomachine/proposal_handle_test.go +++ b/modules/light-clients/06-solomachine/proposal_handle_test.go @@ -5,7 +5,7 @@ import ( host "github.com/cosmos/ibc-go/v5/modules/core/24-host" "github.com/cosmos/ibc-go/v5/modules/core/exported" solomachine "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ) @@ -35,7 +35,7 @@ func (suite *SoloMachineTestSuite) TestCheckSubstituteAndUpdateState() { }, { "substitute is not the solo machine", func() { - substituteClientState = &ibctmtypes.ClientState{} + substituteClientState = &ibctm.ClientState{} }, false, }, { diff --git a/modules/light-clients/06-solomachine/update_test.go b/modules/light-clients/06-solomachine/update_test.go index 5664eb264a2..12ef195b220 100644 --- a/modules/light-clients/06-solomachine/update_test.go +++ b/modules/light-clients/06-solomachine/update_test.go @@ -8,7 +8,7 @@ import ( host "github.com/cosmos/ibc-go/v5/modules/core/24-host" "github.com/cosmos/ibc-go/v5/modules/core/exported" solomachine "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ) @@ -50,7 +50,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { { "invalid client message type", func() { - clientMsg = &ibctmtypes.Header{} + clientMsg = &ibctm.Header{} }, false, }, @@ -210,7 +210,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { { "invalid client message type", func() { - clientMsg = &ibctmtypes.Header{} + clientMsg = &ibctm.Header{} }, false, }, diff --git a/modules/light-clients/07-tendermint/client_state_test.go b/modules/light-clients/07-tendermint/client_state_test.go index 38144e0a3a3..9284e44cec8 100644 --- a/modules/light-clients/07-tendermint/client_state_test.go +++ b/modules/light-clients/07-tendermint/client_state_test.go @@ -12,7 +12,7 @@ import ( commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" host "github.com/cosmos/ibc-go/v5/modules/core/24-host" "github.com/cosmos/ibc-go/v5/modules/core/exported" - tendermint "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ibcmock "github.com/cosmos/ibc-go/v5/testing/mock" ) @@ -30,7 +30,7 @@ var ( func (suite *TendermintTestSuite) TestStatus() { var ( path *ibctesting.Path - clientState *tendermint.ClientState + clientState *ibctm.ClientState ) testCases := []struct { @@ -57,7 +57,7 @@ func (suite *TendermintTestSuite) TestStatus() { suite.coordinator.SetupClients(path) clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - clientState = path.EndpointA.GetClientState().(*tendermint.ClientState) + clientState = path.EndpointA.GetClientState().(*ibctm.ClientState) tc.malleate() @@ -70,22 +70,22 @@ func (suite *TendermintTestSuite) TestStatus() { func (suite *TendermintTestSuite) TestValidate() { testCases := []struct { name string - clientState *tendermint.ClientState + clientState *ibctm.ClientState expPass bool }{ { name: "valid client", - clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), expPass: true, }, { name: "valid client with nil upgrade path", - clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), nil, false, false), + clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), nil, false, false), expPass: true, }, { name: "invalid chainID", - clientState: tendermint.NewClientState(" ", tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + clientState: ibctm.NewClientState(" ", ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), expPass: false, }, { @@ -93,7 +93,7 @@ func (suite *TendermintTestSuite) TestValidate() { // Do not only fix the test, fix the code! // https://github.com/cosmos/ibc-go/issues/177 name: "valid chainID - chainID validation failed for chainID of length 50! ", - clientState: tendermint.NewClientState(fiftyCharChainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + clientState: ibctm.NewClientState(fiftyCharChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), expPass: true, }, { @@ -101,52 +101,52 @@ func (suite *TendermintTestSuite) TestValidate() { // Do not only fix the test, fix the code! // https://github.com/cosmos/ibc-go/issues/177 name: "invalid chainID - chainID validation did not fail for chainID of length 51! ", - clientState: tendermint.NewClientState(fiftyOneCharChainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + clientState: ibctm.NewClientState(fiftyOneCharChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), expPass: false, }, { name: "invalid trust level", - clientState: tendermint.NewClientState(chainID, tendermint.Fraction{Numerator: 0, Denominator: 1}, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + clientState: ibctm.NewClientState(chainID, ibctm.Fraction{Numerator: 0, Denominator: 1}, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), expPass: false, }, { name: "invalid trusting period", - clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, 0, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, 0, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), expPass: false, }, { name: "invalid unbonding period", - clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, trustingPeriod, 0, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, trustingPeriod, 0, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), expPass: false, }, { name: "invalid max clock drift", - clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod, 0, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, 0, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), expPass: false, }, { name: "invalid revision number", - clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(1, 1), commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(1, 1), commitmenttypes.GetSDKSpecs(), upgradePath, false, false), expPass: false, }, { name: "invalid revision height", - clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.ZeroHeight(), commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.ZeroHeight(), commitmenttypes.GetSDKSpecs(), upgradePath, false, false), expPass: false, }, { name: "trusting period not less than unbonding period", - clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), expPass: false, }, { name: "proof specs is nil", - clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, nil, upgradePath, false, false), + clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, nil, upgradePath, false, false), expPass: false, }, { name: "proof specs contains nil", - clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, []*ics23.ProofSpec{ics23.TendermintSpec, nil}, upgradePath, false, false), + clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, []*ics23.ProofSpec{ics23.TendermintSpec, nil}, upgradePath, false, false), expPass: false, }, } @@ -170,7 +170,7 @@ func (suite *TendermintTestSuite) TestInitialize() { }{ { name: "valid consensus", - consensusState: &tendermint.ConsensusState{}, + consensusState: &ibctm.ConsensusState{}, expPass: true, }, { @@ -232,7 +232,7 @@ func (suite *TendermintTestSuite) TestVerifyMembership() { proof, proofHeight = suite.chainB.QueryProof(key) - consensusState := testingpath.EndpointB.GetConsensusState(testingpath.EndpointB.GetClientState().GetLatestHeight()).(*tendermint.ConsensusState) + consensusState := testingpath.EndpointB.GetConsensusState(testingpath.EndpointB.GetClientState().GetLatestHeight()).(*ibctm.ConsensusState) value, err = suite.chainB.Codec.MarshalInterface(consensusState) suite.Require().NoError(err) }, @@ -442,13 +442,13 @@ func (suite *TendermintTestSuite) TestVerifyMembership() { proof, proofHeight = suite.chainB.QueryProof(key) - clientState := testingpath.EndpointB.GetClientState().(*tendermint.ClientState) + clientState := testingpath.EndpointB.GetClientState().(*ibctm.ClientState) value, err = suite.chainB.Codec.MarshalInterface(clientState) suite.Require().NoError(err) tc.malleate() // make changes as necessary - clientState = testingpath.EndpointA.GetClientState().(*tendermint.ClientState) + clientState = testingpath.EndpointA.GetClientState().(*ibctm.ClientState) ctx := suite.chainA.GetContext() store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, testingpath.EndpointA.ClientID) @@ -680,7 +680,7 @@ func (suite *TendermintTestSuite) TestVerifyNonMembership() { tc.malleate() // make changes as necessary - clientState := testingpath.EndpointA.GetClientState().(*tendermint.ClientState) + clientState := testingpath.EndpointA.GetClientState().(*ibctm.ClientState) ctx := suite.chainA.GetContext() store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, testingpath.EndpointA.ClientID) diff --git a/modules/light-clients/07-tendermint/consensus_state_test.go b/modules/light-clients/07-tendermint/consensus_state_test.go index 4775ea75b6e..b20b86fed4d 100644 --- a/modules/light-clients/07-tendermint/consensus_state_test.go +++ b/modules/light-clients/07-tendermint/consensus_state_test.go @@ -5,45 +5,45 @@ import ( commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v5/modules/core/exported" - tendermint "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ) func (suite *TendermintTestSuite) TestConsensusStateValidateBasic() { testCases := []struct { msg string - consensusState *tendermint.ConsensusState + consensusState *ibctm.ConsensusState expectPass bool }{ {"success", - &tendermint.ConsensusState{ + &ibctm.ConsensusState{ Timestamp: suite.now, Root: commitmenttypes.NewMerkleRoot([]byte("app_hash")), NextValidatorsHash: suite.valsHash, }, true}, {"success with sentinel", - &tendermint.ConsensusState{ + &ibctm.ConsensusState{ Timestamp: suite.now, - Root: commitmenttypes.NewMerkleRoot([]byte(tendermint.SentinelRoot)), + Root: commitmenttypes.NewMerkleRoot([]byte(ibctm.SentinelRoot)), NextValidatorsHash: suite.valsHash, }, true}, {"root is nil", - &tendermint.ConsensusState{ + &ibctm.ConsensusState{ Timestamp: suite.now, Root: commitmenttypes.MerkleRoot{}, NextValidatorsHash: suite.valsHash, }, false}, {"root is empty", - &tendermint.ConsensusState{ + &ibctm.ConsensusState{ Timestamp: suite.now, Root: commitmenttypes.MerkleRoot{}, NextValidatorsHash: suite.valsHash, }, false}, {"nextvalshash is invalid", - &tendermint.ConsensusState{ + &ibctm.ConsensusState{ Timestamp: suite.now, Root: commitmenttypes.NewMerkleRoot([]byte("app_hash")), NextValidatorsHash: []byte("hi"), @@ -52,7 +52,7 @@ func (suite *TendermintTestSuite) TestConsensusStateValidateBasic() { }, {"timestamp is zero", - &tendermint.ConsensusState{ + &ibctm.ConsensusState{ Timestamp: time.Time{}, Root: commitmenttypes.NewMerkleRoot([]byte("app_hash")), NextValidatorsHash: suite.valsHash, diff --git a/modules/light-clients/07-tendermint/genesis_test.go b/modules/light-clients/07-tendermint/genesis_test.go index b7b7d85820a..517147a8dc1 100644 --- a/modules/light-clients/07-tendermint/genesis_test.go +++ b/modules/light-clients/07-tendermint/genesis_test.go @@ -4,7 +4,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" - tendermint "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ) @@ -19,26 +19,26 @@ func (suite *TendermintTestSuite) TestExportMetadata() { clientState := path.EndpointA.GetClientState() height := clientState.GetLatestHeight() - initIteration := tendermint.GetIterationKey(clientStore, height) + initIteration := ibctm.GetIterationKey(clientStore, height) suite.Require().NotEqual(0, len(initIteration)) - initProcessedTime, found := tendermint.GetProcessedTime(clientStore, height) + initProcessedTime, found := ibctm.GetProcessedTime(clientStore, height) suite.Require().True(found) - initProcessedHeight, found := tendermint.GetProcessedHeight(clientStore, height) + initProcessedHeight, found := ibctm.GetProcessedHeight(clientStore, height) suite.Require().True(found) gm := clientState.ExportMetadata(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID)) suite.Require().NotNil(gm, "client with metadata returned nil exported metadata") suite.Require().Len(gm, 3, "exported metadata has unexpected length") - suite.Require().Equal(tendermint.ProcessedHeightKey(height), gm[0].GetKey(), "metadata has unexpected key") + suite.Require().Equal(ibctm.ProcessedHeightKey(height), gm[0].GetKey(), "metadata has unexpected key") actualProcessedHeight, err := clienttypes.ParseHeight(string(gm[0].GetValue())) suite.Require().NoError(err) suite.Require().Equal(initProcessedHeight, actualProcessedHeight, "metadata has unexpected value") - suite.Require().Equal(tendermint.ProcessedTimeKey(height), gm[1].GetKey(), "metadata has unexpected key") + suite.Require().Equal(ibctm.ProcessedTimeKey(height), gm[1].GetKey(), "metadata has unexpected key") suite.Require().Equal(initProcessedTime, sdk.BigEndianToUint64(gm[1].GetValue()), "metadata has unexpected value") - suite.Require().Equal(tendermint.IterationKey(height), gm[2].GetKey(), "metadata has unexpected key") + suite.Require().Equal(ibctm.IterationKey(height), gm[2].GetKey(), "metadata has unexpected key") suite.Require().Equal(initIteration, gm[2].GetValue(), "metadata has unexpected value") // test updating client and exporting metadata @@ -48,11 +48,11 @@ func (suite *TendermintTestSuite) TestExportMetadata() { clientState = path.EndpointA.GetClientState() updateHeight := clientState.GetLatestHeight() - iteration := tendermint.GetIterationKey(clientStore, updateHeight) + iteration := ibctm.GetIterationKey(clientStore, updateHeight) suite.Require().NotEqual(0, len(initIteration)) - processedTime, found := tendermint.GetProcessedTime(clientStore, updateHeight) + processedTime, found := ibctm.GetProcessedTime(clientStore, updateHeight) suite.Require().True(found) - processedHeight, found := tendermint.GetProcessedHeight(clientStore, updateHeight) + processedHeight, found := ibctm.GetProcessedHeight(clientStore, updateHeight) suite.Require().True(found) gm = clientState.ExportMetadata(suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID)) @@ -63,27 +63,27 @@ func (suite *TendermintTestSuite) TestExportMetadata() { // initProcessedHeight, initProcessedTime, processedHeight, processedTime, initIteration, iteration // check init processed height and time - suite.Require().Equal(tendermint.ProcessedHeightKey(height), gm[0].GetKey(), "metadata has unexpected key") + suite.Require().Equal(ibctm.ProcessedHeightKey(height), gm[0].GetKey(), "metadata has unexpected key") actualProcessedHeight, err = clienttypes.ParseHeight(string(gm[0].GetValue())) suite.Require().NoError(err) suite.Require().Equal(initProcessedHeight, actualProcessedHeight, "metadata has unexpected value") - suite.Require().Equal(tendermint.ProcessedTimeKey(height), gm[1].GetKey(), "metadata has unexpected key") + suite.Require().Equal(ibctm.ProcessedTimeKey(height), gm[1].GetKey(), "metadata has unexpected key") suite.Require().Equal(initProcessedTime, sdk.BigEndianToUint64(gm[1].GetValue()), "metadata has unexpected value") // check processed height and time after update - suite.Require().Equal(tendermint.ProcessedHeightKey(updateHeight), gm[2].GetKey(), "metadata has unexpected key") + suite.Require().Equal(ibctm.ProcessedHeightKey(updateHeight), gm[2].GetKey(), "metadata has unexpected key") actualProcessedHeight, err = clienttypes.ParseHeight(string(gm[2].GetValue())) suite.Require().NoError(err) suite.Require().Equal(processedHeight, actualProcessedHeight, "metadata has unexpected value") - suite.Require().Equal(tendermint.ProcessedTimeKey(updateHeight), gm[3].GetKey(), "metadata has unexpected key") + suite.Require().Equal(ibctm.ProcessedTimeKey(updateHeight), gm[3].GetKey(), "metadata has unexpected key") suite.Require().Equal(processedTime, sdk.BigEndianToUint64(gm[3].GetValue()), "metadata has unexpected value") // check iteration keys - suite.Require().Equal(tendermint.IterationKey(height), gm[4].GetKey(), "metadata has unexpected key") + suite.Require().Equal(ibctm.IterationKey(height), gm[4].GetKey(), "metadata has unexpected key") suite.Require().Equal(initIteration, gm[4].GetValue(), "metadata has unexpected value") - suite.Require().Equal(tendermint.IterationKey(updateHeight), gm[5].GetKey(), "metadata has unexpected key") + suite.Require().Equal(ibctm.IterationKey(updateHeight), gm[5].GetKey(), "metadata has unexpected key") suite.Require().Equal(iteration, gm[5].GetValue(), "metadata has unexpected value") } diff --git a/modules/light-clients/07-tendermint/header_test.go b/modules/light-clients/07-tendermint/header_test.go index eea6dd65945..8e916177877 100644 --- a/modules/light-clients/07-tendermint/header_test.go +++ b/modules/light-clients/07-tendermint/header_test.go @@ -7,7 +7,7 @@ import ( clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" "github.com/cosmos/ibc-go/v5/modules/core/exported" - tendermint "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ) func (suite *TendermintTestSuite) TestGetHeight() { @@ -21,7 +21,7 @@ func (suite *TendermintTestSuite) TestGetTime() { } func (suite *TendermintTestSuite) TestHeaderValidateBasic() { - var header *tendermint.Header + var header *ibctm.Header testCases := []struct { name string malleate func() diff --git a/modules/light-clients/07-tendermint/misbehaviour_handle_test.go b/modules/light-clients/07-tendermint/misbehaviour_handle_test.go index 52cb17efa0b..7b42d6ab19a 100644 --- a/modules/light-clients/07-tendermint/misbehaviour_handle_test.go +++ b/modules/light-clients/07-tendermint/misbehaviour_handle_test.go @@ -10,7 +10,7 @@ import ( clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" "github.com/cosmos/ibc-go/v5/modules/core/exported" smtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" - tendermint "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ibctestingmock "github.com/cosmos/ibc-go/v5/testing/mock" ) @@ -50,7 +50,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -64,7 +64,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) suite.Require().True(found) - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+3, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -78,7 +78,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) suite.Require().True(found) - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+3, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Hour), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -92,7 +92,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) suite.Require().True(found) - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+1, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+1, trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -113,7 +113,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { trustedVals2, found := suite.chainB.GetValsAtHeight(int64(trustedHeight2.RevisionHeight) + 1) suite.Require().True(found) - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight1, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals1, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight2, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals2, suite.chainB.Signers), } @@ -134,7 +134,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -156,7 +156,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { futureRevision := fmt.Sprintf("%s-%d", strings.TrimSuffix(suite.chainB.ChainID, fmt.Sprintf("-%d", clienttypes.ParseChainID(suite.chainB.ChainID))), height.GetRevisionNumber()+1) - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(futureRevision, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(futureRevision, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -176,7 +176,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -200,7 +200,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { bothSigners := suite.chainB.Signers bothSigners[altValSet.Proposer.Address.String()] = altPrivVal - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), bothValSet, suite.chainB.NextVals, trustedVals, bothSigners), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, bothValSet, suite.chainB.NextVals, trustedVals, bothSigners), } @@ -219,7 +219,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) misbehaviourHeader := suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers) - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: misbehaviourHeader, Header2: misbehaviourHeader, } @@ -232,7 +232,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) suite.Require().True(found) - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+3, trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -250,7 +250,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader("evmos", int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader("evmos", int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -266,7 +266,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, altValSet, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, altValSet, suite.chainB.Signers), } @@ -279,7 +279,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) suite.Require().True(found) - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight.Increment().(clienttypes.Height), suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -304,7 +304,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { suite.chainA.ExpireClient(path.EndpointA.ClientConfig.(*ibctesting.TendermintConfig).TrustingPeriod) - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -322,7 +322,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), altValSet, suite.chainB.NextVals, trustedVals, altSigners), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -340,7 +340,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, altValSet, suite.chainB.NextVals, trustedVals, altSigners), } @@ -358,7 +358,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), altValSet, suite.chainB.NextVals, trustedVals, altSigners), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, altValSet, suite.chainB.NextVals, trustedVals, altSigners), } @@ -433,7 +433,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -447,7 +447,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) suite.Require().True(found) - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+3, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -461,7 +461,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) suite.Require().True(found) - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+3, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Hour), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -475,7 +475,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) suite.Require().True(found) - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+1, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+1, trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -496,7 +496,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { trustedVals2, found := suite.chainB.GetValsAtHeight(int64(trustedHeight2.RevisionHeight) + 1) suite.Require().True(found) - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight1, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals1, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight2, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals2, suite.chainB.Signers), } @@ -521,7 +521,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { bothSigners := suite.chainB.Signers bothSigners[altValSet.Proposer.Address.String()] = altPrivVal - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), bothValSet, suite.chainB.NextVals, trustedVals, bothSigners), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, bothValSet, suite.chainB.NextVals, trustedVals, bothSigners), } @@ -540,7 +540,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) misbehaviourHeader := suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers) - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: misbehaviourHeader, Header2: misbehaviourHeader, } @@ -553,7 +553,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) suite.Require().True(found) - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+3, trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -571,7 +571,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader("evmos", int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader("evmos", int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -587,7 +587,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, altValSet, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, altValSet, suite.chainB.Signers), } @@ -600,7 +600,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) suite.Require().True(found) - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight.Increment().(clienttypes.Height), suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -625,7 +625,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { suite.chainA.ExpireClient(path.EndpointA.ClientConfig.(*ibctesting.TendermintConfig).TrustingPeriod) - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -643,7 +643,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), altValSet, suite.chainB.NextVals, trustedVals, altSigners), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), } @@ -661,7 +661,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, altValSet, suite.chainB.NextVals, trustedVals, altSigners), } @@ -679,7 +679,7 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height) - misbehaviour = &tendermint.Misbehaviour{ + misbehaviour = &ibctm.Misbehaviour{ Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), altValSet, suite.chainB.NextVals, trustedVals, altSigners), Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time, altValSet, suite.chainB.NextVals, trustedVals, altSigners), } diff --git a/modules/light-clients/07-tendermint/misbehaviour_test.go b/modules/light-clients/07-tendermint/misbehaviour_test.go index 8a433a4d0e0..979b5d1c203 100644 --- a/modules/light-clients/07-tendermint/misbehaviour_test.go +++ b/modules/light-clients/07-tendermint/misbehaviour_test.go @@ -9,7 +9,7 @@ import ( clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" "github.com/cosmos/ibc-go/v5/modules/core/exported" - tendermint "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ibctestingmock "github.com/cosmos/ibc-go/v5/testing/mock" ) @@ -17,7 +17,7 @@ import ( func (suite *TendermintTestSuite) TestMisbehaviour() { heightMinus1 := clienttypes.NewHeight(0, height.RevisionHeight-1) - misbehaviour := &tendermint.Misbehaviour{ + misbehaviour := &ibctm.Misbehaviour{ Header1: suite.header, Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, suite.valSet, suite.valSet, suite.valSet, suite.signers), ClientId: clientID, @@ -47,130 +47,130 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { testCases := []struct { name string - misbehaviour *tendermint.Misbehaviour - malleateMisbehaviour func(misbehaviour *tendermint.Misbehaviour) error + misbehaviour *ibctm.Misbehaviour + malleateMisbehaviour func(misbehaviour *ibctm.Misbehaviour) error expPass bool }{ { "valid fork misbehaviour, two headers at same height have different time", - &tendermint.Misbehaviour{ + &ibctm.Misbehaviour{ Header1: suite.header, Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now.Add(time.Minute), suite.valSet, suite.valSet, suite.valSet, suite.signers), ClientId: clientID, }, - func(misbehaviour *tendermint.Misbehaviour) error { return nil }, + func(misbehaviour *ibctm.Misbehaviour) error { return nil }, true, }, { "valid time misbehaviour, both headers at different heights are at same time", - &tendermint.Misbehaviour{ + &ibctm.Misbehaviour{ Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+5), heightMinus1, suite.now, suite.valSet, suite.valSet, suite.valSet, suite.signers), Header2: suite.header, ClientId: clientID, }, - func(misbehaviour *tendermint.Misbehaviour) error { return nil }, + func(misbehaviour *ibctm.Misbehaviour) error { return nil }, true, }, { "misbehaviour Header1 is nil", - tendermint.NewMisbehaviour(clientID, nil, suite.header), - func(m *tendermint.Misbehaviour) error { return nil }, + ibctm.NewMisbehaviour(clientID, nil, suite.header), + func(m *ibctm.Misbehaviour) error { return nil }, false, }, { "misbehaviour Header2 is nil", - tendermint.NewMisbehaviour(clientID, suite.header, nil), - func(m *tendermint.Misbehaviour) error { return nil }, + ibctm.NewMisbehaviour(clientID, suite.header, nil), + func(m *ibctm.Misbehaviour) error { return nil }, false, }, { "valid misbehaviour with different trusted headers", - &tendermint.Misbehaviour{ + &ibctm.Misbehaviour{ Header1: suite.header, Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), clienttypes.NewHeight(0, height.RevisionHeight-3), suite.now.Add(time.Minute), suite.valSet, suite.valSet, bothValSet, suite.signers), ClientId: clientID, }, - func(misbehaviour *tendermint.Misbehaviour) error { return nil }, + func(misbehaviour *ibctm.Misbehaviour) error { return nil }, true, }, { "trusted height is 0 in Header1", - &tendermint.Misbehaviour{ + &ibctm.Misbehaviour{ Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), clienttypes.ZeroHeight(), suite.now.Add(time.Minute), suite.valSet, suite.valSet, suite.valSet, suite.signers), Header2: suite.header, ClientId: clientID, }, - func(misbehaviour *tendermint.Misbehaviour) error { return nil }, + func(misbehaviour *ibctm.Misbehaviour) error { return nil }, false, }, { "trusted height is 0 in Header2", - &tendermint.Misbehaviour{ + &ibctm.Misbehaviour{ Header1: suite.header, Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), clienttypes.ZeroHeight(), suite.now.Add(time.Minute), suite.valSet, suite.valSet, suite.valSet, suite.signers), ClientId: clientID, }, - func(misbehaviour *tendermint.Misbehaviour) error { return nil }, + func(misbehaviour *ibctm.Misbehaviour) error { return nil }, false, }, { "trusted valset is nil in Header1", - &tendermint.Misbehaviour{ + &ibctm.Misbehaviour{ Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now.Add(time.Minute), suite.valSet, suite.valSet, nil, suite.signers), Header2: suite.header, ClientId: clientID, }, - func(misbehaviour *tendermint.Misbehaviour) error { return nil }, + func(misbehaviour *ibctm.Misbehaviour) error { return nil }, false, }, { "trusted valset is nil in Header2", - &tendermint.Misbehaviour{ + &ibctm.Misbehaviour{ Header1: suite.header, Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now.Add(time.Minute), suite.valSet, suite.valSet, nil, suite.signers), ClientId: clientID, }, - func(misbehaviour *tendermint.Misbehaviour) error { return nil }, + func(misbehaviour *ibctm.Misbehaviour) error { return nil }, false, }, { "invalid client ID ", - &tendermint.Misbehaviour{ + &ibctm.Misbehaviour{ Header1: suite.header, Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, suite.valSet, suite.valSet, suite.valSet, suite.signers), ClientId: "GAIA", }, - func(misbehaviour *tendermint.Misbehaviour) error { return nil }, + func(misbehaviour *ibctm.Misbehaviour) error { return nil }, false, }, { "chainIDs do not match", - &tendermint.Misbehaviour{ + &ibctm.Misbehaviour{ Header1: suite.header, Header2: suite.chainA.CreateTMClientHeader("ethermint", int64(height.RevisionHeight), heightMinus1, suite.now, suite.valSet, suite.valSet, suite.valSet, suite.signers), ClientId: clientID, }, - func(misbehaviour *tendermint.Misbehaviour) error { return nil }, + func(misbehaviour *ibctm.Misbehaviour) error { return nil }, false, }, { "header2 height is greater", - &tendermint.Misbehaviour{ + &ibctm.Misbehaviour{ Header1: suite.header, Header2: suite.chainA.CreateTMClientHeader(chainID, 6, clienttypes.NewHeight(0, height.RevisionHeight+1), suite.now, suite.valSet, suite.valSet, suite.valSet, suite.signers), ClientId: clientID, }, - func(misbehaviour *tendermint.Misbehaviour) error { return nil }, + func(misbehaviour *ibctm.Misbehaviour) error { return nil }, false, }, { "header 1 doesn't have 2/3 majority", - &tendermint.Misbehaviour{ + &ibctm.Misbehaviour{ Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, bothValSet, bothValSet, suite.valSet, bothSigners), Header2: suite.header, ClientId: clientID, }, - func(misbehaviour *tendermint.Misbehaviour) error { + func(misbehaviour *ibctm.Misbehaviour) error { // voteSet contains only altVal which is less than 2/3 of total power (height/1height) wrongVoteSet := tmtypes.NewVoteSet(chainID, int64(misbehaviour.Header1.GetHeight().GetRevisionHeight()), 1, tmproto.PrecommitType, altValSet) blockID, err := tmtypes.BlockIDFromProto(&misbehaviour.Header1.Commit.BlockID) @@ -186,12 +186,12 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { }, { "header 2 doesn't have 2/3 majority", - &tendermint.Misbehaviour{ + &ibctm.Misbehaviour{ Header1: suite.header, Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, bothValSet, bothValSet, suite.valSet, bothSigners), ClientId: clientID, }, - func(misbehaviour *tendermint.Misbehaviour) error { + func(misbehaviour *ibctm.Misbehaviour) error { // voteSet contains only altVal which is less than 2/3 of total power (height/1height) wrongVoteSet := tmtypes.NewVoteSet(chainID, int64(misbehaviour.Header2.GetHeight().GetRevisionHeight()), 1, tmproto.PrecommitType, altValSet) blockID, err := tmtypes.BlockIDFromProto(&misbehaviour.Header2.Commit.BlockID) @@ -207,12 +207,12 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { }, { "validators sign off on wrong commit", - &tendermint.Misbehaviour{ + &ibctm.Misbehaviour{ Header1: suite.header, Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, bothValSet, bothValSet, suite.valSet, bothSigners), ClientId: clientID, }, - func(misbehaviour *tendermint.Misbehaviour) error { + func(misbehaviour *ibctm.Misbehaviour) error { tmBlockID := ibctesting.MakeBlockID(tmhash.Sum([]byte("other_hash")), 3, tmhash.Sum([]byte("other_partset"))) misbehaviour.Header2.Commit.BlockID = tmBlockID.ToProto() return nil diff --git a/modules/light-clients/07-tendermint/proposal_handle_test.go b/modules/light-clients/07-tendermint/proposal_handle_test.go index 22703f4b01d..5f1032db69b 100644 --- a/modules/light-clients/07-tendermint/proposal_handle_test.go +++ b/modules/light-clients/07-tendermint/proposal_handle_test.go @@ -5,7 +5,7 @@ import ( clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" "github.com/cosmos/ibc-go/v5/modules/core/exported" - tendermint "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ) @@ -28,7 +28,7 @@ func (suite *TendermintTestSuite) TestCheckSubstituteUpdateStateBasic() { { "non-matching substitute", func() { suite.coordinator.SetupClients(substitutePath) - substituteClientState, ok := suite.chainA.GetClientState(substitutePath.EndpointA.ClientID).(*tendermint.ClientState) + substituteClientState, ok := suite.chainA.GetClientState(substitutePath.EndpointA.ClientID).(*ibctm.ClientState) suite.Require().True(ok) // change trusting period so that test should fail substituteClientState.TrustingPeriod = time.Hour * 24 * 7 @@ -48,7 +48,7 @@ func (suite *TendermintTestSuite) TestCheckSubstituteUpdateStateBasic() { substitutePath = ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupClients(subjectPath) - subjectClientState := suite.chainA.GetClientState(subjectPath.EndpointA.ClientID).(*tendermint.ClientState) + subjectClientState := suite.chainA.GetClientState(subjectPath.EndpointA.ClientID).(*ibctm.ClientState) // expire subject client suite.coordinator.IncrementTimeBy(subjectClientState.TrustingPeriod) @@ -120,7 +120,7 @@ func (suite *TendermintTestSuite) TestCheckSubstituteAndUpdateState() { // construct subject using test case parameters subjectPath := ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupClients(subjectPath) - subjectClientState := suite.chainA.GetClientState(subjectPath.EndpointA.ClientID).(*tendermint.ClientState) + subjectClientState := suite.chainA.GetClientState(subjectPath.EndpointA.ClientID).(*ibctm.ClientState) // apply freezing or expiry as determined by the test case if tc.FreezeClient { @@ -140,7 +140,7 @@ func (suite *TendermintTestSuite) TestCheckSubstituteAndUpdateState() { substitutePath := ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupClients(substitutePath) - substituteClientState := suite.chainA.GetClientState(substitutePath.EndpointA.ClientID).(*tendermint.ClientState) + substituteClientState := suite.chainA.GetClientState(substitutePath.EndpointA.ClientID).(*ibctm.ClientState) // update trusting period of substitute client state substituteClientState.TrustingPeriod = time.Hour * 24 * 7 suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), substitutePath.EndpointA.ClientID, substituteClientState) @@ -154,7 +154,7 @@ func (suite *TendermintTestSuite) TestCheckSubstituteAndUpdateState() { } // get updated substitute - substituteClientState = suite.chainA.GetClientState(substitutePath.EndpointA.ClientID).(*tendermint.ClientState) + substituteClientState = suite.chainA.GetClientState(substitutePath.EndpointA.ClientID).(*ibctm.ClientState) // test that subject gets updated chain-id newChainID := "new-chain-id" @@ -164,36 +164,36 @@ func (suite *TendermintTestSuite) TestCheckSubstituteAndUpdateState() { substituteClientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), substitutePath.EndpointA.ClientID) expectedConsState := substitutePath.EndpointA.GetConsensusState(substituteClientState.GetLatestHeight()) - expectedProcessedTime, found := tendermint.GetProcessedTime(substituteClientStore, substituteClientState.GetLatestHeight()) + expectedProcessedTime, found := ibctm.GetProcessedTime(substituteClientStore, substituteClientState.GetLatestHeight()) suite.Require().True(found) - expectedProcessedHeight, found := tendermint.GetProcessedTime(substituteClientStore, substituteClientState.GetLatestHeight()) + expectedProcessedHeight, found := ibctm.GetProcessedTime(substituteClientStore, substituteClientState.GetLatestHeight()) suite.Require().True(found) - expectedIterationKey := tendermint.GetIterationKey(substituteClientStore, substituteClientState.GetLatestHeight()) + expectedIterationKey := ibctm.GetIterationKey(substituteClientStore, substituteClientState.GetLatestHeight()) updatedClient, err := subjectClientState.CheckSubstituteAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), subjectClientStore, substituteClientStore, substituteClientState) if tc.expPass { suite.Require().NoError(err) - suite.Require().Equal(clienttypes.ZeroHeight(), updatedClient.(*tendermint.ClientState).FrozenHeight) + suite.Require().Equal(clienttypes.ZeroHeight(), updatedClient.(*ibctm.ClientState).FrozenHeight) subjectClientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), subjectPath.EndpointA.ClientID) // check that the correct consensus state was copied over suite.Require().Equal(substituteClientState.GetLatestHeight(), updatedClient.GetLatestHeight()) subjectConsState := subjectPath.EndpointA.GetConsensusState(updatedClient.GetLatestHeight()) - subjectProcessedTime, found := tendermint.GetProcessedTime(subjectClientStore, updatedClient.GetLatestHeight()) + subjectProcessedTime, found := ibctm.GetProcessedTime(subjectClientStore, updatedClient.GetLatestHeight()) suite.Require().True(found) - subjectProcessedHeight, found := tendermint.GetProcessedTime(substituteClientStore, updatedClient.GetLatestHeight()) + subjectProcessedHeight, found := ibctm.GetProcessedTime(substituteClientStore, updatedClient.GetLatestHeight()) suite.Require().True(found) - subjectIterationKey := tendermint.GetIterationKey(substituteClientStore, updatedClient.GetLatestHeight()) + subjectIterationKey := ibctm.GetIterationKey(substituteClientStore, updatedClient.GetLatestHeight()) suite.Require().Equal(expectedConsState, subjectConsState) suite.Require().Equal(expectedProcessedTime, subjectProcessedTime) suite.Require().Equal(expectedProcessedHeight, subjectProcessedHeight) suite.Require().Equal(expectedIterationKey, subjectIterationKey) - suite.Require().Equal(newChainID, updatedClient.(*tendermint.ClientState).ChainId) - suite.Require().Equal(time.Hour*24*7, updatedClient.(*tendermint.ClientState).TrustingPeriod) + suite.Require().Equal(newChainID, updatedClient.(*ibctm.ClientState).ChainId) + suite.Require().Equal(time.Hour*24*7, updatedClient.(*ibctm.ClientState).TrustingPeriod) } else { suite.Require().Error(err) suite.Require().Nil(updatedClient) @@ -205,7 +205,7 @@ func (suite *TendermintTestSuite) TestCheckSubstituteAndUpdateState() { func (suite *TendermintTestSuite) TestIsMatchingClientState() { var ( subjectPath, substitutePath *ibctesting.Path - subjectClientState, substituteClientState *tendermint.ClientState + subjectClientState, substituteClientState *ibctm.ClientState ) testCases := []struct { @@ -215,8 +215,8 @@ func (suite *TendermintTestSuite) TestIsMatchingClientState() { }{ { "matching clients", func() { - subjectClientState = suite.chainA.GetClientState(subjectPath.EndpointA.ClientID).(*tendermint.ClientState) - substituteClientState = suite.chainA.GetClientState(substitutePath.EndpointA.ClientID).(*tendermint.ClientState) + subjectClientState = suite.chainA.GetClientState(subjectPath.EndpointA.ClientID).(*ibctm.ClientState) + substituteClientState = suite.chainA.GetClientState(substitutePath.EndpointA.ClientID).(*ibctm.ClientState) }, true, }, { @@ -245,8 +245,8 @@ func (suite *TendermintTestSuite) TestIsMatchingClientState() { }, { "not matching, trust level is different", func() { - subjectClientState.TrustLevel = tendermint.Fraction{2, 3} - substituteClientState.TrustLevel = tendermint.Fraction{1, 3} + subjectClientState.TrustLevel = ibctm.Fraction{2, 3} + substituteClientState.TrustLevel = ibctm.Fraction{1, 3} }, false, }, } @@ -264,7 +264,7 @@ func (suite *TendermintTestSuite) TestIsMatchingClientState() { tc.malleate() - suite.Require().Equal(tc.expPass, tendermint.IsMatchingClientState(*subjectClientState, *substituteClientState)) + suite.Require().Equal(tc.expPass, ibctm.IsMatchingClientState(*subjectClientState, *substituteClientState)) }) } } diff --git a/modules/light-clients/07-tendermint/tendermint_test.go b/modules/light-clients/07-tendermint/tendermint_test.go index a3610f8c86a..7ad52f62299 100644 --- a/modules/light-clients/07-tendermint/tendermint_test.go +++ b/modules/light-clients/07-tendermint/tendermint_test.go @@ -12,7 +12,7 @@ import ( tmtypes "github.com/tendermint/tendermint/types" clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" - tendermint "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ibctestingmock "github.com/cosmos/ibc-go/v5/testing/mock" "github.com/cosmos/ibc-go/v5/testing/simapp" @@ -50,7 +50,7 @@ type TendermintTestSuite struct { valSet *tmtypes.ValidatorSet signers map[string]tmtypes.PrivValidator valsHash tmbytes.HexBytes - header *tendermint.Header + header *ibctm.Header now time.Time headerTime time.Time clientTime time.Time diff --git a/modules/light-clients/07-tendermint/update_test.go b/modules/light-clients/07-tendermint/update_test.go index 41406e35dd7..306133542b5 100644 --- a/modules/light-clients/07-tendermint/update_test.go +++ b/modules/light-clients/07-tendermint/update_test.go @@ -10,7 +10,7 @@ import ( commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" host "github.com/cosmos/ibc-go/v5/modules/core/24-host" "github.com/cosmos/ibc-go/v5/modules/core/exported" - tendermint "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ibctestingmock "github.com/cosmos/ibc-go/v5/testing/mock" ) @@ -18,7 +18,7 @@ import ( func (suite *TendermintTestSuite) TestVerifyHeader() { var ( path *ibctesting.Path - header *tendermint.Header + header *ibctm.Header ) // Setup different validators and signers for testing different types of updates @@ -322,12 +322,12 @@ func (suite *TendermintTestSuite) TestUpdateState() { }{ { "success with height later than latest height", func() { - tmHeader, ok := clientMessage.(*tendermint.Header) + tmHeader, ok := clientMessage.(*ibctm.Header) suite.Require().True(ok) suite.Require().True(path.EndpointA.GetClientState().GetLatestHeight().LT(tmHeader.GetHeight())) }, func() { - tmHeader, ok := clientMessage.(*tendermint.Header) + tmHeader, ok := clientMessage.(*ibctm.Header) suite.Require().True(ok) clientState := path.EndpointA.GetClientState() @@ -343,7 +343,7 @@ func (suite *TendermintTestSuite) TestUpdateState() { err := path.EndpointA.UpdateClient() suite.Require().NoError(err) - tmHeader, ok := clientMessage.(*tendermint.Header) + tmHeader, ok := clientMessage.(*ibctm.Header) suite.Require().True(ok) suite.Require().True(path.EndpointA.GetClientState().GetLatestHeight().GT(tmHeader.GetHeight())) @@ -365,7 +365,7 @@ func (suite *TendermintTestSuite) TestUpdateState() { clientMessage, err = path.EndpointA.Chain.ConstructUpdateTMClientHeader(path.EndpointA.Counterparty.Chain, path.EndpointA.ClientID) suite.Require().NoError(err) - tmHeader, ok := clientMessage.(*tendermint.Header) + tmHeader, ok := clientMessage.(*ibctm.Header) suite.Require().True(ok) suite.Require().Equal(path.EndpointA.GetClientState().GetLatestHeight(), tmHeader.GetHeight()) @@ -377,7 +377,7 @@ func (suite *TendermintTestSuite) TestUpdateState() { suite.Require().Equal(clientState, prevClientState) suite.Require().True(clientState.GetLatestHeight().EQ(consensusHeights[0])) - tmHeader, ok := clientMessage.(*tendermint.Header) + tmHeader, ok := clientMessage.(*ibctm.Header) suite.Require().True(ok) suite.Require().Equal(path.EndpointA.GetConsensusState(tmHeader.GetHeight()), prevConsensusState) }, true, @@ -408,7 +408,7 @@ func (suite *TendermintTestSuite) TestUpdateState() { suite.Require().NoError(err) }, func() { - tmHeader, ok := clientMessage.(*tendermint.Header) + tmHeader, ok := clientMessage.(*ibctm.Header) suite.Require().True(ok) clientState := path.EndpointA.GetClientState() @@ -422,7 +422,7 @@ func (suite *TendermintTestSuite) TestUpdateState() { }, { "invalid ClientMessage type", func() { - clientMessage = &tendermint.Misbehaviour{} + clientMessage = &ibctm.Misbehaviour{} }, func() {}, false, @@ -451,8 +451,8 @@ func (suite *TendermintTestSuite) TestUpdateState() { if tc.expPass { consensusHeights = clientState.UpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, clientMessage) - header := clientMessage.(*tendermint.Header) - expConsensusState := &tendermint.ConsensusState{ + header := clientMessage.(*ibctm.Header) + expConsensusState := &ibctm.ConsensusState{ Timestamp: header.GetTime(), Root: commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), NextValidatorsHash: header.Header.NextValidatorsHash, @@ -488,7 +488,7 @@ func (suite *TendermintTestSuite) TestPruneConsensusState() { } ctx := path.EndpointA.Chain.GetContext() clientStore := path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) - err := tendermint.IterateConsensusStateAscending(clientStore, getFirstHeightCb) + err := ibctm.IterateConsensusStateAscending(clientStore, getFirstHeightCb) suite.Require().Nil(err) // this height will be expired but not pruned @@ -500,11 +500,11 @@ func (suite *TendermintTestSuite) TestPruneConsensusState() { suite.Require().True(ok) ctx = path.EndpointA.Chain.GetContext() clientStore = path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) - expectedProcessTime, ok := tendermint.GetProcessedTime(clientStore, expiredHeight) + expectedProcessTime, ok := ibctm.GetProcessedTime(clientStore, expiredHeight) suite.Require().True(ok) - expectedProcessHeight, ok := tendermint.GetProcessedHeight(clientStore, expiredHeight) + expectedProcessHeight, ok := ibctm.GetProcessedHeight(clientStore, expiredHeight) suite.Require().True(ok) - expectedConsKey := tendermint.GetIterationKey(clientStore, expiredHeight) + expectedConsKey := ibctm.GetIterationKey(clientStore, expiredHeight) suite.Require().NotNil(expectedConsKey) // Increment the time by a week @@ -526,15 +526,15 @@ func (suite *TendermintTestSuite) TestPruneConsensusState() { suite.Require().Nil(consState, "expired consensus state not pruned") suite.Require().False(ok) // check processed time metadata is pruned - processTime, ok := tendermint.GetProcessedTime(clientStore, pruneHeight) + processTime, ok := ibctm.GetProcessedTime(clientStore, pruneHeight) suite.Require().Equal(uint64(0), processTime, "processed time metadata not pruned") suite.Require().False(ok) - processHeight, ok := tendermint.GetProcessedHeight(clientStore, pruneHeight) + processHeight, ok := ibctm.GetProcessedHeight(clientStore, pruneHeight) suite.Require().Nil(processHeight, "processed height metadata not pruned") suite.Require().False(ok) // check iteration key metadata is pruned - consKey := tendermint.GetIterationKey(clientStore, pruneHeight) + consKey := ibctm.GetIterationKey(clientStore, pruneHeight) suite.Require().Nil(consKey, "iteration key not pruned") // check that second expired consensus state doesn't get deleted @@ -543,17 +543,17 @@ func (suite *TendermintTestSuite) TestPruneConsensusState() { suite.Require().Equal(expectedConsState, consState, "consensus state incorrectly pruned") suite.Require().True(ok) // check processed time metadata is not pruned - processTime, ok = tendermint.GetProcessedTime(clientStore, expiredHeight) + processTime, ok = ibctm.GetProcessedTime(clientStore, expiredHeight) suite.Require().Equal(expectedProcessTime, processTime, "processed time metadata incorrectly pruned") suite.Require().True(ok) // check processed height metadata is not pruned - processHeight, ok = tendermint.GetProcessedHeight(clientStore, expiredHeight) + processHeight, ok = ibctm.GetProcessedHeight(clientStore, expiredHeight) suite.Require().Equal(expectedProcessHeight, processHeight, "processed height metadata incorrectly pruned") suite.Require().True(ok) // check iteration key metadata is not pruned - consKey = tendermint.GetIterationKey(clientStore, expiredHeight) + consKey = ibctm.GetIterationKey(clientStore, expiredHeight) suite.Require().Equal(expectedConsKey, consKey, "iteration key incorrectly pruned") } @@ -576,16 +576,16 @@ func (suite *TendermintTestSuite) TestCheckForMisbehaviour() { { "consensus state already exists, already updated", func() { - header, ok := clientMessage.(*tendermint.Header) + header, ok := clientMessage.(*ibctm.Header) suite.Require().True(ok) - consensusState := &tendermint.ConsensusState{ + consensusState := &ibctm.ConsensusState{ Timestamp: header.GetTime(), Root: commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), NextValidatorsHash: header.Header.NextValidatorsHash, } - tmHeader, ok := clientMessage.(*tendermint.Header) + tmHeader, ok := clientMessage.(*ibctm.Header) suite.Require().True(ok) suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), path.EndpointA.ClientID, tmHeader.GetHeight(), consensusState) }, @@ -594,16 +594,16 @@ func (suite *TendermintTestSuite) TestCheckForMisbehaviour() { { "consensus state already exists, app hash mismatch", func() { - header, ok := clientMessage.(*tendermint.Header) + header, ok := clientMessage.(*ibctm.Header) suite.Require().True(ok) - consensusState := &tendermint.ConsensusState{ + consensusState := &ibctm.ConsensusState{ Timestamp: header.GetTime(), Root: commitmenttypes.NewMerkleRoot([]byte{}), // empty bytes NextValidatorsHash: header.Header.NextValidatorsHash, } - tmHeader, ok := clientMessage.(*tendermint.Header) + tmHeader, ok := clientMessage.(*ibctm.Header) suite.Require().True(ok) suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), path.EndpointA.ClientID, tmHeader.GetHeight(), consensusState) }, @@ -612,7 +612,7 @@ func (suite *TendermintTestSuite) TestCheckForMisbehaviour() { { "previous consensus state exists and header time is before previous consensus state time", func() { - header, ok := clientMessage.(*tendermint.Header) + header, ok := clientMessage.(*ibctm.Header) suite.Require().True(ok) // offset header timestamp before previous consensus state timestamp @@ -623,7 +623,7 @@ func (suite *TendermintTestSuite) TestCheckForMisbehaviour() { { "next consensus state exists and header time is after next consensus state time", func() { - header, ok := clientMessage.(*tendermint.Header) + header, ok := clientMessage.(*ibctm.Header) suite.Require().True(ok) // commit block and update client, adding a new consensus state @@ -653,7 +653,7 @@ func (suite *TendermintTestSuite) TestCheckForMisbehaviour() { // assign the same height, each header will have a different commit hash header1.Header.Height = header2.Header.Height - clientMessage = &tendermint.Misbehaviour{ + clientMessage = &ibctm.Misbehaviour{ Header1: header1, Header2: header2, ClientId: path.EndpointA.ClientID, @@ -739,7 +739,7 @@ func (suite *TendermintTestSuite) TestUpdateStateOnMisbehaviour() { suite.Require().NotEmpty(clientStateBz) newClientState := clienttypes.MustUnmarshalClientState(suite.chainA.Codec, clientStateBz) - suite.Require().Equal(frozenHeight, newClientState.(*tendermint.ClientState).FrozenHeight) + suite.Require().Equal(frozenHeight, newClientState.(*ibctm.ClientState).FrozenHeight) } }) } diff --git a/modules/light-clients/07-tendermint/upgrade_test.go b/modules/light-clients/07-tendermint/upgrade_test.go index 0506e4b6478..301b64288c9 100644 --- a/modules/light-clients/07-tendermint/upgrade_test.go +++ b/modules/light-clients/07-tendermint/upgrade_test.go @@ -6,7 +6,7 @@ import ( clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v5/modules/core/exported" - tendermint "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ) @@ -54,7 +54,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { { name: "successful upgrade to same revision", setup: func() { - upgradedClient = tendermint.NewClientState(suite.chainB.ChainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, clienttypes.NewHeight(clienttypes.ParseChainID(suite.chainB.ChainID), upgradedClient.GetLatestHeight().GetRevisionHeight()+10), commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + upgradedClient = ibctm.NewClientState(suite.chainB.ChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, clienttypes.NewHeight(clienttypes.ParseChainID(suite.chainB.ChainID), upgradedClient.GetLatestHeight().GetRevisionHeight()+10), commitmenttypes.GetSDKSpecs(), upgradePath, false, false) upgradedClient = upgradedClient.ZeroCustomFields() upgradedClientBz, err = clienttypes.MarshalClientState(suite.chainA.App.AppCodec(), upgradedClient) suite.Require().NoError(err) @@ -109,7 +109,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { name: "unsuccessful upgrade: committed client does not have zeroed custom fields", setup: func() { // non-zeroed upgrade client - upgradedClient = tendermint.NewClientState(newChainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + upgradedClient = ibctm.NewClientState(newChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) upgradedClientBz, err = clienttypes.MarshalClientState(suite.chainA.App.AppCodec(), upgradedClient) suite.Require().NoError(err) @@ -145,7 +145,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) // change upgradedClient client-specified parameters - upgradedClient = tendermint.NewClientState("wrongchainID", tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, true, true) + upgradedClient = ibctm.NewClientState("wrongchainID", ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, true, true) suite.coordinator.CommitBlock(suite.chainB) err := path.EndpointA.UpdateClient() @@ -167,7 +167,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) // change upgradedClient client-specified parameters - upgradedClient = tendermint.NewClientState(newChainID, tendermint.DefaultTrustLevel, ubdPeriod, ubdPeriod+trustingPeriod, maxClockDrift+5, lastHeight, commitmenttypes.GetSDKSpecs(), upgradePath, true, false) + upgradedClient = ibctm.NewClientState(newChainID, ibctm.DefaultTrustLevel, ubdPeriod, ubdPeriod+trustingPeriod, maxClockDrift+5, lastHeight, commitmenttypes.GetSDKSpecs(), upgradePath, true, false) suite.coordinator.CommitBlock(suite.chainB) err := path.EndpointA.UpdateClient() @@ -192,7 +192,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) // change submitted upgradedConsensusState - upgradedConsState = &tendermint.ConsensusState{ + upgradedConsState = &ibctm.ConsensusState{ NextValidatorsHash: []byte("maliciousValidators"), } @@ -296,7 +296,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { proofUpgradedConsState, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) // SetClientState with empty upgrade path - tmClient, _ := cs.(*tendermint.ClientState) + tmClient, _ := cs.(*ibctm.ClientState) tmClient.UpgradePath = []string{""} suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID, tmClient) }, @@ -398,7 +398,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { name: "unsuccessful upgrade: final client is not valid", setup: func() { // new client has smaller unbonding period such that old trusting period is no longer valid - upgradedClient = tendermint.NewClientState(newChainID, tendermint.DefaultTrustLevel, trustingPeriod, trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + upgradedClient = ibctm.NewClientState(newChainID, ibctm.DefaultTrustLevel, trustingPeriod, trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) upgradedClientBz, err = clienttypes.MarshalClientState(suite.chainA.App.AppCodec(), upgradedClient) suite.Require().NoError(err) @@ -434,19 +434,19 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { suite.coordinator.SetupClients(path) - clientState := path.EndpointA.GetClientState().(*tendermint.ClientState) + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) revisionNumber := clienttypes.ParseChainID(clientState.ChainId) var err error newChainID, err = clienttypes.SetRevisionNumber(clientState.ChainId, revisionNumber+1) suite.Require().NoError(err) - upgradedClient = tendermint.NewClientState(newChainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, clienttypes.NewHeight(revisionNumber+1, clientState.GetLatestHeight().GetRevisionHeight()+1), commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + upgradedClient = ibctm.NewClientState(newChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, clienttypes.NewHeight(revisionNumber+1, clientState.GetLatestHeight().GetRevisionHeight()+1), commitmenttypes.GetSDKSpecs(), upgradePath, false, false) upgradedClient = upgradedClient.ZeroCustomFields() upgradedClientBz, err = clienttypes.MarshalClientState(suite.chainA.App.AppCodec(), upgradedClient) suite.Require().NoError(err) - upgradedConsState = &tendermint.ConsensusState{ + upgradedConsState = &ibctm.ConsensusState{ NextValidatorsHash: []byte("nextValsHash"), } upgradedConsStateBz, err = clienttypes.MarshalConsensusState(suite.chainA.App.AppCodec(), upgradedConsState) diff --git a/testing/chain.go b/testing/chain.go index 07769553306..9e01513e66b 100644 --- a/testing/chain.go +++ b/testing/chain.go @@ -30,7 +30,7 @@ import ( host "github.com/cosmos/ibc-go/v5/modules/core/24-host" "github.com/cosmos/ibc-go/v5/modules/core/exported" "github.com/cosmos/ibc-go/v5/modules/core/types" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" "github.com/cosmos/ibc-go/v5/testing/mock" "github.com/cosmos/ibc-go/v5/testing/simapp" ) @@ -53,8 +53,8 @@ type TestChain struct { Coordinator *Coordinator App TestingApp ChainID string - LastHeader *ibctmtypes.Header // header for last block height committed - CurrentHeader tmproto.Header // header for current block height + LastHeader *ibctm.Header // header for last block height committed + CurrentHeader tmproto.Header // header for current block height QueryServer types.QueryServer TxConfig client.TxConfig Codec codec.BinaryCodec @@ -392,13 +392,13 @@ func (chain *TestChain) GetPrefix() commitmenttypes.MerklePrefix { // ConstructUpdateTMClientHeader will construct a valid 07-tendermint Header to update the // light client on the source chain. -func (chain *TestChain) ConstructUpdateTMClientHeader(counterparty *TestChain, clientID string) (*ibctmtypes.Header, error) { +func (chain *TestChain) ConstructUpdateTMClientHeader(counterparty *TestChain, clientID string) (*ibctm.Header, error) { return chain.ConstructUpdateTMClientHeaderWithTrustedHeight(counterparty, clientID, clienttypes.ZeroHeight()) } // ConstructUpdateTMClientHeader will construct a valid 07-tendermint Header to update the // light client on the source chain. -func (chain *TestChain) ConstructUpdateTMClientHeaderWithTrustedHeight(counterparty *TestChain, clientID string, trustedHeight clienttypes.Height) (*ibctmtypes.Header, error) { +func (chain *TestChain) ConstructUpdateTMClientHeaderWithTrustedHeight(counterparty *TestChain, clientID string, trustedHeight clienttypes.Height) (*ibctm.Header, error) { header := counterparty.LastHeader // Relayer must query for LatestHeight on client to get TrustedHeight if the trusted height is not set if trustedHeight.IsZero() { @@ -420,7 +420,7 @@ func (chain *TestChain) ConstructUpdateTMClientHeaderWithTrustedHeight(counterpa // NextValidatorsHash tmTrustedVals, ok = counterparty.GetValsAtHeight(int64(trustedHeight.RevisionHeight + 1)) if !ok { - return nil, sdkerrors.Wrapf(ibctmtypes.ErrInvalidHeaderHeight, "could not retrieve trusted validators at trustedHeight: %d", trustedHeight) + return nil, sdkerrors.Wrapf(ibctm.ErrInvalidHeaderHeight, "could not retrieve trusted validators at trustedHeight: %d", trustedHeight) } } // inject trusted fields into last header @@ -444,13 +444,13 @@ func (chain *TestChain) ExpireClient(amount time.Duration) { // CurrentTMClientHeader creates a TM header using the current header parameters // on the chain. The trusted fields in the header are set to nil. -func (chain *TestChain) CurrentTMClientHeader() *ibctmtypes.Header { +func (chain *TestChain) CurrentTMClientHeader() *ibctm.Header { return chain.CreateTMClientHeader(chain.ChainID, chain.CurrentHeader.Height, clienttypes.Height{}, chain.CurrentHeader.Time, chain.Vals, chain.NextVals, nil, chain.Signers) } // CreateTMClientHeader creates a TM header to update the TM client. Args are passed in to allow // caller flexibility to use params that differ from the chain. -func (chain *TestChain) CreateTMClientHeader(chainID string, blockHeight int64, trustedHeight clienttypes.Height, timestamp time.Time, tmValSet, nextVals, tmTrustedVals *tmtypes.ValidatorSet, signers map[string]tmtypes.PrivValidator) *ibctmtypes.Header { +func (chain *TestChain) CreateTMClientHeader(chainID string, blockHeight int64, trustedHeight clienttypes.Height, timestamp time.Time, tmValSet, nextVals, tmTrustedVals *tmtypes.ValidatorSet, signers map[string]tmtypes.PrivValidator) *ibctm.Header { var ( valSet *tmproto.ValidatorSet trustedVals *tmproto.ValidatorSet @@ -509,7 +509,7 @@ func (chain *TestChain) CreateTMClientHeader(chainID string, blockHeight int64, // The trusted fields may be nil. They may be filled before relaying messages to a client. // The relayer is responsible for querying client and injecting appropriate trusted fields. - return &ibctmtypes.Header{ + return &ibctm.Header{ SignedHeader: signedHeader, ValidatorSet: valSet, TrustedHeight: trustedHeight, diff --git a/testing/config.go b/testing/config.go index 15c3cc6fde1..768aa968708 100644 --- a/testing/config.go +++ b/testing/config.go @@ -6,7 +6,7 @@ import ( connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" "github.com/cosmos/ibc-go/v5/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" "github.com/cosmos/ibc-go/v5/testing/mock" ) @@ -15,7 +15,7 @@ type ClientConfig interface { } type TendermintConfig struct { - TrustLevel ibctmtypes.Fraction + TrustLevel ibctm.Fraction TrustingPeriod time.Duration UnbondingPeriod time.Duration MaxClockDrift time.Duration diff --git a/testing/endpoint.go b/testing/endpoint.go index a219f71f219..8331980da93 100644 --- a/testing/endpoint.go +++ b/testing/endpoint.go @@ -13,7 +13,7 @@ import ( commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" host "github.com/cosmos/ibc-go/v5/modules/core/24-host" "github.com/cosmos/ibc-go/v5/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ) // Endpoint is a which represents a channel endpoint and its associated @@ -92,7 +92,7 @@ func (endpoint *Endpoint) CreateClient() (err error) { require.True(endpoint.Chain.T, ok) height := endpoint.Counterparty.Chain.LastHeader.GetHeight().(clienttypes.Height) - clientState = ibctmtypes.NewClientState( + clientState = ibctm.NewClientState( endpoint.Counterparty.Chain.ChainID, tmConfig.TrustLevel, tmConfig.TrustingPeriod, tmConfig.UnbondingPeriod, tmConfig.MaxClockDrift, height, commitmenttypes.GetSDKSpecs(), UpgradePath, tmConfig.AllowUpdateAfterExpiry, tmConfig.AllowUpdateAfterMisbehaviour, ) @@ -165,7 +165,7 @@ func (endpoint *Endpoint) UpgradeChain() error { return fmt.Errorf("cannot upgrade chain if there is no counterparty client") } - clientState := endpoint.Counterparty.GetClientState().(*ibctmtypes.ClientState) + clientState := endpoint.Counterparty.GetClientState().(*ibctm.ClientState) // increment revision number in chainID @@ -190,7 +190,7 @@ func (endpoint *Endpoint) UpgradeChain() error { clientState.LatestHeight = clienttypes.NewHeight(revisionNumber+1, clientState.LatestHeight.GetRevisionHeight()+1) endpoint.Counterparty.SetClientState(clientState) - consensusState := &ibctmtypes.ConsensusState{ + consensusState := &ibctm.ConsensusState{ Timestamp: endpoint.Chain.LastHeader.GetTime(), Root: commitmenttypes.NewMerkleRoot(endpoint.Chain.LastHeader.Header.GetAppHash()), NextValidatorsHash: endpoint.Chain.LastHeader.Header.NextValidatorsHash, diff --git a/testing/simapp/app.go b/testing/simapp/app.go index 1fb0079871b..a7021ebc280 100644 --- a/testing/simapp/app.go +++ b/testing/simapp/app.go @@ -38,7 +38,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/capability" capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - "github.com/cosmos/cosmos-sdk/x/crisis" crisiskeeper "github.com/cosmos/cosmos-sdk/x/crisis/keeper" crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" diff --git a/testing/values.go b/testing/values.go index 2652bee1809..e60de189e03 100644 --- a/testing/values.go +++ b/testing/values.go @@ -12,7 +12,7 @@ import ( ibctransfertypes "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" "github.com/cosmos/ibc-go/v5/testing/mock" "github.com/cosmos/ibc-go/v5/testing/simapp" ) @@ -47,7 +47,7 @@ var ( DefaultOpenInitVersion *connectiontypes.Version // DefaultTrustLevel sets params variables used to create a TM client - DefaultTrustLevel = ibctmtypes.DefaultTrustLevel + DefaultTrustLevel = ibctm.DefaultTrustLevel TestCoin = sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) UpgradePath = []string{"upgrade", "upgradedIBCState"} From 68e8e793c406ccdc980c5f19932b975c5bc247fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?colin=20axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Thu, 4 Aug 2022 14:03:16 +0200 Subject: [PATCH 62/71] apply suggested changes (#1877) --- modules/core/exported/client.go | 95 ++++++++++++++++----------------- modules/core/genesis_test.go | 2 +- 2 files changed, 48 insertions(+), 49 deletions(-) diff --git a/modules/core/exported/client.go b/modules/core/exported/client.go index 1254e89ab10..62b02edfbc4 100644 --- a/modules/core/exported/client.go +++ b/modules/core/exported/client.go @@ -40,17 +40,56 @@ type ClientState interface { GetLatestHeight() Height Validate() error + // Status must return the status of the client. Only Active clients are allowed to process packets. + Status(ctx sdk.Context, clientStore sdk.KVStore, cdc codec.BinaryCodec) Status + + // ExportMetadata must export metadata stored within the clientStore for genesis export + ExportMetadata(clientStore sdk.KVStore) []GenesisMetadata + + // ZeroCustomFields zeroes out any client customizable fields in client state + // Ledger enforced fields are maintained while all custom fields are zero values + // Used to verify upgrades + ZeroCustomFields() ClientState + + // GetTimestampAtHeight must return the timestamp for the consensus state associated with the provided height. + GetTimestampAtHeight( + ctx sdk.Context, + clientStore sdk.KVStore, + cdc codec.BinaryCodec, + height Height, + ) (uint64, error) + // Initialization function // Clients must validate the initial consensus state, and may store any client-specific metadata // necessary for correct light client operation - Initialize(sdk.Context, codec.BinaryCodec, sdk.KVStore, ConsensusState) error + Initialize(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, consensusState ConsensusState) error - // Status function - // Clients must return their status. Only Active clients are allowed to process packets. - Status(ctx sdk.Context, clientStore sdk.KVStore, cdc codec.BinaryCodec) Status + // VerifyMembership is a generic proof verification method which verifies a proof of the existence of a value at a given CommitmentPath at the specified height. + // The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). + VerifyMembership( + ctx sdk.Context, + clientStore sdk.KVStore, + cdc codec.BinaryCodec, + height Height, + delayTimePeriod uint64, + delayBlockPeriod uint64, + proof []byte, + path []byte, + value []byte, + ) error - // Genesis function - ExportMetadata(sdk.KVStore) []GenesisMetadata + // VerifyNonMembership is a generic proof verification method which verifies the absence of a given CommitmentPath at a specified height. + // The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). + VerifyNonMembership( + ctx sdk.Context, + clientStore sdk.KVStore, + cdc codec.BinaryCodec, + height Height, + delayTimePeriod uint64, + delayBlockPeriod uint64, + proof []byte, + path []byte, + ) error // VerifyClientMessage must verify a ClientMessage. A ClientMessage could be a Header, Misbehaviour, or batch update. // It must handle each type of ClientMessage appropriately. Calls to CheckForMisbehaviour, UpdateState, and UpdateStateOnMisbehaviour @@ -60,14 +99,14 @@ type ClientState interface { // Checks for evidence of a misbehaviour in Header or Misbehaviour type. It assumes the ClientMessage // has already been verified. - CheckForMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, msg ClientMessage) bool + CheckForMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg ClientMessage) bool // UpdateStateOnMisbehaviour should perform appropriate state changes on a client state given that misbehaviour has been detected and verified UpdateStateOnMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg ClientMessage) // UpdateState updates and stores as necessary any associated information for an IBC client, such as the ClientState and corresponding ConsensusState. // Upon successful update, a list of consensus heights is returned. It assumes the ClientMessage has already been verified. - UpdateState(sdk.Context, codec.BinaryCodec, sdk.KVStore, ClientMessage) []Height + UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg ClientMessage) []Height // Update and Misbehaviour functions CheckSubstituteAndUpdateState(ctx sdk.Context, cdc codec.BinaryCodec, subjectClientStore, substituteClientStore sdk.KVStore, substituteClient ClientState) (ClientState, error) @@ -88,46 +127,6 @@ type ClientState interface { proofUpgradeClient, proofUpgradeConsState []byte, ) error - // Utility function that zeroes out any client customizable fields in client state - // Ledger enforced fields are maintained while all custom fields are zero values - // Used to verify upgrades - ZeroCustomFields() ClientState - - // State verification functions - - // VerifyMembership is a generic proof verification method which verifies a proof of the existence of a value at a given CommitmentPath at the specified height. - // The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). - VerifyMembership( - ctx sdk.Context, - clientStore sdk.KVStore, - cdc codec.BinaryCodec, - height Height, - delayTimePeriod uint64, - delayBlockPeriod uint64, - proof []byte, - path []byte, - value []byte, - ) error - - // VerifyNonMembership is a generic proof verification method which verifies the absence of a given CommitmentPath at a specified height. - // The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). - VerifyNonMembership( - ctx sdk.Context, - clientStore sdk.KVStore, - cdc codec.BinaryCodec, - height Height, - delayTimePeriod uint64, - delayBlockPeriod uint64, - proof []byte, - path []byte, - ) error - - GetTimestampAtHeight( - ctx sdk.Context, - clientStore sdk.KVStore, - cdc codec.BinaryCodec, - height Height, - ) (uint64, error) } // ConsensusState is the state of the consensus process diff --git a/modules/core/genesis_test.go b/modules/core/genesis_test.go index 786339fda2d..c433e834de1 100644 --- a/modules/core/genesis_test.go +++ b/modules/core/genesis_test.go @@ -24,7 +24,7 @@ const ( connectionID = "connection-0" clientID = "07-tendermint-0" connectionID2 = "connection-1" - clientID2 = "07-tendermin-1" + clientID2 = "07-tendermint-1" port1 = "firstport" port2 = "secondport" From 3d33e8b6e91eab0b7fb29f0f6e5019c74442614d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?colin=20axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Thu, 4 Aug 2022 14:03:31 +0200 Subject: [PATCH 63/71] documentation fixes (#1876) --- docs/architecture/adr-002-go-module-versioning.md | 6 +++--- docs/migrations/{v4-to-v5.md => v5-to-v6.md} | 8 ++++---- docs/roadmap/roadmap.md | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) rename docs/migrations/{v4-to-v5.md => v5-to-v6.md} (95%) diff --git a/docs/architecture/adr-002-go-module-versioning.md b/docs/architecture/adr-002-go-module-versioning.md index 983937e8537..a046792b142 100644 --- a/docs/architecture/adr-002-go-module-versioning.md +++ b/docs/architecture/adr-002-go-module-versioning.md @@ -23,7 +23,7 @@ v1.0.0 was decided to be used instead of v0.1.0 primarily for the following reas When a Go module is released under v1.0.0, all following releases must follow Go semantic versioning. Thus when the go API is broken, the Go module major version **must** be incremented. -For example, changing the go package version from `v2` to `v3` bumps the import from `github.com/cosmos/ibc-go/v2` to `github.com/cosmos/ibc-go/v5`. +For example, changing the go package version from `v2` to `v3` bumps the import from `github.com/cosmos/ibc-go/v2` to `github.com/cosmos/ibc-go/v3`. If the Go module version is not incremented then attempting to go get a module @v3.0.0 without the suffix results in: `invalid version: module contains a go.mod file, so major version must be compatible: should be v0 or v1, not v3` @@ -33,7 +33,7 @@ Not including a go.mod in our release is not a viable option. #### Attempting to import multiple go module versions for ibc-go -Attempting to import two versions of ibc-go, such as `github.com/cosmos/ibc-go/v2` and `github.com/cosmos/ibc-go/v5`, will result in multiple issues. +Attempting to import two versions of ibc-go, such as `github.com/cosmos/ibc-go/v2` and `github.com/cosmos/ibc-go/v3`, will result in multiple issues. The Cosmos SDK does global registration of error and governance proposal types. The errors and proposals used in ibc-go would need to now register their naming based on the go module version. @@ -76,7 +76,7 @@ For example, lets say this solution is implemented in v3. Then `github.com/cosmos/ibc-go/v2` cannot be imported with any other ibc-go version -`github.com/cosmos/ibc-go/v5` cannot be imported with any previous ibc-go versions +`github.com/cosmos/ibc-go/v3` cannot be imported with any previous ibc-go versions `github.com/cosmos/ibc-go/v4` may be imported with ibc-go versions v3+ diff --git a/docs/migrations/v4-to-v5.md b/docs/migrations/v5-to-v6.md similarity index 95% rename from docs/migrations/v4-to-v5.md rename to docs/migrations/v5-to-v6.md index a02e953f984..f27dee55bb7 100644 --- a/docs/migrations/v4-to-v5.md +++ b/docs/migrations/v5-to-v6.md @@ -1,4 +1,4 @@ -# Migrating from ibc-go v4 to v5 +# Migrating from ibc-go v5 to v6 This document is intended to highlight significant changes which may require more information than presented in the CHANGELOG. Any changes that must be done by a user of ibc-go should be documented here. @@ -60,12 +60,12 @@ The `GetRoot` function has been removed from consensus state interface since it The `09-localhost` light client implementation has been removed because it is currently non-functional. An upgrade handler has been added to supply chain developers with the logic needed to prune the ibc client store and successfully complete the removal of `09-localhost`. -Add the following to the application upgrade handler in `app/app.go`, calling `MigrateToV5` to perform store migration logic. +Add the following to the application upgrade handler in `app/app.go`, calling `MigrateToV6` to perform store migration logic. ```go import ( // ... - ibcv5 "github.com/cosmos/ibc-go/v5/modules/core/migrations/v5" + ibcv6 "github.com/cosmos/ibc-go/v6/modules/core/migrations/v6" ) // ... @@ -74,7 +74,7 @@ app.UpgradeKeeper.SetUpgradeHandler( upgradeName, func(ctx sdk.Context, _ upgradetypes.Plan, _ module.VersionMap) (module.VersionMap, error) { // prune the 09-localhost client from the ibc client store - ibcv5.MigrateToV5(ctx, app.IBCKeeper.ClientKeeper) + ibcv6.MigrateToV6(ctx, app.IBCKeeper.ClientKeeper) return app.mm.RunMigrations(ctx, app.configurator, fromVM) }, diff --git a/docs/roadmap/roadmap.md b/docs/roadmap/roadmap.md index 12414076e57..eaf416adfb9 100644 --- a/docs/roadmap/roadmap.md +++ b/docs/roadmap/roadmap.md @@ -14,7 +14,7 @@ This roadmap should be read as a high-level guide, rather than a commitment to s At a high level we will focus on: -At a high level we will focus on: +### Features - Releasing [v4.0.0](https://github.com/cosmos/ibc-go/milestone/26), which includes the ICS-29 Fee Middleware module. - Finishing and releasing the [refactoring of 02-client](https://github.com/cosmos/ibc-go/milestone/16). This refactor will make the development of light clients easier. From 163b706962fce99cb7adb1b178fc5b0972fa4545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?colin=20axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Thu, 4 Aug 2022 14:29:03 +0200 Subject: [PATCH 64/71] fix: complete changes for CheckSubstituteAndUpdateState (#1878) * complete changes for CheckSubstituteAndUpdateState * Update modules/core/02-client/keeper/proposal.go --- modules/core/02-client/keeper/events.go | 5 ++- modules/core/02-client/keeper/proposal.go | 9 +++--- .../core/02-client/legacy/v100/solomachine.go | 2 +- modules/core/exported/client.go | 5 +-- .../06-solomachine/proposal_handle.go | 31 +++++++------------ .../06-solomachine/proposal_handle_test.go | 14 ++++----- .../07-tendermint/proposal_handle.go | 16 +++++----- .../07-tendermint/proposal_handle_test.go | 8 ++--- 8 files changed, 39 insertions(+), 51 deletions(-) diff --git a/modules/core/02-client/keeper/events.go b/modules/core/02-client/keeper/events.go index 8936442b8eb..98a212eefe3 100644 --- a/modules/core/02-client/keeper/events.go +++ b/modules/core/02-client/keeper/events.go @@ -82,13 +82,12 @@ func EmitUpgradeClientEvent(ctx sdk.Context, clientID string, clientState export } // EmitUpdateClientProposalEvent emits an update client proposal event -func EmitUpdateClientProposalEvent(ctx sdk.Context, clientID string, clientState exported.ClientState) { +func EmitUpdateClientProposalEvent(ctx sdk.Context, clientID, clientType string) { ctx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypeUpdateClientProposal, sdk.NewAttribute(types.AttributeKeySubjectClientID, clientID), - sdk.NewAttribute(types.AttributeKeyClientType, clientState.ClientType()), - sdk.NewAttribute(types.AttributeKeyConsensusHeight, clientState.GetLatestHeight().String()), + sdk.NewAttribute(types.AttributeKeyClientType, clientType), ), ) } diff --git a/modules/core/02-client/keeper/proposal.go b/modules/core/02-client/keeper/proposal.go index d25ae0ef34d..be24cfdc643 100644 --- a/modules/core/02-client/keeper/proposal.go +++ b/modules/core/02-client/keeper/proposal.go @@ -44,19 +44,18 @@ func (k Keeper) ClientUpdateProposal(ctx sdk.Context, p *types.ClientUpdatePropo return sdkerrors.Wrapf(types.ErrClientStateNotFound, "substitute client is not Active, status is %s", status) } - clientState, err := subjectClientState.CheckSubstituteAndUpdateState(ctx, k.cdc, subjectClientStore, substituteClientStore, substituteClientState) - if err != nil { + if err := subjectClientState.CheckSubstituteAndUpdateState(ctx, k.cdc, subjectClientStore, substituteClientStore, substituteClientState); err != nil { return err } - k.Logger(ctx).Info("client updated after governance proposal passed", "client-id", p.SubjectClientId, "height", clientState.GetLatestHeight().String()) + k.Logger(ctx).Info("client updated after governance proposal passed", "client-id", p.SubjectClientId) defer func() { telemetry.IncrCounterWithLabels( []string{"ibc", "client", "update"}, 1, []metrics.Label{ - telemetry.NewLabel(types.LabelClientType, clientState.ClientType()), + telemetry.NewLabel(types.LabelClientType, substituteClientState.ClientType()), telemetry.NewLabel(types.LabelClientID, p.SubjectClientId), telemetry.NewLabel(types.LabelUpdateType, "proposal"), }, @@ -64,7 +63,7 @@ func (k Keeper) ClientUpdateProposal(ctx sdk.Context, p *types.ClientUpdatePropo }() // emitting events in the keeper for proposal updates to clients - EmitUpdateClientProposalEvent(ctx, p.SubjectClientId, clientState) + EmitUpdateClientProposalEvent(ctx, p.SubjectClientId, substituteClientState.ClientType()) return nil } diff --git a/modules/core/02-client/legacy/v100/solomachine.go b/modules/core/02-client/legacy/v100/solomachine.go index 9df81bee22a..d16dad0bfbb 100644 --- a/modules/core/02-client/legacy/v100/solomachine.go +++ b/modules/core/02-client/legacy/v100/solomachine.go @@ -130,7 +130,7 @@ func (cs ClientState) CheckMisbehaviourAndUpdateState( func (cs ClientState) CheckSubstituteAndUpdateState( ctx sdk.Context, _ codec.BinaryCodec, _, _ sdk.KVStore, _ exported.ClientState, -) (exported.ClientState, error) { +) error { panic("legacy solo machine is deprecated!") } diff --git a/modules/core/exported/client.go b/modules/core/exported/client.go index 62b02edfbc4..ac76a647dc5 100644 --- a/modules/core/exported/client.go +++ b/modules/core/exported/client.go @@ -108,8 +108,9 @@ type ClientState interface { // Upon successful update, a list of consensus heights is returned. It assumes the ClientMessage has already been verified. UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg ClientMessage) []Height - // Update and Misbehaviour functions - CheckSubstituteAndUpdateState(ctx sdk.Context, cdc codec.BinaryCodec, subjectClientStore, substituteClientStore sdk.KVStore, substituteClient ClientState) (ClientState, error) + // CheckSubstituteAndUpdateState must verify that the provided substitute may be used to update the subject client. + // The light client must set the updated client and consensus states within the clientStore for the subject client. + CheckSubstituteAndUpdateState(ctx sdk.Context, cdc codec.BinaryCodec, subjectClientStore, substituteClientStore sdk.KVStore, substituteClient ClientState) error // Upgrade functions // NOTE: proof heights are not included as upgrade to a new revision is expected to pass only on the last diff --git a/modules/light-clients/06-solomachine/proposal_handle.go b/modules/light-clients/06-solomachine/proposal_handle.go index 1abb33f31fa..bef2db14299 100644 --- a/modules/light-clients/06-solomachine/proposal_handle.go +++ b/modules/light-clients/06-solomachine/proposal_handle.go @@ -21,45 +21,36 @@ import ( func (cs ClientState) CheckSubstituteAndUpdateState( ctx sdk.Context, cdc codec.BinaryCodec, subjectClientStore, _ sdk.KVStore, substituteClient exported.ClientState, -) (exported.ClientState, error) { +) error { if !cs.AllowUpdateAfterProposal { - return nil, sdkerrors.Wrapf( - clienttypes.ErrUpdateClientFailed, - "solo machine client is not allowed to updated with a proposal", - ) + return sdkerrors.Wrapf(clienttypes.ErrUpdateClientFailed, "solo machine client is not allowed to updated with a proposal") } substituteClientState, ok := substituteClient.(*ClientState) if !ok { - return nil, sdkerrors.Wrapf( - clienttypes.ErrInvalidClientType, "substitute client state type %T, expected %T", substituteClient, &ClientState{}, - ) + return sdkerrors.Wrapf(clienttypes.ErrInvalidClientType, "substitute client state type %T, expected %T", substituteClient, &ClientState{}) } subjectPublicKey, err := cs.ConsensusState.GetPubKey() if err != nil { - return nil, sdkerrors.Wrap(err, "failed to get consensus public key") + return sdkerrors.Wrap(err, "failed to get consensus public key") } substitutePublicKey, err := substituteClientState.ConsensusState.GetPubKey() if err != nil { - return nil, sdkerrors.Wrap(err, "failed to get substitute client public key") + return sdkerrors.Wrap(err, "failed to get substitute client public key") } if reflect.DeepEqual(subjectPublicKey, substitutePublicKey) { - return nil, sdkerrors.Wrapf( - clienttypes.ErrInvalidHeader, "subject and substitute have the same public key", - ) + return sdkerrors.Wrapf(clienttypes.ErrInvalidHeader, "subject and substitute have the same public key") } - clientState := &cs - // update to substitute parameters - clientState.Sequence = substituteClientState.Sequence - clientState.ConsensusState = substituteClientState.ConsensusState - clientState.IsFrozen = false + cs.Sequence = substituteClientState.Sequence + cs.ConsensusState = substituteClientState.ConsensusState + cs.IsFrozen = false - setClientState(subjectClientStore, cdc, clientState) + setClientState(subjectClientStore, cdc, &cs) - return clientState, nil + return nil } diff --git a/modules/light-clients/06-solomachine/proposal_handle_test.go b/modules/light-clients/06-solomachine/proposal_handle_test.go index 8b8ee96c53a..21a28564320 100644 --- a/modules/light-clients/06-solomachine/proposal_handle_test.go +++ b/modules/light-clients/06-solomachine/proposal_handle_test.go @@ -72,21 +72,21 @@ func (suite *SoloMachineTestSuite) TestCheckSubstituteAndUpdateState() { subjectClientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), sm.ClientID) substituteClientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), substitute.ClientID) - updatedClient, err := subjectClientState.CheckSubstituteAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), subjectClientStore, substituteClientStore, substituteClientState) + err := subjectClientState.CheckSubstituteAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), subjectClientStore, substituteClientStore, substituteClientState) if tc.expPass { suite.Require().NoError(err) - suite.Require().Equal(substituteClientState.(*solomachine.ClientState).ConsensusState, updatedClient.(*solomachine.ClientState).ConsensusState) - suite.Require().Equal(substituteClientState.(*solomachine.ClientState).Sequence, updatedClient.(*solomachine.ClientState).Sequence) - suite.Require().Equal(false, updatedClient.(*solomachine.ClientState).IsFrozen) - // ensure updated client state is set in store bz := subjectClientStore.Get(host.ClientStateKey()) - suite.Require().Equal(clienttypes.MustMarshalClientState(suite.chainA.Codec, updatedClient), bz) + updatedClient := clienttypes.MustUnmarshalClientState(suite.chainA.App.AppCodec(), bz).(*solomachine.ClientState) + + suite.Require().Equal(substituteClientState.(*solomachine.ClientState).ConsensusState, updatedClient.ConsensusState) + suite.Require().Equal(substituteClientState.(*solomachine.ClientState).Sequence, updatedClient.Sequence) + suite.Require().Equal(false, updatedClient.IsFrozen) + } else { suite.Require().Error(err) - suite.Require().Nil(updatedClient) } }) } diff --git a/modules/light-clients/07-tendermint/proposal_handle.go b/modules/light-clients/07-tendermint/proposal_handle.go index bba5f779a8f..e1195a8925c 100644 --- a/modules/light-clients/07-tendermint/proposal_handle.go +++ b/modules/light-clients/07-tendermint/proposal_handle.go @@ -27,16 +27,14 @@ import ( func (cs ClientState) CheckSubstituteAndUpdateState( ctx sdk.Context, cdc codec.BinaryCodec, subjectClientStore, substituteClientStore sdk.KVStore, substituteClient exported.ClientState, -) (exported.ClientState, error) { +) error { substituteClientState, ok := substituteClient.(*ClientState) if !ok { - return nil, sdkerrors.Wrapf( - clienttypes.ErrInvalidClient, "expected type %T, got %T", &ClientState{}, substituteClient, - ) + return sdkerrors.Wrapf(clienttypes.ErrInvalidClient, "expected type %T, got %T", &ClientState{}, substituteClient) } if !IsMatchingClientState(cs, *substituteClientState) { - return nil, sdkerrors.Wrap(clienttypes.ErrInvalidSubstitute, "subject client state does not match substitute client state") + return sdkerrors.Wrap(clienttypes.ErrInvalidSubstitute, "subject client state does not match substitute client state") } if cs.Status(ctx, subjectClientStore, cdc) == exported.Frozen { @@ -50,7 +48,7 @@ func (cs ClientState) CheckSubstituteAndUpdateState( consensusState, found := GetConsensusState(substituteClientStore, cdc, height) if !found { - return nil, sdkerrors.Wrap(clienttypes.ErrConsensusStateNotFound, "unable to retrieve latest consensus state for substitute client") + return sdkerrors.Wrap(clienttypes.ErrConsensusStateNotFound, "unable to retrieve latest consensus state for substitute client") } setConsensusState(subjectClientStore, cdc, consensusState, height) @@ -58,12 +56,12 @@ func (cs ClientState) CheckSubstituteAndUpdateState( // set metadata stored for the substitute consensus state processedHeight, found := GetProcessedHeight(substituteClientStore, height) if !found { - return nil, sdkerrors.Wrap(clienttypes.ErrUpdateClientFailed, "unable to retrieve processed height for substitute client latest height") + return sdkerrors.Wrap(clienttypes.ErrUpdateClientFailed, "unable to retrieve processed height for substitute client latest height") } processedTime, found := GetProcessedTime(substituteClientStore, height) if !found { - return nil, sdkerrors.Wrap(clienttypes.ErrUpdateClientFailed, "unable to retrieve processed time for substitute client latest height") + return sdkerrors.Wrap(clienttypes.ErrUpdateClientFailed, "unable to retrieve processed time for substitute client latest height") } setConsensusMetadataWithValues(subjectClientStore, height, processedHeight, processedTime) @@ -78,7 +76,7 @@ func (cs ClientState) CheckSubstituteAndUpdateState( // in 02-client. setClientState(subjectClientStore, cdc, &cs) - return &cs, nil + return nil } // IsMatchingClientState returns true if all the client state parameters match diff --git a/modules/light-clients/07-tendermint/proposal_handle_test.go b/modules/light-clients/07-tendermint/proposal_handle_test.go index 5f1032db69b..3d7e5e3e688 100644 --- a/modules/light-clients/07-tendermint/proposal_handle_test.go +++ b/modules/light-clients/07-tendermint/proposal_handle_test.go @@ -59,9 +59,8 @@ func (suite *TendermintTestSuite) TestCheckSubstituteUpdateStateBasic() { subjectClientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), subjectPath.EndpointA.ClientID) substituteClientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), substitutePath.EndpointA.ClientID) - updatedClient, err := subjectClientState.CheckSubstituteAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), subjectClientStore, substituteClientStore, substituteClientState) + err := subjectClientState.CheckSubstituteAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), subjectClientStore, substituteClientStore, substituteClientState) suite.Require().Error(err) - suite.Require().Nil(updatedClient) }) } } @@ -170,10 +169,12 @@ func (suite *TendermintTestSuite) TestCheckSubstituteAndUpdateState() { suite.Require().True(found) expectedIterationKey := ibctm.GetIterationKey(substituteClientStore, substituteClientState.GetLatestHeight()) - updatedClient, err := subjectClientState.CheckSubstituteAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), subjectClientStore, substituteClientStore, substituteClientState) + err := subjectClientState.CheckSubstituteAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), subjectClientStore, substituteClientStore, substituteClientState) if tc.expPass { suite.Require().NoError(err) + + updatedClient := subjectPath.EndpointA.GetClientState() suite.Require().Equal(clienttypes.ZeroHeight(), updatedClient.(*ibctm.ClientState).FrozenHeight) subjectClientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), subjectPath.EndpointA.ClientID) @@ -196,7 +197,6 @@ func (suite *TendermintTestSuite) TestCheckSubstituteAndUpdateState() { suite.Require().Equal(time.Hour*24*7, updatedClient.(*ibctm.ClientState).TrustingPeriod) } else { suite.Require().Error(err) - suite.Require().Nil(updatedClient) } }) } From 81f2d0940efb48a1d72042b470a62088b68fd7f0 Mon Sep 17 00:00:00 2001 From: Sean King Date: Thu, 4 Aug 2022 18:25:12 +0200 Subject: [PATCH 65/71] 02-client refactor: fix changelog (#1873) * chore: changelog * chore: fix --- CHANGELOG.md | 62 ++++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 26478b2f2d5..b07900cb2fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,11 +44,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### API Breaking -* (06-solomachine) [\#1679](https://github.com/cosmos/ibc-go/pull/1679) Remove `types` sub-package from `06-solomachine` lightclient directory. -* (07-tendermint) [\#1677](https://github.com/cosmos/ibc-go/pull/1677) Remove `types` sub-package from `07-tendermint` lightclient directory. -* (06-solomachine) [\#1687](https://github.com/cosmos/ibc-go/pull/1687) Bump `06-solomachine` protobuf version from `v2` to `v3`. -* (06-solomachine) [\#1687](https://github.com/cosmos/ibc-go/pull/1687) Removed `DataType` enum and associated message types from `06-solomachine`. `DataType` has been removed from `SignBytes` and `SignatureAndData` in favour of `path`. -* (core/03-connection) [\#1797](https://github.com/cosmos/ibc-go/pull/1797) Remove `PreviousConnectionID` from `NewMsgConnectionOpenTry` arguments. `MsgConnectionOpenTry.ValidateBasic()` returns error if the deprecated `PreviousConnectionID` is not empty. +* (core/03-connection) [\#1797](https://github.com/cosmos/ibc-go/pull/1797) Remove `PreviousConnectionID` from `NewMsgConnectionOpenTry` arguments. `MsgConnectionOpenTry.ValidateBasic()` returns error if the deprecated `PreviousConnectionID` is not empty * (core/04-channel) [\#1792](https://github.com/cosmos/ibc-go/pull/1792) Remove `PreviousChannelID` from `NewMsgChannelOpenTry` arguments. `MsgChannelOpenTry.ValidateBasic()` returns error if the deprecated `PreviousChannelID` is not empty. * (core/04-channel) [\#1418](https://github.com/cosmos/ibc-go/pull/1418) `NewPacketId` has been renamed to `NewPacketID` to comply with go linting rules. * (core/ante) [\#1418](https://github.com/cosmos/ibc-go/pull/1418) `AnteDecorator` has been renamed to `RedundancyDecorator` to comply with go linting rules and to give more clarity to the purpose of the Decorator. @@ -65,23 +61,22 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (transfer)[\#1565](https://github.com/cosmos/ibc-go/pull/1565) Removing `NewErrorAcknowledgement` in favour of `channeltypes.NewErrorAcknowledgement`. * (channel)[\#1565](https://github.com/cosmos/ibc-go/pull/1565) Updating `NewErrorAcknowledgement` to accept an error instead of a string and removing the possibility of non-deterministic writes to application state. * (core/04-channel)[\#1636](https://github.com/cosmos/ibc-go/pull/1636) Removing `SplitChannelVersion` and `MergeChannelVersions` functions since they are not used. +- +* (06-solomachine) [\#1679](https://github.com/cosmos/ibc-go/pull/1679) Remove `types` sub-package from `06-solomachine` lightclient directory. +* (07-tendermint) [\#1677](https://github.com/cosmos/ibc-go/pull/1677) Remove `types` sub-package from `07-tendermint` lightclient directory. +* (06-solomachine) [\#1687](https://github.com/cosmos/ibc-go/pull/1687) Bump `06-solomachine` protobuf version from `v2` to `v3`. +* (06-solomachine) [\#1687](https://github.com/cosmos/ibc-go/pull/1687) Removed `DataType` enum and associated message types from `06-solomachine`. `DataType` has been removed from `SignBytes` and `SignatureAndData` in favour of `path`. +* (02-client) [\#598](https://github.com/cosmos/ibc-go/pull/598) The client state and consensus state return value has been removed from `VerifyUpgradeAndUpdateState`. Light client implementations must update the client state and consensus state after verifying a valid client upgrade. +* (06-solomachine) [\#1100](https://github.com/cosmos/ibc-go/pull/1100) Remove `GetClientID` function from 06-solomachine `Misbehaviour` type. +* (06-solomachine) [\#1100](https://github.com/cosmos/ibc-go/pull/1100) Deprecate `ClientId` field in 06-solomachine `Misbehaviour` type. +* (07-tendermint) [\#1097](https://github.com/cosmos/ibc-go/pull/1097) Remove `GetClientID` function from 07-tendermint `Misbehaviour` type. +* (07-tendermint) [\#1097](https://github.com/cosmos/ibc-go/pull/1097) Deprecate `ClientId` field in 07-tendermint `Misbehaviour` type. +* (modules/core/exported) [\#1107](https://github.com/cosmos/ibc-go/pull/1107) Merging the `Header` and `Misbehaviour` interfaces into a single `ClientMessage` type ### State Machine Breaking ### Improvements -* (modules/core/02-client) [\#1188](https://github.com/cosmos/ibc-go/pull/1188/files) Routing `MsgSubmitMisbehaviour` to `UpdateClient` keeper function. Deprecating `SubmitMisbehaviour` endpoint. -* (modules/core/02-client) [\#1208](https://github.com/cosmos/ibc-go/pull/1208) Replace `CheckHeaderAndUpdateState` usage in 02-client with calls to `VerifyClientMessage`, `CheckForMisbehaviour`, `UpdateStateOnMisbehaviour` and `UpdateState`. -* (modules/light-clients/09-localhost) [\#1187](https://github.com/cosmos/ibc-go/pull/1187/) Removing localhost light client implementation as it is not functional. An upgrade handler is provided in `modules/migrations/v5` to prune `09-localhost` clients and consensus states from the store. -* [\#1186](https://github.com/cosmos/ibc-go/pull/1186/files) Removing `GetRoot` function from ConsensusState interface in `02-client`. `GetRoot` is unused by core IBC. -* (modules/core/02-client) [\#1196](https://github.com/cosmos/ibc-go/pull/1196) Adding VerifyClientMessage to ClientState interface. -* (modules/core/02-client) [\#1198](https://github.com/cosmos/ibc-go/pull/1198) Adding UpdateStateOnMisbehaviour to ClientState interface. -* (modules/core/02-client) [\#1170](https://github.com/cosmos/ibc-go/pull/1170) Updating `ClientUpdateProposal` to set client state in lightclient implementations `CheckSubstituteAndUpdateState` methods. -* (modules/core/02-client) [\#1197](https://github.com/cosmos/ibc-go/pull/1197) Adding `CheckForMisbehaviour` to `ClientState` interface. -* (modules/core/02-client) [\#1195](https://github.com/cosmos/ibc-go/pull/1210) Removing `CheckHeaderAndUpdateState` from `ClientState` interface & associated light client implementations. -* (modules/core/02-client) [\#1189](https://github.com/cosmos/ibc-go/pull/1212) Removing `CheckMisbehaviourAndUpdateState` from `ClientState` interface & associated light client implementations. -* (modules/core/exported) [\#1206](https://github.com/cosmos/ibc-go/pull/1206) Adding new method `UpdateState` to `ClientState` interface. -* (modules/core/02-client) [\#1741](https://github.com/cosmos/ibc-go/pull/1741) Emitting a new `upgrade_chain` event upon setting upgrade consensus state. * (linting) [\#1418](https://github.com/cosmos/ibc-go/pull/1418) Fix linting errors, resulting compatiblity with go1.18 linting style, golangci-lint 1.46.2 and the revivie linter. This caused breaking changes in core/04-channel, core/ante, and the testing library. * (app/20-transfer) [\#1680](https://github.com/cosmos/ibc-go/pull/1680) Adds migration to correct any malformed trace path information of tokens with denoms that contains slashes. The transfer module consensus version has been bumped to 2. * (app/20-transfer) [\#1730](https://github.com/cosmos/ibc-go/pull/1730) parse the ics20 denomination provided via a packet using the channel identifier format specified by ibc-go. @@ -93,17 +88,26 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (app/29-fee) [\#1341](https://github.com/cosmos/ibc-go/pull/1341) Check if the fee module is locked and if the fee module is enabled before refunding all fees * (transfer) [\#1414](https://github.com/cosmos/ibc-go/pull/1414) Emitting Sender address from `fungible_token_packet` events in `OnRecvPacket` and `OnAcknowledgementPacket`. * (testing/simapp) [\#1397](https://github.com/cosmos/ibc-go/pull/1397) Adding mock module to maccperms and adding check to ensure mock module is not a blocked account address. +- * (modules/light-clients/07-tendermint) [\#1713](https://github.com/cosmos/ibc-go/pull/1713) Allow client upgrade proposals to update `TrustingPeriod`. See ADR-026 for context. +* (modules/core/02-client) [\#1188](https://github.com/cosmos/ibc-go/pull/1188/files) Routing `MsgSubmitMisbehaviour` to `UpdateClient` keeper function. Deprecating `SubmitMisbehaviour` endpoint. +* (modules/core/02-client) [\#1208](https://github.com/cosmos/ibc-go/pull/1208) Replace `CheckHeaderAndUpdateState` usage in 02-client with calls to `VerifyClientMessage`, `CheckForMisbehaviour`, `UpdateStateOnMisbehaviour` and `UpdateState`. +* (modules/light-clients/09-localhost) [\#1187](https://github.com/cosmos/ibc-go/pull/1187/) Removing localhost light client implementation as it is not functional. An upgrade handler is provided in `modules/migrations/v5` to prune `09-localhost` clients and consensus states from the store. +* [\#1186](https://github.com/cosmos/ibc-go/pull/1186/files) Removing `GetRoot` function from ConsensusState interface in `02-client`. `GetRoot` is unused by core IBC. +* (modules/core/02-client) [\#1196](https://github.com/cosmos/ibc-go/pull/1196) Adding VerifyClientMessage to ClientState interface. +* (modules/core/02-client) [\#1198](https://github.com/cosmos/ibc-go/pull/1198) Adding UpdateStateOnMisbehaviour to ClientState interface. +* (modules/core/02-client) [\#1170](https://github.com/cosmos/ibc-go/pull/1170) Updating `ClientUpdateProposal` to set client state in lightclient implementations `CheckSubstituteAndUpdateState` methods. +* (modules/core/02-client) [\#1197](https://github.com/cosmos/ibc-go/pull/1197) Adding `CheckForMisbehaviour` to `ClientState` interface. +* (modules/core/02-client) [\#1195](https://github.com/cosmos/ibc-go/pull/1210) Removing `CheckHeaderAndUpdateState` from `ClientState` interface & associated light client implementations. +* (modules/core/02-client) [\#1189](https://github.com/cosmos/ibc-go/pull/1212) Removing `CheckMisbehaviourAndUpdateState` from `ClientState` interface & associated light client implementations. +* (modules/core/exported) [\#1206](https://github.com/cosmos/ibc-go/pull/1206) Adding new method `UpdateState` to `ClientState` interface. +* (modules/core/02-client) [\#1741](https://github.com/cosmos/ibc-go/pull/1741) Emitting a new `upgrade_chain` event upon setting upgrade consensus state. +* (client) [\#724](https://github.com/cosmos/ibc-go/pull/724) `IsRevisionFormat` and `IsClientIDFormat` have been updated to disallow newlines before the dash used to separate the chainID and revision number, and the client type and client sequence. +* (02-client/cli) [\#897](https://github.com/cosmos/ibc-go/pull/897) Remove `GetClientID()` from `Misbehaviour` interface. Submit client misbehaviour cli command requires an explicit client id now. ### Features * [\#276](https://github.com/cosmos/ibc-go/pull/276) Adding the Fee Middleware module v1 - -### Bug Fixes - -* (light-clients/solomachine) [#1839](https://github.com/cosmos/ibc-go/issues/1839) Fixed usage of the new diversifier in validation of changing diversifiers for the solo machine. The current diversifier must sign over the new diversifier. -* (07-tendermint) [\#1674](https://github.com/cosmos/ibc-go/pull/1674) Submitted ClientState is zeroed out before checking the proof in order to prevent the proposal from containing information governance is not actually voting on. -* (modules/core/02-client)[\#1676](https://github.com/cosmos/ibc-go/pull/1676) ClientState must be zeroed out for `UpgradeProposals` to pass validation. This prevents a proposal containing information governance is not actually voting on. * (apps/29-fee) [\#1229](https://github.com/cosmos/ibc-go/pull/1229) Adding CLI commands for getting all unrelayed incentivized packets and packet by packet-id. * (apps/29-fee) [\#1224](https://github.com/cosmos/ibc-go/pull/1224) Adding Query/CounterpartyAddress and CLI to ICS29 fee middleware * (apps/29-fee) [\#1225](https://github.com/cosmos/ibc-go/pull/1225) Adding Query/FeeEnabledChannel and Query/FeeEnabledChannels with CLIs to ICS29 fee middleware. @@ -114,6 +118,10 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (apps/29-fee) [\#1774](https://github.com/cosmos/ibc-go/pull/1774) Change non nil relayer assertion to non empty to avoid import/export issues for genesis upgrades. * (makefile) [\#1785](https://github.com/cosmos/ibc-go/pull/1785) Fetch the correct versions of protocol buffers dependencies from tendermint, cosmos-sdk, and ics23. * (apps/29-fee) [\#1278](https://github.com/cosmos/ibc-go/pull/1278) The URI path for the query to get all incentivized packets for a specific channel did not follow the same format as the rest of queries. +- +* (light-clients/solomachine) [#1839](https://github.com/cosmos/ibc-go/issues/1839) Fixed usage of the new diversifier in validation of changing diversifiers for the solo machine. The current diversifier must sign over the new diversifier. +* (07-tendermint) [\#1674](https://github.com/cosmos/ibc-go/pull/1674) Submitted ClientState is zeroed out before checking the proof in order to prevent the proposal from containing information governance is not actually voting on. +* (modules/core/02-client)[\#1676](https://github.com/cosmos/ibc-go/pull/1676) ClientState must be zeroed out for `UpgradeProposals` to pass validation. This prevents a proposal containing information governance is not actually voting on. ## [v3.1.1](https://github.com/cosmos/ibc-go/releases/tag/v3.1.1) - 2022-08-02 @@ -189,11 +197,6 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### API Breaking -* (02-client) [\#598](https://github.com/cosmos/ibc-go/pull/598) The client state and consensus state return value has been removed from `VerifyUpgradeAndUpdateState`. Light client implementations must update the client state and consensus state after verifying a valid client upgrade. -* (06-solomachine) [\#1100](https://github.com/cosmos/ibc-go/pull/1100) Remove `GetClientID` function from 06-solomachine `Misbehaviour` type. -* (06-solomachine) [\#1100](https://github.com/cosmos/ibc-go/pull/1100) Deprecate `ClientId` field in 06-solomachine `Misbehaviour` type. -* (07-tendermint) [\#1097](https://github.com/cosmos/ibc-go/pull/1097) Remove `GetClientID` function from 07-tendermint `Misbehaviour` type. -* (07-tendermint) [\#1097](https://github.com/cosmos/ibc-go/pull/1097) Deprecate `ClientId` field in 07-tendermint `Misbehaviour` type. * (testing) [\#939](https://github.com/cosmos/ibc-go/pull/939) Support custom power reduction for testing. * (modules/core/05-port) [\#1086](https://github.com/cosmos/ibc-go/pull/1086) Added `counterpartyChannelID` argument to IBCModule.OnChanOpenAck * (channel) [\#848](https://github.com/cosmos/ibc-go/pull/848) Added `ChannelId` to MsgChannelOpenInitResponse @@ -205,7 +208,6 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (transfer) [\#517](https://github.com/cosmos/ibc-go/pull/517) Separates the ICS 26 callback functions from `AppModule` into a new type `IBCModule` for ICS 20 transfer. * (modules/core/02-client) [\#536](https://github.com/cosmos/ibc-go/pull/536) `GetSelfConsensusState` return type changed from bool to error. * (channel) [\#644](https://github.com/cosmos/ibc-go/pull/644) Removes `CounterpartyHops` function from the ChannelKeeper. -* (modules/core/exported) [\#1107](https://github.com/cosmos/ibc-go/pull/1107) Merging the `Header` and `Misbehaviour` interfaces into a single `ClientMessage` type * (testing) [\#776](https://github.com/cosmos/ibc-go/pull/776) Adding helper fn to generate capability name for testing callbacks * (testing) [\#892](https://github.com/cosmos/ibc-go/pull/892) IBC Mock modules store the scoped keeper and portID within the IBCMockApp. They also maintain reference to the AppModule to update the AppModule's list of IBC applications it references. Allows for the mock module to be reused as a base application in middleware stacks. * (channel) [\#882](https://github.com/cosmos/ibc-go/pull/882) The `WriteAcknowledgement` API now takes `exported.Acknowledgement` instead of a byte array @@ -230,7 +232,6 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (client) [\#724](https://github.com/cosmos/ibc-go/pull/724) `IsRevisionFormat` and `IsClientIDFormat` have been updated to disallow newlines before the dash used to separate the chainID and revision number, and the client type and client sequence. * (channel) [\#644](https://github.com/cosmos/ibc-go/pull/644) Adds `GetChannelConnection` to the ChannelKeeper. This function returns the connectionID and connection state associated with a channel. * (channel) [\647](https://github.com/cosmos/ibc-go/pull/647) Reorganizes channel handshake handling to set channel state after IBC application callbacks. -* (client) [\#724](https://github.com/cosmos/ibc-go/pull/724) `IsRevisionFormat` and `IsClientIDFormat` have been updated to disallow newlines before the dash used to separate the chainID and revision number, and the client type and client sequence. * (interchain-accounts) [\#1466](https://github.com/cosmos/ibc-go/pull/1466) Emit event when there is an acknowledgement during `OnRecvPacket`. ### Features @@ -667,7 +668,6 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Client Breaking Changes * (02-client/cli) [\#196](https://github.com/cosmos/ibc-go/pull/196) Rename `node-state` cli command to `self-consensus-state`. -* (02-client/cli) [\#897](https://github.com/cosmos/ibc-go/pull/897) Remove `GetClientID()` from `Misbehaviour` interface. Submit client misbehaviour cli command requires an explicit client id now. ## IBC in the Cosmos SDK Repository From 311a563cd6c101517195c899ed49a6f3627a1dda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?colin=20axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Fri, 5 Aug 2022 18:46:47 +0200 Subject: [PATCH 66/71] fix: revert unnecessary change in 02-client-refactor branch (#1883) * revert error naming changes * fix error naming in 03-connection usage --- modules/core/02-client/keeper/client.go | 4 ++-- modules/core/02-client/keeper/proposal.go | 2 +- modules/core/02-client/types/errors.go | 2 +- modules/core/03-connection/keeper/keeper.go | 2 +- modules/core/03-connection/keeper/verify.go | 16 ++++++++-------- modules/core/04-channel/keeper/packet.go | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/modules/core/02-client/keeper/client.go b/modules/core/02-client/keeper/client.go index a1f6b4a2e8a..21ffba25e90 100644 --- a/modules/core/02-client/keeper/client.go +++ b/modules/core/02-client/keeper/client.go @@ -59,7 +59,7 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, clientMsg exporte clientStore := k.ClientStore(ctx, clientID) if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(types.ErrClientStateNotFound, "cannot update client (%s) with status %s", clientID, status) + return sdkerrors.Wrapf(types.ErrClientNotActive, "cannot update client (%s) with status %s", clientID, status) } if err := clientState.VerifyClientMessage(ctx, k.cdc, clientStore, clientMsg); err != nil { @@ -120,7 +120,7 @@ func (k Keeper) UpgradeClient(ctx sdk.Context, clientID string, upgradedClient e clientStore := k.ClientStore(ctx, clientID) if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(types.ErrClientStateNotFound, "cannot upgrade client (%s) with status %s", clientID, status) + return sdkerrors.Wrapf(types.ErrClientNotActive, "cannot upgrade client (%s) with status %s", clientID, status) } if err := clientState.VerifyUpgradeAndUpdateState(ctx, k.cdc, clientStore, diff --git a/modules/core/02-client/keeper/proposal.go b/modules/core/02-client/keeper/proposal.go index be24cfdc643..aa0846021cc 100644 --- a/modules/core/02-client/keeper/proposal.go +++ b/modules/core/02-client/keeper/proposal.go @@ -41,7 +41,7 @@ func (k Keeper) ClientUpdateProposal(ctx sdk.Context, p *types.ClientUpdatePropo substituteClientStore := k.ClientStore(ctx, p.SubstituteClientId) if status := substituteClientState.Status(ctx, substituteClientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(types.ErrClientStateNotFound, "substitute client is not Active, status is %s", status) + return sdkerrors.Wrapf(types.ErrClientNotActive, "substitute client is not Active, status is %s", status) } if err := subjectClientState.CheckSubstituteAndUpdateState(ctx, k.cdc, subjectClientStore, substituteClientStore, substituteClientState); err != nil { diff --git a/modules/core/02-client/types/errors.go b/modules/core/02-client/types/errors.go index f97ab0014cb..0c2bd65f339 100644 --- a/modules/core/02-client/types/errors.go +++ b/modules/core/02-client/types/errors.go @@ -33,5 +33,5 @@ var ( ErrInvalidHeight = sdkerrors.Register(SubModuleName, 26, "invalid height") ErrInvalidSubstitute = sdkerrors.Register(SubModuleName, 27, "invalid client state substitute") ErrInvalidUpgradeProposal = sdkerrors.Register(SubModuleName, 28, "invalid upgrade proposal") - ErrClientStateNotFound = sdkerrors.Register(SubModuleName, 29, "client state not found") + ErrClientNotActive = sdkerrors.Register(SubModuleName, 29, "client state is not active") ) diff --git a/modules/core/03-connection/keeper/keeper.go b/modules/core/03-connection/keeper/keeper.go index 72e7c8dca13..7995a52c4b3 100644 --- a/modules/core/03-connection/keeper/keeper.go +++ b/modules/core/03-connection/keeper/keeper.go @@ -89,7 +89,7 @@ func (k Keeper) GetTimestampAtHeight(ctx sdk.Context, connection types.Connectio clientState, found := k.clientKeeper.GetClientState(ctx, connection.GetClientID()) if !found { return 0, sdkerrors.Wrapf( - clienttypes.ErrClientStateNotFound, "clientID (%s)", connection.GetClientID(), + clienttypes.ErrClientNotFound, "clientID (%s)", connection.GetClientID(), ) } diff --git a/modules/core/03-connection/keeper/verify.go b/modules/core/03-connection/keeper/verify.go index 3b27990e669..bafea2bd190 100644 --- a/modules/core/03-connection/keeper/verify.go +++ b/modules/core/03-connection/keeper/verify.go @@ -32,7 +32,7 @@ func (k Keeper) VerifyClientState( } if status := targetClient.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(clienttypes.ErrClientStateNotFound, "client (%s) status is %s", clientID, status) + return sdkerrors.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) } merklePath := commitmenttypes.NewMerklePath(host.FullClientStatePath(connection.GetCounterparty().GetClientID())) @@ -81,7 +81,7 @@ func (k Keeper) VerifyClientConsensusState( } if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(clienttypes.ErrClientStateNotFound, "client (%s) status is %s", clientID, status) + return sdkerrors.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) } merklePath := commitmenttypes.NewMerklePath(host.FullConsensusStatePath(connection.GetCounterparty().GetClientID(), consensusHeight)) @@ -130,7 +130,7 @@ func (k Keeper) VerifyConnectionState( } if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(clienttypes.ErrClientStateNotFound, "client (%s) status is %s", clientID, status) + return sdkerrors.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) } merklePath := commitmenttypes.NewMerklePath(host.ConnectionPath(connectionID)) @@ -185,7 +185,7 @@ func (k Keeper) VerifyChannelState( } if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(clienttypes.ErrClientStateNotFound, "client (%s) status is %s", clientID, status) + return sdkerrors.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) } merklePath := commitmenttypes.NewMerklePath(host.ChannelPath(portID, channelID)) @@ -241,7 +241,7 @@ func (k Keeper) VerifyPacketCommitment( } if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(clienttypes.ErrClientStateNotFound, "client (%s) status is %s", clientID, status) + return sdkerrors.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) } // get time and block delays @@ -291,7 +291,7 @@ func (k Keeper) VerifyPacketAcknowledgement( } if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(clienttypes.ErrClientStateNotFound, "client (%s) status is %s", clientID, status) + return sdkerrors.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) } // get time and block delays @@ -341,7 +341,7 @@ func (k Keeper) VerifyPacketReceiptAbsence( } if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(clienttypes.ErrClientStateNotFound, "client (%s) status is %s", clientID, status) + return sdkerrors.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) } // get time and block delays @@ -390,7 +390,7 @@ func (k Keeper) VerifyNextSequenceRecv( } if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(clienttypes.ErrClientStateNotFound, "client (%s) status is %s", clientID, status) + return sdkerrors.Wrapf(clienttypes.ErrClientNotActive, "client (%s) status is %s", clientID, status) } // get time and block delays diff --git a/modules/core/04-channel/keeper/packet.go b/modules/core/04-channel/keeper/packet.go index 41b13de7c16..3fa66a6e989 100644 --- a/modules/core/04-channel/keeper/packet.go +++ b/modules/core/04-channel/keeper/packet.go @@ -71,7 +71,7 @@ func (k Keeper) SendPacket( // prevent accidental sends with clients that cannot be updated clientStore := k.clientKeeper.ClientStore(ctx, connectionEnd.GetClientID()) if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { - return sdkerrors.Wrapf(clienttypes.ErrClientStateNotFound, "cannot send packet using client (%s) with status %s", connectionEnd.GetClientID(), status) + return sdkerrors.Wrapf(clienttypes.ErrClientNotActive, "cannot send packet using client (%s) with status %s", connectionEnd.GetClientID(), status) } // check if packet is timed out on the receiving chain From 173c1fb6cfc4b9c291edfeda90e299e972d1c06e Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Mon, 8 Aug 2022 13:12:42 +0200 Subject: [PATCH 67/71] updating solomachine to use setClientState helper in update funcs (#1891) --- modules/light-clients/06-solomachine/update.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/light-clients/06-solomachine/update.go b/modules/light-clients/06-solomachine/update.go index 5eb628876c1..4594363a5af 100644 --- a/modules/light-clients/06-solomachine/update.go +++ b/modules/light-clients/06-solomachine/update.go @@ -8,7 +8,6 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v5/modules/core/24-host" "github.com/cosmos/ibc-go/v5/modules/core/exported" ) @@ -118,7 +117,7 @@ func (cs ClientState) UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, client cs.Sequence++ cs.ConsensusState = consensusState - clientStore.Set(host.ClientStateKey(), clienttypes.MustMarshalClientState(cdc, &cs)) + setClientState(clientStore, cdc, &cs) return []exported.Height{clienttypes.NewHeight(0, cs.Sequence)} } @@ -137,5 +136,5 @@ func (cs ClientState) CheckForMisbehaviour(_ sdk.Context, _ codec.BinaryCodec, _ func (cs ClientState) UpdateStateOnMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, _ exported.ClientMessage) { cs.IsFrozen = true - clientStore.Set(host.ClientStateKey(), clienttypes.MustMarshalClientState(cdc, &cs)) + setClientState(clientStore, cdc, &cs) } From a7407cc140a23894acd6a0073604e0b24af5c696 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?colin=20axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Mon, 8 Aug 2022 14:23:40 +0200 Subject: [PATCH 68/71] Update CHANGELOG.md Co-authored-by: Aditya --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b55c7415968..9f57f64aee5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -122,7 +122,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (apps/29-fee) [\#1278](https://github.com/cosmos/ibc-go/pull/1278) The URI path for the query to get all incentivized packets for a specific channel did not follow the same format as the rest of queries. - * (light-clients/solomachine) [#1839](https://github.com/cosmos/ibc-go/issues/1839) Fixed usage of the new diversifier in validation of changing diversifiers for the solo machine. The current diversifier must sign over the new diversifier. -* (07-tendermint) [\#1674](https://github.com/cosmos/ibc-go/pull/1674) Submitted ClientState is zeroed out before checking the proof in order to prevent the proposal from containing information governance is not actually voting on. +* (light-clients/07-tendermint) [\#1674](https://github.com/cosmos/ibc-go/pull/1674) Submitted ClientState is zeroed out before checking the proof in order to prevent the proposal from containing information governance is not actually voting on. * (modules/core/02-client)[\#1676](https://github.com/cosmos/ibc-go/pull/1676) ClientState must be zeroed out for `UpgradeProposals` to pass validation. This prevents a proposal containing information governance is not actually voting on. ## [v3.1.1](https://github.com/cosmos/ibc-go/releases/tag/v3.1.1) - 2022-08-02 From fc4bfaf0c9e0e3ca99c728c710c879a54a75b64c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?colin=20axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Mon, 8 Aug 2022 14:24:46 +0200 Subject: [PATCH 69/71] Update docs/migrations/v5-to-v6.md Co-authored-by: Aditya --- docs/migrations/v5-to-v6.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/migrations/v5-to-v6.md b/docs/migrations/v5-to-v6.md index f27dee55bb7..70e9519f4e3 100644 --- a/docs/migrations/v5-to-v6.md +++ b/docs/migrations/v5-to-v6.md @@ -41,7 +41,7 @@ The `CheckHeaderAndUpdateState` function has been split into 4 new functions: - `UpdateState` updates and stores as necessary any associated information for an IBC client, such as the `ClientState` and corresponding `ConsensusState`. An error is returned if `ClientMessage` is of type `Misbehaviour`. Upon successful update, a list containing the updated consensus state height is returned. -The `CheckMisbehaviourAndUpdateState` function has been removed from `ClientState` interface. This functionality is now encapsulated by the usage of `VerifyClientMessage`, `CheckForMisbehaviour`, `UpdateStateOnMisbehaviour`, `UpdateState`. +The `CheckMisbehaviourAndUpdateState` function has been removed from `ClientState` interface. This functionality is now encapsulated by the usage of `VerifyClientMessage`, `CheckForMisbehaviour`, `UpdateStateOnMisbehaviour`. The function `GetTimestampAtHeight` has been added to the `ClientState` interface. It should return the timestamp for a consensus state associated with the provided height. From 9a4ed681e7aa440722739d109db00dbc2ed038b4 Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Mon, 8 Aug 2022 14:28:21 +0200 Subject: [PATCH 70/71] chore: remove error return in IterateConsensusStateAscending (#1896) * removing error return in tendermint store.go * updating tests and legacy code * updating changelog --- CHANGELOG.md | 3 ++- modules/core/02-client/legacy/v100/store.go | 5 +---- modules/light-clients/07-tendermint/store.go | 14 ++++---------- modules/light-clients/07-tendermint/update.go | 4 +--- modules/light-clients/07-tendermint/update_test.go | 3 +-- 5 files changed, 9 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f57f64aee5..9845d1492ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -71,7 +71,8 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (06-solomachine) [\#1100](https://github.com/cosmos/ibc-go/pull/1100) Deprecate `ClientId` field in 06-solomachine `Misbehaviour` type. * (07-tendermint) [\#1097](https://github.com/cosmos/ibc-go/pull/1097) Remove `GetClientID` function from 07-tendermint `Misbehaviour` type. * (07-tendermint) [\#1097](https://github.com/cosmos/ibc-go/pull/1097) Deprecate `ClientId` field in 07-tendermint `Misbehaviour` type. -* (modules/core/exported) [\#1107](https://github.com/cosmos/ibc-go/pull/1107) Merging the `Header` and `Misbehaviour` interfaces into a single `ClientMessage` type +* (modules/core/exported) [\#1107](https://github.com/cosmos/ibc-go/pull/1107) Merging the `Header` and `Misbehaviour` interfaces into a single `ClientMessage` type. +* (07-tendermint) [\#1896](https://github.com/cosmos/ibc-go/pull/1896) Remove error return from `IterateConsensusStateAscending` in `07-tendermint`. ### State Machine Breaking diff --git a/modules/core/02-client/legacy/v100/store.go b/modules/core/02-client/legacy/v100/store.go index b59f512f92d..e83b116c956 100644 --- a/modules/core/02-client/legacy/v100/store.go +++ b/modules/core/02-client/legacy/v100/store.go @@ -96,10 +96,7 @@ func MigrateStore(ctx sdk.Context, storeKey storetypes.StoreKey, cdc codec.Binar // add iteration keys so pruning will be successful addConsensusMetadata(ctx, clientStore) - if err = ibctm.PruneAllExpiredConsensusStates(ctx, clientStore, cdc, tmClientState); err != nil { - return err - } - + ibctm.PruneAllExpiredConsensusStates(ctx, clientStore, cdc, tmClientState) default: continue } diff --git a/modules/light-clients/07-tendermint/store.go b/modules/light-clients/07-tendermint/store.go index 33957568e2d..e8824d06e1d 100644 --- a/modules/light-clients/07-tendermint/store.go +++ b/modules/light-clients/07-tendermint/store.go @@ -215,7 +215,7 @@ func GetHeightFromIterationKey(iterKey []byte) exported.Height { // IterateConsensusStateAscending iterates through the consensus states in ascending order. It calls the provided // callback on each height, until stop=true is returned. -func IterateConsensusStateAscending(clientStore sdk.KVStore, cb func(height exported.Height) (stop bool)) error { +func IterateConsensusStateAscending(clientStore sdk.KVStore, cb func(height exported.Height) (stop bool)) { iterator := sdk.KVStorePrefixIterator(clientStore, []byte(KeyIterateConsensusStatePrefix)) defer iterator.Close() @@ -223,10 +223,9 @@ func IterateConsensusStateAscending(clientStore sdk.KVStore, cb func(height expo iterKey := iterator.Key() height := GetHeightFromIterationKey(iterKey) if cb(height) { - return nil + break } } - return nil } // GetNextConsensusState returns the lowest consensus state that is larger than the given height. @@ -278,7 +277,7 @@ func GetPreviousConsensusState(clientStore sdk.KVStore, cdc codec.BinaryCodec, h func PruneAllExpiredConsensusStates( ctx sdk.Context, clientStore sdk.KVStore, cdc codec.BinaryCodec, clientState *ClientState, -) (err error) { +) { var heights []exported.Height pruneCb := func(height exported.Height) bool { @@ -294,17 +293,12 @@ func PruneAllExpiredConsensusStates( return false } - err = IterateConsensusStateAscending(clientStore, pruneCb) - if err != nil { - return err - } + IterateConsensusStateAscending(clientStore, pruneCb) for _, height := range heights { deleteConsensusState(clientStore, height) deleteConsensusMetadata(clientStore, height) } - - return nil } // Helper function for GetNextConsensusState and GetPreviousConsensusState diff --git a/modules/light-clients/07-tendermint/update.go b/modules/light-clients/07-tendermint/update.go index 39481df125a..32b5857ad19 100644 --- a/modules/light-clients/07-tendermint/update.go +++ b/modules/light-clients/07-tendermint/update.go @@ -186,9 +186,7 @@ func (cs ClientState) pruneOldestConsensusState(ctx sdk.Context, cdc codec.Binar return true } - if err := IterateConsensusStateAscending(clientStore, pruneCb); err != nil { - panic(err) - } + IterateConsensusStateAscending(clientStore, pruneCb) // if pruneHeight is set, delete consensus state and metadata if pruneHeight != nil { diff --git a/modules/light-clients/07-tendermint/update_test.go b/modules/light-clients/07-tendermint/update_test.go index 306133542b5..0b5b55eb1cc 100644 --- a/modules/light-clients/07-tendermint/update_test.go +++ b/modules/light-clients/07-tendermint/update_test.go @@ -488,8 +488,7 @@ func (suite *TendermintTestSuite) TestPruneConsensusState() { } ctx := path.EndpointA.Chain.GetContext() clientStore := path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) - err := ibctm.IterateConsensusStateAscending(clientStore, getFirstHeightCb) - suite.Require().Nil(err) + ibctm.IterateConsensusStateAscending(clientStore, getFirstHeightCb) // this height will be expired but not pruned path.EndpointA.UpdateClient() From 87bae337f173f9d66916b55b2d6b11e35f1ed917 Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Mon, 8 Aug 2022 14:43:07 +0200 Subject: [PATCH 71/71] removing solomachine consensus state nil check and test cases (#1895) --- .../light-clients/06-solomachine/client_state.go | 4 ---- .../06-solomachine/client_state_test.go | 14 -------------- 2 files changed, 18 deletions(-) diff --git a/modules/light-clients/06-solomachine/client_state.go b/modules/light-clients/06-solomachine/client_state.go index fd660c3785a..29474f0d32b 100644 --- a/modules/light-clients/06-solomachine/client_state.go +++ b/modules/light-clients/06-solomachine/client_state.go @@ -231,10 +231,6 @@ func produceVerificationArgs( return nil, nil, 0, 0, err } - if cs.ConsensusState == nil { - return nil, nil, 0, 0, sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "consensus state cannot be empty") - } - // sequence is encoded in the revision height of height struct sequence := height.GetRevisionHeight() latestSequence := cs.GetLatestHeight().GetRevisionHeight() diff --git a/modules/light-clients/06-solomachine/client_state_test.go b/modules/light-clients/06-solomachine/client_state_test.go index 89d41b7d18f..68bf15cace9 100644 --- a/modules/light-clients/06-solomachine/client_state_test.go +++ b/modules/light-clients/06-solomachine/client_state_test.go @@ -447,13 +447,6 @@ func (suite *SoloMachineTestSuite) TestVerifyMembership() { }, true, }, - { - "consensus state in client state is nil", - func() { - clientState = solomachine.NewClientState(1, nil, false) - }, - false, - }, { "client state latest height is less than sequence", func() { @@ -665,13 +658,6 @@ func (suite *SoloMachineTestSuite) TestVerifyNonMembership() { }, true, }, - { - "consensus state in client state is nil", - func() { - clientState = solomachine.NewClientState(1, nil, false) - }, - false, - }, { "client state latest height is less than sequence", func() {

    av2fjL-LGJ4S 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 z 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 literal 0 HcmV?d00001 diff --git a/docs/ibc/apps.md b/docs/ibc/apps.md index bb2716fa0b1..f729d7dfd71 100644 --- a/docs/ibc/apps.md +++ b/docs/ibc/apps.md @@ -464,13 +464,13 @@ which implements everything discussed above. Here are the useful parts of the module to look at: [Binding to transfer -port](https://github.com/cosmos/ibc-go/blob/main/modules/apps/transfer/types/genesis.go) +port](https://github.com/cosmos/ibc-go/blob/main/modules/apps/transfer/keeper/genesis.go) [Sending transfer packets](https://github.com/cosmos/ibc-go/blob/main/modules/apps/transfer/keeper/relay.go) [Implementing IBC -callbacks](https://github.com/cosmos/ibc-go/blob/main/modules/apps/transfer/module.go) +callbacks](https://github.com/cosmos/ibc-go/blob/main/modules/apps/transfer/ibc_module.go) ## Next {hide} diff --git a/docs/ibc/middleware/develop.md b/docs/ibc/middleware/develop.md index 1d75e3965a8..705040b1db7 100644 --- a/docs/ibc/middleware/develop.md +++ b/docs/ibc/middleware/develop.md @@ -49,6 +49,7 @@ type Middleware interface { type ICS4Wrapper interface { SendPacket(ctx sdk.Context, chanCap *capabilitytypes.Capability, packet exported.Packet) error WriteAcknowledgement(ctx sdk.Context, chanCap *capabilitytypes.Capability, packet exported.Packet, ack []byte) error + GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) } ``` @@ -239,4 +240,24 @@ func SendPacket(appPacket channeltypes.Packet) { return ics4Keeper.SendPacket(packet) } + +// middleware must return the underlying application version +func GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) { + version, found := ics4Keeper.GetAppVersion(ctx, portID, channelID) + if !found { + return "", false + } + + if !MiddlewareEnabled { + return version, true + } + + // unwrap channel version + metadata, err := Unmarshal(version) + if err != nil { + panic(fmt.Errof("unable to unmarshal version: %w", err)) + } + + return metadata.AppVersion, true +} ``` diff --git a/docs/ibc/proto-docs.md b/docs/ibc/proto-docs.md index 32b500639a3..30722206dc7 100644 --- a/docs/ibc/proto-docs.md +++ b/docs/ibc/proto-docs.md @@ -4,6 +4,95 @@ ## Table of Contents +- [ibc/applications/fee/v1/ack.proto](#ibc/applications/fee/v1/ack.proto) + - [IncentivizedAcknowledgement](#ibc.applications.fee.v1.IncentivizedAcknowledgement) + +- [ibc/core/client/v1/client.proto](#ibc/core/client/v1/client.proto) + - [ClientConsensusStates](#ibc.core.client.v1.ClientConsensusStates) + - [ClientUpdateProposal](#ibc.core.client.v1.ClientUpdateProposal) + - [ConsensusStateWithHeight](#ibc.core.client.v1.ConsensusStateWithHeight) + - [Height](#ibc.core.client.v1.Height) + - [IdentifiedClientState](#ibc.core.client.v1.IdentifiedClientState) + - [Params](#ibc.core.client.v1.Params) + - [UpgradeProposal](#ibc.core.client.v1.UpgradeProposal) + +- [ibc/core/channel/v1/channel.proto](#ibc/core/channel/v1/channel.proto) + - [Acknowledgement](#ibc.core.channel.v1.Acknowledgement) + - [Channel](#ibc.core.channel.v1.Channel) + - [Counterparty](#ibc.core.channel.v1.Counterparty) + - [IdentifiedChannel](#ibc.core.channel.v1.IdentifiedChannel) + - [Packet](#ibc.core.channel.v1.Packet) + - [PacketId](#ibc.core.channel.v1.PacketId) + - [PacketState](#ibc.core.channel.v1.PacketState) + + - [Order](#ibc.core.channel.v1.Order) + - [State](#ibc.core.channel.v1.State) + +- [ibc/applications/fee/v1/fee.proto](#ibc/applications/fee/v1/fee.proto) + - [Fee](#ibc.applications.fee.v1.Fee) + - [IdentifiedPacketFees](#ibc.applications.fee.v1.IdentifiedPacketFees) + - [PacketFee](#ibc.applications.fee.v1.PacketFee) + - [PacketFees](#ibc.applications.fee.v1.PacketFees) + +- [ibc/applications/fee/v1/genesis.proto](#ibc/applications/fee/v1/genesis.proto) + - [FeeEnabledChannel](#ibc.applications.fee.v1.FeeEnabledChannel) + - [ForwardRelayerAddress](#ibc.applications.fee.v1.ForwardRelayerAddress) + - [GenesisState](#ibc.applications.fee.v1.GenesisState) + - [RegisteredRelayerAddress](#ibc.applications.fee.v1.RegisteredRelayerAddress) + +- [ibc/applications/fee/v1/metadata.proto](#ibc/applications/fee/v1/metadata.proto) + - [Metadata](#ibc.applications.fee.v1.Metadata) + +- [ibc/applications/fee/v1/query.proto](#ibc/applications/fee/v1/query.proto) + - [QueryCounterpartyAddressRequest](#ibc.applications.fee.v1.QueryCounterpartyAddressRequest) + - [QueryCounterpartyAddressResponse](#ibc.applications.fee.v1.QueryCounterpartyAddressResponse) + - [QueryFeeEnabledChannelRequest](#ibc.applications.fee.v1.QueryFeeEnabledChannelRequest) + - [QueryFeeEnabledChannelResponse](#ibc.applications.fee.v1.QueryFeeEnabledChannelResponse) + - [QueryFeeEnabledChannelsRequest](#ibc.applications.fee.v1.QueryFeeEnabledChannelsRequest) + - [QueryFeeEnabledChannelsResponse](#ibc.applications.fee.v1.QueryFeeEnabledChannelsResponse) + - [QueryIncentivizedPacketRequest](#ibc.applications.fee.v1.QueryIncentivizedPacketRequest) + - [QueryIncentivizedPacketResponse](#ibc.applications.fee.v1.QueryIncentivizedPacketResponse) + - [QueryIncentivizedPacketsForChannelRequest](#ibc.applications.fee.v1.QueryIncentivizedPacketsForChannelRequest) + - [QueryIncentivizedPacketsForChannelResponse](#ibc.applications.fee.v1.QueryIncentivizedPacketsForChannelResponse) + - [QueryIncentivizedPacketsRequest](#ibc.applications.fee.v1.QueryIncentivizedPacketsRequest) + - [QueryIncentivizedPacketsResponse](#ibc.applications.fee.v1.QueryIncentivizedPacketsResponse) + - [QueryTotalAckFeesRequest](#ibc.applications.fee.v1.QueryTotalAckFeesRequest) + - [QueryTotalAckFeesResponse](#ibc.applications.fee.v1.QueryTotalAckFeesResponse) + - [QueryTotalRecvFeesRequest](#ibc.applications.fee.v1.QueryTotalRecvFeesRequest) + - [QueryTotalRecvFeesResponse](#ibc.applications.fee.v1.QueryTotalRecvFeesResponse) + - [QueryTotalTimeoutFeesRequest](#ibc.applications.fee.v1.QueryTotalTimeoutFeesRequest) + - [QueryTotalTimeoutFeesResponse](#ibc.applications.fee.v1.QueryTotalTimeoutFeesResponse) + + - [Query](#ibc.applications.fee.v1.Query) + +- [ibc/applications/fee/v1/tx.proto](#ibc/applications/fee/v1/tx.proto) + - [MsgPayPacketFee](#ibc.applications.fee.v1.MsgPayPacketFee) + - [MsgPayPacketFeeAsync](#ibc.applications.fee.v1.MsgPayPacketFeeAsync) + - [MsgPayPacketFeeAsyncResponse](#ibc.applications.fee.v1.MsgPayPacketFeeAsyncResponse) + - [MsgPayPacketFeeResponse](#ibc.applications.fee.v1.MsgPayPacketFeeResponse) + - [MsgRegisterCounterpartyAddress](#ibc.applications.fee.v1.MsgRegisterCounterpartyAddress) + - [MsgRegisterCounterpartyAddressResponse](#ibc.applications.fee.v1.MsgRegisterCounterpartyAddressResponse) + + - [Msg](#ibc.applications.fee.v1.Msg) + +- [ibc/applications/interchain_accounts/controller/v1/controller.proto](#ibc/applications/interchain_accounts/controller/v1/controller.proto) + - [Params](#ibc.applications.interchain_accounts.controller.v1.Params) + +- [ibc/applications/interchain_accounts/controller/v1/query.proto](#ibc/applications/interchain_accounts/controller/v1/query.proto) + - [QueryParamsRequest](#ibc.applications.interchain_accounts.controller.v1.QueryParamsRequest) + - [QueryParamsResponse](#ibc.applications.interchain_accounts.controller.v1.QueryParamsResponse) + + - [Query](#ibc.applications.interchain_accounts.controller.v1.Query) + +- [ibc/applications/interchain_accounts/host/v1/host.proto](#ibc/applications/interchain_accounts/host/v1/host.proto) + - [Params](#ibc.applications.interchain_accounts.host.v1.Params) + +- [ibc/applications/interchain_accounts/host/v1/query.proto](#ibc/applications/interchain_accounts/host/v1/query.proto) + - [QueryParamsRequest](#ibc.applications.interchain_accounts.host.v1.QueryParamsRequest) + - [QueryParamsResponse](#ibc.applications.interchain_accounts.host.v1.QueryParamsResponse) + + - [Query](#ibc.applications.interchain_accounts.host.v1.Query) + - [ibc/applications/interchain_accounts/v1/account.proto](#ibc/applications/interchain_accounts/v1/account.proto) - [InterchainAccount](#ibc.applications.interchain_accounts.v1.InterchainAccount) @@ -42,15 +131,6 @@ - [Query](#ibc.applications.transfer.v1.Query) -- [ibc/core/client/v1/client.proto](#ibc/core/client/v1/client.proto) - - [ClientConsensusStates](#ibc.core.client.v1.ClientConsensusStates) - - [ClientUpdateProposal](#ibc.core.client.v1.ClientUpdateProposal) - - [ConsensusStateWithHeight](#ibc.core.client.v1.ConsensusStateWithHeight) - - [Height](#ibc.core.client.v1.Height) - - [IdentifiedClientState](#ibc.core.client.v1.IdentifiedClientState) - - [Params](#ibc.core.client.v1.Params) - - [UpgradeProposal](#ibc.core.client.v1.UpgradeProposal) - - [ibc/applications/transfer/v1/tx.proto](#ibc/applications/transfer/v1/tx.proto) - [MsgTransfer](#ibc.applications.transfer.v1.MsgTransfer) - [MsgTransferResponse](#ibc.applications.transfer.v1.MsgTransferResponse) @@ -60,17 +140,6 @@ - [ibc/applications/transfer/v2/packet.proto](#ibc/applications/transfer/v2/packet.proto) - [FungibleTokenPacketData](#ibc.applications.transfer.v2.FungibleTokenPacketData) -- [ibc/core/channel/v1/channel.proto](#ibc/core/channel/v1/channel.proto) - - [Acknowledgement](#ibc.core.channel.v1.Acknowledgement) - - [Channel](#ibc.core.channel.v1.Channel) - - [Counterparty](#ibc.core.channel.v1.Counterparty) - - [IdentifiedChannel](#ibc.core.channel.v1.IdentifiedChannel) - - [Packet](#ibc.core.channel.v1.Packet) - - [PacketState](#ibc.core.channel.v1.PacketState) - - - [Order](#ibc.core.channel.v1.Order) - - [State](#ibc.core.channel.v1.State) - - [ibc/core/channel/v1/genesis.proto](#ibc/core/channel/v1/genesis.proto) - [GenesisState](#ibc.core.channel.v1.GenesisState) - [PacketSequence](#ibc.core.channel.v1.PacketSequence) @@ -268,23 +337,24 @@ - +

  • Bb6?ehyMyA0m4=k2?> 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�H1 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 z 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≥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