diff --git a/CHANGELOG.md b/CHANGELOG.md index 6aff1fcc83e..9845d1492ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,7 +44,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### 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/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. @@ -61,6 +61,18 @@ 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. +* (07-tendermint) [\#1896](https://github.com/cosmos/ibc-go/pull/1896) Remove error return from `IterateConsensusStateAscending` in `07-tendermint`. ### State Machine Breaking @@ -79,7 +91,22 @@ 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 @@ -94,6 +121,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. +* (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 @@ -191,6 +222,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) [\#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. @@ -200,9 +232,9 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (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. +* (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 diff --git a/README.md b/README.md index f4a830c5149..ff3e074b04f 100644 --- a/README.md +++ b/README.md @@ -64,8 +64,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/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) 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/docs/ibc/integration.md b/docs/ibc/integration.md index d2d9f057ea3..0b01ffd7347 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/middleware/develop.md b/docs/ibc/middleware/develop.md index 3c0c74a0186..7ee020ca9c8 100644 --- a/docs/ibc/middleware/develop.md +++ b/docs/ibc/middleware/develop.md @@ -408,6 +408,26 @@ func GetAppVersion( return metadata.AppVersion, true } + +// 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 +} ``` See [here](https://github.com/cosmos/ibc-go/blob/48a6ae512b4ea42c29fdf6c6f5363f50645591a2/modules/apps/29-fee/ibc_middleware.go#L355-L358) an example implementation of this function for the ICS29 Fee Middleware module. diff --git a/docs/ibc/proto-docs.md b/docs/ibc/proto-docs.md index c612a0180b3..48219f3d66b 100644 --- a/docs/ibc/proto-docs.md +++ b/docs/ibc/proto-docs.md @@ -295,9 +295,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) @@ -338,6 +335,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) @@ -3549,13 +3556,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 | @@ -3577,13 +3585,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 | @@ -4316,39 +4324,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 | - - - - - @@ -4995,6 +4970,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

@@ -5100,7 +5238,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/docs/migrations/v5-to-v6.md b/docs/migrations/v5-to-v6.md new file mode 100644 index 00000000000..70e9519f4e3 --- /dev/null +++ b/docs/migrations/v5-to-v6.md @@ -0,0 +1,101 @@ +# 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. + +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`. + +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 `MigrateToV6` to perform store migration logic. + +```go +import ( + // ... + ibcv6 "github.com/cosmos/ibc-go/v6/modules/core/migrations/v6" +) + +// ... + +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 + ibcv6.MigrateToV6(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/apps/27-interchain-accounts/host/keeper/relay_test.go b/modules/apps/27-interchain-accounts/host/keeper/relay_test.go index 02d4ff07179..89058aad4e1 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/relay_test.go +++ b/modules/apps/27-interchain-accounts/host/keeper/relay_test.go @@ -308,7 +308,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), } @@ -464,7 +464,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/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/mbt_relay_test.go b/modules/apps/transfer/keeper/mbt_relay_test.go index a09c4203e8f..6a4706f9f5a 100644 --- a/modules/apps/transfer/keeper/mbt_relay_test.go +++ b/modules/apps/transfer/keeper/mbt_relay_test.go @@ -313,7 +313,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) @@ -345,7 +345,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 7cfb0cae2fc..1e57e3f2236 100644 --- a/modules/apps/transfer/keeper/relay_test.go +++ b/modules/apps/transfer/keeper/relay_test.go @@ -112,13 +112,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() @@ -133,7 +133,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 { @@ -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 @@ -229,14 +229,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) @@ -309,7 +309,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()) @@ -405,7 +405,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 95aa0c4d4e3..b6a88f7e651 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/abci.go b/modules/core/02-client/abci.go index 9fe681e2276..c4fcc91ec37 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/v5/modules/core/02-client/keeper" - "github.com/cosmos/ibc-go/v5/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint/types" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ) -// 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 { @@ -20,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, } @@ -29,16 +28,7 @@ func BeginBlocker(ctx sdk.Context, k keeper.Keeper) { // SetUpgradedConsensusState always returns nil, hence the blank here. _ = k.SetUpgradedConsensusState(ctx, plan.Height, bz) + keeper.EmitUpgradeChainEvent(ctx, plan.Height) } } - - _, 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 c3bbb187892..acdfd32f389 100644 --- a/modules/core/02-client/abci_test.go +++ b/modules/core/02-client/abci_test.go @@ -1,8 +1,10 @@ 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" @@ -10,9 +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" - "github.com/cosmos/ibc-go/v5/modules/core/exported" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint/types" - localhosttypes "github.com/cosmos/ibc-go/v5/modules/light-clients/09-localhost/types" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ) @@ -30,13 +30,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 +37,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 +44,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) } } @@ -88,7 +72,59 @@ 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) } + +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/client/cli/cli.go b/modules/core/02-client/client/cli/cli.go index 57e9425f995..bdf42460573 100644 --- a/modules/core/02-client/client/cli/cli.go +++ b/modules/core/02-client/client/cli/cli.go @@ -45,7 +45,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 71d50f90dae..794b6f5ca5e 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.Header - 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,13 +127,15 @@ 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 [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 { @@ -141,8 +143,9 @@ func NewSubmitMisbehaviourCmd() *cobra.Command { } cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) - var misbehaviour exported.Misbehaviour - misbehaviourContentOrFileName := args[0] + var misbehaviour exported.ClientMessage + 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 +159,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/client/utils/utils.go b/modules/core/02-client/client/utils/utils.go index 6005e58ddce..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/types" + 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/genesis.go b/modules/core/02-client/genesis.go index 602b13aba42..18dc745620d 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 fbfe2d0eab5..21ffba25e90 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" - metrics "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,20 +33,16 @@ 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.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) @@ -57,7 +50,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, clientMsg exported.ClientMessage) error { clientState, found := k.GetClientState(ctx, clientID) if !found { return sdkerrors.Wrapf(types.ErrClientNotFound, "cannot update client with ID %s", clientID) @@ -69,77 +62,48 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.H 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) + if err := clientState.VerifyClientMessage(ctx, k.cdc, clientStore, clientMsg); err != nil { + return err } - // 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.MustMarshalHeader(k.cdc, header)) - // set default consensus height with header height - consensusHeight = header.GetHeight() + 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) - // 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 - // 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.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, newClientState, consensusHeight, headerStr) - } else { + 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"), + }, + ) - k.Logger(ctx).Info("client frozen due to misbehaviour", "client-id", clientID) + EmitSubmitMisbehaviourEvent(ctx, clientID, clientState) - 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"), - }, - ) - }() - - EmitSubmitMisbehaviourEventOnUpdate(ctx, clientID, newClientState, consensusHeight, headerStr) + return nil } + consensusHeights := clientState.UpdateState(ctx, k.cdc, clientStore, clientMsg) + + k.Logger(ctx).Info("client state updated", "client-id", clientID, "heights", consensusHeights) + + 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, k.cdc, clientMsg) + return nil } @@ -159,72 +123,24 @@ 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()) - - defer func() { - telemetry.IncrCounterWithLabels( - []string{"ibc", "client", "upgrade"}, - 1, - []metrics.Label{ - telemetry.NewLabel(types.LabelClientType, updatedClientState.ClientType()), - telemetry.NewLabel(types.LabelClientID, clientID), - }, - ) - }() - - // emitting events in the keeper emits for client upgrades - EmitUpgradeClientEvent(ctx, clientID, updatedClientState) - - return nil -} - -// 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()) - if !found { - return sdkerrors.Wrapf(types.ErrClientNotFound, "cannot check misbehaviour for client with ID %s", misbehaviour.GetClientID()) - } - - clientStore := k.ClientStore(ctx, misbehaviour.GetClientID()) - - 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) - } + k.Logger(ctx).Info("client state upgraded", "client-id", clientID, "height", upgradedClient.GetLatestHeight().String()) - 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, misbehaviour.GetClientID(), clientState) - k.Logger(ctx).Info("client frozen due to misbehaviour", "client-id", misbehaviour.GetClientID()) - - defer func() { - telemetry.IncrCounterWithLabels( - []string{"ibc", "client", "misbehaviour"}, - 1, - []metrics.Label{ - telemetry.NewLabel(types.LabelClientType, misbehaviour.ClientType()), - telemetry.NewLabel(types.LabelClientID, misbehaviour.GetClientID()), - }, - ) - }() + defer telemetry.IncrCounterWithLabels( + []string{"ibc", "client", "upgrade"}, + 1, + []metrics.Label{ + telemetry.NewLabel(types.LabelClientType, upgradedClient.ClientType()), + telemetry.NewLabel(types.LabelClientID, clientID), + }, + ) - EmitSubmitMisbehaviourEvent(ctx, misbehaviour.GetClientID(), clientState) + EmitUpgradeClientEvent(ctx, clientID, upgradedClient) return nil } diff --git a/modules/core/02-client/keeper/client_test.go b/modules/core/02-client/keeper/client_test.go index 09fd3078cc4..22b3c3556fd 100644 --- a/modules/core/02-client/keeper/client_test.go +++ b/modules/core/02-client/keeper/client_test.go @@ -6,16 +6,14 @@ import ( "time" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - tmtypes "github.com/tendermint/tendermint/types" "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" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint/types" - localhosttypes "github.com/cosmos/ibc-go/v5/modules/light-clients/09-localhost/types" + solomachinetypes "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" + 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" ) func (suite *KeeperTestSuite) TestCreateClient() { @@ -24,8 +22,8 @@ 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}, - {"client type not supported", localhosttypes.NewClientState(testChainID, clienttypes.NewHeight(0, 1)), false}, + {"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}, } for i, tc := range cases { @@ -44,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) } @@ -68,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 @@ -102,24 +100,24 @@ 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{ + prevConsState := &ibctm.ConsensusState{ Timestamp: suite.past, NextValidatorsHash: suite.chainB.Vals.Hash(), } 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{ + nextConsState := &ibctm.ConsensusState{ Timestamp: suite.past.Add(time.Minute), NextValidatorsHash: suite.chainB.Vals.Hash(), } 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) @@ -129,23 +127,23 @@ 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{ + prevConsState := &ibctm.ConsensusState{ Timestamp: suite.past, NextValidatorsHash: suite.chainB.Vals.Hash(), } 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{ + nextConsState := &ibctm.ConsensusState{ Timestamp: suite.past.Add(time.Minute), NextValidatorsHash: suite.chainB.Vals.Hash(), } 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) @@ -155,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) @@ -183,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) @@ -191,8 +189,8 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { updateHeader = createFutureUpdateFn(clientState.GetLatestHeight().(types.Height)) }, false, false}, {"client is not active", func() { - clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) - clientState.FrozenHeight = types.NewHeight(0, 1) + 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)) }, false, false}, @@ -224,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, @@ -254,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 @@ -275,7 +260,6 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { lastHeight exported.Height proofUpgradedClient, proofUpgradedConsState []byte upgradedClientBz, upgradedConsStateBz []byte - err error ) testCases := []struct { @@ -287,7 +271,7 @@ 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 err := suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) @@ -296,7 +280,6 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { suite.Require().NoError(err) // commit upgrade store changes and update clients - suite.coordinator.CommitBlock(suite.chainB) err = path.EndpointA.UpdateClient() suite.Require().NoError(err) @@ -313,7 +296,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 err := suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) @@ -343,7 +326,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 err := suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) @@ -364,9 +347,9 @@ 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(0, 1) + tmClient.FrozenHeight = types.NewHeight(1, 1) suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID, tmClient) }, expPass: false, @@ -375,7 +358,7 @@ 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 err := suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) @@ -384,7 +367,9 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { suite.Require().NoError(err) // change upgradedClient client-specified parameters - upgradedClient = ibctmtypes.NewClientState("wrongchainID", ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, true, true) + tmClient := upgradedClient.(*ibctm.ClientState) + tmClient.ChainId = "wrongchainID" + upgradedClient = tmClient suite.coordinator.CommitBlock(suite.chainB) err = path.EndpointA.UpdateClient() @@ -404,12 +389,19 @@ 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().(*ibctm.ClientState) + revisionNumber := clienttypes.ParseChainID(clientState.ChainId) + + newChainID, err := clienttypes.SetRevisionNumber(clientState.ChainId, revisionNumber+1) + suite.Require().NoError(err) + + 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) @@ -417,9 +409,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 { @@ -430,256 +419,10 @@ 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, 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) + header, err := suite.chainA.ConstructUpdateTMClientHeader(suite.chainB, path.EndpointA.ClientID) suite.Require().NoError(err) @@ -703,7 +446,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/keeper/events.go b/modules/core/02-client/keeper/events.go index 2411ad307c4..98a212eefe3 100644 --- a/modules/core/02-client/keeper/events.go +++ b/modules/core/02-client/keeper/events.go @@ -1,9 +1,14 @@ package keeper import ( + "encoding/hex" "fmt" + "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/v5/modules/core/02-client/types" "github.com/cosmos/ibc-go/v5/modules/core/exported" @@ -26,14 +31,32 @@ 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, 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() + } + + consensusHeightsAttr := make([]string, len(consensusHeights)) + for i, height := range consensusHeights { + consensusHeightsAttr[i] = height.String() + } + ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( types.EventTypeUpdateClient, sdk.NewAttribute(types.AttributeKeyClientID, clientID), - sdk.NewAttribute(types.AttributeKeyClientType, clientState.ClientType()), - sdk.NewAttribute(types.AttributeKeyConsensusHeight, consensusHeight.String()), - sdk.NewAttribute(types.AttributeKeyHeader, headerStr), + sdk.NewAttribute(types.AttributeKeyClientType, clientType), + // 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( sdk.EventTypeMessage, @@ -59,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), ), ) } @@ -92,15 +114,13 @@ 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) { - ctx.EventManager().EmitEvent( +// EmitUpgradeChainEvent emits an upgrade chain event. +func EmitUpgradeChainEvent(ctx sdk.Context, height int64) { + ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( - types.EventTypeSubmitMisbehaviour, - sdk.NewAttribute(types.AttributeKeyClientID, clientID), - sdk.NewAttribute(types.AttributeKeyClientType, clientState.ClientType()), - sdk.NewAttribute(types.AttributeKeyConsensusHeight, consensusHeight.String()), - sdk.NewAttribute(types.AttributeKeyHeader, headerStr), + 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/keeper/grpc_query_test.go b/modules/core/02-client/keeper/grpc_query_test.go index 0e8277aa971..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/types" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ) @@ -142,7 +142,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{ @@ -158,13 +158,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()) @@ -534,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) @@ -551,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 81ca1488ba7..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/types" + 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 19694c9fa91..e38f7a0d430 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/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/types" - localhosttypes "github.com/cosmos/ibc-go/v5/modules/light-clients/09-localhost/types" + solomachinetypes "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" + 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" @@ -44,7 +44,6 @@ const ( var ( testClientHeight = types.NewHeight(0, 5) testClientHeightRevision1 = types.NewHeight(1, 5) - newClientHeight = types.NewHeight(1, 1) ) type KeeperTestSuite struct { @@ -58,13 +57,14 @@ 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 now time.Time past time.Time + solomachine *ibctesting.Solomachine signers map[string]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++ { @@ -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()) @@ -140,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) @@ -154,13 +149,14 @@ 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") } 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 @@ -169,62 +165,62 @@ 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, }, - { - "invalid client type", - localhosttypes.NewClientState(suite.chainA.ChainID, testClientHeight), - false, - }, { "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.NewHeight(0, uint64(suite.chainA.GetContext().BlockHeight())), 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, + }, + { + "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), + 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, }, } @@ -244,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)) @@ -256,11 +252,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) @@ -271,23 +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("clientC", &ibctmtypes.ClientState{}), types.NewIdentifiedClientState("clientD", &localhosttypes.ClientState{}), + types.NewIdentifiedClientState("07-tendermint-1", &ibctm.ClientState{}), types.NewIdentifiedClientState("clientB", &ibctm.ClientState{}), } suite.chainA.App.GetIBCKeeper().ClientKeeper.SetAllClientMetadata(suite.chainA.GetContext(), expectedGenMetadata) @@ -325,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.go b/modules/core/02-client/keeper/proposal.go index 95e3544a277..aa0846021cc 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) @@ -49,20 +44,18 @@ func (k Keeper) ClientUpdateProposal(ctx sdk.Context, p *types.ClientUpdatePropo return sdkerrors.Wrapf(types.ErrClientNotActive, "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.SetClientState(ctx, p.SubjectClientId, clientState) - 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"), }, @@ -70,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/keeper/proposal_test.go b/modules/core/02-client/keeper/proposal_test.go index 6123666a7ab..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/types" + 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,23 +40,13 @@ 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) }, 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) @@ -78,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) @@ -88,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) @@ -98,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) @@ -128,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 @@ -158,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 @@ -188,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, @@ -209,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 9442b59b2a4..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/types" + 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 7235b984261..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/types" + 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/solomachine.go b/modules/core/02-client/legacy/v100/solomachine.go index e9121760e5b..d16dad0bfbb 100644 --- a/modules/core/02-client/legacy/v100/solomachine.go +++ b/modules/core/02-client/legacy/v100/solomachine.go @@ -88,16 +88,40 @@ 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, _ exported.ClientMessage, +) { + 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!") +} + +// UpdateState panis! +func (cs *ClientState) UpdateState(_ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, _ exported.ClientMessage) []exported.Height { + panic("legacy solo machine is deprecated!") +} + // 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!") } @@ -106,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!") } @@ -114,7 +138,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!") } @@ -187,6 +211,42 @@ 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, + 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!") @@ -197,11 +257,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/02-client/legacy/v100/store.go b/modules/core/02-client/legacy/v100/store.go index f20b24ba2a1..e83b116c956 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/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/types" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint/types" + smtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" + 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,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 = ibctmtypes.PruneAllExpiredConsensusStates(ctx, clientStore, cdc, tmClientState); err != nil { - return err - } - + ibctm.PruneAllExpiredConsensusStates(ctx, clientStore, cdc, tmClientState) default: continue } @@ -170,7 +167,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 4cebf0a53cb..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/types" + 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/light-clients/06-solomachine/types/solomachine.pb.go b/modules/core/02-client/migrations/v6/solomachine.pb.go similarity index 92% rename from modules/light-clients/06-solomachine/types/solomachine.pb.go rename to modules/core/02-client/migrations/v6/solomachine.pb.go index 2a287b9ced7..bbcb1b6a355 100644 --- a/modules/light-clients/06-solomachine/types/solomachine.pb.go +++ b/modules/core/02-client/migrations/v6/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 v6 import ( fmt "fmt" @@ -823,93 +823,93 @@ func init() { } var fileDescriptor_141333b361aae010 = []byte{ - // 1370 bytes of a gzipped FileDescriptorProto + // 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, 0x32, 0xb9, 0xde, 0x05, 0x37, 0x6d, 0x73, 0x6e, 0x95, 0x18, 0x23, - 0xca, 0x81, 0x68, 0xc2, 0x1d, 0x6a, 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, 0xbd, 0x01, 0x57, 0x02, 0xcd, 0x8a, - 0x28, 0xdd, 0x93, 0x35, 0x5d, 0x3a, 0xd8, 0xdf, 0x2f, 0x6b, 0xfb, 0xb2, 0xa2, 0xa5, 0xce, 0x72, - 0xe9, 0xd1, 0x98, 0x4f, 0x11, 0x20, 0x90, 0xb3, 0xef, 0x03, 0x7f, 0xcc, 0x4c, 0x94, 0xee, 0x29, - 0x07, 0x9f, 0xec, 0xc9, 0xa5, 0x0f, 0x65, 0xdf, 0x76, 0x95, 0xdb, 0x1a, 0x8d, 0xf9, 0x8b, 0x04, - 0x5d, 0x00, 0xd9, 0xf7, 0x5e, 0x40, 0xa0, 0xca, 0x92, 0x5c, 0xae, 0x68, 0xba, 0x58, 0xac, 0xca, - 0x8a, 0x24, 0xa7, 0xce, 0x71, 0x99, 0xd1, 0x98, 0x4f, 0x13, 0x94, 0x82, 0x14, 0x63, 0x6f, 0xc2, - 0xd5, 0xc0, 0x5e, 0x91, 0x3f, 0xd5, 0xf4, 0xaa, 0xfc, 0xd1, 0xa1, 0x07, 0x79, 0x34, 0x1f, 0xa7, - 0xd6, 0x48, 0xe0, 0x1e, 0x32, 0x03, 0x3c, 0x39, 0xcb, 0x43, 0x2a, 0xb0, 0xbb, 0x23, 0x8b, 0x25, - 0x59, 0x4d, 0x25, 0x48, 0x65, 0xc8, 0x8e, 0x8b, 0x3f, 0xf9, 0x31, 0xbb, 0x52, 0xfc, 0xfc, 0xb7, - 0x67, 0x59, 0xe6, 0xe9, 0xb3, 0x2c, 0xf3, 0xe7, 0xb3, 0x2c, 0xf3, 0xdd, 0xf3, 0xec, 0xca, 0xd3, - 0xe7, 0xd9, 0x95, 0xdf, 0x9f, 0x67, 0x57, 0x1e, 0xdc, 0x6e, 0x58, 0x6e, 0xb3, 0x57, 0xcb, 0x9b, - 0xb8, 0x5d, 0x30, 0xb1, 0xd3, 0xc6, 0x4e, 0xc1, 0xaa, 0x99, 0xd7, 0x1b, 0xb8, 0xd0, 0xbf, 0x51, - 0x68, 0xe3, 0x7a, 0xaf, 0x85, 0x1c, 0xf2, 0x3f, 0x75, 0x7d, 0xf6, 0x43, 0xf5, 0xd6, 0xcd, 0xeb, - 0xe1, 0x7f, 0x2a, 0xef, 0x9a, 0x71, 0x6a, 0xab, 0xfe, 0x3c, 0x7b, 0xfb, 0xaf, 0x00, 0x00, 0x00, - 0xff, 0xff, 0x32, 0xda, 0xb7, 0x54, 0x80, 0x0d, 0x00, 0x00, + 0x10, 0x3f, 0xa7, 0xe9, 0xf5, 0xb2, 0xb9, 0xde, 0x05, 0x37, 0x6d, 0x73, 0x6e, 0x95, 0x18, 0x23, + 0xca, 0x81, 0x68, 0xcc, 0x1d, 0x6a, 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, 0x1b, 0xe0, 0x4a, 0xa8, 0x59, + 0x95, 0xe4, 0xfb, 0x8a, 0x6e, 0xc8, 0x07, 0xfb, 0xfb, 0x15, 0x7d, 0x5f, 0x51, 0xf5, 0xcc, 0x59, + 0x2e, 0x3b, 0x1a, 0xf3, 0x19, 0x02, 0x84, 0x72, 0xf6, 0x43, 0xc0, 0x1f, 0x33, 0x93, 0xe4, 0xfb, + 0xea, 0xc1, 0xa7, 0x7b, 0x4a, 0xf9, 0x23, 0x25, 0xb0, 0x5d, 0xe5, 0xb6, 0x46, 0x63, 0xfe, 0x22, + 0x41, 0x17, 0x40, 0xf6, 0x83, 0x97, 0x10, 0x68, 0x8a, 0xac, 0x54, 0xaa, 0xba, 0x21, 0x95, 0x6a, + 0x8a, 0x2a, 0x2b, 0x99, 0x73, 0x5c, 0x6e, 0x34, 0xe6, 0xb3, 0x04, 0xa5, 0x20, 0xc5, 0xd8, 0x9b, + 0xe0, 0x6a, 0x68, 0xaf, 0x2a, 0x9f, 0xe9, 0x46, 0x4d, 0xf9, 0xf8, 0xd0, 0x87, 0x7c, 0x9a, 0x4f, + 0x32, 0x6b, 0x24, 0x70, 0x1f, 0x99, 0x01, 0xbe, 0x9c, 0xe5, 0x41, 0x26, 0xb4, 0xbb, 0xab, 0x48, + 0x65, 0x45, 0xcb, 0xa4, 0x48, 0x65, 0xc8, 0x8e, 0x4b, 0x3e, 0xfd, 0x31, 0xbf, 0x52, 0x7a, 0xf8, + 0xdb, 0xf3, 0x3c, 0xf3, 0xec, 0x79, 0x9e, 0xf9, 0xf3, 0x79, 0x9e, 0xf9, 0xee, 0x45, 0x7e, 0xe5, + 0xd9, 0x8b, 0xfc, 0xca, 0xef, 0x2f, 0xf2, 0x2b, 0x0f, 0x6f, 0x37, 0x1c, 0xdc, 0xec, 0xd5, 0x8b, + 0x16, 0x6a, 0x8b, 0x16, 0xf2, 0xda, 0xc8, 0x13, 0x9d, 0xba, 0x75, 0xbd, 0x81, 0xc4, 0xfe, 0x0d, + 0xb1, 0x8d, 0xec, 0x5e, 0x0b, 0x7a, 0xe4, 0x97, 0xe6, 0x9d, 0xdd, 0xeb, 0x64, 0x24, 0x8a, 0x6d, + 0xa7, 0xd1, 0x35, 0xfd, 0x99, 0xe0, 0x89, 0xfd, 0x9b, 0xf5, 0xd5, 0x60, 0x92, 0xbd, 0xfb, 0x57, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x9c, 0xae, 0x69, 0xbe, 0x7a, 0x0d, 0x00, 0x00, } func (m *ClientState) Marshal() (dAtA []byte, err error) { diff --git a/modules/core/02-client/proposal_handler_test.go b/modules/core/02-client/proposal_handler_test.go index 7cbe4de91a5..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/types" + 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 6cb7f24811b..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/types" + 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.go b/modules/core/02-client/types/codec.go index 293b466fd10..247507e8c32 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 e8bfb0b1beb..cfcd395b0c9 100644 --- a/modules/core/02-client/types/codec_test.go +++ b/modules/core/02-client/types/codec_test.go @@ -6,8 +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/types" - localhosttypes "github.com/cosmos/ibc-go/v5/modules/light-clients/09-localhost/types" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ) @@ -30,12 +29,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), - true, - }, - { - "localhost client", - localhosttypes.NewClientState(chainID, clientHeight), + ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), true, }, { @@ -115,15 +109,15 @@ 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", - ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2).CreateHeader(), + ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2).CreateHeader("solomachine"), true, }, { @@ -141,54 +135,7 @@ func (suite *TypesTestSuite) TestPackHeader() { testCasesAny := []caseAny{} for _, tc := range testCases { - clientAny, err := types.PackHeader(tc.header) - 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.UnpackHeader(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) + clientAny, err := types.PackClientMessage(tc.clientMessage) if tc.expPass { suite.Require().NoError(err, tc.name) } else { @@ -199,10 +146,10 @@ func (suite *TypesTestSuite) TestPackMisbehaviour() { } for i, tc := range testCasesAny { - cs, err := types.UnpackMisbehaviour(tc.any) + cs, err := types.UnpackClientMessage(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 90bbbd16965..b974eb8b942 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 3aa01c09708..7142153ff7e 100644 --- a/modules/core/02-client/types/encoding_test.go +++ b/modules/core/02-client/types/encoding_test.go @@ -2,27 +2,27 @@ 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/types" + 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), } // 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/errors.go b/modules/core/02-client/types/errors.go index c40bae878cc..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") - ErrClientNotActive = sdkerrors.Register(SubModuleName, 29, "client is not active") + ErrClientNotActive = sdkerrors.Register(SubModuleName, 29, "client state is not active") ) diff --git a/modules/core/02-client/types/events.go b/modules/core/02-client/types/events.go index 78ed0028e65..4e0bd1ba96f 100644 --- a/modules/core/02-client/types/events.go +++ b/modules/core/02-client/types/events.go @@ -12,9 +12,11 @@ const ( AttributeKeySubjectClientID = "subject_client_id" AttributeKeyClientType = "client_type" AttributeKeyConsensusHeight = "consensus_height" + AttributeKeyConsensusHeights = "consensus_heights" AttributeKeyHeader = "header" + AttributeKeyUpgradeStore = "upgrade_store" + AttributeKeyUpgradePlanHeight = "upgrade_plan_height" AttributeKeyUpgradePlanTitle = "title" - AttributeKeyUpgradePlanHeight = "height" ) // IBC client events vars @@ -24,6 +26,7 @@ var ( EventTypeUpgradeClient = "upgrade_client" EventTypeSubmitMisbehaviour = "client_misbehaviour" EventTypeUpdateClientProposal = "update_client_proposal" + EventTypeUpgradeChain = "upgrade_chain" EventTypeUpgradeClientProposal = "upgrade_client_proposal" AttributeValueCategory = fmt.Sprintf("%s_%s", host.ModuleName, SubModuleName) diff --git a/modules/core/02-client/types/genesis.go b/modules/core/02-client/types/genesis.go index 9d12b224765..784bae42f79 100644 --- a/modules/core/02-client/types/genesis.go +++ b/modules/core/02-client/types/genesis.go @@ -200,10 +200,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 2fba3a19647..08d55606c61 100644 --- a/modules/core/02-client/types/genesis_test.go +++ b/modules/core/02-client/types/genesis_test.go @@ -9,23 +9,23 @@ 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/types" - localhosttypes "github.com/cosmos/ibc-go/v5/modules/light-clients/09-localhost/types" + solomachinetypes "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" + 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" ) const ( - chainID = "chainID" - tmClientID0 = "07-tendermint-0" - tmClientID1 = "07-tendermint-1" - invalidClientID = "myclient-0" - clientID = tmClientID0 + tmClientID0 = "07-tendermint-0" + tmClientID1 = "07-tendermint-1" + invalidClientID = "myclient-0" + soloMachineClientID = "06-solomachine-0" + clientID = tmClientID0 height = 10 ) -var clientHeight = types.NewHeight(0, 10) +var clientHeight = types.NewHeight(1, 10) func (suite *TypesTestSuite) TestMarshalGenesisState() { cdc := suite.chainA.App.AppCodec() @@ -58,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 @@ -76,10 +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), - ), - types.NewIdentifiedClientState( - exported.Localhost+"-1", localhosttypes.NewClientState("chainID", clientHeight), + tmClientID0, ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -88,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, ), ), @@ -104,21 +101,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, 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)), + }, + 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, ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -127,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, ), ), @@ -141,32 +152,12 @@ 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( []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", clientHeight), + tmClientID0, ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -174,8 +165,8 @@ func (suite *TypesTestSuite) TestValidateGenesis() { tmClientID1, []types.ConsensusStateWithHeight{ types.NewConsensusStateWithHeight( - types.NewHeight(0, 1), - ibctmtypes.NewConsensusState( + types.NewHeight(1, 1), + ibctm.NewConsensusState( header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, ), ), @@ -194,10 +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), - ), - types.NewIdentifiedClientState( - exported.Localhost, localhosttypes.NewClientState("chaindID", clientHeight), + tmClientID0, ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -206,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, ), ), @@ -225,10 +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), - ), - types.NewIdentifiedClientState( - exported.Localhost, localhosttypes.NewClientState("chaindID", clientHeight), + tmClientID0, ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -236,8 +221,8 @@ func (suite *TypesTestSuite) TestValidateGenesis() { tmClientID0, []types.ConsensusStateWithHeight{ types.NewConsensusStateWithHeight( - types.NewHeight(0, 1), - ibctmtypes.NewConsensusState( + types.NewHeight(1, 1), + ibctm.NewConsensusState( time.Time{}, commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, ), ), @@ -256,10 +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), - ), - types.NewIdentifiedClientState( - exported.Localhost, localhosttypes.NewClientState("chainID", clientHeight), + tmClientID0, ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -268,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, ), ), @@ -287,10 +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), - ), - types.NewIdentifiedClientState( - exported.Localhost, localhosttypes.NewClientState("chainID", clientHeight), + clientID, ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -299,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, ), ), @@ -315,7 +294,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { }, ), }, - types.NewParams(exported.Tendermint, exported.Localhost), + types.NewParams(exported.Tendermint), false, 0, ), @@ -326,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, ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -335,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, ), ), @@ -361,10 +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), - ), - types.NewIdentifiedClientState( - exported.Localhost, localhosttypes.NewClientState("chainID", clientHeight), + tmClientID0, ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -373,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, ), ), @@ -392,10 +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), - ), - types.NewIdentifiedClientState( - exported.Localhost, localhosttypes.NewClientState("chainID", clientHeight), + tmClientID0, ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -404,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, ), ), @@ -413,51 +386,20 @@ 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( []types.IdentifiedClientState{ types.NewIdentifiedClientState( - tmClientID0, ibctmtypes.NewClientState(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( - exported.Localhost+"-1", localhosttypes.NewClientState("chainID", clientHeight), + tmClientID1, ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -466,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, ), ), @@ -474,7 +416,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { ), }, nil, - types.NewParams(exported.Tendermint, exported.Localhost), + types.NewParams(exported.Tendermint), false, 0, ), @@ -485,10 +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), - ), - types.NewIdentifiedClientState( - exported.Localhost+"-1", localhosttypes.NewClientState("chainID", clientHeight), + "my-client", ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []types.ClientConsensusStates{ @@ -497,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, ), ), @@ -505,7 +444,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { ), }, nil, - types.NewParams(exported.Tendermint, exported.Localhost), + types.NewParams(exported.Tendermint), false, 5, ), @@ -514,18 +453,14 @@ 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), - ibctmtypes.NewConsensusState( + ibctm.NewConsensusState( header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, ), ), @@ -533,7 +468,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 da1ef19275c..8e339199923 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 @@ -105,16 +102,16 @@ 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, 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 } @@ -124,16 +121,13 @@ 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) + clientMsg, err := UnpackClientMessage(msg.ClientMessage) if err != nil { return err } - if err := header.ValidateBasic(); err != nil { + if err := clientMsg.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) } @@ -148,8 +142,8 @@ func (msg MsgUpdateClient) GetSigners() []sdk.AccAddress { // UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces func (msg MsgUpdateClient) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { - var header exported.Header - return unpacker.UnpackAny(msg.Header, &header) + var clientMsg exported.ClientMessage + return unpacker.UnpackAny(msg.ClientMessage, &clientMsg) } // NewMsgUpgradeClient creates a new MsgUpgradeClient instance @@ -233,8 +227,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 } @@ -252,20 +246,13 @@ 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 } 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) } @@ -281,6 +268,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/02-client/types/msgs_test.go b/modules/core/02-client/types/msgs_test.go index 3ea81a9d0a7..b42445c030c 100644 --- a/modules/core/02-client/types/msgs_test.go +++ b/modules/core/02-client/types/msgs_test.go @@ -9,9 +9,8 @@ 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" - solomachinetypes "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine/types" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint/types" + solomachinetypes "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/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) { @@ -55,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) }, @@ -100,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) }, @@ -109,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, @@ -124,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 @@ -168,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) @@ -203,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) }, }, @@ -268,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, @@ -276,7 +277,7 @@ func (suite *TypesTestSuite) TestMsgUpdateClient_ValidateBasic() { { "failed to unpack header", func() { - msg.Header = nil + msg.ClientMessage = nil }, false, }, @@ -291,7 +292,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, @@ -304,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 { @@ -338,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) }, @@ -451,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) @@ -492,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) }, @@ -549,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) }, @@ -558,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.go b/modules/core/02-client/types/proposal.go index 37b4cee74e2..4bc7a84a810 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 6b856211901..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/types" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ) @@ -109,20 +109,31 @@ 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, &ibctm.ClientState{ + FrozenHeight: types.Height{ + RevisionHeight: 10, + }, + }) suite.Require().NoError(err) }, false, }, { "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, }, @@ -138,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{ @@ -173,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) @@ -183,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 @@ -207,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/02-client/types/tx.pb.go b/modules/core/02-client/types/tx.pb.go index b2d823567cb..a9cf9917069 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"` } @@ -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{ - // 602 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, 0x82, 0x90, 0xaa, 0x6e, 0xa4, 0x13, 0x43, 0x24, 0x70, 0xc5, 0x00, 0x4b, - 0xf0, 0x9f, 0xeb, 0xe5, 0x44, 0xec, 0x8b, 0x7c, 0x76, 0x44, 0xbe, 0x01, 0x23, 0x03, 0x1f, 0xa0, - 0x62, 0xe0, 0xb3, 0x30, 0x76, 0x60, 0x60, 0x8a, 0xaa, 0x64, 0x61, 0xce, 0x27, 0x40, 0xf1, 0x39, - 0x21, 0x76, 0xe3, 0x28, 0xe2, 0xcf, 0xe6, 0xf3, 0xfb, 0xdc, 0xf3, 0xbc, 0x8f, 0x9f, 0xf7, 0x7c, - 0xe0, 0x80, 0x5a, 0x36, 0xb2, 0x99, 0x8f, 0x91, 0xdd, 0xa5, 0xd8, 0x0b, 0x50, 0xbf, 0x8e, 0x82, - 0xf7, 0xb0, 0xe7, 0xb3, 0x80, 0xc9, 0x32, 0xb5, 0x6c, 0x38, 0x2d, 0x42, 0x51, 0x84, 0xfd, 0xba, - 0x52, 0x26, 0x8c, 0xb0, 0xa8, 0x8c, 0xa6, 0x4f, 0x02, 0xa9, 0xec, 0x13, 0xc6, 0x48, 0x17, 0xa3, - 0x68, 0x65, 0x85, 0x97, 0xc8, 0xf4, 0x06, 0xa2, 0xa4, 0xdf, 0x48, 0x60, 0xbb, 0xc5, 0xc9, 0xb9, - 0x8f, 0xcd, 0x00, 0x9f, 0x47, 0x3c, 0xf2, 0x0b, 0x50, 0x12, 0x8c, 0x6d, 0x1e, 0x98, 0x01, 0xae, - 0x48, 0x55, 0xa9, 0xb6, 0xd5, 0x28, 0x43, 0xc1, 0x02, 0x67, 0x2c, 0xf0, 0x99, 0x37, 0x68, 0xee, - 0x4d, 0x86, 0xda, 0xbd, 0x81, 0xe9, 0x76, 0xcf, 0xf4, 0xc5, 0x3d, 0xba, 0xb1, 0x25, 0x96, 0x17, - 0xd3, 0x95, 0xfc, 0x1a, 0x6c, 0xdb, 0xcc, 0xe3, 0xd8, 0xe3, 0x21, 0x8f, 0x49, 0x37, 0x56, 0x90, - 0x2a, 0x93, 0xa1, 0xb6, 0x1b, 0x93, 0x26, 0xb7, 0xe9, 0xc6, 0xdd, 0xf9, 0x1b, 0x41, 0xbd, 0x0b, - 0x0a, 0x9c, 0x12, 0x0f, 0xfb, 0x95, 0x7c, 0x55, 0xaa, 0x15, 0x8d, 0x78, 0x75, 0xb6, 0xf9, 0xe1, - 0x4a, 0xcb, 0xfd, 0xb8, 0xd2, 0x72, 0xfa, 0x3e, 0xd8, 0x4b, 0x39, 0x34, 0x30, 0xef, 0x4d, 0x59, - 0xf4, 0x4f, 0xc2, 0xfd, 0xab, 0x9e, 0xf3, 0xcb, 0x7d, 0x1d, 0x14, 0x63, 0x27, 0xd4, 0x89, 0xac, - 0x17, 0x9b, 0xe5, 0xc9, 0x50, 0xdb, 0x49, 0x98, 0xa4, 0x8e, 0x6e, 0x6c, 0x8a, 0xe7, 0xe7, 0x8e, - 0x7c, 0x0c, 0x0a, 0x1d, 0x6c, 0x3a, 0xd8, 0x5f, 0xe5, 0xca, 0x88, 0x31, 0x6b, 0x77, 0xbc, 0xd8, - 0xd5, 0xbc, 0xe3, 0x6f, 0x79, 0xb0, 0x13, 0xd5, 0x88, 0x6f, 0x3a, 0x7f, 0xd0, 0x72, 0x3a, 0xe3, - 0x8d, 0x7f, 0x91, 0x71, 0xfe, 0x2f, 0x65, 0xfc, 0x12, 0x94, 0x7b, 0x3e, 0x63, 0x97, 0xed, 0x50, - 0xd8, 0x6e, 0x0b, 0xdd, 0xca, 0x7f, 0x55, 0xa9, 0x56, 0x6a, 0x6a, 0x93, 0xa1, 0x76, 0x20, 0x98, - 0x96, 0xa1, 0x74, 0x43, 0x8e, 0x5e, 0x27, 0x3f, 0xd9, 0x3b, 0x70, 0x98, 0x02, 0xa7, 0x7a, 0xff, - 0x3f, 0xe2, 0xae, 0x4d, 0x86, 0xda, 0xd1, 0x52, 0xee, 0x74, 0xcf, 0x4a, 0x42, 0x24, 0x6b, 0x46, - 0x0b, 0x19, 0x89, 0x2b, 0xa0, 0x92, 0x4e, 0x75, 0x1e, 0xf9, 0x17, 0x09, 0xdc, 0x6f, 0x71, 0x72, - 0x11, 0x5a, 0x2e, 0x0d, 0x5a, 0x94, 0x5b, 0xb8, 0x63, 0xf6, 0x29, 0x0b, 0xfd, 0xdf, 0xc9, 0xfd, - 0x14, 0x94, 0xdc, 0x05, 0x8a, 0x95, 0x03, 0x9b, 0x40, 0xae, 0x31, 0xb6, 0x1a, 0x38, 0x5c, 0xda, - 0xe7, 0xcc, 0x49, 0xe3, 0x73, 0x1e, 0xe4, 0x5b, 0x9c, 0xc8, 0x6f, 0x41, 0x29, 0xf1, 0xc3, 0x79, - 0x00, 0x6f, 0xff, 0xca, 0x60, 0xea, 0xcc, 0x2a, 0x8f, 0xd6, 0x00, 0xcd, 0x94, 0xa6, 0x0a, 0x89, - 0x43, 0x9d, 0xa5, 0xb0, 0x08, 0xca, 0x54, 0x58, 0x76, 0x10, 0x65, 0x1b, 0xdc, 0x49, 0x4e, 0xd4, - 0x51, 0xe6, 0xee, 0x05, 0x94, 0x72, 0xbc, 0x0e, 0x6a, 0x2e, 0xe2, 0x03, 0x79, 0x49, 0xec, 0x0f, - 0x33, 0x38, 0x6e, 0x43, 0x95, 0xfa, 0xda, 0xd0, 0x99, 0x66, 0xd3, 0xf8, 0x3a, 0x52, 0xa5, 0xeb, - 0x91, 0x2a, 0xdd, 0x8c, 0x54, 0xe9, 0xe3, 0x58, 0xcd, 0x5d, 0x8f, 0xd5, 0xdc, 0xf7, 0xb1, 0x9a, - 0x7b, 0x73, 0x4a, 0x68, 0xd0, 0x09, 0x2d, 0x68, 0x33, 0x17, 0xd9, 0x8c, 0xbb, 0x8c, 0x23, 0x6a, - 0xd9, 0x27, 0x84, 0xa1, 0xfe, 0x53, 0xe4, 0x32, 0x27, 0xec, 0x62, 0x2e, 0x6e, 0xab, 0xc7, 0x8d, - 0x93, 0xf8, 0xc2, 0x0a, 0x06, 0x3d, 0xcc, 0xad, 0x42, 0x34, 0x57, 0x4f, 0x7e, 0x06, 0x00, 0x00, - 0xff, 0xff, 0xaa, 0xe7, 0x1e, 0x7d, 0xd0, 0x06, 0x00, 0x00, + // 623 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0xcd, 0x6e, 0xd3, 0x4c, + 0x14, 0x8d, 0x9b, 0xef, 0x8b, 0x9a, 0x69, 0xfa, 0x23, 0x13, 0x52, 0xd7, 0x55, 0xed, 0xc8, 0x74, + 0x11, 0x04, 0xb5, 0x49, 0x2a, 0x24, 0x54, 0xd8, 0x90, 0xae, 0x58, 0x44, 0x02, 0x57, 0x2c, 0x60, + 0x13, 0xfc, 0x33, 0x9d, 0x8e, 0x88, 0x3d, 0x91, 0xc7, 0x8e, 0xc8, 0x1b, 0xb0, 0xe4, 0x11, 0x2a, + 0x78, 0x01, 0x1e, 0x83, 0x65, 0x17, 0x2c, 0x58, 0x45, 0x55, 0xb2, 0x61, 0x9d, 0x27, 0x40, 0xf1, + 0x38, 0xc1, 0x76, 0xec, 0x28, 0x12, 0xb0, 0xf3, 0xcc, 0x3d, 0x73, 0xce, 0x3d, 0x3e, 0x77, 0x6c, + 0x70, 0x88, 0x4d, 0x4b, 0xb3, 0x88, 0x07, 0x35, 0xab, 0x87, 0xa1, 0xeb, 0x6b, 0x83, 0xa6, 0xe6, + 0x7f, 0x50, 0xfb, 0x1e, 0xf1, 0x09, 0xcf, 0x63, 0xd3, 0x52, 0x67, 0x45, 0x95, 0x15, 0xd5, 0x41, + 0x53, 0xac, 0x22, 0x82, 0x48, 0x58, 0xd6, 0x66, 0x4f, 0x0c, 0x29, 0x1e, 0x20, 0x42, 0x50, 0x0f, + 0x6a, 0xe1, 0xca, 0x0c, 0x2e, 0x35, 0xc3, 0x1d, 0xb2, 0x92, 0x72, 0xcb, 0x81, 0xdd, 0x0e, 0x45, + 0xe7, 0x1e, 0x34, 0x7c, 0x78, 0x1e, 0xf2, 0xf0, 0x2f, 0x41, 0x85, 0x31, 0x76, 0xa9, 0x6f, 0xf8, + 0x50, 0xe0, 0xea, 0x5c, 0x63, 0xab, 0x55, 0x55, 0x19, 0x8b, 0x3a, 0x67, 0x51, 0x9f, 0xbb, 0xc3, + 0xf6, 0xfe, 0x74, 0x24, 0xdf, 0x19, 0x1a, 0x4e, 0xef, 0x4c, 0x89, 0x9f, 0x51, 0xf4, 0x2d, 0xb6, + 0xbc, 0x98, 0xad, 0xf8, 0x37, 0x60, 0xd7, 0x22, 0x2e, 0x85, 0x2e, 0x0d, 0x68, 0x44, 0xba, 0xb1, + 0x82, 0x54, 0x9c, 0x8e, 0xe4, 0x5a, 0x44, 0x9a, 0x3c, 0xa6, 0xe8, 0x3b, 0x8b, 0x1d, 0x46, 0x5d, + 0x03, 0x25, 0x8a, 0x91, 0x0b, 0x3d, 0xa1, 0x58, 0xe7, 0x1a, 0x65, 0x3d, 0x5a, 0x9d, 0x6d, 0x7e, + 0xbc, 0x96, 0x0b, 0x3f, 0xaf, 0xe5, 0x82, 0x72, 0x00, 0xf6, 0x53, 0x0e, 0x75, 0x48, 0xfb, 0x33, + 0x16, 0xe5, 0x0b, 0x73, 0xff, 0xba, 0x6f, 0xff, 0x76, 0xdf, 0x04, 0xe5, 0xc8, 0x09, 0xb6, 0x43, + 0xeb, 0xe5, 0x76, 0x75, 0x3a, 0x92, 0xf7, 0x12, 0x26, 0xb1, 0xad, 0xe8, 0x9b, 0xec, 0xf9, 0x85, + 0xcd, 0x3f, 0x05, 0x3b, 0xd1, 0xbe, 0x03, 0x29, 0x35, 0xd0, 0x4a, 0x77, 0xfa, 0x36, 0xc3, 0x76, + 0x18, 0x74, 0x6d, 0x03, 0xf1, 0x26, 0x17, 0x06, 0xbe, 0x17, 0xc1, 0x5e, 0x58, 0x43, 0x9e, 0x61, + 0xff, 0x81, 0x83, 0x74, 0xe4, 0x1b, 0xff, 0x22, 0xf2, 0xe2, 0x5f, 0x8a, 0xfc, 0x15, 0xa8, 0xf6, + 0x3d, 0x42, 0x2e, 0xbb, 0x01, 0xb3, 0xdd, 0x65, 0xba, 0xc2, 0x7f, 0x75, 0xae, 0x51, 0x69, 0xcb, + 0xd3, 0x91, 0x7c, 0xc8, 0x98, 0xb2, 0x50, 0x8a, 0xce, 0x87, 0xdb, 0xc9, 0x57, 0xf6, 0x1e, 0x1c, + 0xa5, 0xc0, 0xa9, 0xde, 0xff, 0x0f, 0xb9, 0x1b, 0xd3, 0x91, 0x7c, 0x9c, 0xc9, 0x9d, 0xee, 0x59, + 0x4c, 0x88, 0xe4, 0x8d, 0x6c, 0x29, 0x27, 0x71, 0x11, 0x08, 0xe9, 0x54, 0x17, 0x91, 0x7f, 0xe5, + 0xc0, 0xdd, 0x0e, 0x45, 0x17, 0x81, 0xe9, 0x60, 0xbf, 0x83, 0xa9, 0x09, 0xaf, 0x8c, 0x01, 0x26, + 0x81, 0xc7, 0x9f, 0x2e, 0xe7, 0x5e, 0xcb, 0xca, 0x5d, 0xe0, 0x62, 0xc9, 0x3f, 0x03, 0x15, 0x27, + 0x46, 0xb2, 0x32, 0xf9, 0x0d, 0x81, 0xd3, 0x13, 0x68, 0x5e, 0x4c, 0x0e, 0x6f, 0x88, 0x58, 0xb6, + 0x23, 0x83, 0xa3, 0xcc, 0x8e, 0xe7, 0x9e, 0x5a, 0x9f, 0x8b, 0xa0, 0xd8, 0xa1, 0x88, 0x7f, 0x07, + 0x2a, 0x89, 0x2f, 0xd1, 0x3d, 0x75, 0xf9, 0x1b, 0xa7, 0xa6, 0x2e, 0xb3, 0xf8, 0x60, 0x0d, 0xd0, + 0x5c, 0x69, 0xa6, 0x90, 0xb8, 0xed, 0x79, 0x0a, 0x71, 0x50, 0xae, 0x42, 0xd6, 0x95, 0xe4, 0x2d, + 0xb0, 0x9d, 0x9c, 0xad, 0xe3, 0xdc, 0xd3, 0x31, 0x94, 0xf8, 0x70, 0x1d, 0xd4, 0x42, 0xc4, 0x03, + 0x7c, 0xc6, 0x00, 0xdc, 0xcf, 0xe1, 0x58, 0x86, 0x8a, 0xcd, 0xb5, 0xa1, 0x73, 0xcd, 0xb6, 0xfe, + 0x6d, 0x2c, 0x71, 0x37, 0x63, 0x89, 0xbb, 0x1d, 0x4b, 0xdc, 0xa7, 0x89, 0x54, 0xb8, 0x99, 0x48, + 0x85, 0x1f, 0x13, 0xa9, 0xf0, 0xf6, 0x09, 0xc2, 0xfe, 0x55, 0x60, 0xaa, 0x16, 0x71, 0x34, 0x8b, + 0x50, 0x87, 0x50, 0x0d, 0x9b, 0xd6, 0x09, 0x22, 0xda, 0xe0, 0xb1, 0xe6, 0x10, 0x3b, 0xe8, 0x41, + 0xca, 0x7e, 0x63, 0x8f, 0x5a, 0x27, 0xd1, 0x9f, 0xcc, 0x1f, 0xf6, 0x21, 0x35, 0x4b, 0xe1, 0x7c, + 0x9d, 0xfe, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x88, 0x72, 0x43, 0x59, 0xe9, 0x06, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -716,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 } @@ -982,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) @@ -1347,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 { @@ -1374,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/03-connection/keeper/handshake_test.go b/modules/core/03-connection/keeper/handshake_test.go index 64ef8f8b813..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/types" + 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/keeper.go b/modules/core/03-connection/keeper/keeper.go index 9e23503ad50..7995a52c4b3 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.ErrClientNotFound, "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 df1a6131d3e..2062105dac9 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/v5/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" ibctesting "github.com/cosmos/ibc-go/v5/testing" ) @@ -111,7 +112,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 @@ -122,10 +126,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}, } @@ -136,7 +144,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 3471cdcb7ac..bafea2bd190 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/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" ) @@ -31,9 +35,27 @@ func (k Keeper) VerifyClientState( return sdkerrors.Wrapf(clienttypes.ErrClientNotActive, "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.ErrClientNotActive, "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.ErrClientNotActive, "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.ErrClientNotActive, "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/03-connection/keeper/verify_test.go b/modules/core/03-connection/keeper/verify_test.go index b5592362936..3ad64e9b285 100644 --- a/modules/core/03-connection/keeper/verify_test.go +++ b/modules/core/03-connection/keeper/verify_test.go @@ -9,12 +9,12 @@ 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/types" + 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" ) -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) @@ -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}, @@ -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() @@ -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}, @@ -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) @@ -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 58c02063378..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/types" + 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.go b/modules/core/04-channel/keeper/packet.go index ad160bd8a0e..3fa66a6e989 100644 --- a/modules/core/04-channel/keeper/packet.go +++ b/modules/core/04-channel/keeper/packet.go @@ -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 1080b1ebb73..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/types" + 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" ) @@ -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" @@ -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 @@ -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) diff --git a/modules/core/ante/ante_test.go b/modules/core/ante/ante_test.go index 3d074429a66..363ea28e398 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) @@ -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: @@ -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/exported/client.go b/modules/core/exported/client.go index 4dce203bea4..ac76a647dc5 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" @@ -44,137 +40,93 @@ type ClientState interface { GetLatestHeight() Height Validate() 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 - - // Status function - // Clients must return their status. Only Active clients are allowed to process packets. + // 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 - // Genesis function - ExportMetadata(sdk.KVStore) []GenesisMetadata - - // Update and Misbehaviour functions + // ExportMetadata must export metadata stored within the clientStore for genesis export + ExportMetadata(clientStore sdk.KVStore) []GenesisMetadata - CheckHeaderAndUpdateState(sdk.Context, codec.BinaryCodec, sdk.KVStore, Header) (ClientState, ConsensusState, error) - CheckMisbehaviourAndUpdateState(sdk.Context, codec.BinaryCodec, sdk.KVStore, Misbehaviour) (ClientState, error) - CheckSubstituteAndUpdateState(ctx sdk.Context, cdc codec.BinaryCodec, subjectClientStore, substituteClientStore sdk.KVStore, substituteClient ClientState) (ClientState, error) - - // Upgrade functions - // NOTE: proof heights are not included as upgrade to a new revision is expected to pass only on the last - // height committed by the current revision. Clients are responsible for ensuring that the planned last - // 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. - VerifyUpgradeAndUpdateState( - ctx sdk.Context, - cdc codec.BinaryCodec, - store sdk.KVStore, - newClient ClientState, - newConsState ConsensusState, - proofUpgradeClient, - proofUpgradeConsState []byte, - ) (ClientState, ConsensusState, error) - // Utility function that zeroes out any client customizable fields in client state + // 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 - // State verification functions - - 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( + // GetTimestampAtHeight must return the timestamp for the consensus state associated with the provided height. + GetTimestampAtHeight( ctx sdk.Context, - store sdk.KVStore, + clientStore sdk.KVStore, cdc codec.BinaryCodec, height Height, - delayTimePeriod uint64, - delayBlockPeriod uint64, - prefix Prefix, - proof []byte, - portID, - channelID string, - sequence uint64, - commitmentBytes []byte, - ) error - VerifyPacketAcknowledgement( + ) (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(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, consensusState ConsensusState) error + + // 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, - store sdk.KVStore, + clientStore sdk.KVStore, cdc codec.BinaryCodec, height Height, delayTimePeriod uint64, delayBlockPeriod uint64, - prefix Prefix, proof []byte, - portID, - channelID string, - sequence uint64, - acknowledgement []byte, + path []byte, + value []byte, ) error - VerifyPacketReceiptAbsence( + + // 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, - store sdk.KVStore, + clientStore sdk.KVStore, cdc codec.BinaryCodec, height Height, delayTimePeriod uint64, delayBlockPeriod uint64, - prefix Prefix, proof []byte, - portID, - channelID string, - sequence uint64, + path []byte, ) error - VerifyNextSequenceRecv( + + // 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, 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(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg ClientMessage) []Height + + // 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 + // height committed by the current revision. Clients are responsible for ensuring that the planned last + // 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, - store sdk.KVStore, cdc codec.BinaryCodec, - height Height, - delayTimePeriod uint64, - delayBlockPeriod uint64, - prefix Prefix, - proof []byte, - portID, - channelID string, - nextSequenceRecv uint64, + store sdk.KVStore, + newClient ClientState, + newConsState ConsensusState, + proofUpgradeClient, + proofUpgradeConsState []byte, ) error } @@ -184,31 +136,19 @@ 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 ValidateBasic() error } -// Misbehaviour defines counterparty misbehaviour for a specific consensus type -type Misbehaviour interface { - proto.Message - - ClientType() string - GetClientID() string - ValidateBasic() error -} - -// Header is the consensus state update information -type Header 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 - GetHeight() Height ValidateBasic() error } diff --git a/modules/core/genesis.go b/modules/core/genesis.go index 363b9a07717..e7875770dac 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 0a4fa2e1fe4..c433e834de1 100644 --- a/modules/core/genesis_test.go +++ b/modules/core/genesis_test.go @@ -15,8 +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/types" - localhosttypes "github.com/cosmos/ibc-go/v5/modules/light-clients/09-localhost/types" + 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" ) @@ -25,8 +24,7 @@ const ( connectionID = "connection-0" clientID = "07-tendermint-0" connectionID2 = "connection-1" - clientID2 = "07-tendermin-1" - localhostID = exported.Localhost + "-1" + clientID2 = "07-tendermint-1" port1 = "firstport" port2 = "secondport" @@ -35,7 +33,7 @@ const ( channel2 = "channel-1" ) -var clientHeight = clienttypes.NewHeight(0, 10) +var clientHeight = clienttypes.NewHeight(1, 10) type IBCTestSuite struct { suite.Suite @@ -77,10 +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), - ), - clienttypes.NewIdentifiedClientState( - localhostID, localhosttypes.NewClientState("chaindID", clientHeight), + clientID, ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []clienttypes.ClientConsensusStates{ @@ -89,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, ), ), @@ -105,8 +100,8 @@ func (suite *IBCTestSuite) TestValidateGenesis() { }, ), }, - clienttypes.NewParams(exported.Tendermint, exported.Localhost), - true, + clienttypes.NewParams(exported.Tendermint), + false, 2, ), ConnectionGenesis: connectiontypes.NewGenesisState( @@ -157,10 +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), - ), - clienttypes.NewIdentifiedClientState( - localhostID, localhosttypes.NewClientState("(chaindID)", clienttypes.ZeroHeight()), + clientID, ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, nil, @@ -241,10 +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), - ), - clienttypes.NewIdentifiedClientState( - exported.Localhost, localhosttypes.NewClientState("chaindID", clientHeight), + clientID, ibctm.NewClientState(suite.chainA.ChainID, ibctm.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), }, []clienttypes.ClientConsensusStates{ @@ -253,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, ), ), @@ -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/keeper/msg_server.go b/modules/core/keeper/msg_server.go index 500d1dd8cf7..5f6c77a3981 100644 --- a/modules/core/keeper/msg_server.go +++ b/modules/core/keeper/msg_server.go @@ -46,12 +46,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.UnpackHeader(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 } @@ -80,16 +80,18 @@ 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) - misbehaviour, err := clienttypes.UnpackMisbehaviour(msg.Misbehaviour) + misbehaviour, err := clienttypes.UnpackClientMessage(msg.Misbehaviour) if err != nil { return nil, err } - if err := k.ClientKeeper.CheckMisbehaviourAndUpdateState(ctx, 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/modules/core/keeper/msg_server_test.go b/modules/core/keeper/msg_server_test.go index 420e8c429e6..1fd6a73ad87 100644 --- a/modules/core/keeper/msg_server_test.go +++ b/modules/core/keeper/msg_server_test.go @@ -10,13 +10,13 @@ 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/types" + 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" ) var ( - timeoutHeight = clienttypes.NewHeight(0, 10000) + timeoutHeight = clienttypes.NewHeight(1, 10000) maxSequence = uint64(10) ) @@ -627,15 +627,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() @@ -644,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"), } @@ -684,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"), } @@ -721,9 +719,18 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { path = ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupClients(path) + var err error + clientState := path.EndpointA.GetClientState().(*ibctm.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/core/legacy/v100/genesis_test.go b/modules/core/legacy/v100/genesis_test.go index fa06a137756..b50a802b935 100644 --- a/modules/core/legacy/v100/genesis_test.go +++ b/modules/core/legacy/v100/genesis_test.go @@ -14,7 +14,7 @@ import ( 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" - v100 "github.com/cosmos/ibc-go/v5/modules/core/legacy/v100" + "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" diff --git a/modules/core/migrations/v5/migrations.go b/modules/core/migrations/v5/migrations.go new file mode 100644 index 00000000000..dec7eb79d80 --- /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/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 +// 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..2e70ede1cae --- /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/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 { + 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))))) + } + } + }) + } +} diff --git a/modules/core/module.go b/modules/core/module.go index 19b5d445fb7..a2888a6d6bf 100644 --- a/modules/core/module.go +++ b/modules/core/module.go @@ -98,9 +98,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 @@ -157,7 +154,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/simulation/decoder_test.go b/modules/core/simulation/decoder_test.go index c9575ccbe31..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/types" + 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 d597141005f..5911d6d7215 100644 --- a/modules/core/types/codec.go +++ b/modules/core/types/codec.go @@ -7,9 +7,8 @@ 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" commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" - solomachinetypes "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine/types" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint/types" - localhosttypes "github.com/cosmos/ibc-go/v5/modules/light-clients/09-localhost/types" + solomachinetypes "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ) // RegisterInterfaces registers x/ibc interfaces into protobuf Any. @@ -18,7 +17,6 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) { connectiontypes.RegisterInterfaces(registry) channeltypes.RegisterInterfaces(registry) solomachinetypes.RegisterInterfaces(registry) - ibctmtypes.RegisterInterfaces(registry) - localhosttypes.RegisterInterfaces(registry) + ibctm.RegisterInterfaces(registry) commitmenttypes.RegisterInterfaces(registry) } diff --git a/modules/light-clients/06-solomachine/client_state.go b/modules/light-clients/06-solomachine/client_state.go new file mode 100644 index 00000000000..29474f0d32b --- /dev/null +++ b/modules/light-clients/06-solomachine/client_state.go @@ -0,0 +1,260 @@ +package solomachine + +import ( + "reflect" + + "github.com/cosmos/cosmos-sdk/codec" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + + 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) + +// NewClientState creates a new ClientState instance. +func NewClientState(latestSequence uint64, consensusState *ConsensusState, allowUpdateAfterProposal bool) *ClientState { + return &ClientState{ + Sequence: latestSequence, + IsFrozen: false, + ConsensusState: consensusState, + AllowUpdateAfterProposal: allowUpdateAfterProposal, + } +} + +// ClientType is Solo Machine. +func (cs ClientState) ClientType() string { + return exported.Solomachine +} + +// GetLatestHeight returns the latest sequence number. +// Return exported.Height to satisfy ClientState interface +// Revision number is always 0 for a solo-machine. +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 +// - Frozen: otherwise solo machine is frozen +func (cs ClientState) Status(_ sdk.Context, _ sdk.KVStore, _ codec.BinaryCodec) exported.Status { + if cs.IsFrozen { + return exported.Frozen + } + + return exported.Active +} + +// Validate performs basic validation of the client state fields. +func (cs ClientState) Validate() error { + if cs.Sequence == 0 { + return sdkerrors.Wrap(clienttypes.ErrInvalidClient, "sequence cannot be 0") + } + if cs.ConsensusState == nil { + return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "consensus state cannot be nil") + } + return cs.ConsensusState.ValidateBasic() +} + +// ZeroCustomFields returns solomachine client state with client-specific fields FrozenSequence, +// and AllowUpdateAfterProposal zeroed out +func (cs ClientState) ZeroCustomFields() exported.ClientState { + return NewClientState( + cs.Sequence, cs.ConsensusState, false, + ) +} + +// Initialize will check that initial consensus state is equal to the latest consensus state of the initial client. +func (cs ClientState) Initialize(_ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, consState exported.ConsensusState) error { + if !reflect.DeepEqual(cs.ConsensusState, consState) { + return sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "consensus state in initial client does not equal initial consensus state. expected: %s, got: %s", + cs.ConsensusState, consState) + } + return nil +} + +// ExportMetadata is a no-op since solomachine does not store any metadata in client store +func (cs ClientState) ExportMetadata(_ sdk.KVStore) []exported.GenesisMetadata { + return nil +} + +// VerifyUpgradeAndUpdateState returns an error since solomachine client does not support upgrades +func (cs ClientState) VerifyUpgradeAndUpdateState( + _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, + _ exported.ClientState, _ exported.ConsensusState, _, _ []byte, +) error { + return sdkerrors.Wrap(clienttypes.ErrInvalidUpgradeClient, "cannot upgrade solomachine client") +} + +// 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 { + publicKey, sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, 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") + } + + signBytes := &SignBytes{ + Sequence: sequence, + Timestamp: timestamp, + Diversifier: cs.ConsensusState.Diversifier, + Path: []byte(merklePath.String()), + Data: value, + } + + signBz, err := cdc.Marshal(signBytes) + if err != nil { + return err + } + + if err := VerifySignature(publicKey, signBz, sigData); err != nil { + return err + } + + cs.Sequence++ + cs.ConsensusState.Timestamp = timestamp + setClientState(clientStore, cdc, cs) + + return nil +} + +// 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, + clientStore sdk.KVStore, + cdc codec.BinaryCodec, + height exported.Height, + delayTimePeriod uint64, + delayBlockPeriod uint64, + proof []byte, + path []byte, +) error { + publicKey, sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, 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") + } + + signBytes := &SignBytes{ + Sequence: sequence, + Timestamp: timestamp, + Diversifier: cs.ConsensusState.Diversifier, + Path: []byte(merklePath.String()), + Data: nil, + } + + signBz, err := cdc.Marshal(signBytes) + if err != nil { + return err + } + + if err := VerifySignature(publicKey, signBz, sigData); err != nil { + return err + } + + cs.Sequence++ + cs.ConsensusState.Timestamp = timestamp + setClientState(clientStore, cdc, cs) + + 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 +// along with the solo-machine sequence encoded in the proofHeight. +func produceVerificationArgs( + cdc codec.BinaryCodec, + cs *ClientState, + height exported.Height, + 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) + } + + if proof == nil { + return nil, nil, 0, 0, sdkerrors.Wrap(ErrInvalidProof, "proof cannot be empty") + } + + 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") + } + + sigData, err := UnmarshalSignatureData(cdc, timestampedSigData.SignatureData) + if err != nil { + return nil, nil, 0, 0, err + } + + // 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( + sdkerrors.ErrInvalidHeight, + "client state sequence != proof sequence (%d != %d)", latestSequence, sequence, + ) + } + + if cs.ConsensusState.GetTimestamp() > timestamp { + return nil, nil, 0, 0, sdkerrors.Wrapf(ErrInvalidProof, "the consensus state timestamp is greater than the signature timestamp (%d >= %d)", cs.ConsensusState.GetTimestamp(), timestamp) + } + + publicKey, err := cs.ConsensusState.GetPubKey() + if err != nil { + return nil, nil, 0, 0, err + } + + return publicKey, sigData, timestamp, sequence, nil +} + +// sets the client state to the store +func setClientState(store sdk.KVStore, cdc codec.BinaryCodec, clientState exported.ClientState) { + bz := clienttypes.MustMarshalClientState(cdc, clientState) + store.Set([]byte(host.KeyClientState), bz) +} diff --git a/modules/light-clients/06-solomachine/client_state_test.go b/modules/light-clients/06-solomachine/client_state_test.go new file mode 100644 index 00000000000..68bf15cace9 --- /dev/null +++ b/modules/light-clients/06-solomachine/client_state_test.go @@ -0,0 +1,875 @@ +package solomachine_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/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" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + solomachine "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" +) + +const ( + counterpartyClientIdentifier = "chainA" + testConnectionID = "connectionid" + testChannelID = "testchannelid" + testPortID = "testportid" +) + +func (suite *SoloMachineTestSuite) TestStatus() { + clientState := suite.solomachine.ClientState() + // solo machine discards arguments + status := clientState.Status(suite.chainA.GetContext(), nil, nil) + suite.Require().Equal(exported.Active, status) + + // freeze solo machine + clientState.IsFrozen = true + status = clientState.Status(suite.chainA.GetContext(), nil, nil) + suite.Require().Equal(exported.Frozen, status) +} + +func (suite *SoloMachineTestSuite) TestClientStateValidateBasic() { + // test singlesig and multisig public keys + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + testCases := []struct { + name string + clientState *solomachine.ClientState + expPass bool + }{ + { + "valid client state", + sm.ClientState(), + true, + }, + { + "empty ClientState", + &solomachine.ClientState{}, + false, + }, + { + "sequence is zero", + solomachine.NewClientState(0, &solomachine.ConsensusState{sm.ConsensusState().PublicKey, sm.Diversifier, sm.Time}, false), + false, + }, + { + "timestamp is zero", + solomachine.NewClientState(1, &solomachine.ConsensusState{sm.ConsensusState().PublicKey, sm.Diversifier, 0}, false), + false, + }, + { + "diversifier is blank", + solomachine.NewClientState(1, &solomachine.ConsensusState{sm.ConsensusState().PublicKey, " ", 1}, false), + false, + }, + { + "pubkey is empty", + solomachine.NewClientState(1, &solomachine.ConsensusState{nil, sm.Diversifier, sm.Time}, false), + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + + err := tc.clientState.Validate() + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } + } +} + +func (suite *SoloMachineTestSuite) TestInitialize() { + // test singlesig and multisig public keys + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + malleatedConsensus := sm.ClientState().ConsensusState + malleatedConsensus.Timestamp = malleatedConsensus.Timestamp + 10 + + testCases := []struct { + name string + consState exported.ConsensusState + expPass bool + }{ + { + "valid consensus state", + sm.ConsensusState(), + true, + }, + { + "nil consensus state", + nil, + false, + }, + { + "invalid consensus state: Tendermint consensus state", + &ibctm.ConsensusState{}, + false, + }, + { + "invalid consensus state: consensus state does not match consensus state in client", + malleatedConsensus, + false, + }, + } + + for _, tc := range testCases { + err := sm.ClientState().Initialize( + suite.chainA.GetContext(), suite.chainA.Codec, + suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "solomachine"), + 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 *SoloMachineTestSuite) TestVerifyMembership() { + // test singlesig and multisig public keys + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + var ( + clientState *solomachine.ClientState + err error + height clienttypes.Height + path []byte + proof []byte + testingPath *ibctesting.Path + signBytes solomachine.SignBytes + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() {}, + true, + }, + { + "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, + }, + { + "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, + }, + { + "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, + }, + { + "success: channel state verification", + func() { + suite.coordinator.SetupConnections(testingPath) + suite.coordinator.CreateMockChannels(testingPath) + + 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, + }, + { + "success: next sequence recv verification", + func() { + suite.coordinator.SetupConnections(testingPath) + suite.coordinator.CreateMockChannels(testingPath) + + nextSeqRecv, found := suite.chainA.GetSimApp().IBCKeeper.ChannelKeeper.GetNextSequenceRecv(suite.chainA.GetContext(), ibctesting.MockPort, ibctesting.FirstChannelID) + suite.Require().True(found) + + 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), + } + + 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, + }, + { + "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) + + 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, + }, + { + "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, + } + + 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, + }, + { + "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, + }, + { + "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, + }, + { + "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", + func() { + consensusState := &solomachine.ConsensusState{ + Timestamp: sm.Time + 1, + PublicKey: sm.ConsensusState().PublicKey, + } + + 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", + func() { + proof = nil + }, + false, + }, + { + "proof verification failed", + func() { + signBytes.Data = []byte("invalid membership data value") + }, + false, + }, + } + + for _, tc := range testCases { + 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"), + } + + 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) + + 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, 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) TestVerifyNonMembership() { + // test singlesig and multisig public keys + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + var ( + clientState *solomachine.ClientState + err error + height clienttypes.Height + path []byte + proof []byte + signBytes solomachine.SignBytes + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() {}, + true, + }, + { + "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, + } + + 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", + func() { + consensusState := &solomachine.ConsensusState{ + Timestamp: sm.Time, + PublicKey: sm.ConsensusState().PublicKey, + } + + 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", + func() { + consensusState := &solomachine.ConsensusState{ + Timestamp: sm.Time + 1, + PublicKey: sm.ConsensusState().PublicKey, + } + + 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", + func() { + proof = nil + }, + false, + }, + { + "proof verification failed", + func() { + signBytes.Data = []byte("invalid non-membership data value") + + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + + 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, + } + + 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) + + tc.malleate() + + var expSeq uint64 + if clientState.ConsensusState != nil { + expSeq = clientState.Sequence + 1 + } + + 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) + 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) + } + }) + } + } +} + +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 *solomachine.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/06-solomachine/codec.go b/modules/light-clients/06-solomachine/codec.go new file mode 100644 index 00000000000..2792e19c759 --- /dev/null +++ b/modules/light-clients/06-solomachine/codec.go @@ -0,0 +1,42 @@ +package solomachine + +import ( + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + + "github.com/cosmos/ibc-go/v5/modules/core/exported" +) + +// RegisterInterfaces register the ibc channel submodule interfaces to protobuf +// Any. +func RegisterInterfaces(registry codectypes.InterfaceRegistry) { + registry.RegisterImplementations( + (*exported.ClientState)(nil), + &ClientState{}, + ) + registry.RegisterImplementations( + (*exported.ConsensusState)(nil), + &ConsensusState{}, + ) + registry.RegisterImplementations( + (*exported.ClientMessage)(nil), + &Header{}, + ) + registry.RegisterImplementations( + (*exported.ClientMessage)(nil), + &Misbehaviour{}, + ) +} + +func UnmarshalSignatureData(cdc codec.BinaryCodec, data []byte) (signing.SignatureData, error) { + protoSigData := &signing.SignatureDescriptor_Data{} + if err := cdc.Unmarshal(data, protoSigData); err != nil { + return nil, sdkerrors.Wrapf(err, "failed to unmarshal proof into type %T", protoSigData) + } + + sigData := signing.SignatureDataFromProto(protoSigData) + + return sigData, nil +} diff --git a/modules/light-clients/06-solomachine/types/consensus_state.go b/modules/light-clients/06-solomachine/consensus_state.go similarity index 92% rename from modules/light-clients/06-solomachine/types/consensus_state.go rename to modules/light-clients/06-solomachine/consensus_state.go index 76a034b2760..1f63823663f 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" @@ -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/consensus_state_test.go similarity index 62% rename from modules/light-clients/06-solomachine/types/consensus_state_test.go rename to modules/light-clients/06-solomachine/consensus_state_test.go index 5a2f519af1e..6319c418931 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/v5/modules/core/exported" - "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine/types" + solomachine "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" ibctesting "github.com/cosmos/ibc-go/v5/testing" ) @@ -11,46 +11,45 @@ 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() { // 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 85% rename from modules/light-clients/06-solomachine/types/errors.go rename to modules/light-clients/06-solomachine/errors.go index 3e27f60732a..7f41349337d 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" @@ -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/types/header.go b/modules/light-clients/06-solomachine/header.go similarity index 85% rename from modules/light-clients/06-solomachine/types/header.go rename to modules/light-clients/06-solomachine/header.go index e22d0d605a9..b5bfe11f28d 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" @@ -10,20 +10,13 @@ import ( "github.com/cosmos/ibc-go/v5/modules/core/exported" ) -var _ exported.Header = &Header{} +var _ exported.ClientMessage = &Header{} // ClientType defines that the Header is a Solo Machine. 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/header_test.go b/modules/light-clients/06-solomachine/header_test.go similarity index 82% rename from modules/light-clients/06-solomachine/types/header_test.go rename to modules/light-clients/06-solomachine/header_test.go index fb771b4c684..1a0ce46ee33 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/v5/modules/core/exported" - "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine/types" + solomachine "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" ibctesting "github.com/cosmos/ibc-go/v5/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(sm.Diversifier) 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 83% rename from modules/light-clients/06-solomachine/types/misbehaviour.go rename to modules/light-clients/06-solomachine/misbehaviour.go index d13b1407be8..526fc76e839 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" @@ -10,24 +10,19 @@ import ( "github.com/cosmos/ibc-go/v5/modules/core/exported" ) -var _ exported.Misbehaviour = &Misbehaviour{} +var _ exported.ClientMessage = &Misbehaviour{} // ClientType is a Solo Machine light client. 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 Evidence interface. +// Type implements Misbehaviour interface. func (misbehaviour Misbehaviour) Type() string { return exported.TypeClientMisbehaviour } -// ValidateBasic implements Evidence interface. +// ValidateBasic implements Misbehaviour interface. func (misbehaviour Misbehaviour) ValidateBasic() error { if err := host.ClientIdentifierValidator(misbehaviour.ClientId); err != nil { return sdkerrors.Wrap(err, "invalid client identifier for solo machine") @@ -66,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 new file mode 100644 index 00000000000..e108931b7ea --- /dev/null +++ b/modules/light-clients/06-solomachine/misbehaviour_handle.go @@ -0,0 +1,46 @@ +package solomachine + +import ( + "github.com/cosmos/cosmos-sdk/codec" + + commitmenttypes "github.com/cosmos/ibc-go/v5/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 + if err := cdc.Unmarshal(sigAndData.Path, new(commitmenttypes.MerklePath)); err != nil { + return err + } + + 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 + } + + sigData, err := UnmarshalSignatureData(cdc, sigAndData.Signature) + if err != nil { + return err + } + + publicKey, err := cs.ConsensusState.GetPubKey() + if err != nil { + return err + } + + if err := VerifySignature(publicKey, data, sigData); err != nil { + return err + } + + return nil +} diff --git a/modules/light-clients/06-solomachine/types/misbehaviour_test.go b/modules/light-clients/06-solomachine/misbehaviour_test.go similarity index 63% rename from modules/light-clients/06-solomachine/types/misbehaviour_test.go rename to modules/light-clients/06-solomachine/misbehaviour_test.go index bdcdc664504..2e6044b67d1 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/v5/modules/core/exported" - "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine/types" + solomachine "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" ibctesting "github.com/cosmos/ibc-go/v5/testing" ) @@ -10,100 +10,99 @@ 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() { // 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 + "data path for SignatureOne is unspecified", + func(misbehaviour *solomachine.Misbehaviour) { + misbehaviour.SignatureOne.Path = []byte{} }, false, }, { - "data type for SignatureTwo is unspecified", - func(misbehaviour *types.Misbehaviour) { - misbehaviour.SignatureTwo.DataType = types.UNSPECIFIED + "data path for SignatureTwo is unspecified", + func(misbehaviour *solomachine.Misbehaviour) { + misbehaviour.SignatureTwo.Path = []byte{} }, 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 +112,8 @@ func (suite *SoloMachineTestSuite) TestMisbehaviourValidateBasic() { tc := tc 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 4acbc3d7a25..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/v5/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/proof.go b/modules/light-clients/06-solomachine/proof.go new file mode 100644 index 00000000000..386830340cf --- /dev/null +++ b/modules/light-clients/06-solomachine/proof.go @@ -0,0 +1,43 @@ +package solomachine + +import ( + 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" +) + +// VerifySignature verifies if the the provided public key generated the signature +// over the given data. Single and Multi signature public keys are supported. +// The signature data type must correspond to the public key type. An error is +// returned if signature verification fails or an invalid SignatureData type is +// provided. +func VerifySignature(pubKey cryptotypes.PubKey, signBytes []byte, sigData signing.SignatureData) error { + switch pubKey := pubKey.(type) { + case multisig.PubKey: + data, ok := sigData.(*signing.MultiSignatureData) + if !ok { + return sdkerrors.Wrapf(ErrSignatureVerificationFailed, "invalid signature data type, expected %T, got %T", (*signing.MultiSignatureData)(nil), data) + } + + // The function supplied fulfills the VerifyMultisignature interface. No special + // adjustments need to be made to the sign bytes based on the sign mode. + if err := pubKey.VerifyMultisignature(func(signing.SignMode) ([]byte, error) { + return signBytes, nil + }, data); err != nil { + return err + } + + default: + data, ok := sigData.(*signing.SingleSignatureData) + if !ok { + return sdkerrors.Wrapf(ErrSignatureVerificationFailed, "invalid signature data type, expected %T, got %T", (*signing.SingleSignatureData)(nil), data) + } + + if !pubKey.VerifySignature(signBytes, data.Signature) { + return ErrSignatureVerificationFailed + } + } + + return nil +} diff --git a/modules/light-clients/06-solomachine/proof_test.go b/modules/light-clients/06-solomachine/proof_test.go new file mode 100644 index 00000000000..2fa853d46a5 --- /dev/null +++ b/modules/light-clients/06-solomachine/proof_test.go @@ -0,0 +1,67 @@ +package solomachine_test + +import ( + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + + solomachine "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" +) + +func (suite *SoloMachineTestSuite) TestVerifySignature() { + cdc := suite.chainA.App.AppCodec() + signBytes := []byte("sign bytes") + + singleSignature := suite.solomachine.GenerateSignature(signBytes) + singleSigData, err := solomachine.UnmarshalSignatureData(cdc, singleSignature) + suite.Require().NoError(err) + + multiSignature := suite.solomachineMulti.GenerateSignature(signBytes) + multiSigData, err := solomachine.UnmarshalSignatureData(cdc, multiSignature) + suite.Require().NoError(err) + + testCases := []struct { + name string + publicKey cryptotypes.PubKey + sigData signing.SignatureData + expPass bool + }{ + { + "single signature with regular public key", + suite.solomachine.PublicKey, + singleSigData, + true, + }, + { + "multi signature with multisig public key", + suite.solomachineMulti.PublicKey, + multiSigData, + true, + }, + { + "single signature with multisig public key", + suite.solomachineMulti.PublicKey, + singleSigData, + false, + }, + { + "multi signature with regular public key", + suite.solomachine.PublicKey, + multiSigData, + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + err := solomachine.VerifySignature(tc.publicKey, signBytes, tc.sigData) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} diff --git a/modules/light-clients/06-solomachine/types/proposal_handle.go b/modules/light-clients/06-solomachine/proposal_handle.go similarity index 62% rename from modules/light-clients/06-solomachine/types/proposal_handle.go rename to modules/light-clients/06-solomachine/proposal_handle.go index 6cbcb2b3627..bef2db14299 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" @@ -21,43 +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, &cs) - return clientState, nil + return nil } 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 51% rename from modules/light-clients/06-solomachine/types/proposal_handle_test.go rename to modules/light-clients/06-solomachine/proposal_handle_test.go index 800f563f0b9..21a28564320 100644 --- a/modules/light-clients/06-solomachine/types/proposal_handle_test.go +++ b/modules/light-clients/06-solomachine/proposal_handle_test.go @@ -1,20 +1,22 @@ -package types_test +package solomachine_test import ( + 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" - "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine/types" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint/types" + solomachine "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/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 @@ -33,7 +35,7 @@ func (suite *SoloMachineTestSuite) TestCheckSubstituteAndUpdateState() { }, { "substitute is not the solo machine", func() { - substituteClientState = &ibctmtypes.ClientState{} + substituteClientState = &ibctm.ClientState{} }, false, }, { @@ -44,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, }, } @@ -60,27 +62,31 @@ 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) + err := subjectClientState.CheckSubstituteAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), subjectClientStore, substituteClientStore, substituteClientState) 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) + // ensure updated client state is set in store + bz := subjectClientStore.Get(host.ClientStateKey()) + 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/06-solomachine/types/solomachine.go b/modules/light-clients/06-solomachine/solomachine.go similarity index 69% rename from modules/light-clients/06-solomachine/types/solomachine.go rename to modules/light-clients/06-solomachine/solomachine.go index 2e7fc46307e..41511910bf4 100644 --- a/modules/light-clients/06-solomachine/types/solomachine.go +++ b/modules/light-clients/06-solomachine/solomachine.go @@ -1,10 +1,8 @@ -package types +package solomachine import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - - "github.com/cosmos/ibc-go/v5/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 new file mode 100644 index 00000000000..4714f24f568 --- /dev/null +++ b/modules/light-clients/06-solomachine/solomachine.pb.go @@ -0,0 +1,2350 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/lightclients/solomachine/v3/solomachine.proto + +package solomachine + +import ( + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/codec/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 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_264187157b9220a4, []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_264187157b9220a4, []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_264187157b9220a4, []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 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"` +} + +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_264187157b9220a4, []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"` + 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_264187157b9220a4, []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_264187157b9220a4, []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 { + // 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"` + // 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"` +} + +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_264187157b9220a4, []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_264187157b9220a4, []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 + +func init() { + 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/v3/solomachine.proto", fileDescriptor_264187157b9220a4) +} + +var fileDescriptor_264187157b9220a4 = []byte{ + // 784 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0x4d, 0x6f, 0xe3, 0x44, + 0x18, 0x8e, 0x5d, 0xb7, 0x4a, 0x26, 0x21, 0x2d, 0x56, 0x5a, 0xb9, 0x01, 0xc5, 0x91, 0x0f, 0xa8, + 0x97, 0xda, 0xb4, 0x11, 0x1c, 0xca, 0x85, 0xa6, 0x15, 0xe2, 0xa3, 0x88, 0xca, 0x6d, 0x2f, 0x5c, + 0xac, 0xb1, 0x3d, 0x71, 0x46, 0x38, 0x1e, 0xe3, 0x19, 0x27, 0x0a, 0xe2, 0x80, 0x38, 0x71, 0xe4, + 0xc2, 0x1d, 0x21, 0xf1, 0x3b, 0xb8, 0xb2, 0xb7, 0x1e, 0xf7, 0x14, 0xad, 0xda, 0x7f, 0x90, 0x5f, + 0xb0, 0xf2, 0xd8, 0x89, 0x3f, 0xda, 0x6d, 0xa5, 0xdd, 0xbd, 0xcd, 0xbc, 0x9f, 0xcf, 0xfb, 0xcc, + 0xf3, 0xda, 0xe0, 0x08, 0xdb, 0x8e, 0xe1, 0x63, 0x6f, 0xcc, 0x1c, 0x1f, 0xa3, 0x80, 0x51, 0x83, + 0x12, 0x9f, 0x4c, 0xa0, 0x33, 0xc6, 0x01, 0x32, 0xa6, 0x83, 0xe2, 0x55, 0x0f, 0x23, 0xc2, 0x88, + 0xac, 0x62, 0xdb, 0xd1, 0x8b, 0x29, 0x7a, 0x31, 0x66, 0x3a, 0xe8, 0x76, 0x3c, 0xe2, 0x11, 0x1e, + 0x6b, 0x24, 0xa7, 0x34, 0xad, 0xbb, 0xef, 0x11, 0xe2, 0xf9, 0xc8, 0xe0, 0x37, 0x3b, 0x1e, 0x19, + 0x30, 0x98, 0xa7, 0x2e, 0xed, 0x3f, 0x11, 0x34, 0xcf, 0x78, 0xad, 0x2b, 0x06, 0x19, 0x92, 0xbb, + 0xa0, 0x4e, 0xd1, 0xcf, 0x31, 0x0a, 0x1c, 0xa4, 0x08, 0x7d, 0xe1, 0x40, 0x32, 0xd7, 0x77, 0xf9, + 0x08, 0x34, 0x30, 0xb5, 0x46, 0x11, 0xf9, 0x05, 0x05, 0x8a, 0xd8, 0x17, 0x0e, 0xea, 0xc3, 0xce, + 0x72, 0xa1, 0xee, 0xcc, 0xe1, 0xc4, 0x3f, 0xd1, 0xd6, 0x2e, 0xcd, 0xac, 0x63, 0xfa, 0x15, 0x3f, + 0xca, 0x0c, 0x6c, 0x3b, 0x24, 0xa0, 0x28, 0xa0, 0x31, 0xb5, 0x68, 0xd2, 0x41, 0xd9, 0xe8, 0x0b, + 0x07, 0xcd, 0x63, 0x43, 0x7f, 0x66, 0x14, 0xfd, 0x6c, 0x95, 0xc7, 0x81, 0x0d, 0xbb, 0xcb, 0x85, + 0xba, 0x97, 0x76, 0xaa, 0x54, 0xd4, 0xcc, 0xb6, 0x53, 0x8a, 0x95, 0x11, 0xf8, 0x08, 0xfa, 0x3e, + 0x99, 0x59, 0x71, 0xe8, 0x42, 0x86, 0x2c, 0x38, 0x62, 0x28, 0xb2, 0xc2, 0x88, 0x84, 0x84, 0x42, + 0x5f, 0x91, 0x38, 0xf4, 0x4f, 0x96, 0x0b, 0x55, 0x4b, 0x0b, 0x3e, 0x11, 0xac, 0x99, 0x0a, 0xf7, + 0xde, 0x70, 0xe7, 0x69, 0xe2, 0xbb, 0xcc, 0x5c, 0x27, 0xd2, 0x1f, 0x7f, 0xab, 0x35, 0xed, 0x1f, + 0x01, 0xb4, 0xcb, 0x58, 0xe5, 0x6f, 0x01, 0x08, 0x63, 0xdb, 0xc7, 0x8e, 0xf5, 0x13, 0x9a, 0x73, + 0x1a, 0x9b, 0xc7, 0x1d, 0x3d, 0x7d, 0x04, 0x7d, 0xf5, 0x08, 0xfa, 0x69, 0x30, 0x1f, 0xee, 0x2e, + 0x17, 0xea, 0x87, 0x29, 0x88, 0x3c, 0x43, 0x33, 0x1b, 0xe9, 0xe5, 0x3b, 0x34, 0x97, 0xfb, 0xa0, + 0xe9, 0xe2, 0x29, 0x8a, 0x28, 0x1e, 0x61, 0x14, 0x71, 0xda, 0x1b, 0x66, 0xd1, 0x24, 0x7f, 0x0c, + 0x1a, 0x0c, 0x4f, 0x10, 0x65, 0x70, 0x12, 0x72, 0x76, 0x25, 0x33, 0x37, 0x64, 0x20, 0x7f, 0x17, + 0xc1, 0xd6, 0xd7, 0x08, 0xba, 0x28, 0x7a, 0xf2, 0x85, 0x4b, 0xa5, 0xc4, 0x4a, 0xa9, 0xc4, 0x4b, + 0xb1, 0x17, 0x40, 0x16, 0x47, 0xe9, 0x33, 0xb6, 0xcc, 0xdc, 0x20, 0xdf, 0x80, 0x76, 0x80, 0x66, + 0x56, 0x61, 0x70, 0xe9, 0x89, 0xc1, 0xf7, 0x97, 0x0b, 0x75, 0x37, 0x1d, 0xbc, 0x9c, 0xa5, 0x99, + 0xad, 0x00, 0xcd, 0x2e, 0xd7, 0xf3, 0x9f, 0x81, 0xed, 0x24, 0xa0, 0xc8, 0xc1, 0x66, 0xc2, 0x41, + 0x51, 0x10, 0x95, 0x00, 0xcd, 0x4c, 0x90, 0x9c, 0xe7, 0x86, 0x8c, 0x84, 0x17, 0x22, 0x68, 0x7d, + 0x8f, 0xa9, 0x8d, 0xc6, 0x70, 0x8a, 0x49, 0x1c, 0xc9, 0x03, 0xd0, 0x48, 0xc5, 0x67, 0x61, 0x97, + 0x73, 0xd1, 0x18, 0xee, 0xe5, 0x82, 0x5e, 0xbb, 0x34, 0x45, 0x30, 0xeb, 0xe9, 0xed, 0x1b, 0xb7, + 0xc4, 0x9f, 0x58, 0xe1, 0x2f, 0x04, 0x1f, 0xac, 0x09, 0xb1, 0x48, 0xb0, 0x12, 0xfb, 0xd1, 0xb3, + 0x62, 0xbf, 0x5a, 0x65, 0x9d, 0x06, 0xee, 0x39, 0x64, 0x70, 0xa8, 0x2c, 0x17, 0x6a, 0x27, 0xc5, + 0x51, 0xaa, 0xa8, 0x99, 0xad, 0xf5, 0xfd, 0x87, 0xa0, 0xd2, 0x91, 0xcd, 0x48, 0x46, 0xfa, 0xfb, + 0xea, 0xc8, 0x66, 0xa4, 0xd8, 0xf1, 0x7a, 0x46, 0x32, 0x2e, 0x7f, 0x05, 0x3b, 0xd5, 0x0a, 0x65, + 0x7d, 0x08, 0x55, 0x7d, 0xc8, 0x40, 0x0a, 0x21, 0x1b, 0x73, 0xce, 0x5a, 0x26, 0x3f, 0x27, 0x36, + 0x17, 0x32, 0x98, 0x89, 0x89, 0x9f, 0xcb, 0x1a, 0x94, 0x1e, 0x97, 0xf3, 0x6f, 0x02, 0x50, 0xae, + 0x57, 0x36, 0xe4, 0xae, 0x91, 0x70, 0x18, 0x5f, 0x82, 0x76, 0x3e, 0x00, 0x2f, 0xcf, 0xb1, 0x14, + 0x25, 0x57, 0xf6, 0x6b, 0x66, 0xce, 0xe1, 0xf9, 0x03, 0x08, 0xe2, 0xe3, 0x10, 0xfe, 0x12, 0x40, + 0x23, 0xe9, 0x3b, 0x9c, 0x33, 0x44, 0xdf, 0x61, 0xa9, 0x2a, 0xfb, 0xbd, 0xf1, 0x70, 0xbf, 0x57, + 0xc4, 0x49, 0x8f, 0x10, 0xb7, 0x99, 0x13, 0x97, 0xe1, 0xfa, 0x57, 0x00, 0x20, 0xdd, 0x74, 0x3e, + 0xca, 0x05, 0x68, 0x66, 0xfb, 0xf5, 0xec, 0xb7, 0x28, 0x91, 0xbe, 0x5c, 0x5a, 0xc9, 0xec, 0x63, + 0x94, 0xee, 0xe3, 0x1b, 0x96, 0x51, 0x7c, 0xbb, 0x65, 0x1c, 0x8e, 0xfe, 0xbf, 0xeb, 0x09, 0xb7, + 0x77, 0x3d, 0xe1, 0xd5, 0x5d, 0x4f, 0xf8, 0xf3, 0xbe, 0x57, 0xbb, 0xbd, 0xef, 0xd5, 0x5e, 0xde, + 0xf7, 0x6a, 0x3f, 0x5e, 0x78, 0x98, 0x8d, 0x63, 0x5b, 0x77, 0xc8, 0xc4, 0x70, 0x08, 0x9d, 0x10, + 0x6a, 0x60, 0xdb, 0x39, 0xf4, 0x88, 0x31, 0xfd, 0xcc, 0x98, 0x10, 0x37, 0xf6, 0x11, 0x4d, 0xff, + 0x9b, 0x87, 0xab, 0x1f, 0xe7, 0xa7, 0x9f, 0x1f, 0x16, 0xe4, 0xfd, 0x45, 0xe1, 0x6c, 0x6f, 0xf1, + 0x19, 0x07, 0xaf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x62, 0x3e, 0x62, 0x81, 0x6e, 0x07, 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 len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0x12 + } + 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 len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0x22 + } + 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 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)) + } + 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 *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 { + 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 != 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 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 != 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 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 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/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 c789df47228..c73a8ecebc4 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/v5/modules/core/24-host" "github.com/cosmos/ibc-go/v5/modules/core/exported" - "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine/types" + solomachine "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" ibctesting "github.com/cosmos/ibc-go/v5/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/client_state.go b/modules/light-clients/06-solomachine/types/client_state.go deleted file mode 100644 index e489486ed4f..00000000000 --- a/modules/light-clients/06-solomachine/types/client_state.go +++ /dev/null @@ -1,491 +0,0 @@ -package types - -import ( - "reflect" - - "github.com/cosmos/cosmos-sdk/codec" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/types/tx/signing" - - 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) - -// NewClientState creates a new ClientState instance. -func NewClientState(latestSequence uint64, consensusState *ConsensusState, allowUpdateAfterProposal bool) *ClientState { - return &ClientState{ - Sequence: latestSequence, - IsFrozen: false, - ConsensusState: consensusState, - AllowUpdateAfterProposal: allowUpdateAfterProposal, - } -} - -// ClientType is Solo Machine. -func (cs ClientState) ClientType() string { - return exported.Solomachine -} - -// GetLatestHeight returns the latest sequence number. -// Return exported.Height to satisfy ClientState interface -// Revision number is always 0 for a solo-machine. -func (cs ClientState) GetLatestHeight() exported.Height { - return clienttypes.NewHeight(0, cs.Sequence) -} - -// Status returns the status of the solo machine client. -// The client may be: -// - Active: if frozen sequence is 0 -// - Frozen: otherwise solo machine is frozen -func (cs ClientState) Status(_ sdk.Context, _ sdk.KVStore, _ codec.BinaryCodec) exported.Status { - if cs.IsFrozen { - return exported.Frozen - } - - return exported.Active -} - -// Validate performs basic validation of the client state fields. -func (cs ClientState) Validate() error { - if cs.Sequence == 0 { - return sdkerrors.Wrap(clienttypes.ErrInvalidClient, "sequence cannot be 0") - } - if cs.ConsensusState == nil { - return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "consensus state cannot be nil") - } - return cs.ConsensusState.ValidateBasic() -} - -// ZeroCustomFields returns solomachine client state with client-specific fields FrozenSequence, -// and AllowUpdateAfterProposal zeroed out -func (cs ClientState) ZeroCustomFields() exported.ClientState { - return NewClientState( - cs.Sequence, cs.ConsensusState, false, - ) -} - -// Initialize will check that initial consensus state is equal to the latest consensus state of the initial client. -func (cs ClientState) Initialize(_ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, consState exported.ConsensusState) error { - if !reflect.DeepEqual(cs.ConsensusState, consState) { - return sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "consensus state in initial client does not equal initial consensus state. expected: %s, got: %s", - cs.ConsensusState, consState) - } - return nil -} - -// ExportMetadata is a no-op since solomachine does not store any metadata in client store -func (cs ClientState) ExportMetadata(_ sdk.KVStore) []exported.GenesisMetadata { - return nil -} - -// VerifyUpgradeAndUpdateState returns an error since solomachine client does not support upgrades -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") -} - -// 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( - ctx sdk.Context, - store sdk.KVStore, - cdc codec.BinaryCodec, - height exported.Height, - _ uint64, - _ uint64, - prefix exported.Prefix, - proof []byte, - portID, - channelID string, - packetSequence uint64, - commitmentBytes []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) - 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 - } - - ackPath := commitmenttypes.NewMerklePath(host.PacketAcknowledgementPath(portID, channelID, packetSequence)) - path, err := commitmenttypes.ApplyPrefix(prefix, ackPath) - if err != nil { - return err - } - - signBz, err := PacketAcknowledgementSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path, acknowledgement) - 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 -} - -// 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, - cdc codec.BinaryCodec, - height exported.Height, - _ uint64, - _ uint64, - prefix exported.Prefix, - proof []byte, - portID, - channelID string, - packetSequence uint64, -) error { - publicKey, sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, 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 - } - - nextSequenceRecvPath := commitmenttypes.NewMerklePath(host.NextSequenceRecvPath(portID, channelID)) - path, err := commitmenttypes.ApplyPrefix(prefix, nextSequenceRecvPath) - if err != nil { - return err - } - - signBz, err := NextSequenceRecvSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path, nextSequenceRecv) - 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 -} - -// 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 -// along with the solo-machine sequence encoded in the proofHeight. -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 { - 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") - } - - sigData, err := UnmarshalSignatureData(cdc, timestampedSigData.SignatureData) - if err != nil { - return nil, nil, 0, 0, err - } - - if cs.ConsensusState == nil { - return nil, nil, 0, 0, sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "consensus state cannot be empty") - } - - latestSequence := cs.GetLatestHeight().GetRevisionHeight() - if latestSequence != sequence { - return nil, nil, 0, 0, sdkerrors.Wrapf( - sdkerrors.ErrInvalidHeight, - "client state sequence != proof sequence (%d != %d)", latestSequence, sequence, - ) - } - - if cs.ConsensusState.GetTimestamp() > timestamp { - return nil, nil, 0, 0, sdkerrors.Wrapf(ErrInvalidProof, "the consensus state timestamp is greater than the signature timestamp (%d >= %d)", cs.ConsensusState.GetTimestamp(), timestamp) - } - - publicKey, err := cs.ConsensusState.GetPubKey() - if err != nil { - return nil, nil, 0, 0, err - } - - return publicKey, sigData, timestamp, sequence, nil -} - -// sets the client state to the store -func setClientState(store sdk.KVStore, cdc codec.BinaryCodec, clientState exported.ClientState) { - bz := clienttypes.MustMarshalClientState(cdc, clientState) - store.Set([]byte(host.KeyClientState), bz) -} diff --git a/modules/light-clients/06-solomachine/types/client_state_test.go b/modules/light-clients/06-solomachine/types/client_state_test.go deleted file mode 100644 index 496688bb43d..00000000000 --- a/modules/light-clients/06-solomachine/types/client_state_test.go +++ /dev/null @@ -1,844 +0,0 @@ -package types_test - -import ( - 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/light-clients/06-solomachine/types" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/ibc-go/v5/testing" -) - -const ( - counterpartyClientIdentifier = "chainA" - testConnectionID = "connectionid" - testChannelID = "testchannelid" - testPortID = "testportid" -) - -var ( - prefix = &commitmenttypes.MerklePrefix{ - KeyPrefix: []byte("ibc"), - } - consensusHeight = clienttypes.ZeroHeight() -) - -func (suite *SoloMachineTestSuite) TestStatus() { - clientState := suite.solomachine.ClientState() - // solo machine discards arguments - status := clientState.Status(suite.chainA.GetContext(), nil, nil) - suite.Require().Equal(exported.Active, status) - - // freeze solo machine - clientState.IsFrozen = true - status = clientState.Status(suite.chainA.GetContext(), nil, nil) - suite.Require().Equal(exported.Frozen, status) -} - -func (suite *SoloMachineTestSuite) TestClientStateValidateBasic() { - // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - testCases := []struct { - name string - clientState *types.ClientState - expPass bool - }{ - { - "valid client state", - solomachine.ClientState(), - true, - }, - { - "empty ClientState", - &types.ClientState{}, - false, - }, - { - "sequence is zero", - types.NewClientState(0, &types.ConsensusState{solomachine.ConsensusState().PublicKey, solomachine.Diversifier, solomachine.Time}, false), - false, - }, - { - "timestamp is zero", - types.NewClientState(1, &types.ConsensusState{solomachine.ConsensusState().PublicKey, solomachine.Diversifier, 0}, false), - false, - }, - { - "diversifier is blank", - types.NewClientState(1, &types.ConsensusState{solomachine.ConsensusState().PublicKey, " ", 1}, false), - false, - }, - { - "pubkey is empty", - types.NewClientState(1, &types.ConsensusState{nil, solomachine.Diversifier, solomachine.Time}, false), - false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - err := tc.clientState.Validate() - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } - } -} - -func (suite *SoloMachineTestSuite) TestInitialize() { - // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - malleatedConsensus := solomachine.ClientState().ConsensusState - malleatedConsensus.Timestamp = malleatedConsensus.Timestamp + 10 - - testCases := []struct { - name string - consState exported.ConsensusState - expPass bool - }{ - { - "valid consensus state", - solomachine.ConsensusState(), - true, - }, - { - "nil consensus state", - nil, - false, - }, - { - "invalid consensus state: Tendermint consensus state", - &ibctmtypes.ConsensusState{}, - false, - }, - { - "invalid consensus state: consensus state does not match consensus state in client", - malleatedConsensus, - false, - }, - } - - for _, tc := range testCases { - err := solomachine.ClientState().Initialize( - suite.chainA.GetContext(), suite.chainA.Codec, - suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), "solomachine"), - 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 *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) - - // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - value, err := types.ClientStateSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, clientState) - suite.Require().NoError(err) - - sig := solomachine.GenerateSignature(value) - - signatureDoc := &types.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: solomachine.Time, - } - - proof, err := suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - - testCases := []struct { - name string - clientState *types.ClientState - prefix exported.Prefix - proof []byte - expPass bool - }{ - { - "successful verification", - solomachine.ClientState(), - prefix, - proof, - true, - }, - { - "ApplyPrefix failed", - solomachine.ClientState(), - nil, - proof, - false, - }, - { - "consensus state in client state is nil", - types.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, - }, false), - prefix, - proof, - false, - }, - { - "consensus state timestamp is greater than signature", - types.NewClientState(solomachine.Sequence, - &types.ConsensusState{ - Timestamp: solomachine.Time + 1, - PublicKey: solomachine.ConsensusState().PublicKey, - }, false), - prefix, - proof, - false, - }, - - { - "proof is nil", - solomachine.ClientState(), - prefix, - nil, - false, - }, - { - "proof verification failed", - solomachine.ClientState(), - prefix, - suite.GetInvalidProof(), - false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - var expSeq uint64 - if tc.clientState.ConsensusState != nil { - expSeq = tc.clientState.Sequence + 1 - } - - // 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) - - err := tc.clientState.VerifyClientState( - suite.store, suite.chainA.Codec, height, tc.prefix, counterpartyClientIdentifier, tc.proof, clientState, - ) - - if tc.expPass { - 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) - - path := suite.solomachine.GetConsensusStatePath(counterpartyClientIdentifier, consensusHeight) - - // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - value, err := types.ConsensusStateSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, consensusState) - suite.Require().NoError(err) - - sig := solomachine.GenerateSignature(value) - signatureDoc := &types.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: solomachine.Time, - } - - proof, err := suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - - testCases := []struct { - name string - clientState *types.ClientState - prefix exported.Prefix - proof []byte - expPass bool - }{ - { - "successful verification", - solomachine.ClientState(), - prefix, - proof, - true, - }, - { - "ApplyPrefix failed", - solomachine.ClientState(), - nil, - proof, - false, - }, - { - "consensus state in client state is nil", - types.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, - }, false), - prefix, - proof, - false, - }, - { - "consensus state timestamp is greater than signature", - types.NewClientState(solomachine.Sequence, - &types.ConsensusState{ - Timestamp: solomachine.Time + 1, - PublicKey: solomachine.ConsensusState().PublicKey, - }, false), - prefix, - proof, - false, - }, - - { - "proof is nil", - solomachine.ClientState(), - prefix, - nil, - false, - }, - { - "proof verification failed", - solomachine.ClientState(), - prefix, - suite.GetInvalidProof(), - false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - var expSeq uint64 - if tc.clientState.ConsensusState != nil { - expSeq = tc.clientState.Sequence + 1 - } - - // 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) - - err := tc.clientState.VerifyClientConsensusState( - suite.store, suite.chainA.Codec, height, counterpartyClientIdentifier, consensusHeight, tc.prefix, tc.proof, consensusState, - ) - - if tc.expPass { - 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) TestVerifyConnectionState() { - counterparty := connectiontypes.NewCounterparty("clientB", testConnectionID, *prefix) - conn := connectiontypes.NewConnectionEnd(connectiontypes.OPEN, "clientA", counterparty, connectiontypes.ExportedVersionsToProto(connectiontypes.GetCompatibleVersions()), 0) - - path := suite.solomachine.GetConnectionStatePath(testConnectionID) - - // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - value, err := types.ConnectionStateSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, conn) - suite.Require().NoError(err) - - sig := solomachine.GenerateSignature(value) - signatureDoc := &types.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: solomachine.Time, - } - - proof, err := suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - - testCases := []struct { - name string - clientState *types.ClientState - prefix exported.Prefix - proof []byte - expPass bool - }{ - { - "successful verification", - solomachine.ClientState(), - prefix, - proof, - true, - }, - { - "ApplyPrefix failed", - solomachine.ClientState(), - commitmenttypes.NewMerklePrefix([]byte{}), - proof, - false, - }, - { - "proof is nil", - solomachine.ClientState(), - prefix, - nil, - false, - }, - { - "proof verification failed", - solomachine.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, solomachine.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) - - // test singlesig and multisig public keys - for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - value, err := types.ChannelStateSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, ch) - suite.Require().NoError(err) - - sig := solomachine.GenerateSignature(value) - signatureDoc := &types.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: solomachine.Time, - } - - proof, err := suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - - testCases := []struct { - name string - clientState *types.ClientState - prefix exported.Prefix - proof []byte - expPass bool - }{ - { - "successful verification", - solomachine.ClientState(), - prefix, - proof, - true, - }, - { - "ApplyPrefix failed", - solomachine.ClientState(), - nil, - proof, - false, - }, - { - "proof is nil", - solomachine.ClientState(), - prefix, - nil, - false, - }, - { - "proof verification failed", - solomachine.ClientState(), - prefix, - suite.GetInvalidProof(), - false, - }, - } - - for i, tc := range testCases { - tc := tc - - expSeq := tc.clientState.Sequence + 1 - - err := tc.clientState.VerifyChannelState( - suite.store, suite.chainA.Codec, solomachine.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 _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - path := solomachine.GetPacketCommitmentPath(testPortID, testChannelID) - - value, err := types.PacketCommitmentSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, commitmentBytes) - suite.Require().NoError(err) - - sig := solomachine.GenerateSignature(value) - signatureDoc := &types.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: solomachine.Time, - } - - proof, err := suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - - testCases := []struct { - name string - clientState *types.ClientState - prefix exported.Prefix - proof []byte - expPass bool - }{ - { - "successful verification", - solomachine.ClientState(), - prefix, - proof, - true, - }, - { - "ApplyPrefix failed", - solomachine.ClientState(), - commitmenttypes.NewMerklePrefix([]byte{}), - proof, - false, - }, - { - "proof is nil", - solomachine.ClientState(), - prefix, - nil, - false, - }, - { - "proof verification failed", - solomachine.ClientState(), - prefix, - suite.GetInvalidProof(), - 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, solomachine.GetHeight(), 0, 0, tc.prefix, tc.proof, testPortID, testChannelID, solomachine.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 _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - path := solomachine.GetPacketAcknowledgementPath(testPortID, testChannelID) - - value, err := types.PacketAcknowledgementSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, ack) - suite.Require().NoError(err) - - sig := solomachine.GenerateSignature(value) - signatureDoc := &types.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: solomachine.Time, - } - - proof, err := suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - - testCases := []struct { - name string - clientState *types.ClientState - prefix exported.Prefix - proof []byte - expPass bool - }{ - { - "successful verification", - solomachine.ClientState(), - prefix, - proof, - true, - }, - { - "ApplyPrefix failed", - solomachine.ClientState(), - commitmenttypes.NewMerklePrefix([]byte{}), - proof, - false, - }, - { - "proof is nil", - solomachine.ClientState(), - prefix, - nil, - false, - }, - { - "proof verification failed", - solomachine.ClientState(), - prefix, - suite.GetInvalidProof(), - false, - }, - } - - for i, tc := range testCases { - tc := tc - - expSeq := tc.clientState.Sequence + 1 - 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, - ) - - 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 _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - // absence uses receipt path as well - path := solomachine.GetPacketReceiptPath(testPortID, testChannelID) - - value, err := types.PacketReceiptAbsenceSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path) - suite.Require().NoError(err) - - sig := solomachine.GenerateSignature(value) - signatureDoc := &types.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: solomachine.Time, - } - - proof, err := suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - - testCases := []struct { - name string - clientState *types.ClientState - prefix exported.Prefix - proof []byte - expPass bool - }{ - { - "successful verification", - solomachine.ClientState(), - prefix, - proof, - true, - }, - { - "ApplyPrefix failed", - solomachine.ClientState(), - commitmenttypes.NewMerklePrefix([]byte{}), - proof, - false, - }, - { - "proof is nil", - solomachine.ClientState(), - prefix, - nil, - false, - }, - { - "proof verification failed", - solomachine.ClientState(), - prefix, - suite.GetInvalidProof(), - false, - }, - } - - for i, tc := range testCases { - tc := tc - - expSeq := tc.clientState.Sequence + 1 - 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, - ) - - 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 _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - nextSeqRecv := solomachine.Sequence + 1 - path := solomachine.GetNextSequenceRecvPath(testPortID, testChannelID) - - value, err := types.NextSequenceRecvSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, nextSeqRecv) - suite.Require().NoError(err) - - sig := solomachine.GenerateSignature(value) - signatureDoc := &types.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: solomachine.Time, - } - - proof, err := suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - - testCases := []struct { - name string - clientState *types.ClientState - prefix exported.Prefix - proof []byte - expPass bool - }{ - { - "successful verification", - solomachine.ClientState(), - prefix, - proof, - true, - }, - { - "ApplyPrefix failed", - solomachine.ClientState(), - commitmenttypes.NewMerklePrefix([]byte{}), - proof, - false, - }, - { - "proof is nil", - solomachine.ClientState(), - prefix, - nil, - false, - }, - { - "proof verification failed", - solomachine.ClientState(), - prefix, - suite.GetInvalidProof(), - false, - }, - } - - for i, tc := range testCases { - tc := tc - - expSeq := tc.clientState.Sequence + 1 - 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, - ) - - 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) - } - } - } -} diff --git a/modules/light-clients/06-solomachine/types/codec.go b/modules/light-clients/06-solomachine/types/codec.go deleted file mode 100644 index 3946b3ee2b5..00000000000 --- a/modules/light-clients/06-solomachine/types/codec.go +++ /dev/null @@ -1,131 +0,0 @@ -package types - -import ( - "github.com/cosmos/cosmos-sdk/codec" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/types/tx/signing" - - clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v5/modules/core/exported" -) - -// RegisterInterfaces register the ibc channel submodule interfaces to protobuf -// Any. -func RegisterInterfaces(registry codectypes.InterfaceRegistry) { - registry.RegisterImplementations( - (*exported.ClientState)(nil), - &ClientState{}, - ) - registry.RegisterImplementations( - (*exported.ConsensusState)(nil), - &ConsensusState{}, - ) - registry.RegisterImplementations( - (*exported.Header)(nil), - &Header{}, - ) - registry.RegisterImplementations( - (*exported.Misbehaviour)(nil), - &Misbehaviour{}, - ) -} - -func UnmarshalSignatureData(cdc codec.BinaryCodec, data []byte) (signing.SignatureData, error) { - protoSigData := &signing.SignatureDescriptor_Data{} - if err := cdc.Unmarshal(data, protoSigData); err != nil { - return nil, sdkerrors.Wrapf(err, "failed to unmarshal proof into type %T", protoSigData) - } - - sigData := signing.SignatureDataFromProto(protoSigData) - - 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/types/codec_test.go b/modules/light-clients/06-solomachine/types/codec_test.go deleted file mode 100644 index bef62d2654d..00000000000 --- a/modules/light-clients/06-solomachine/types/codec_test.go +++ /dev/null @@ -1,186 +0,0 @@ -package types_test - -import ( - 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" - "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine/types" - ibctesting "github.com/cosmos/ibc-go/v5/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/misbehaviour_handle.go b/modules/light-clients/06-solomachine/types/misbehaviour_handle.go deleted file mode 100644 index 805847e9b21..00000000000 --- a/modules/light-clients/06-solomachine/types/misbehaviour_handle.go +++ /dev/null @@ -1,86 +0,0 @@ -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/v5/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v5/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.Misbehaviour, -) (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 := verifySignatureAndData(cdc, cs, 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 { - 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. -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 - - // ensure data can be unmarshaled to the specified data type - if _, err := UnmarshalDataByType(cdc, sigAndData.DataType, sigAndData.Data); err != nil { - return err - } - - data, err := MisbehaviourSignBytes( - cdc, - misbehaviour.Sequence, sigAndData.Timestamp, - clientState.ConsensusState.Diversifier, - sigAndData.DataType, - sigAndData.Data, - ) - if err != nil { - return err - } - - sigData, err := UnmarshalSignatureData(cdc, sigAndData.Signature) - if err != nil { - return err - } - - publicKey, err := clientState.ConsensusState.GetPubKey() - if err != nil { - return err - } - - if err := VerifySignature(publicKey, data, sigData); err != nil { - return err - } - - return nil -} 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 945aff3c481..00000000000 --- a/modules/light-clients/06-solomachine/types/misbehaviour_handle_test.go +++ /dev/null @@ -1,264 +0,0 @@ -package types_test - -import ( - "github.com/cosmos/ibc-go/v5/modules/core/exported" - "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine/types" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/ibc-go/v5/testing" -) - -func (suite *SoloMachineTestSuite) TestCheckMisbehaviourAndUpdateState() { - var ( - clientState exported.ClientState - misbehaviour exported.Misbehaviour - ) - - // 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/06-solomachine/types/proof.go b/modules/light-clients/06-solomachine/types/proof.go deleted file mode 100644 index 35ce4d9cd54..00000000000 --- a/modules/light-clients/06-solomachine/types/proof.go +++ /dev/null @@ -1,477 +0,0 @@ -package types - -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/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" -) - -// VerifySignature verifies if the the provided public key generated the signature -// over the given data. Single and Multi signature public keys are supported. -// The signature data type must correspond to the public key type. An error is -// returned if signature verification fails or an invalid SignatureData type is -// provided. -func VerifySignature(pubKey cryptotypes.PubKey, signBytes []byte, sigData signing.SignatureData) error { - switch pubKey := pubKey.(type) { - case multisig.PubKey: - data, ok := sigData.(*signing.MultiSignatureData) - if !ok { - return sdkerrors.Wrapf(ErrSignatureVerificationFailed, "invalid signature data type, expected %T, got %T", (*signing.MultiSignatureData)(nil), data) - } - - // The function supplied fulfills the VerifyMultisignature interface. No special - // adjustments need to be made to the sign bytes based on the sign mode. - if err := pubKey.VerifyMultisignature(func(signing.SignMode) ([]byte, error) { - return signBytes, nil - }, data); err != nil { - return err - } - - default: - data, ok := sigData.(*signing.SingleSignatureData) - if !ok { - return sdkerrors.Wrapf(ErrSignatureVerificationFailed, "invalid signature data type, expected %T, got %T", (*signing.SingleSignatureData)(nil), data) - } - - if !pubKey.VerifySignature(signBytes, data.Signature) { - return ErrSignatureVerificationFailed - } - } - - 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/types/proof_test.go b/modules/light-clients/06-solomachine/types/proof_test.go deleted file mode 100644 index 6a79998ab33..00000000000 --- a/modules/light-clients/06-solomachine/types/proof_test.go +++ /dev/null @@ -1,103 +0,0 @@ -package types_test - -import ( - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - "github.com/cosmos/cosmos-sdk/types/tx/signing" - - "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine/types" - solomachinetypes "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine/types" - ibctesting "github.com/cosmos/ibc-go/v5/testing" -) - -func (suite *SoloMachineTestSuite) TestVerifySignature() { - cdc := suite.chainA.App.AppCodec() - signBytes := []byte("sign bytes") - - singleSignature := suite.solomachine.GenerateSignature(signBytes) - singleSigData, err := solomachinetypes.UnmarshalSignatureData(cdc, singleSignature) - suite.Require().NoError(err) - - multiSignature := suite.solomachineMulti.GenerateSignature(signBytes) - multiSigData, err := solomachinetypes.UnmarshalSignatureData(cdc, multiSignature) - suite.Require().NoError(err) - - testCases := []struct { - name string - publicKey cryptotypes.PubKey - sigData signing.SignatureData - expPass bool - }{ - { - "single signature with regular public key", - suite.solomachine.PublicKey, - singleSigData, - true, - }, - { - "multi signature with multisig public key", - suite.solomachineMulti.PublicKey, - multiSigData, - true, - }, - { - "single signature with multisig public key", - suite.solomachineMulti.PublicKey, - singleSigData, - false, - }, - { - "multi signature with regular public key", - suite.solomachine.PublicKey, - multiSigData, - false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - err := solomachinetypes.VerifySignature(tc.publicKey, signBytes, tc.sigData) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} - -func (suite *SoloMachineTestSuite) TestClientStateSignBytes() { - cdc := suite.chainA.App.AppCodec() - - for _, solomachine := 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()) - suite.Require().NoError(err) - suite.Require().NotNil(bz) - - // nil client state - bz, err = types.ClientStateSignBytes(cdc, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, nil) - suite.Require().Error(err) - suite.Require().Nil(bz) - } -} - -func (suite *SoloMachineTestSuite) TestConsensusStateSignBytes() { - cdc := suite.chainA.App.AppCodec() - - for _, solomachine := 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()) - suite.Require().NoError(err) - suite.Require().NotNil(bz) - - // nil consensus state - bz, err = types.ConsensusStateSignBytes(cdc, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, nil) - suite.Require().Error(err) - suite.Require().Nil(bz) - } -} diff --git a/modules/light-clients/06-solomachine/types/update.go b/modules/light-clients/06-solomachine/types/update.go deleted file mode 100644 index 38db4a6b9cd..00000000000 --- a/modules/light-clients/06-solomachine/types/update.go +++ /dev/null @@ -1,90 +0,0 @@ -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/v5/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v5/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, - header exported.Header, -) (exported.ClientState, exported.ConsensusState, error) { - smHeader, ok := header.(*Header) - if !ok { - return nil, nil, sdkerrors.Wrapf( - clienttypes.ErrInvalidHeader, "header type %T, expected %T", header, &Header{}, - ) - } - - 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 { - // assert update sequence is current sequence - if header.Sequence != clientState.Sequence { - return sdkerrors.Wrapf( - clienttypes.ErrInvalidHeader, - "header sequence does not match the client state sequence (%d != %d)", header.Sequence, clientState.Sequence, - ) - } - - // assert update timestamp is not less than current consensus state timestamp - if header.Timestamp < clientState.ConsensusState.Timestamp { - return sdkerrors.Wrapf( - clienttypes.ErrInvalidHeader, - "header timestamp is less than to the consensus state timestamp (%d < %d)", header.Timestamp, clientState.ConsensusState.Timestamp, - ) - } - - // assert currently registered public key signed over the new public key with correct sequence - data, err := HeaderSignBytes(cdc, header) - if err != nil { - return err - } - - sigData, err := UnmarshalSignatureData(cdc, header.Signature) - if err != nil { - return err - } - - publicKey, err := clientState.ConsensusState.GetPubKey() - if err != nil { - return err - } - - if err := VerifySignature(publicKey, data, sigData); err != nil { - return sdkerrors.Wrap(ErrInvalidHeader, err.Error()) - } - - 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{ - PublicKey: header.NewPublicKey, - Diversifier: header.NewDiversifier, - Timestamp: header.Timestamp, - } - - // increment sequence number - clientState.Sequence++ - clientState.ConsensusState = consensusState - return clientState, consensusState -} diff --git a/modules/light-clients/06-solomachine/types/update_test.go b/modules/light-clients/06-solomachine/types/update_test.go deleted file mode 100644 index f1bcaac9d98..00000000000 --- a/modules/light-clients/06-solomachine/types/update_test.go +++ /dev/null @@ -1,181 +0,0 @@ -package types_test - -import ( - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/cosmos/ibc-go/v5/modules/core/exported" - "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine/types" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/ibc-go/v5/testing" -) - -func (suite *SoloMachineTestSuite) TestCheckHeaderAndUpdateState() { - var ( - clientState exported.ClientState - header exported.Header - ) - - // 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) - } - }) - } - } -} diff --git a/modules/light-clients/06-solomachine/update.go b/modules/light-clients/06-solomachine/update.go new file mode 100644 index 00000000000..4594363a5af --- /dev/null +++ b/modules/light-clients/06-solomachine/update.go @@ -0,0 +1,140 @@ +package solomachine + +import ( + "fmt" + + "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/v5/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v5/modules/core/exported" +) + +// 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 != cs.Sequence { + return sdkerrors.Wrapf( + clienttypes.ErrInvalidHeader, + "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 < cs.ConsensusState.Timestamp { + return sdkerrors.Wrapf( + clienttypes.ErrInvalidHeader, + "header timestamp is less than to the consensus state timestamp (%d < %d)", header.Timestamp, cs.ConsensusState.Timestamp, + ) + } + + // assert currently registered public key signed over the new public key with correct sequence + 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 + } + + sigData, err := UnmarshalSignatureData(cdc, header.Signature) + if err != nil { + return err + } + + publicKey, err := cs.ConsensusState.GetPubKey() + if err != nil { + return err + } + + if err := VerifySignature(publicKey, data, sigData); err != nil { + return sdkerrors.Wrap(ErrInvalidHeader, err.Error()) + } + + 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 := cs.verifySignatureAndData(cdc, misbehaviour, misbehaviour.SignatureOne); err != nil { + return sdkerrors.Wrap(err, "failed to verify signature one") + } + + // verify second signature + if err := cs.verifySignatureAndData(cdc, misbehaviour, misbehaviour.SignatureTwo); err != nil { + return sdkerrors.Wrap(err, "failed to verify signature two") + } + + return nil +} + +// UpdateState updates the consensus state to the new public key and an incremented sequence. +// 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 { + panic(fmt.Errorf("unsupported ClientMessage: %T", clientMsg)) + } + + // create new solomachine ConsensusState + consensusState := &ConsensusState{ + PublicKey: smHeader.NewPublicKey, + Diversifier: smHeader.NewDiversifier, + Timestamp: smHeader.Timestamp, + } + + cs.Sequence++ + cs.ConsensusState = consensusState + + setClientState(clientStore, cdc, &cs) + + return []exported.Height{clienttypes.NewHeight(0, cs.Sequence)} +} + +// 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(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, _ exported.ClientMessage) { + cs.IsFrozen = true + + setClientState(clientStore, cdc, &cs) +} diff --git a/modules/light-clients/06-solomachine/update_test.go b/modules/light-clients/06-solomachine/update_test.go new file mode 100644 index 00000000000..12ef195b220 --- /dev/null +++ b/modules/light-clients/06-solomachine/update_test.go @@ -0,0 +1,564 @@ +package solomachine_test + +import ( + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/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" + solomachine "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v5/testing" +) + +func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { + var ( + clientMsg exported.ClientMessage + clientState *solomachine.ClientState + ) + + // test singlesig and multisig public keys + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + testCases := []struct { + name string + setup func() + expPass bool + }{ + { + "successful header", + func() { + clientMsg = sm.CreateHeader(sm.Diversifier) + }, + true, + }, + { + "successful header with new diversifier", + func() { + clientMsg = sm.CreateHeader(sm.Diversifier + "0") + }, + true, + }, + { + "successful misbehaviour", + func() { + clientMsg = sm.CreateMisbehaviour() + }, + true, + }, + { + "invalid client message type", + func() { + clientMsg = &ibctm.Header{} + }, + false, + }, + { + "wrong sequence in header", + func() { + // store in temp before assigning to interface type + h := sm.CreateHeader(sm.Diversifier) + h.Sequence++ + clientMsg = h + }, + false, + }, + { + "invalid header Signature", + func() { + h := sm.CreateHeader(sm.Diversifier) + h.Signature = suite.GetInvalidProof() + clientMsg = h + }, false, + }, + { + "invalid timestamp in header", + func() { + h := sm.CreateHeader(sm.Diversifier) + h.Timestamp-- + clientMsg = h + }, false, + }, + { + "signature uses wrong sequence", + func() { + + sm.Sequence++ + clientMsg = sm.CreateHeader(sm.Diversifier) + }, + false, + }, + { + "signature uses new pubkey to sign", + func() { + // store in temp before assinging to interface type + cs := sm.ClientState() + h := sm.CreateHeader(sm.Diversifier) + + publicKey, err := codectypes.NewAnyWithValue(sm.PublicKey) + suite.NoError(err) + + data := &solomachine.HeaderData{ + NewPubKey: publicKey, + NewDiversifier: h.NewDiversifier, + } + + dataBz, err := suite.chainA.Codec.Marshal(data) + suite.Require().NoError(err) + + // generate invalid signature + signBytes := &solomachine.SignBytes{ + Sequence: cs.Sequence, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte("invalid signature data"), + Data: dataBz, + } + + signBz, err := suite.chainA.Codec.Marshal(signBytes) + suite.Require().NoError(err) + + sig := sm.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 := sm.ClientState() + oldPubKey := sm.PublicKey + h := sm.CreateHeader(sm.Diversifier) + + // generate invalid signature + data := append(sdk.Uint64ToBigEndian(cs.Sequence), oldPubKey.Bytes()...) + sig := sm.GenerateSignature(data) + h.Signature = sig + + clientState = cs + clientMsg = h + }, + false, + }, + { + "consensus state public key is nil - header", + func() { + clientState.ConsensusState.PublicKey = nil + clientMsg = sm.CreateHeader(sm.Diversifier) + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + clientState = sm.ClientState() + + // setup test + tc.setup() + + err := clientState.VerifyClientMessage(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, clientMsg) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } + } +} + +func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { + var ( + clientMsg exported.ClientMessage + clientState *solomachine.ClientState + ) + + // test singlesig and multisig public keys + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + testCases := []struct { + name string + setup func() + expPass bool + }{ + { + "successful misbehaviour", + func() { + clientMsg = sm.CreateMisbehaviour() + }, + true, + }, + { + "old misbehaviour is successful (timestamp is less than current consensus state)", + func() { + clientState = sm.ClientState() + sm.Time = sm.Time - 5 + clientMsg = sm.CreateMisbehaviour() + }, true, + }, + { + "invalid client message type", + func() { + clientMsg = &ibctm.Header{} + }, + false, + }, + { + "consensus state pubkey is nil", + func() { + clientState.ConsensusState.PublicKey = nil + clientMsg = sm.CreateMisbehaviour() + }, + false, + }, + { + "invalid SignatureOne SignatureData", + func() { + m := sm.CreateMisbehaviour() + + m.SignatureOne.Signature = suite.GetInvalidProof() + clientMsg = m + }, false, + }, + { + "invalid SignatureTwo SignatureData", + func() { + m := sm.CreateMisbehaviour() + + m.SignatureTwo.Signature = suite.GetInvalidProof() + clientMsg = m + }, false, + }, + { + "invalid SignatureOne timestamp", + func() { + m := sm.CreateMisbehaviour() + + m.SignatureOne.Timestamp = 1000000000000 + clientMsg = m + }, false, + }, + { + "invalid SignatureTwo timestamp", + func() { + m := sm.CreateMisbehaviour() + + m.SignatureTwo.Timestamp = 1000000000000 + clientMsg = m + }, false, + }, + { + "invalid first signature data", + func() { + // store in temp before assigning to interface type + m := sm.CreateMisbehaviour() + + msg := []byte("DATA ONE") + signBytes := &solomachine.SignBytes{ + Sequence: sm.Sequence + 1, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte("invalid signature data"), + Data: msg, + } + + data, err := suite.chainA.Codec.Marshal(signBytes) + suite.Require().NoError(err) + + sig := sm.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 := sm.CreateMisbehaviour() + + msg := []byte("DATA TWO") + signBytes := &solomachine.SignBytes{ + Sequence: sm.Sequence + 1, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte("invalid signature data"), + Data: msg, + } + + data, err := suite.chainA.Codec.Marshal(signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(data) + + m.SignatureTwo.Signature = sig + m.SignatureTwo.Data = msg + clientMsg = m + }, + false, + }, + { + "wrong pubkey generates first signature", + func() { + badMisbehaviour := sm.CreateMisbehaviour() + + // update public key to a new one + sm.CreateHeader(sm.Diversifier) + m := sm.CreateMisbehaviour() + + // set SignatureOne to use the wrong signature + m.SignatureOne = badMisbehaviour.SignatureOne + clientMsg = m + }, false, + }, + { + "wrong pubkey generates second signature", + func() { + badMisbehaviour := sm.CreateMisbehaviour() + + // update public key to a new one + sm.CreateHeader(sm.Diversifier) + m := sm.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 := sm.CreateMisbehaviour() + + // Signature One + msg := []byte("DATA ONE") + // sequence used is plus 1 + signBytes := &solomachine.SignBytes{ + Sequence: sm.Sequence + 1, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte("invalid signature data"), + Data: msg, + } + + data, err := suite.chainA.Codec.Marshal(signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(data) + + m.SignatureOne.Signature = sig + m.SignatureOne.Data = msg + + // Signature Two + msg = []byte("DATA TWO") + // sequence used is minus 1 + + signBytes = &solomachine.SignBytes{ + Sequence: sm.Sequence - 1, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte("invalid signature data"), + Data: msg, + } + data, err = suite.chainA.Codec.Marshal(signBytes) + suite.Require().NoError(err) + + sig = sm.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 = sm.ClientState() + + // setup test + tc.setup() + + err := clientState.VerifyClientMessage(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, clientMsg) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } + } +} + +func (suite *SoloMachineTestSuite) TestUpdateState() { + var ( + clientState exported.ClientState + clientMsg exported.ClientMessage + ) + + // test singlesig and multisig public keys + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + testCases := []struct { + name string + setup func() + expPass bool + }{ + { + "successful update", + func() { + clientState = sm.ClientState() + clientMsg = sm.CreateHeader(sm.Diversifier) + }, + true, + }, + { + "invalid type misbehaviour", + func() { + clientState = sm.ClientState() + clientMsg = sm.CreateMisbehaviour() + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + tc.setup() // setup test + + if tc.expPass { + 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.(*solomachine.ClientState).Sequence, consensusHeights[0].GetRevisionHeight()) + + 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) + }) + } + + }) + } + } +} + +func (suite *SoloMachineTestSuite) TestCheckForMisbehaviour() { + var ( + clientMsg exported.ClientMessage + ) + + // test singlesig and multisig public keys + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() { + clientMsg = sm.CreateMisbehaviour() + }, + true, + }, + { + "normal header returns false", + func() { + clientMsg = sm.CreateHeader(sm.Diversifier) + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + clientState := sm.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 _, sm := 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 := sm.ClientState() + + tc.malleate() + + clientState.UpdateStateOnMisbehaviour(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, nil) + + if tc.expPass { + clientStateBz := suite.store.Get(host.ClientStateKey()) + suite.Require().NotEmpty(clientStateBz) + + newClientState := clienttypes.MustUnmarshalClientState(suite.chainA.Codec, clientStateBz) + + suite.Require().True(newClientState.(*solomachine.ClientState).IsFrozen) + } + }) + } + } +} diff --git a/modules/light-clients/07-tendermint/client_state.go b/modules/light-clients/07-tendermint/client_state.go new file mode 100644 index 00000000000..0c3715e4db1 --- /dev/null +++ b/modules/light-clients/07-tendermint/client_state.go @@ -0,0 +1,335 @@ +package tendermint + +import ( + "strings" + "time" + + ics23 "github.com/confio/ics23/go" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/tendermint/tendermint/light" + 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" + "github.com/cosmos/ibc-go/v5/modules/core/exported" +) + +var _ exported.ClientState = (*ClientState)(nil) + +// NewClientState creates a new ClientState instance +func NewClientState( + chainID string, trustLevel Fraction, + trustingPeriod, ubdPeriod, maxClockDrift time.Duration, + latestHeight clienttypes.Height, specs []*ics23.ProofSpec, + upgradePath []string, allowUpdateAfterExpiry, allowUpdateAfterMisbehaviour bool, +) *ClientState { + return &ClientState{ + ChainId: chainID, + TrustLevel: trustLevel, + TrustingPeriod: trustingPeriod, + UnbondingPeriod: ubdPeriod, + MaxClockDrift: maxClockDrift, + LatestHeight: latestHeight, + FrozenHeight: clienttypes.ZeroHeight(), + ProofSpecs: specs, + UpgradePath: upgradePath, + AllowUpdateAfterExpiry: allowUpdateAfterExpiry, + AllowUpdateAfterMisbehaviour: allowUpdateAfterMisbehaviour, + } +} + +// GetChainID returns the chain-id +func (cs ClientState) GetChainID() string { + return cs.ChainId +} + +// ClientType is tendermint. +func (cs ClientState) ClientType() string { + return exported.Tendermint +} + +// GetLatestHeight returns latest block height. +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 +// - Frozen: Frozen Height is not zero +// - Expired: the latest consensus state timestamp + trusting period <= current time +// +// A frozen client will become expired, so the Frozen status +// has higher precedence. +func (cs ClientState) Status( + ctx sdk.Context, + clientStore sdk.KVStore, + cdc codec.BinaryCodec, +) exported.Status { + if !cs.FrozenHeight.IsZero() { + return exported.Frozen + } + + // get latest consensus state from clientStore to check for expiry + 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 + } + + if cs.IsExpired(consState.Timestamp, ctx.BlockTime()) { + return exported.Expired + } + + return exported.Active +} + +// IsExpired returns whether or not the client has passed the trusting period since the last +// update (in which case no headers are considered valid). +func (cs ClientState) IsExpired(latestTimestamp, now time.Time) bool { + expirationTime := latestTimestamp.Add(cs.TrustingPeriod) + return !expirationTime.After(now) +} + +// Validate performs a basic validation of the client state fields. +func (cs ClientState) Validate() error { + if strings.TrimSpace(cs.ChainId) == "" { + return sdkerrors.Wrap(ErrInvalidChainID, "chain id cannot be empty string") + } + + // NOTE: the value of tmtypes.MaxChainIDLen may change in the future. + // If this occurs, the code here must account for potential difference + // between the tendermint version being run by the counterparty chain + // and the tendermint version used by this light client. + // https://github.com/cosmos/ibc-go/issues/177 + if len(cs.ChainId) > tmtypes.MaxChainIDLen { + return sdkerrors.Wrapf(ErrInvalidChainID, "chainID is too long; got: %d, max: %d", len(cs.ChainId), tmtypes.MaxChainIDLen) + } + + if err := light.ValidateTrustLevel(cs.TrustLevel.ToTendermint()); err != nil { + return err + } + if cs.TrustingPeriod == 0 { + return sdkerrors.Wrap(ErrInvalidTrustingPeriod, "trusting period cannot be zero") + } + if cs.UnbondingPeriod == 0 { + return sdkerrors.Wrap(ErrInvalidUnbondingPeriod, "unbonding period cannot be zero") + } + if cs.MaxClockDrift == 0 { + return sdkerrors.Wrap(ErrInvalidMaxClockDrift, "max clock drift cannot be zero") + } + + // the latest height revision number must match the chain id revision number + if cs.LatestHeight.RevisionNumber != clienttypes.ParseChainID(cs.ChainId) { + return sdkerrors.Wrapf(ErrInvalidHeaderHeight, + "latest height revision number must match chain id revision number (%d != %d)", cs.LatestHeight.RevisionNumber, clienttypes.ParseChainID(cs.ChainId)) + } + if cs.LatestHeight.RevisionHeight == 0 { + return sdkerrors.Wrapf(ErrInvalidHeaderHeight, "tendermint client's latest height revision height cannot be zero") + } + if cs.TrustingPeriod >= cs.UnbondingPeriod { + return sdkerrors.Wrapf( + ErrInvalidTrustingPeriod, + "trusting period (%s) should be < unbonding period (%s)", cs.TrustingPeriod, cs.UnbondingPeriod, + ) + } + + if cs.ProofSpecs == nil { + return sdkerrors.Wrap(ErrInvalidProofSpecs, "proof specs cannot be nil for tm client") + } + for i, spec := range cs.ProofSpecs { + if spec == nil { + return sdkerrors.Wrapf(ErrInvalidProofSpecs, "proof spec cannot be nil at index: %d", i) + } + } + // UpgradePath may be empty, but if it isn't, each key must be non-empty + for i, k := range cs.UpgradePath { + if strings.TrimSpace(k) == "" { + return sdkerrors.Wrapf(clienttypes.ErrInvalidClient, "key in upgrade path at index %d cannot be empty", i) + } + } + + return nil +} + +// GetProofSpecs returns the format the client expects for proof verification +// as a string array specifying the proof type for each position in chained proof +func (cs ClientState) GetProofSpecs() []*ics23.ProofSpec { + return cs.ProofSpecs +} + +// ZeroCustomFields returns a ClientState that is a copy of the current ClientState +// with all client customizable fields zeroed out +func (cs ClientState) ZeroCustomFields() exported.ClientState { + // copy over all chain-specified fields + // and leave custom fields empty + return &ClientState{ + ChainId: cs.ChainId, + UnbondingPeriod: cs.UnbondingPeriod, + LatestHeight: cs.LatestHeight, + ProofSpecs: cs.ProofSpecs, + UpgradePath: cs.UpgradePath, + } +} + +// Initialize will check that initial consensus state is a Tendermint consensus state +// and will store ProcessedTime for initial consensus state as ctx.BlockTime() +func (cs ClientState) Initialize(ctx sdk.Context, _ codec.BinaryCodec, clientStore sdk.KVStore, consState exported.ConsensusState) error { + if _, ok := consState.(*ConsensusState); !ok { + return sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "invalid initial consensus state. expected type: %T, got: %T", + &ConsensusState{}, consState) + } + // set metadata for initial consensus state. + setConsensusMetadata(ctx, clientStore, cs.GetLatestHeight()) + 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 +} + +// 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, + 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 { + 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) + } + + } + + 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 +} diff --git a/modules/light-clients/07-tendermint/client_state_test.go b/modules/light-clients/07-tendermint/client_state_test.go new file mode 100644 index 00000000000..9284e44cec8 --- /dev/null +++ b/modules/light-clients/07-tendermint/client_state_test.go @@ -0,0 +1,700 @@ +package tendermint_test + +import ( + "time" + + ics23 "github.com/confio/ics23/go" + sdk "github.com/cosmos/cosmos-sdk/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" + 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" + 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" +) + +const ( + // Do not change the length of these variables + fiftyCharChainID = "12345678901234567890123456789012345678901234567890" + fiftyOneCharChainID = "123456789012345678901234567890123456789012345678901" +) + +var ( + invalidProof = []byte("invalid proof") +) + +func (suite *TendermintTestSuite) TestStatus() { + var ( + path *ibctesting.Path + clientState *ibctm.ClientState + ) + + testCases := []struct { + name string + malleate func() + expStatus exported.Status + }{ + {"client is active", func() {}, exported.Active}, + {"client is frozen", func() { + clientState.FrozenHeight = clienttypes.NewHeight(0, 1) + path.EndpointA.SetClientState(clientState) + }, exported.Frozen}, + {"client status without consensus state", func() { + clientState.LatestHeight = clientState.LatestHeight.Increment().(clienttypes.Height) + path.EndpointA.SetClientState(clientState) + }, exported.Expired}, + {"client status is expired", func() { + suite.coordinator.IncrementTimeBy(clientState.TrustingPeriod) + }, exported.Expired}, + } + + for _, tc := range testCases { + path = ibctesting.NewPath(suite.chainA, suite.chainB) + suite.coordinator.SetupClients(path) + + clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + clientState = path.EndpointA.GetClientState().(*ibctm.ClientState) + + tc.malleate() + + status := clientState.Status(suite.chainA.GetContext(), clientStore, suite.chainA.App.AppCodec()) + suite.Require().Equal(tc.expStatus, status) + + } +} + +func (suite *TendermintTestSuite) TestValidate() { + testCases := []struct { + name string + clientState *ibctm.ClientState + expPass bool + }{ + { + name: "valid client", + 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: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), nil, false, false), + expPass: true, + }, + { + name: "invalid chainID", + clientState: ibctm.NewClientState(" ", ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + expPass: false, + }, + { + // NOTE: if this test fails, the code must account for the change in chainID length across tendermint versions! + // 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: ibctm.NewClientState(fiftyCharChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + expPass: true, + }, + { + // NOTE: if this test fails, the code must account for the change in chainID length across tendermint versions! + // 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: ibctm.NewClientState(fiftyOneCharChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + expPass: false, + }, + { + name: "invalid trust level", + 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: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, 0, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + expPass: false, + }, + { + name: "invalid unbonding period", + clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, trustingPeriod, 0, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + expPass: false, + }, + { + name: "invalid max clock drift", + clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, 0, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + expPass: false, + }, + { + name: "invalid revision number", + 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: 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: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + expPass: false, + }, + { + name: "proof specs is nil", + clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, nil, upgradePath, false, false), + expPass: false, + }, + { + name: "proof specs contains nil", + clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, []*ics23.ProofSpec{ics23.TendermintSpec, nil}, upgradePath, false, false), + 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 *TendermintTestSuite) TestInitialize() { + + testCases := []struct { + name string + consensusState exported.ConsensusState + expPass bool + }{ + { + name: "valid consensus", + consensusState: &ibctm.ConsensusState{}, + expPass: true, + }, + { + name: "invalid consensus: consensus state is solomachine consensus", + consensusState: ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2).ConsensusState(), + expPass: false, + }, + } + + path := ibctesting.NewPath(suite.chainA, suite.chainB) + err := path.EndpointA.CreateClient() + suite.Require().NoError(err) + + clientState := suite.chainA.GetClientState(path.EndpointA.ClientID) + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + + for _, tc := range testCases { + err := clientState.Initialize(suite.chainA.GetContext(), suite.chainA.Codec, store, tc.consensusState) + if tc.expPass { + suite.Require().NoError(err, "valid case returned an error") + } else { + suite.Require().Error(err, "invalid case didn't return an error") + } + } +} + +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()).(*ibctm.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(1, 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(1, 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(1, 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 proved + 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().(*ibctm.ClientState) + value, err = suite.chainB.Codec.MarshalInterface(clientState) + suite.Require().NoError(err) + + tc.malleate() // make changes as necessary + + clientState = testingpath.EndpointA.GetClientState().(*ibctm.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) + } + }) + } +} + +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 proved + 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().(*ibctm.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) + } + }) + } +} diff --git a/modules/light-clients/07-tendermint/types/codec.go b/modules/light-clients/07-tendermint/codec.go similarity index 87% rename from modules/light-clients/07-tendermint/types/codec.go rename to modules/light-clients/07-tendermint/codec.go index bf25097b4af..5e98991f24e 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" @@ -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/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 7a2a32d2789..ce0e3006afe 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 73% rename from modules/light-clients/07-tendermint/types/consensus_state_test.go rename to modules/light-clients/07-tendermint/consensus_state_test.go index d4b24ddb510..b20b86fed4d 100644 --- a/modules/light-clients/07-tendermint/types/consensus_state_test.go +++ b/modules/light-clients/07-tendermint/consensus_state_test.go @@ -1,58 +1,49 @@ -package types_test +package tendermint_test import ( "time" 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/light-clients/07-tendermint/types" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ) func (suite *TendermintTestSuite) TestConsensusStateValidateBasic() { testCases := []struct { msg string - consensusState *types.ConsensusState + consensusState *ibctm.ConsensusState expectPass bool }{ - { - "success", - &types.ConsensusState{ + {"success", + &ibctm.ConsensusState{ Timestamp: suite.now, Root: commitmenttypes.NewMerkleRoot([]byte("app_hash")), NextValidatorsHash: suite.valsHash, }, - true, - }, - { - "success with sentinel", - &types.ConsensusState{ + true}, + {"success with sentinel", + &ibctm.ConsensusState{ Timestamp: suite.now, - Root: commitmenttypes.NewMerkleRoot([]byte(types.SentinelRoot)), + Root: commitmenttypes.NewMerkleRoot([]byte(ibctm.SentinelRoot)), NextValidatorsHash: suite.valsHash, }, - true, - }, - { - "root is nil", - &types.ConsensusState{ + true}, + {"root is nil", + &ibctm.ConsensusState{ Timestamp: suite.now, Root: commitmenttypes.MerkleRoot{}, NextValidatorsHash: suite.valsHash, }, - false, - }, - { - "root is empty", - &types.ConsensusState{ + false}, + {"root is empty", + &ibctm.ConsensusState{ Timestamp: suite.now, Root: commitmenttypes.MerkleRoot{}, NextValidatorsHash: suite.valsHash, }, - false, - }, - { - "nextvalshash is invalid", - &types.ConsensusState{ + false}, + {"nextvalshash is invalid", + &ibctm.ConsensusState{ Timestamp: suite.now, Root: commitmenttypes.NewMerkleRoot([]byte("app_hash")), NextValidatorsHash: []byte("hi"), @@ -60,9 +51,8 @@ func (suite *TendermintTestSuite) TestConsensusStateValidateBasic() { false, }, - { - "timestamp is zero", - &types.ConsensusState{ + {"timestamp is zero", + &ibctm.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 8f9a8731a80..ab9159a00a8 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 77% rename from modules/light-clients/07-tendermint/types/genesis_test.go rename to modules/light-clients/07-tendermint/genesis_test.go index 86a601d1f60..517147a8dc1 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/v5/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint/types" + 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 := types.GetIterationKey(clientStore, height) + initIteration := ibctm.GetIterationKey(clientStore, height) suite.Require().NotEqual(0, len(initIteration)) - initProcessedTime, found := types.GetProcessedTime(clientStore, height) + initProcessedTime, found := ibctm.GetProcessedTime(clientStore, height) suite.Require().True(found) - initProcessedHeight, found := types.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(types.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(types.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(types.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 := types.GetIterationKey(clientStore, updateHeight) + iteration := ibctm.GetIterationKey(clientStore, updateHeight) suite.Require().NotEqual(0, len(initIteration)) - processedTime, found := types.GetProcessedTime(clientStore, updateHeight) + processedTime, found := ibctm.GetProcessedTime(clientStore, updateHeight) suite.Require().True(found) - processedHeight, found := types.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(types.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(types.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(types.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(types.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(types.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(types.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/types/header.go b/modules/light-clients/07-tendermint/header.go similarity index 97% rename from modules/light-clients/07-tendermint/types/header.go rename to modules/light-clients/07-tendermint/header.go index 2da01504710..9fc00198245 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" @@ -12,7 +12,7 @@ import ( "github.com/cosmos/ibc-go/v5/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/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 933ae8d9d70..8e916177877 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/v5/modules/core/02-client/types" "github.com/cosmos/ibc-go/v5/modules/core/exported" - "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint/types" + 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 *types.Header + var header *ibctm.Header testCases := []struct { name string malleate func() diff --git a/modules/light-clients/07-tendermint/types/misbehaviour.go b/modules/light-clients/07-tendermint/misbehaviour.go similarity index 95% rename from modules/light-clients/07-tendermint/types/misbehaviour.go rename to modules/light-clients/07-tendermint/misbehaviour.go index a15699767f3..3fc237621ab 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" @@ -12,7 +12,7 @@ import ( "github.com/cosmos/ibc-go/v5/modules/core/exported" ) -var _ exported.Misbehaviour = &Misbehaviour{} +var _ exported.ClientMessage = &Misbehaviour{} // FrozenHeight is same for all misbehaviour var FrozenHeight = clienttypes.NewHeight(0, 1) @@ -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. @@ -130,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/misbehaviour_handle.go similarity index 61% rename from modules/light-clients/07-tendermint/types/misbehaviour_handle.go rename to modules/light-clients/07-tendermint/misbehaviour_handle.go index 17883824cb6..0417dd4da17 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" @@ -10,10 +10,9 @@ import ( tmtypes "github.com/tendermint/tendermint/types" clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v5/modules/core/exported" ) -// CheckMisbehaviourAndUpdateState determines whether or not two conflicting +// 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 @@ -21,57 +20,42 @@ import ( // 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.Misbehaviour, -) (exported.ClientState, error) { - tmMisbehaviour, ok := misbehaviour.(*Misbehaviour) - if !ok { - return nil, sdkerrors.Wrapf(clienttypes.ErrInvalidClientType, "expected type %T, got %T", misbehaviour, &Misbehaviour{}) - } - - // The status of the client is checked in 02-client - +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 if tmMisbehaviour.Header1.SignedHeader.Header.Time.After(tmMisbehaviour.Header2.SignedHeader.Header.Time) { + } else if misbehaviour.Header1.SignedHeader.Header.Time.After(misbehaviour.Header2.SignedHeader.Header.Time) { // 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). - return nil, sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "headers are not at same height and are monotonically increasing") + 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) - if err != nil { - return nil, sdkerrors.Wrapf(err, "could not get trusted consensus state from clientStore for Header1 at TrustedHeight: %s", tmMisbehaviour.Header1) + 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) } - // Get consensus bytes from clientStore - tmConsensusState2, err := GetConsensusState(clientStore, cdc, tmMisbehaviour.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) + 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 @@ -80,19 +64,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 @@ -127,6 +109,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/misbehaviour_handle_test.go b/modules/light-clients/07-tendermint/misbehaviour_handle_test.go new file mode 100644 index 00000000000..7b42d6ab19a --- /dev/null +++ b/modules/light-clients/07-tendermint/misbehaviour_handle_test.go @@ -0,0 +1,717 @@ +package tendermint_test + +import ( + "fmt" + "strings" + "time" + + tmtypes "github.com/tendermint/tendermint/types" + + 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" + 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" +) + +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 = &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), + } + }, + 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 = &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), + } + }, + 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 = &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), + } + }, + 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 = &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), + } + }, 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 = &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), + } + + }, + 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 = &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), + } + + // increment revision number + err = path.EndpointB.UpgradeChain() + suite.Require().NoError(err) + }, + 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 = &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), + } + }, + 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 = &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), + } + }, + 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 = &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), + } + }, 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 = &ibctm.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 = &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), + } + }, 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 = &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), + } + + }, 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 = &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), + } + }, 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 = &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), + } + }, 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 = &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), + } + }, 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 = &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), + } + }, 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 = &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), + } + }, 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 = &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), + } + }, 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) + + 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 = &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), + } + }, + 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 = &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), + } + }, + 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 = &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), + } + }, + 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 = &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), + } + }, 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 = &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), + } + + }, + 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 = &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), + } + }, 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 = &ibctm.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 = &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), + } + }, 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 = &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), + } + + }, 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 = &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), + } + }, 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 = &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), + } + }, 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 = &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), + } + }, 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 = &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), + } + }, 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 = &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), + } + }, 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 = &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), + } + }, 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) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } + + // NOTE: reset chain creation to revision format + ibctesting.ChainIDSuffix = "-1" +} diff --git a/modules/light-clients/07-tendermint/types/misbehaviour_test.go b/modules/light-clients/07-tendermint/misbehaviour_test.go similarity index 82% rename from modules/light-clients/07-tendermint/types/misbehaviour_test.go rename to modules/light-clients/07-tendermint/misbehaviour_test.go index 0731cd25489..979b5d1c203 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/v5/modules/core/02-client/types" "github.com/cosmos/ibc-go/v5/modules/core/exported" - "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint/types" + 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,14 +17,13 @@ import ( func (suite *TendermintTestSuite) TestMisbehaviour() { heightMinus1 := clienttypes.NewHeight(0, height.RevisionHeight-1) - misbehaviour := &types.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, } suite.Require().Equal(exported.Tendermint, misbehaviour.ClientType()) - suite.Require().Equal(clientID, misbehaviour.GetClientID()) } func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { @@ -48,130 +47,130 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { testCases := []struct { name string - misbehaviour *types.Misbehaviour - malleateMisbehaviour func(misbehaviour *types.Misbehaviour) error + misbehaviour *ibctm.Misbehaviour + malleateMisbehaviour func(misbehaviour *ibctm.Misbehaviour) error expPass bool }{ { "valid fork misbehaviour, two headers at same height have different time", - &types.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 *types.Misbehaviour) error { return nil }, + func(misbehaviour *ibctm.Misbehaviour) error { return nil }, true, }, { "valid time misbehaviour, both headers at different heights are at same time", - &types.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 *types.Misbehaviour) error { return nil }, + func(misbehaviour *ibctm.Misbehaviour) error { return nil }, true, }, { "misbehaviour Header1 is nil", - types.NewMisbehaviour(clientID, nil, suite.header), - func(m *types.Misbehaviour) error { return nil }, + ibctm.NewMisbehaviour(clientID, nil, suite.header), + func(m *ibctm.Misbehaviour) error { return nil }, false, }, { "misbehaviour Header2 is nil", - types.NewMisbehaviour(clientID, suite.header, nil), - func(m *types.Misbehaviour) error { return nil }, + ibctm.NewMisbehaviour(clientID, suite.header, nil), + func(m *ibctm.Misbehaviour) error { return nil }, false, }, { "valid misbehaviour with different trusted headers", - &types.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 *types.Misbehaviour) error { return nil }, + func(misbehaviour *ibctm.Misbehaviour) error { return nil }, true, }, { "trusted height is 0 in Header1", - &types.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 *types.Misbehaviour) error { return nil }, + func(misbehaviour *ibctm.Misbehaviour) error { return nil }, false, }, { "trusted height is 0 in Header2", - &types.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 *types.Misbehaviour) error { return nil }, + func(misbehaviour *ibctm.Misbehaviour) error { return nil }, false, }, { "trusted valset is nil in Header1", - &types.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 *types.Misbehaviour) error { return nil }, + func(misbehaviour *ibctm.Misbehaviour) error { return nil }, false, }, { "trusted valset is nil in Header2", - &types.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 *types.Misbehaviour) error { return nil }, + func(misbehaviour *ibctm.Misbehaviour) error { return nil }, false, }, { "invalid client ID ", - &types.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 *types.Misbehaviour) error { return nil }, + func(misbehaviour *ibctm.Misbehaviour) error { return nil }, false, }, { "chainIDs do not match", - &types.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 *types.Misbehaviour) error { return nil }, + func(misbehaviour *ibctm.Misbehaviour) error { return nil }, false, }, { "header2 height is greater", - &types.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 *types.Misbehaviour) error { return nil }, + func(misbehaviour *ibctm.Misbehaviour) error { return nil }, false, }, { "header 1 doesn't have 2/3 majority", - &types.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 *types.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) @@ -187,12 +186,12 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { }, { "header 2 doesn't have 2/3 majority", - &types.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 *types.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) @@ -208,12 +207,12 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { }, { "validators sign off on wrong commit", - &types.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 *types.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/module.go b/modules/light-clients/07-tendermint/module.go index 532f3158f44..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/v5/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 78% rename from modules/light-clients/07-tendermint/types/proposal_handle.go rename to modules/light-clients/07-tendermint/proposal_handle.go index 9a75c5f9b9f..39700ed3418 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" @@ -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 { @@ -48,22 +46,22 @@ 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 sdkerrors.Wrap(clienttypes.ErrConsensusStateNotFound, "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) 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) @@ -76,8 +74,9 @@ 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 + return nil } // IsMatchingClientState returns true if all the client state parameters match 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 83% rename from modules/light-clients/07-tendermint/types/proposal_handle_test.go rename to modules/light-clients/07-tendermint/proposal_handle_test.go index 62e65e05053..3d7e5e3e688 100644 --- a/modules/light-clients/07-tendermint/types/proposal_handle_test.go +++ b/modules/light-clients/07-tendermint/proposal_handle_test.go @@ -1,11 +1,11 @@ -package types_test +package tendermint_test import ( "time" clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" "github.com/cosmos/ibc-go/v5/modules/core/exported" - "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint/types" + 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).(*types.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).(*types.ClientState) + subjectClientState := suite.chainA.GetClientState(subjectPath.EndpointA.ClientID).(*ibctm.ClientState) // expire subject client suite.coordinator.IncrementTimeBy(subjectClientState.TrustingPeriod) @@ -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) }) } } @@ -120,7 +119,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).(*ibctm.ClientState) // apply freezing or expiry as determined by the test case if tc.FreezeClient { @@ -140,7 +139,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).(*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 +153,7 @@ func (suite *TendermintTestSuite) TestCheckSubstituteAndUpdateState() { } // get updated substitute - substituteClientState = suite.chainA.GetClientState(substitutePath.EndpointA.ClientID).(*types.ClientState) + substituteClientState = suite.chainA.GetClientState(substitutePath.EndpointA.ClientID).(*ibctm.ClientState) // test that subject gets updated chain-id newChainID := "new-chain-id" @@ -164,39 +163,40 @@ 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 := ibctm.GetProcessedTime(substituteClientStore, substituteClientState.GetLatestHeight()) suite.Require().True(found) - expectedProcessedHeight, found := types.GetProcessedTime(substituteClientStore, substituteClientState.GetLatestHeight()) + expectedProcessedHeight, found := ibctm.GetProcessedTime(substituteClientStore, substituteClientState.GetLatestHeight()) suite.Require().True(found) - expectedIterationKey := types.GetIterationKey(substituteClientStore, substituteClientState.GetLatestHeight()) + 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) - suite.Require().Equal(clienttypes.ZeroHeight(), updatedClient.(*types.ClientState).FrozenHeight) + + 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) // 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 := ibctm.GetProcessedTime(subjectClientStore, updatedClient.GetLatestHeight()) suite.Require().True(found) - subjectProcessedHeight, found := types.GetProcessedTime(substituteClientStore, updatedClient.GetLatestHeight()) + subjectProcessedHeight, found := ibctm.GetProcessedTime(substituteClientStore, updatedClient.GetLatestHeight()) suite.Require().True(found) - subjectIterationKey := types.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.(*types.ClientState).ChainId) - suite.Require().Equal(time.Hour*24*7, updatedClient.(*types.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 *types.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).(*types.ClientState) - substituteClientState = suite.chainA.GetClientState(substitutePath.EndpointA.ClientID).(*types.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 = types.Fraction{2, 3} - substituteClientState.TrustLevel = types.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, types.IsMatchingClientState(*subjectClientState, *substituteClientState)) + suite.Require().Equal(tc.expPass, ibctm.IsMatchingClientState(*subjectClientState, *substituteClientState)) }) } } diff --git a/modules/light-clients/07-tendermint/types/store.go b/modules/light-clients/07-tendermint/store.go similarity index 91% rename from modules/light-clients/07-tendermint/types/store.go rename to modules/light-clients/07-tendermint/store.go index 301918c6bdf..e8824d06e1d 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" @@ -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/v5/modules/core/02-client/types" host "github.com/cosmos/ibc-go/v5/modules/core/24-host" @@ -43,38 +42,30 @@ var ( KeyIteration = []byte("/iterationKey") ) -// SetConsensusState stores the consensus state at the given height. -func SetConsensusState(clientStore sdk.KVStore, cdc codec.BinaryCodec, consensusState *ConsensusState, height exported.Height) { +// 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) val := clienttypes.MustMarshalConsensusState(cdc, consensusState) 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 @@ -224,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() @@ -232,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. @@ -287,13 +277,12 @@ 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 { - 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 } @@ -304,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/types/store_test.go b/modules/light-clients/07-tendermint/store_test.go similarity index 60% rename from modules/light-clients/07-tendermint/types/store_test.go rename to modules/light-clients/07-tendermint/store_test.go index a482fad007e..851a39c1945 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/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/types" - "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint/types" + 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" ) @@ -23,31 +23,32 @@ 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() { // 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, + }, false, true, }, { "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, + }, 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) + tendermint.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 := tendermint.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) } }) @@ -96,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") @@ -112,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") } @@ -129,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") } } @@ -139,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 { @@ -156,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 2df221d3d39..46a11f4537f 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" @@ -144,7 +144,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"` } @@ -322,75 +323,75 @@ func init() { } var fileDescriptor_c6d6cf2b288949be = []byte{ - // 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, 0xb7, 0xc9, 0x24, 0xdd, 0xf6, 0x6b, 0x4a, 0x37, 0x2d, 0xdd, 0x38, 0x32, - 0x52, 0xc9, 0x81, 0xda, 0x24, 0x0b, 0x42, 0xaa, 0xb8, 0xe0, 0xee, 0xa2, 0x16, 0xb1, 0x52, 0xe5, - 0xf2, 0x43, 0x42, 0x42, 0x66, 0x62, 0x4f, 0x92, 0xd1, 0xda, 0x1e, 0xcb, 0x33, 0x09, 0x2d, 0x7f, - 0x01, 0x1c, 0x90, 0xf6, 0x88, 0x38, 0x71, 0xe0, 0x8f, 0xd9, 0x63, 0x8f, 0x9c, 0x0c, 0x6a, 0xaf, - 0x9c, 0x72, 0xe4, 0x84, 0xe6, 0x87, 0xed, 0x69, 0xb6, 0x4b, 0xb5, 0x5c, 0xa2, 0x79, 0xef, 0x7d, - 0xde, 0xe7, 0x93, 0x79, 0xf3, 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, 0x76, - 0xb5, 0x7c, 0x76, 0x91, 0x22, 0xea, 0xcc, 0x60, 0x84, 0x43, 0xc8, 0x48, 0x26, 0x19, 0x76, 0xf7, - 0x5e, 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, 0x8b, 0x71, - 0x86, 0x63, 0x44, 0x19, 0x8c, 0xd3, 0x02, 0xc0, 0xf7, 0x17, 0x90, 0x0c, 0x39, 0xf2, 0xef, 0xf2, - 0x3d, 0xc9, 0x95, 0x02, 0xbc, 0x53, 0x01, 0x48, 0x1c, 0x63, 0x16, 0x17, 0xa0, 0xd2, 0x52, 0xc0, - 0xad, 0x31, 0x19, 0x13, 0xb1, 0x74, 0xf8, 0x4a, 0x7a, 0xad, 0xbf, 0xd6, 0x40, 0xf3, 0x48, 0xf0, - 0x9d, 0x31, 0xc8, 0x90, 0xb1, 0x03, 0xea, 0xc1, 0x04, 0xe2, 0xc4, 0xc7, 0x61, 0xbb, 0xd6, 0xad, - 0xf5, 0x1a, 0xde, 0x9a, 0xb0, 0x4f, 0x42, 0x03, 0x81, 0x26, 0xcb, 0xa6, 0x94, 0xf9, 0x11, 0x9a, - 0xa1, 0xa8, 0xbd, 0xdc, 0xad, 0xf5, 0x9a, 0x83, 0x9e, 0xfd, 0xef, 0xf5, 0xb4, 0x3f, 0xc9, 0x60, - 0xc0, 0x37, 0xec, 0xee, 0xbe, 0xc8, 0xcd, 0xa5, 0x79, 0x6e, 0x1a, 0x17, 0x30, 0x8e, 0x0e, 0x2d, - 0x8d, 0xca, 0xf2, 0x80, 0xb0, 0x3e, 0xe3, 0x86, 0x31, 0x02, 0x1b, 0xc2, 0xc2, 0xc9, 0xd8, 0x4f, - 0x51, 0x86, 0x49, 0xd8, 0x5e, 0x11, 0x52, 0x3b, 0xb6, 0x2c, 0x96, 0x5d, 0x14, 0xcb, 0x7e, 0xac, - 0x8a, 0xe9, 0x5a, 0x8a, 0x7b, 0x5b, 0xe3, 0xae, 0xf2, 0xad, 0x9f, 0xff, 0x30, 0x6b, 0xde, 0xfd, - 0xc2, 0x7b, 0x2a, 0x9c, 0x06, 0x06, 0x9b, 0xd3, 0x64, 0x48, 0x92, 0x50, 0x13, 0x5a, 0xbd, 0x4b, - 0xe8, 0x6d, 0x25, 0xf4, 0x40, 0x0a, 0x2d, 0x12, 0x48, 0xa5, 0x8d, 0xd2, 0xad, 0xa4, 0x10, 0xd8, - 0x88, 0xe1, 0xb9, 0x1f, 0x44, 0x24, 0x78, 0xe6, 0x87, 0x19, 0x1e, 0xb1, 0xf6, 0xff, 0x5e, 0x73, - 0x4b, 0x0b, 0xf9, 0x52, 0x68, 0x3d, 0x86, 0xe7, 0x47, 0xdc, 0xf9, 0x98, 0xfb, 0x8c, 0x6f, 0xc0, - 0xfa, 0x28, 0x23, 0xdf, 0xa3, 0xc4, 0x9f, 0x20, 0x7e, 0x20, 0xed, 0x7b, 0x42, 0x64, 0x57, 0x1c, - 0x11, 0x6f, 0x11, 0x5b, 0x75, 0xce, 0xac, 0x6f, 0x1f, 0x0b, 0x84, 0xbb, 0xa7, 0x54, 0xb6, 0xa4, - 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, 0xb0, 0x69, 0xe3, 0x80, 0x0e, 0x1e, 0xd9, - 0xa7, 0x3c, 0x72, 0x96, 0xa2, 0xc0, 0xdd, 0xae, 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, 0xc1, 0x3c, 0x37, 0xdf, 0x50, 0xe7, 0xa6, 0x45, 0x2d, 0xaf, 0xa9, 0xcc, 0x53, - 0xc8, 0x26, 0x06, 0x04, 0x3b, 0x30, 0x8a, 0xc8, 0x77, 0xfe, 0x34, 0x0d, 0x21, 0x43, 0x3e, 0x1c, - 0x31, 0x94, 0xf9, 0xe8, 0x3c, 0xc5, 0xd9, 0x45, 0x1b, 0x74, 0x6b, 0xbd, 0xba, 0xbb, 0x3f, 0xcf, - 0xcd, 0xae, 0x24, 0x7a, 0x25, 0xd4, 0x6a, 0xd7, 0xbc, 0x6d, 0x11, 0xfd, 0x42, 0x04, 0x3f, 0xe6, - 0xb1, 0x27, 0x22, 0x64, 0x50, 0x60, 0xde, 0x92, 0x17, 0x63, 0x3a, 0x44, 0x13, 0x38, 0xc3, 0x64, - 0x9a, 0xb5, 0x9b, 0x42, 0xe8, 0xdd, 0x79, 0x6e, 0xee, 0xbf, 0x52, 0x48, 0x4f, 0xe0, 0x72, 0x7b, - 0x8b, 0x72, 0x4f, 0x35, 0xc0, 0xe1, 0xea, 0x0f, 0xbf, 0x9a, 0x4b, 0xd6, 0x6f, 0xcb, 0xe0, 0xfe, - 0x11, 0x49, 0x28, 0x4a, 0xe8, 0x94, 0xca, 0x1b, 0xef, 0x82, 0x46, 0x39, 0x74, 0xc4, 0x95, 0xe7, - 0x47, 0xba, 0xd8, 0x96, 0x9f, 0x17, 0x08, 0xb7, 0xce, 0x8f, 0xf4, 0x39, 0xef, 0xbe, 0x2a, 0xcd, - 0xf8, 0x08, 0xac, 0x66, 0x84, 0x30, 0x35, 0x13, 0x2c, 0xad, 0x23, 0xaa, 0x29, 0x34, 0xeb, 0xdb, - 0x4f, 0x51, 0xf6, 0x2c, 0x42, 0x1e, 0x21, 0xcc, 0x5d, 0xe5, 0x34, 0x9e, 0xc8, 0x32, 0x7e, 0xac, - 0x81, 0xad, 0x04, 0x9d, 0x33, 0xbf, 0x9c, 0xb4, 0xd4, 0x9f, 0x40, 0x3a, 0x11, 0xf7, 0xbe, 0xe5, - 0x7e, 0x35, 0xcf, 0xcd, 0xb7, 0x64, 0x15, 0x6e, 0x43, 0x59, 0x7f, 0xe7, 0xe6, 0xfb, 0x63, 0xcc, - 0x26, 0xd3, 0x21, 0x97, 0xd3, 0xe7, 0xbf, 0xb6, 0x8c, 0xf0, 0x90, 0x3a, 0xc3, 0x0b, 0x86, 0xa8, - 0x7d, 0x8c, 0xce, 0x5d, 0xbe, 0xf0, 0x0c, 0x4e, 0xf7, 0x65, 0xc9, 0x76, 0x0c, 0xe9, 0x44, 0x95, - 0xe9, 0xa7, 0x65, 0xd0, 0xd2, 0xab, 0x67, 0xf4, 0x41, 0x43, 0x36, 0x77, 0x39, 0x17, 0xdd, 0xad, - 0x79, 0x6e, 0x6e, 0xca, 0xbf, 0x55, 0x86, 0x2c, 0xaf, 0x2e, 0xd7, 0x27, 0xa1, 0x01, 0x41, 0x7d, - 0x82, 0x60, 0x88, 0x32, 0xbf, 0xaf, 0xea, 0xb2, 0x7f, 0xd7, 0xac, 0x3c, 0x16, 0x78, 0xb7, 0x73, - 0x95, 0x9b, 0x6b, 0x72, 0xdd, 0x9f, 0xe7, 0xe6, 0x86, 0x14, 0x29, 0xc8, 0x2c, 0x6f, 0x4d, 0x2e, - 0xfb, 0x9a, 0xc4, 0x40, 0xcd, 0xc8, 0xff, 0x20, 0x31, 0x78, 0x49, 0x62, 0x50, 0x4a, 0x0c, 0x54, - 0x3d, 0x7e, 0x59, 0x01, 0xf7, 0x24, 0xda, 0x80, 0x60, 0x9d, 0xe2, 0x71, 0x82, 0x42, 0x5f, 0x42, - 0x54, 0xcb, 0x74, 0x74, 0x1d, 0xf9, 0x1e, 0x9e, 0x09, 0x98, 0x12, 0xdc, 0xbb, 0xcc, 0xcd, 0x5a, - 0x35, 0x09, 0x6e, 0x50, 0x58, 0x5e, 0x8b, 0x6a, 0x58, 0x3e, 0x68, 0xca, 0x33, 0xf6, 0x29, 0x2a, - 0xda, 0xea, 0x16, 0x89, 0xf2, 0xf0, 0xce, 0x10, 0x73, 0xdb, 0x15, 0xfd, 0x8d, 0x74, 0xcb, 0x6b, - 0xcd, 0x34, 0x9c, 0xf1, 0x2d, 0x90, 0x4f, 0x81, 0xd0, 0x17, 0x83, 0x6c, 0xe5, 0xce, 0x41, 0xf6, - 0x50, 0x0d, 0xb2, 0x37, 0xb5, 0x07, 0xa6, 0xcc, 0xb7, 0xbc, 0x75, 0xe5, 0x50, 0xa3, 0x2c, 0x02, - 0x46, 0x81, 0xa8, 0x9a, 0x55, 0x3d, 0x2e, 0x77, 0xed, 0xe2, 0xe1, 0x3c, 0x37, 0x77, 0x6e, 0xaa, - 0x54, 0x1c, 0x96, 0xf7, 0x7f, 0xe5, 0xac, 0xda, 0xd6, 0xfa, 0x14, 0xd4, 0x8b, 0x47, 0xd6, 0xd8, - 0x03, 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, 0x8b, 0xab, 0x4e, 0xed, - 0xf2, 0xaa, 0x53, 0xfb, 0xf3, 0xaa, 0x53, 0x7b, 0x7e, 0xdd, 0x59, 0xba, 0xbc, 0xee, 0x2c, 0xfd, - 0x7e, 0xdd, 0x59, 0xfa, 0xfa, 0x89, 0x76, 0xc5, 0x02, 0x42, 0x63, 0x42, 0xf9, 0xa7, 0xd7, 0xc1, - 0x98, 0x38, 0xb3, 0x0f, 0x9c, 0x98, 0x84, 0xd3, 0x08, 0x51, 0xf9, 0x21, 0x76, 0x50, 0x7c, 0x89, - 0xbd, 0xf7, 0xe1, 0xc1, 0xe2, 0xa7, 0xd2, 0xf0, 0x9e, 0x18, 0x29, 0x8f, 0xfe, 0x09, 0x00, 0x00, - 0xff, 0xff, 0x98, 0x6c, 0x12, 0x96, 0xb8, 0x09, 0x00, 0x00, + // 1080 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, 0xb6, 0x98, 0xd2, 0x4d, 0x4b, 0x37, 0x8e, 0x8c, 0x54, + 0x72, 0xa0, 0x36, 0x49, 0x41, 0x48, 0x85, 0x0b, 0xee, 0x82, 0xda, 0x15, 0x2b, 0x55, 0x2e, 0x3f, + 0x24, 0x24, 0x64, 0x26, 0xf6, 0x24, 0x19, 0xad, 0xed, 0xb1, 0x3c, 0x93, 0xd0, 0xf2, 0x17, 0xc0, + 0x89, 0x3d, 0x22, 0x4e, 0x1c, 0xf8, 0x63, 0xf6, 0xd8, 0x23, 0x27, 0x83, 0xda, 0x2b, 0xa7, 0x1c, + 0x39, 0xa1, 0xf9, 0x61, 0x7b, 0xda, 0xed, 0x52, 0x2d, 0x97, 0x68, 0xde, 0x7b, 0xdf, 0xfb, 0xbe, + 0xcc, 0xbc, 0x37, 0x6f, 0x0c, 0x1c, 0x3c, 0x0c, 0x9c, 0x08, 0x8f, 0x27, 0x2c, 0x88, 0x30, 0x4a, + 0x18, 0x75, 0x18, 0x4a, 0x42, 0x94, 0xc5, 0x38, 0x61, 0xce, 0xac, 0xaf, 0x59, 0x76, 0x9a, 0x11, + 0x46, 0x8c, 0x0e, 0x1e, 0x06, 0xb6, 0x9e, 0x60, 0x6b, 0x90, 0x59, 0x7f, 0xbb, 0xab, 0xe5, 0xb3, + 0xf3, 0x14, 0x51, 0x67, 0x06, 0x23, 0x1c, 0x42, 0x46, 0x32, 0xc9, 0xb0, 0xbd, 0xf3, 0x02, 0x42, + 0xfc, 0xaa, 0x68, 0x2b, 0xcd, 0x08, 0x19, 0x15, 0x56, 0x67, 0x4c, 0xc8, 0x38, 0x42, 0x8e, 0xb0, + 0x86, 0xd3, 0x91, 0x13, 0x4e, 0x33, 0xc8, 0x30, 0x49, 0x54, 0xdc, 0xbc, 0x19, 0x67, 0x38, 0x46, + 0x94, 0xc1, 0x38, 0x2d, 0x00, 0x7c, 0x7f, 0x01, 0xc9, 0x90, 0x23, 0xff, 0x2e, 0xdf, 0x93, 0x5c, + 0x29, 0xc0, 0x3b, 0x15, 0x80, 0xc4, 0x31, 0x66, 0x71, 0x01, 0x2a, 0x2d, 0x05, 0xdc, 0x18, 0x93, + 0x31, 0x11, 0x4b, 0x87, 0xaf, 0xa4, 0xd7, 0xfa, 0x7b, 0x05, 0x34, 0x0f, 0x05, 0xdf, 0x29, 0x83, + 0x0c, 0x19, 0x5b, 0xa0, 0x1e, 0x4c, 0x20, 0x4e, 0x7c, 0x1c, 0xb6, 0x6b, 0xdd, 0x5a, 0xaf, 0xe1, + 0xad, 0x08, 0xfb, 0x38, 0x34, 0x10, 0x68, 0xb2, 0x6c, 0x4a, 0x99, 0x1f, 0xa1, 0x19, 0x8a, 0xda, + 0x8b, 0xdd, 0x5a, 0xaf, 0x39, 0xe8, 0xd9, 0xff, 0x7d, 0x9e, 0xf6, 0x67, 0x19, 0x0c, 0xf8, 0x86, + 0xdd, 0xed, 0xe7, 0xb9, 0xb9, 0x30, 0xcf, 0x4d, 0xe3, 0x1c, 0xc6, 0xd1, 0x81, 0xa5, 0x51, 0x59, + 0x1e, 0x10, 0xd6, 0xe7, 0xdc, 0x30, 0x46, 0x60, 0x4d, 0x58, 0x38, 0x19, 0xfb, 0x29, 0xca, 0x30, + 0x09, 0xdb, 0x4b, 0x42, 0x6a, 0xcb, 0x96, 0x87, 0x65, 0x17, 0x87, 0x65, 0x3f, 0x52, 0x87, 0xe9, + 0x5a, 0x8a, 0x7b, 0x53, 0xe3, 0xae, 0xf2, 0xad, 0x5f, 0xfe, 0x34, 0x6b, 0xde, 0xfd, 0xc2, 0x7b, + 0x22, 0x9c, 0x06, 0x06, 0xeb, 0xd3, 0x64, 0x48, 0x92, 0x50, 0x13, 0x5a, 0xbe, 0x4b, 0xe8, 0x6d, + 0x25, 0xf4, 0x40, 0x0a, 0xdd, 0x24, 0x90, 0x4a, 0x6b, 0xa5, 0x5b, 0x49, 0x21, 0xb0, 0x16, 0xc3, + 0x33, 0x3f, 0x88, 0x48, 0xf0, 0xd4, 0x0f, 0x33, 0x3c, 0x62, 0xed, 0xd7, 0x5e, 0x71, 0x4b, 0x37, + 0xf2, 0xa5, 0xd0, 0x6a, 0x0c, 0xcf, 0x0e, 0xb9, 0xf3, 0x11, 0xf7, 0x19, 0xdf, 0x82, 0xd5, 0x51, + 0x46, 0x7e, 0x40, 0x89, 0x3f, 0x41, 0xbc, 0x20, 0xed, 0x7b, 0x42, 0x64, 0x5b, 0x94, 0x88, 0xb7, + 0x88, 0xad, 0x3a, 0x67, 0xd6, 0xb7, 0x8f, 0x04, 0xc2, 0xdd, 0x51, 0x2a, 0x1b, 0x52, 0xe5, 0x5a, + 0xba, 0xe5, 0xb5, 0xa4, 0x2d, 0xb1, 0x9c, 0x3e, 0x82, 0x0c, 0x51, 0x56, 0xd0, 0xaf, 0xbc, 0x2a, + 0xfd, 0xb5, 0x74, 0xcb, 0x6b, 0x49, 0x5b, 0xd1, 0x1f, 0x83, 0xa6, 0xb8, 0x3a, 0x3e, 0x4d, 0x51, + 0x40, 0xdb, 0xf5, 0xee, 0x52, 0xaf, 0x39, 0x58, 0xb7, 0x71, 0x40, 0x07, 0xfb, 0xf6, 0x09, 0x8f, + 0x9c, 0xa6, 0x28, 0x70, 0x37, 0xab, 0x16, 0xd2, 0xe0, 0x96, 0x07, 0xd2, 0x02, 0x42, 0x8d, 0x03, + 0xd0, 0x9a, 0xa6, 0xe3, 0x0c, 0x86, 0xc8, 0x4f, 0x21, 0x9b, 0xb4, 0x1b, 0xdd, 0xa5, 0x5e, 0xc3, + 0x7d, 0x30, 0xcf, 0xcd, 0x37, 0x54, 0xdd, 0xb4, 0xa8, 0xe5, 0x35, 0x95, 0x79, 0x02, 0xd9, 0xc4, + 0x80, 0x60, 0x0b, 0x46, 0x11, 0xf9, 0xde, 0x9f, 0xa6, 0x21, 0x64, 0xc8, 0x87, 0x23, 0x86, 0x32, + 0x1f, 0x9d, 0xa5, 0x38, 0x3b, 0x6f, 0x83, 0x6e, 0xad, 0x57, 0x77, 0x77, 0xe7, 0xb9, 0xd9, 0x95, + 0x44, 0x2f, 0x85, 0x5a, 0xed, 0x9a, 0xb7, 0x29, 0xa2, 0x5f, 0x8a, 0xe0, 0x27, 0x3c, 0xf6, 0xa9, + 0x08, 0x19, 0x14, 0x98, 0xb7, 0xe4, 0xc5, 0x98, 0x0e, 0xd1, 0x04, 0xce, 0x30, 0x99, 0x66, 0xed, + 0xa6, 0x10, 0x7a, 0x77, 0x9e, 0x9b, 0xbb, 0x2f, 0x15, 0xd2, 0x13, 0xb8, 0xdc, 0xce, 0x4d, 0xb9, + 0x27, 0x1a, 0xe0, 0x60, 0xf9, 0xc7, 0xdf, 0xcc, 0x05, 0xeb, 0xf7, 0x45, 0x70, 0xff, 0x90, 0x24, + 0x14, 0x25, 0x74, 0x4a, 0xe5, 0x8d, 0x77, 0x41, 0xa3, 0x1c, 0x3a, 0xe2, 0xca, 0xf3, 0x92, 0xde, + 0x6c, 0xcb, 0x2f, 0x0a, 0x84, 0x5b, 0xe7, 0x25, 0x7d, 0xc6, 0xbb, 0xaf, 0x4a, 0x33, 0x3e, 0x06, + 0xcb, 0x19, 0x21, 0x4c, 0xcd, 0x04, 0x4b, 0xeb, 0x88, 0x6a, 0x0a, 0xcd, 0xfa, 0xf6, 0x13, 0x94, + 0x3d, 0x8d, 0x90, 0x47, 0x08, 0x73, 0x97, 0x39, 0x8d, 0x27, 0xb2, 0x8c, 0x9f, 0x6a, 0x60, 0x23, + 0x41, 0x67, 0xcc, 0x2f, 0x27, 0x2d, 0xf5, 0x27, 0x90, 0x4e, 0xc4, 0xbd, 0x6f, 0xb9, 0x5f, 0xcf, + 0x73, 0xf3, 0x2d, 0x79, 0x0a, 0xb7, 0xa1, 0xac, 0x7f, 0x72, 0xf3, 0xfd, 0x31, 0x66, 0x93, 0xe9, + 0x90, 0xcb, 0xe9, 0xf3, 0x5f, 0x5b, 0x46, 0x78, 0x48, 0x9d, 0xe1, 0x39, 0x43, 0xd4, 0x3e, 0x42, + 0x67, 0x2e, 0x5f, 0x78, 0x06, 0xa7, 0xfb, 0xaa, 0x64, 0x3b, 0x82, 0x74, 0xa2, 0x8e, 0xe9, 0xe7, + 0x45, 0xd0, 0xd2, 0x4f, 0xcf, 0xd8, 0x07, 0x0d, 0xd9, 0xdc, 0xe5, 0x5c, 0x14, 0x8d, 0xb8, 0x2e, + 0xff, 0x56, 0x19, 0xe2, 0x65, 0xa8, 0x4b, 0xeb, 0x38, 0x34, 0x20, 0xa8, 0x4f, 0x10, 0x0c, 0x51, + 0xe6, 0xf7, 0xd5, 0xc9, 0xec, 0xde, 0x35, 0x2d, 0x8f, 0x04, 0xde, 0xed, 0x5c, 0xe6, 0xe6, 0x8a, + 0x5c, 0xf7, 0xe7, 0xb9, 0xb9, 0x26, 0x65, 0x0a, 0x32, 0xcb, 0x5b, 0x91, 0xcb, 0xbe, 0x26, 0x31, + 0x50, 0x53, 0xf2, 0x7f, 0x48, 0x0c, 0x5e, 0x90, 0x18, 0x94, 0x12, 0x03, 0x75, 0x22, 0xbf, 0x2e, + 0x81, 0x7b, 0x12, 0x6d, 0x40, 0xb0, 0x4a, 0xf1, 0x38, 0x41, 0xa1, 0x2f, 0x21, 0xaa, 0x69, 0x3a, + 0xba, 0x8e, 0x7c, 0x11, 0x4f, 0x05, 0x4c, 0x09, 0xee, 0x5c, 0xe4, 0x66, 0xad, 0x9a, 0x05, 0xd7, + 0x28, 0x2c, 0xaf, 0x45, 0x35, 0x2c, 0x1f, 0x35, 0x65, 0x95, 0x7d, 0x8a, 0x8a, 0xc6, 0xba, 0x45, + 0xa2, 0x2c, 0xdf, 0x29, 0x62, 0x6e, 0xbb, 0xa2, 0xbf, 0x96, 0x6e, 0x79, 0xad, 0x99, 0x86, 0x33, + 0xbe, 0x03, 0xf2, 0x31, 0x10, 0xfa, 0x62, 0x94, 0x2d, 0xdd, 0x39, 0xca, 0x1e, 0xaa, 0x51, 0xf6, + 0xa6, 0xf6, 0xc4, 0x94, 0xf9, 0x96, 0xb7, 0xaa, 0x1c, 0x6a, 0x98, 0x45, 0xc0, 0x28, 0x10, 0x55, + 0xbb, 0xaa, 0xe7, 0xe5, 0xae, 0x5d, 0x3c, 0x9c, 0xe7, 0xe6, 0xd6, 0x75, 0x95, 0x8a, 0xc3, 0xf2, + 0x5e, 0x57, 0xce, 0xaa, 0x71, 0xad, 0xc7, 0xa0, 0x5e, 0x3c, 0xb3, 0xc6, 0x0e, 0x68, 0x24, 0xd3, + 0x18, 0x65, 0x3c, 0x22, 0x2a, 0xb3, 0xec, 0x55, 0x0e, 0xa3, 0x0b, 0x9a, 0x21, 0x4a, 0x48, 0x8c, + 0x13, 0x11, 0x5f, 0x14, 0x71, 0xdd, 0xe5, 0x86, 0xcf, 0x2f, 0x3b, 0xb5, 0x8b, 0xcb, 0x4e, 0xed, + 0xaf, 0xcb, 0x4e, 0xed, 0xd9, 0x55, 0x67, 0xe1, 0xe2, 0xaa, 0xb3, 0xf0, 0xc7, 0x55, 0x67, 0xe1, + 0x9b, 0xc7, 0xda, 0x25, 0x0b, 0x08, 0x8d, 0x09, 0xe5, 0x1f, 0x5f, 0x7b, 0x63, 0xe2, 0xcc, 0x3e, + 0x70, 0x62, 0x12, 0x4e, 0x23, 0x44, 0xe5, 0xa7, 0xd8, 0x5e, 0xf1, 0x2d, 0xf6, 0xde, 0x87, 0x7b, + 0xd5, 0x5e, 0x3f, 0xaa, 0x96, 0xc3, 0x7b, 0x62, 0xb2, 0xec, 0xff, 0x1b, 0x00, 0x00, 0xff, 0xff, + 0xcc, 0x58, 0x27, 0x4c, 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 d82d91e2059..7ad52f62299 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/v5/modules/core/02-client/types" - ibctmtypes "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint/types" + 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 *ibctmtypes.Header + header *ibctm.Header now time.Time headerTime time.Time clientTime time.Time diff --git a/modules/light-clients/07-tendermint/types/client_state.go b/modules/light-clients/07-tendermint/types/client_state.go deleted file mode 100644 index 8db1903e76a..00000000000 --- a/modules/light-clients/07-tendermint/types/client_state.go +++ /dev/null @@ -1,583 +0,0 @@ -package types - -import ( - "strings" - "time" - - ics23 "github.com/confio/ics23/go" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/tendermint/tendermint/light" - tmtypes "github.com/tendermint/tendermint/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" - 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) - -// NewClientState creates a new ClientState instance -func NewClientState( - chainID string, trustLevel Fraction, - trustingPeriod, ubdPeriod, maxClockDrift time.Duration, - latestHeight clienttypes.Height, specs []*ics23.ProofSpec, - upgradePath []string, allowUpdateAfterExpiry, allowUpdateAfterMisbehaviour bool, -) *ClientState { - return &ClientState{ - ChainId: chainID, - TrustLevel: trustLevel, - TrustingPeriod: trustingPeriod, - UnbondingPeriod: ubdPeriod, - MaxClockDrift: maxClockDrift, - LatestHeight: latestHeight, - FrozenHeight: clienttypes.ZeroHeight(), - ProofSpecs: specs, - UpgradePath: upgradePath, - AllowUpdateAfterExpiry: allowUpdateAfterExpiry, - AllowUpdateAfterMisbehaviour: allowUpdateAfterMisbehaviour, - } -} - -// GetChainID returns the chain-id -func (cs ClientState) GetChainID() string { - return cs.ChainId -} - -// ClientType is tendermint. -func (cs ClientState) ClientType() string { - return exported.Tendermint -} - -// GetLatestHeight returns latest block height. -func (cs ClientState) GetLatestHeight() exported.Height { - return cs.LatestHeight -} - -// Status returns the status of the tendermint client. -// The client may be: -// - Active: FrozenHeight is zero and client is not expired -// - Frozen: Frozen Height is not zero -// - Expired: the latest consensus state timestamp + trusting period <= current time -// -// A frozen client will become expired, so the Frozen status -// has higher precedence. -func (cs ClientState) Status( - ctx sdk.Context, - clientStore sdk.KVStore, - cdc codec.BinaryCodec, -) exported.Status { - if !cs.FrozenHeight.IsZero() { - return exported.Frozen - } - - // get latest consensus state from clientStore to check for expiry - consState, err := GetConsensusState(clientStore, cdc, cs.GetLatestHeight()) - if err != nil { - // 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()) { - return exported.Expired - } - - return exported.Active -} - -// IsExpired returns whether or not the client has passed the trusting period since the last -// update (in which case no headers are considered valid). -func (cs ClientState) IsExpired(latestTimestamp, now time.Time) bool { - expirationTime := latestTimestamp.Add(cs.TrustingPeriod) - return !expirationTime.After(now) -} - -// Validate performs a basic validation of the client state fields. -func (cs ClientState) Validate() error { - if strings.TrimSpace(cs.ChainId) == "" { - return sdkerrors.Wrap(ErrInvalidChainID, "chain id cannot be empty string") - } - - // NOTE: the value of tmtypes.MaxChainIDLen may change in the future. - // If this occurs, the code here must account for potential difference - // between the tendermint version being run by the counterparty chain - // and the tendermint version used by this light client. - // https://github.com/cosmos/ibc-go/issues/177 - if len(cs.ChainId) > tmtypes.MaxChainIDLen { - return sdkerrors.Wrapf(ErrInvalidChainID, "chainID is too long; got: %d, max: %d", len(cs.ChainId), tmtypes.MaxChainIDLen) - } - - if err := light.ValidateTrustLevel(cs.TrustLevel.ToTendermint()); err != nil { - return err - } - if cs.TrustingPeriod == 0 { - return sdkerrors.Wrap(ErrInvalidTrustingPeriod, "trusting period cannot be zero") - } - if cs.UnbondingPeriod == 0 { - return sdkerrors.Wrap(ErrInvalidUnbondingPeriod, "unbonding period cannot be zero") - } - if cs.MaxClockDrift == 0 { - return sdkerrors.Wrap(ErrInvalidMaxClockDrift, "max clock drift cannot be zero") - } - - // the latest height revision number must match the chain id revision number - if cs.LatestHeight.RevisionNumber != clienttypes.ParseChainID(cs.ChainId) { - return sdkerrors.Wrapf(ErrInvalidHeaderHeight, - "latest height revision number must match chain id revision number (%d != %d)", cs.LatestHeight.RevisionNumber, clienttypes.ParseChainID(cs.ChainId)) - } - if cs.LatestHeight.RevisionHeight == 0 { - return sdkerrors.Wrapf(ErrInvalidHeaderHeight, "tendermint client's latest height revision height cannot be zero") - } - if cs.TrustingPeriod >= cs.UnbondingPeriod { - return sdkerrors.Wrapf( - ErrInvalidTrustingPeriod, - "trusting period (%s) should be < unbonding period (%s)", cs.TrustingPeriod, cs.UnbondingPeriod, - ) - } - - if cs.ProofSpecs == nil { - return sdkerrors.Wrap(ErrInvalidProofSpecs, "proof specs cannot be nil for tm client") - } - for i, spec := range cs.ProofSpecs { - if spec == nil { - return sdkerrors.Wrapf(ErrInvalidProofSpecs, "proof spec cannot be nil at index: %d", i) - } - } - // UpgradePath may be empty, but if it isn't, each key must be non-empty - for i, k := range cs.UpgradePath { - if strings.TrimSpace(k) == "" { - return sdkerrors.Wrapf(clienttypes.ErrInvalidClient, "key in upgrade path at index %d cannot be empty", i) - } - } - - return nil -} - -// GetProofSpecs returns the format the client expects for proof verification -// as a string array specifying the proof type for each position in chained proof -func (cs ClientState) GetProofSpecs() []*ics23.ProofSpec { - return cs.ProofSpecs -} - -// ZeroCustomFields returns a ClientState that is a copy of the current ClientState -// with all client customizable fields zeroed out -func (cs ClientState) ZeroCustomFields() exported.ClientState { - // copy over all chain-specified fields - // and leave custom fields empty - return &ClientState{ - ChainId: cs.ChainId, - UnbondingPeriod: cs.UnbondingPeriod, - LatestHeight: cs.LatestHeight, - ProofSpecs: cs.ProofSpecs, - UpgradePath: cs.UpgradePath, - } -} - -// Initialize will check that initial consensus state is a Tendermint consensus state -// and will store ProcessedTime for initial consensus state as ctx.BlockTime() -func (cs ClientState) Initialize(ctx sdk.Context, _ codec.BinaryCodec, clientStore sdk.KVStore, consState exported.ConsensusState) error { - if _, ok := consState.(*ConsensusState); !ok { - return sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "invalid initial consensus state. expected type: %T, got: %T", - &ConsensusState{}, consState) - } - // set metadata for initial consensus state. - setConsensusMetadata(ctx, clientStore, cs.GetLatestHeight()) - return nil -} - -// VerifyClientState verifies a proof of the client state of the running chain -// stored on the target 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 { - 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. -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. -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. -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. -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. -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. -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. -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 -} - -// 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) - } - 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. -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, 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") - } - - 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 deleted file mode 100644 index 9c9a781fdb3..00000000000 --- a/modules/light-clients/07-tendermint/types/client_state_test.go +++ /dev/null @@ -1,880 +0,0 @@ -package types_test - -import ( - "time" - - ics23 "github.com/confio/ics23/go" - - 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/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/ibc-go/v5/testing" - ibcmock "github.com/cosmos/ibc-go/v5/testing/mock" -) - -const ( - testClientID = "clientidone" - testConnectionID = "connectionid" - testPortID = "testportid" - testChannelID = "testchannelid" - testSequence = 1 - - // Do not change the length of these variables - fiftyCharChainID = "12345678901234567890123456789012345678901234567890" - fiftyOneCharChainID = "123456789012345678901234567890123456789012345678901" -) - -var invalidProof = []byte("invalid proof") - -func (suite *TendermintTestSuite) TestStatus() { - var ( - path *ibctesting.Path - clientState *types.ClientState - ) - - testCases := []struct { - name string - malleate func() - expStatus exported.Status - }{ - {"client is active", func() {}, exported.Active}, - {"client is frozen", func() { - clientState.FrozenHeight = clienttypes.NewHeight(0, 1) - path.EndpointA.SetClientState(clientState) - }, exported.Frozen}, - {"client status without consensus state", func() { - clientState.LatestHeight = clientState.LatestHeight.Increment().(clienttypes.Height) - path.EndpointA.SetClientState(clientState) - }, exported.Expired}, - {"client status is expired", func() { - suite.coordinator.IncrementTimeBy(clientState.TrustingPeriod) - }, exported.Expired}, - } - - for _, tc := range testCases { - path = ibctesting.NewPath(suite.chainA, suite.chainB) - suite.coordinator.SetupClients(path) - - clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - clientState = path.EndpointA.GetClientState().(*types.ClientState) - - tc.malleate() - - status := clientState.Status(suite.chainA.GetContext(), clientStore, suite.chainA.App.AppCodec()) - suite.Require().Equal(tc.expStatus, status) - - } -} - -func (suite *TendermintTestSuite) TestValidate() { - testCases := []struct { - name string - clientState *types.ClientState - expPass bool - }{ - { - name: "valid client", - clientState: types.NewClientState(chainID, types.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), - expPass: true, - }, - { - name: "invalid chainID", - clientState: types.NewClientState(" ", types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), - expPass: false, - }, - { - // NOTE: if this test fails, the code must account for the change in chainID length across tendermint versions! - // 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), - expPass: true, - }, - { - // NOTE: if this test fails, the code must account for the change in chainID length across tendermint versions! - // 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), - 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), - expPass: false, - }, - { - name: "invalid trusting period", - clientState: types.NewClientState(chainID, types.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), - expPass: false, - }, - { - name: "invalid max clock drift", - clientState: types.NewClientState(chainID, types.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), - expPass: false, - }, - { - name: "invalid revision height", - clientState: types.NewClientState(chainID, types.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), - expPass: false, - }, - { - name: "proof specs is nil", - clientState: types.NewClientState(chainID, types.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), - 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 *TendermintTestSuite) TestInitialize() { - testCases := []struct { - name string - consensusState exported.ConsensusState - expPass bool - }{ - { - name: "valid consensus", - consensusState: &types.ConsensusState{}, - expPass: true, - }, - { - name: "invalid consensus: consensus state is solomachine consensus", - consensusState: ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2).ConsensusState(), - expPass: false, - }, - } - - path := ibctesting.NewPath(suite.chainA, suite.chainB) - err := path.EndpointA.CreateClient() - suite.Require().NoError(err) - - clientState := suite.chainA.GetClientState(path.EndpointA.ClientID) - store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - - for _, tc := range testCases { - err := clientState.Initialize(suite.chainA.GetContext(), suite.chainA.Codec, store, tc.consensusState) - if tc.expPass { - suite.Require().NoError(err, "valid case returned an error") - } else { - suite.Require().Error(err, "invalid case didn't return an error") - } - } -} - -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 -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 -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. -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(0, 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. -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(0, 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(0, 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. -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(0, 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) - } - }) - } -} diff --git a/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go b/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go deleted file mode 100644 index 6e699dcaa49..00000000000 --- a/modules/light-clients/07-tendermint/types/misbehaviour_handle_test.go +++ /dev/null @@ -1,425 +0,0 @@ -package types_test - -import ( - "fmt" - "time" - - "github.com/tendermint/tendermint/crypto/tmhash" - 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" - "github.com/cosmos/ibc-go/v5/modules/core/exported" - "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint/types" - ibctestingmock "github.com/cosmos/ibc-go/v5/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.Misbehaviour - 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) - } - }) - } -} diff --git a/modules/light-clients/07-tendermint/types/update_test.go b/modules/light-clients/07-tendermint/types/update_test.go deleted file mode 100644 index aaf1936d8d7..00000000000 --- a/modules/light-clients/07-tendermint/types/update_test.go +++ /dev/null @@ -1,450 +0,0 @@ -package types_test - -import ( - "fmt" - "time" - - 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" - "github.com/cosmos/ibc-go/v5/modules/core/exported" - types "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/ibc-go/v5/testing" - ibctestingmock "github.com/cosmos/ibc-go/v5/testing/mock" -) - -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) - 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) - - testCases := []struct { - name string - setup func(*TendermintTestSuite) - 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) { - 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: "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) { - 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 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) { - 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, - }, - { - 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 { - 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) TestPruneConsensusState() { - // create path and setup clients - path := ibctesting.NewPath(suite.chainA, suite.chainB) - suite.coordinator.SetupClients(path) - - // get the first height as it will be pruned first. - var pruneHeight exported.Height - getFirstHeightCb := func(height exported.Height) bool { - pruneHeight = height - return true - } - ctx := path.EndpointA.Chain.GetContext() - clientStore := path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) - err := types.IterateConsensusStateAscending(clientStore, getFirstHeightCb) - suite.Require().Nil(err) - - // this height will be expired but not pruned - path.EndpointA.UpdateClient() - expiredHeight := path.EndpointA.GetClientState().GetLatestHeight() - - // expected values that must still remain in store after pruning - expectedConsState, ok := path.EndpointA.Chain.GetConsensusState(path.EndpointA.ClientID, expiredHeight) - 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) - suite.Require().True(ok) - expectedProcessHeight, ok := types.GetProcessedHeight(clientStore, expiredHeight) - suite.Require().True(ok) - expectedConsKey := types.GetIterationKey(clientStore, expiredHeight) - suite.Require().NotNil(expectedConsKey) - - // 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 - path.EndpointA.UpdateClient() - - // 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) - path.EndpointA.UpdateClient() - - ctx = path.EndpointA.Chain.GetContext() - clientStore = path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) - - // check that the first expired consensus state got deleted along with all associated metadata - consState, ok := path.EndpointA.Chain.GetConsensusState(path.EndpointA.ClientID, pruneHeight) - 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) - suite.Require().Equal(uint64(0), processTime, "processed time metadata not pruned") - suite.Require().False(ok) - processHeight, ok := types.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) - suite.Require().Nil(consKey, "iteration key not pruned") - - // check that second expired consensus state doesn't get deleted - // this ensures that there is a cap on gas cost of UpdateClient - consState, ok = path.EndpointA.Chain.GetConsensusState(path.EndpointA.ClientID, expiredHeight) - 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) - 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) - 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) - suite.Require().Equal(expectedConsKey, consKey, "iteration key incorrectly pruned") -} diff --git a/modules/light-clients/07-tendermint/types/update.go b/modules/light-clients/07-tendermint/update.go similarity index 50% rename from modules/light-clients/07-tendermint/types/update.go rename to modules/light-clients/07-tendermint/update.go index 90243235a52..32b5857ad19 100644 --- a/modules/light-clients/07-tendermint/types/update.go +++ b/modules/light-clients/07-tendermint/update.go @@ -1,9 +1,9 @@ -package types +package tendermint import ( "bytes" + "fmt" "reflect" - "time" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -13,13 +13,26 @@ 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" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" "github.com/cosmos/ibc-go/v5/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: +// 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, + 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 @@ -27,150 +40,18 @@ import ( // - 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 -// -// 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). -// -// 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). -// -// 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.Header, -) (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 - } - - // 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 { - 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 - } - - // 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 - } - err = IterateConsensusStateAscending(clientStore, pruneCb) - if err != nil { - return nil, nil, err - } - 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 := update(ctx, clientStore, &cs, tmHeader) - return newClientState, consensusState, nil -} - -// 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") - } +func (cs *ClientState) verifyHeader( + ctx sdk.Context, clientStore sdk.KVStore, cdc codec.BinaryCodec, + header *Header, +) error { + currentTimestamp := ctx.BlockTime() - // 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, - ) + // Retrieve trusted consensus states for each Header in misbehaviour + 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) } - 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, -) error { if err := checkTrustedHeader(header, consState); err != nil { return err } @@ -208,21 +89,11 @@ func checkValidity( ) } - chainID := clientState.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, @@ -239,28 +110,160 @@ 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 } -// 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 +// 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) []exported.Height { + header, ok := clientMsg.(*Header) + if !ok { + 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 []exported.Height{header.GetHeight()} + } + + 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(), Root: commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), 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 clientState, consensusState + return []exported.Height{height} +} + +// 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 + ) + + pruneCb := func(height exported.Height) bool { + consState, found := GetConsensusState(clientStore, cdc, height) + // this error should never occur + if !found { + panic(sdkerrors.Wrapf(clienttypes.ErrConsensusStateNotFound, "failed to retrieve consensus state at height: %s", height)) + } + + if cs.IsExpired(consState.Timestamp, ctx.BlockTime()) { + pruneHeight = height + } + + return true + } + + IterateConsensusStateAscending(clientStore, pruneCb) + + // if pruneHeight is set, delete consensus state and metadata + if pruneHeight != nil { + deleteConsensusState(clientStore, pruneHeight) + deleteConsensusMetadata(clientStore, pruneHeight) + } +} + +// 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. + 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(existingConsState, tmHeader.ConsensusState()) { //nolint:gosimple + 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, _ exported.ClientMessage) { + cs.FrozenHeight = FrozenHeight + + 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 } diff --git a/modules/light-clients/07-tendermint/update_test.go b/modules/light-clients/07-tendermint/update_test.go new file mode 100644 index 00000000000..0b5b55eb1cc --- /dev/null +++ b/modules/light-clients/07-tendermint/update_test.go @@ -0,0 +1,745 @@ +package tendermint_test + +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" + "github.com/cosmos/ibc-go/v5/modules/core/exported" + 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" +) + +func (suite *TendermintTestSuite) TestVerifyHeader() { + var ( + path *ibctesting.Path + header *ibctm.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, + }, + { + 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) + }, + 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) + }, + 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) + }, + 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 { + 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) + + 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, header) + + if tc.expPass { + suite.Require().NoError(err, tc.name) + } else { + suite.Require().Error(err) + } + } +} + +func (suite *TendermintTestSuite) TestUpdateState() { + var ( + path *ibctesting.Path + clientMessage exported.ClientMessage + clientStore sdk.KVStore + consensusHeights []exported.Height + pruneHeight clienttypes.Height + prevClientState exported.ClientState + prevConsensusState exported.ConsensusState + ) + + testCases := []struct { + name string + malleate func() + expResult func() + expPass bool + }{ + { + "success with height later than latest height", func() { + tmHeader, ok := clientMessage.(*ibctm.Header) + suite.Require().True(ok) + suite.Require().True(path.EndpointA.GetClientState().GetLatestHeight().LT(tmHeader.GetHeight())) + }, + func() { + tmHeader, ok := clientMessage.(*ibctm.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, + }, + { + "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) + + tmHeader, ok := clientMessage.(*ibctm.Header) + suite.Require().True(ok) + suite.Require().True(path.EndpointA.GetClientState().GetLatestHeight().GT(tmHeader.GetHeight())) + + prevClientState = path.EndpointA.GetClientState() + }, + func() { + 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, + }, + { + "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) + + tmHeader, ok := clientMessage.(*ibctm.Header) + suite.Require().True(ok) + suite.Require().Equal(path.EndpointA.GetClientState().GetLatestHeight(), tmHeader.GetHeight()) + + prevClientState = path.EndpointA.GetClientState() + prevConsensusState = path.EndpointA.GetConsensusState(tmHeader.GetHeight()) + }, + func() { + clientState := path.EndpointA.GetClientState() + suite.Require().Equal(clientState, prevClientState) + suite.Require().True(clientState.GetLatestHeight().EQ(consensusHeights[0])) + + tmHeader, ok := clientMessage.(*ibctm.Header) + suite.Require().True(ok) + suite.Require().Equal(path.EndpointA.GetConsensusState(tmHeader.GetHeight()), prevConsensusState) + }, 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() { + tmHeader, ok := clientMessage.(*ibctm.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) + suite.Require().False(found) + }, true, + }, + { + "invalid ClientMessage type", func() { + clientMessage = &ibctm.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() + clientStore = suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + + if tc.expPass { + consensusHeights = clientState.UpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, clientMessage) + + header := clientMessage.(*ibctm.Header) + expConsensusState := &ibctm.ConsensusState{ + Timestamp: header.GetTime(), + 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().Panics(func() { + clientState.UpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, clientMessage) + }) + } + + // perform custom checks + tc.expResult() + }) + } +} + +func (suite *TendermintTestSuite) TestPruneConsensusState() { + // create path and setup clients + path := ibctesting.NewPath(suite.chainA, suite.chainB) + suite.coordinator.SetupClients(path) + + // get the first height as it will be pruned first. + var pruneHeight exported.Height + getFirstHeightCb := func(height exported.Height) bool { + pruneHeight = height + return true + } + ctx := path.EndpointA.Chain.GetContext() + clientStore := path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) + ibctm.IterateConsensusStateAscending(clientStore, getFirstHeightCb) + + // this height will be expired but not pruned + path.EndpointA.UpdateClient() + expiredHeight := path.EndpointA.GetClientState().GetLatestHeight() + + // expected values that must still remain in store after pruning + expectedConsState, ok := path.EndpointA.Chain.GetConsensusState(path.EndpointA.ClientID, expiredHeight) + suite.Require().True(ok) + ctx = path.EndpointA.Chain.GetContext() + clientStore = path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) + expectedProcessTime, ok := ibctm.GetProcessedTime(clientStore, expiredHeight) + suite.Require().True(ok) + expectedProcessHeight, ok := ibctm.GetProcessedHeight(clientStore, expiredHeight) + suite.Require().True(ok) + expectedConsKey := ibctm.GetIterationKey(clientStore, expiredHeight) + suite.Require().NotNil(expectedConsKey) + + // 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 + path.EndpointA.UpdateClient() + + // 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) + path.EndpointA.UpdateClient() + + ctx = path.EndpointA.Chain.GetContext() + clientStore = path.EndpointA.Chain.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, path.EndpointA.ClientID) + + // check that the first expired consensus state got deleted along with all associated metadata + consState, ok := path.EndpointA.Chain.GetConsensusState(path.EndpointA.ClientID, pruneHeight) + suite.Require().Nil(consState, "expired consensus state not pruned") + suite.Require().False(ok) + // check processed time metadata is pruned + processTime, ok := ibctm.GetProcessedTime(clientStore, pruneHeight) + suite.Require().Equal(uint64(0), processTime, "processed time metadata not pruned") + suite.Require().False(ok) + 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 := ibctm.GetIterationKey(clientStore, pruneHeight) + suite.Require().Nil(consKey, "iteration key not pruned") + + // check that second expired consensus state doesn't get deleted + // this ensures that there is a cap on gas cost of UpdateClient + consState, ok = path.EndpointA.Chain.GetConsensusState(path.EndpointA.ClientID, expiredHeight) + suite.Require().Equal(expectedConsState, consState, "consensus state incorrectly pruned") + suite.Require().True(ok) + // check processed time metadata is not pruned + 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 = 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 = ibctm.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.(*ibctm.Header) + suite.Require().True(ok) + + consensusState := &ibctm.ConsensusState{ + Timestamp: header.GetTime(), + Root: commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), + NextValidatorsHash: header.Header.NextValidatorsHash, + } + + tmHeader, ok := clientMessage.(*ibctm.Header) + suite.Require().True(ok) + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), path.EndpointA.ClientID, tmHeader.GetHeight(), consensusState) + }, + false, + }, + { + "consensus state already exists, app hash mismatch", + func() { + header, ok := clientMessage.(*ibctm.Header) + suite.Require().True(ok) + + consensusState := &ibctm.ConsensusState{ + Timestamp: header.GetTime(), + Root: commitmenttypes.NewMerkleRoot([]byte{}), // empty bytes + NextValidatorsHash: header.Header.NextValidatorsHash, + } + + tmHeader, ok := clientMessage.(*ibctm.Header) + suite.Require().True(ok) + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), path.EndpointA.ClientID, tmHeader.GetHeight(), consensusState) + }, + true, + }, + { + "previous consensus state exists and header time is before previous consensus state time", + func() { + header, ok := clientMessage.(*ibctm.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.(*ibctm.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 = &ibctm.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() + clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + + foundMisbehaviour := clientState.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) + + clientState.UpdateStateOnMisbehaviour(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, nil) + + if tc.expPass { + clientStateBz := clientStore.Get(host.ClientStateKey()) + suite.Require().NotEmpty(clientStateBz) + + newClientState := clienttypes.MustUnmarshalClientState(suite.chainA.Codec, clientStateBz) + suite.Require().Equal(frozenHeight, newClientState.(*ibctm.ClientState).FrozenHeight) + } + }) + } +} diff --git a/modules/light-clients/07-tendermint/types/upgrade.go b/modules/light-clients/07-tendermint/upgrade.go similarity index 78% rename from modules/light-clients/07-tendermint/types/upgrade.go rename to modules/light-clients/07-tendermint/upgrade.go index 07ce9823d2e..277ced61273 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" @@ -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 @@ -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,52 +46,52 @@ 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 // 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 nil, nil, 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 - bz, err := cdc.MarshalInterface(upgradedClient) + bz, err := cdc.MarshalInterface(upgradedClient.ZeroCustomFields()) 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/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 fb21057a1e7..301b64288c9 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,14 +6,13 @@ 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" - "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint/types" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v5/testing" ) -var newChainId = "newChainId-1" - func (suite *TendermintTestSuite) TestVerifyUpgrade() { var ( + newChainID string upgradedClient exported.ClientState upgradedConsState exported.ConsensusState lastHeight clienttypes.Height @@ -55,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 = 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) @@ -112,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 = 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) @@ -148,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 = ibctm.NewClientState("wrongchainID", ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, true, true) suite.coordinator.CommitBlock(suite.chainB) err := path.EndpointA.UpdateClient() @@ -170,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 = 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() @@ -195,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 = &ibctm.ConsensusState{ NextValidatorsHash: []byte("maliciousValidators"), } @@ -299,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.(*ibctm.ClientState) tmClient.UpgradePath = []string{""} suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID, tmClient) }, @@ -401,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 = 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) @@ -436,12 +433,20 @@ 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().(*ibctm.ClientState) + revisionNumber := clienttypes.ParseChainID(clientState.ChainId) + + var err error + newChainID, err = clienttypes.SetRevisionNumber(clientState.ChainId, revisionNumber+1) + suite.Require().NoError(err) + + 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 = &types.ConsensusState{ + upgradedConsState = &ibctm.ConsensusState{ NextValidatorsHash: []byte("nextValsHash"), } upgradedConsStateBz, err = clienttypes.MarshalConsensusState(suite.chainA.App.AppCodec(), upgradedConsState) @@ -455,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() - clientState, consensusState, err := cs.VerifyUpgradeAndUpdateState( + err = cs.VerifyUpgradeAndUpdateState( suite.chainA.GetContext(), suite.cdc, clientStore, @@ -467,14 +472,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/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 cf1615a8023..00000000000 --- a/modules/light-clients/09-localhost/module.go +++ /dev/null @@ -1,10 +0,0 @@ -package localhost - -import ( - "github.com/cosmos/ibc-go/v5/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 ceda52b2354..00000000000 --- a/modules/light-clients/09-localhost/types/client_state.go +++ /dev/null @@ -1,340 +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/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/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, _ 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). - cs.ChainId = ctx.ChainID() - revision := clienttypes.ParseChainID(cs.ChainId) - cs.Height = clienttypes.NewHeight(revision, uint64(ctx.BlockHeight())) - return cs, nil, nil -} - -// 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.Misbehaviour, -) (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, -) (exported.ClientState, exported.ConsensusState, error) { - return nil, nil, sdkerrors.Wrap(clienttypes.ErrInvalidUpgradeClient, "cannot upgrade localhost client") -} - -// 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 83eb0e8ea51..00000000000 --- a/modules/light-clients/09-localhost/types/client_state_test.go +++ /dev/null @@ -1,526 +0,0 @@ -package types_test - -import ( - sdk "github.com/cosmos/cosmos-sdk/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" - 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/types" - "github.com/cosmos/ibc-go/v5/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 0e0a31d31d7..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/v5/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 7c674191e64..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/v5/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, 0x99, 0xea, 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, 0x75, 0xe7, 0x2e, 0x11, 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 50bfdcb62f9..00000000000 --- a/modules/light-clients/09-localhost/types/localhost_test.go +++ /dev/null @@ -1,41 +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/v5/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v5/modules/core/exported" - "github.com/cosmos/ibc-go/v5/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/core/client/v1/tx.proto b/proto/ibc/core/client/v1/tx.proto index 8e716226d95..27f81917e4c 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; } @@ -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 diff --git a/proto/ibc/lightclients/localhost/v1/localhost.proto b/proto/ibc/lightclients/localhost/v1/localhost.proto deleted file mode 100644 index 909c4ebcef5..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/v5/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]; -} diff --git a/proto/ibc/lightclients/solomachine/v2/solomachine.proto b/proto/ibc/lightclients/solomachine/v2/solomachine.proto index 61dcc45b2d7..5cc5ba9c551 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/v5/modules/light-clients/06-solomachine/types"; +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 new file mode 100644 index 00000000000..5475a16bd93 --- /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/v5/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/proto/ibc/lightclients/tendermint/v1/tendermint.proto b/proto/ibc/lightclients/tendermint/v1/tendermint.proto index 48b47aee6cb..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/v5/modules/light-clients/07-tendermint/types"; +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"; @@ -79,7 +79,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\""]; } diff --git a/testing/README.md b/testing/README.md index c682ba9ed8e..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/v4/modules/apps/transfer/simapp" - ibctesting "github.com/cosmos/ibc-go/v4/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/chain.go b/testing/chain.go index ab7f707007c..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/types" + 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 @@ -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, @@ -385,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() { @@ -413,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 @@ -437,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 @@ -502,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 2090681a53e..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/types" + 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/coordinator.go b/testing/coordinator.go index f2cc49f9a91..6374a17b170 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 b44fcad035f..8331980da93 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" @@ -12,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/types" + ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" ) // Endpoint is a which represents a channel endpoint and its associated @@ -91,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, ) @@ -131,7 +132,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: @@ -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().(*ibctm.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 := &ibctm.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( diff --git a/testing/solomachine.go b/testing/solomachine.go index 50974e58268..d22e67afd24 100644 --- a/testing/solomachine.go +++ b/testing/solomachine.go @@ -16,7 +16,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" - solomachinetypes "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine/types" + solomachinetypes "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" ) // Solomachine is a testing helper used to simulate a counterparty @@ -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, } diff --git a/testing/values.go b/testing/values.go index 46df3b5ba27..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/types" + 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"}