diff --git a/spec/client/ics-007-tendermint-client/README.md b/spec/client/ics-007-tendermint-client/README.md index f481a42c1..8386d3d35 100644 --- a/spec/client/ics-007-tendermint-client/README.md +++ b/spec/client/ics-007-tendermint-client/README.md @@ -8,7 +8,7 @@ implements: 2 version compatibility: ibc-go v7.0.0 author: Christopher Goes created: 2019-12-10 -modified: 2019-12-19 +modified: 2024-08-19 --- ## Synopsis @@ -59,7 +59,7 @@ This specification depends on correct instantiation of the [Tendermint consensus ### Client state -The Tendermint client state tracks the current revision, current validator set, trusting period, unbonding period, latest height, latest timestamp (block time), and a possible frozen height. +The Tendermint client state tracks the current revision, current validator set, trusting period, unbonding period, delayTimePeriod, delayBlockPeriod, latest height, latest timestamp (block time), and a possible frozen height. ```typescript interface ClientState { @@ -67,6 +67,8 @@ interface ClientState { trustLevel: Rational trustingPeriod: uint64 unbondingPeriod: uint64 + delayTimePeriod: uint64 + delayBlockPeriod: uint64 latestHeight: Height frozenHeight: Maybe upgradePath: []string @@ -377,8 +379,6 @@ These functions utilise the `proofSpecs` with which the client was initialised. function verifyMembership( clientState: ClientState, height: Height, - delayTimePeriod: uint64, - delayBlockPeriod: uint64, proof: CommitmentProof, path: CommitmentPath, value: []byte @@ -388,9 +388,9 @@ function verifyMembership( // check that the client is unfrozen or frozen at a higher height assert(clientState.frozenHeight === null || clientState.frozenHeight > height) // assert that enough time has elapsed - assert(currentTimestamp() >= processedTime + delayPeriodTime) + assert(currentTimestamp() >= processedTime + clientState.delayTimePeriod) // assert that enough blocks have elapsed - assert(currentHeight() >= processedHeight + delayPeriodBlocks) + assert(currentHeight() >= processedHeight + clientState.delayBlockPeriod) // fetch the previously verified commitment root & verify membership // Implementations may choose how to pass in the identifier // ibc-go provides the identifier-prefixed store to this method @@ -406,8 +406,6 @@ function verifyMembership( function verifyNonMembership( clientState: ClientState, height: Height, - delayTimePeriod: uint64, - delayBlockPeriod: uint64, proof: CommitmentProof, path: CommitmentPath ): Error { @@ -416,9 +414,9 @@ function verifyNonMembership( // check that the client is unfrozen or frozen at a higher height assert(clientState.frozenHeight === null || clientState.frozenHeight > height) // assert that enough time has elapsed - assert(currentTimestamp() >= processedTime + delayPeriodTime) + assert(currentTimestamp() >= processedTime + clientState.delayTimePeriod) // assert that enough blocks have elapsed - assert(currentHeight() >= processedHeight + delayPeriodBlocks) + assert(currentHeight() >= processedHeight + clientState.delayBlockPeriod) // fetch the previously verified commitment root & verify membership // Implementations may choose how to pass in the identifier // ibc-go provides the identifier-prefixed store to this method @@ -452,8 +450,11 @@ Not applicable. Alterations to the client verification algorithm will require a ## History December 10th, 2019 - Initial version + December 19th, 2019 - Final first draft +August 19th, 2024 - [Support for IBC/TAO V2](https://github.com/cosmos/ibc/pull/1137) + ## Copyright All content herein is licensed under [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0). diff --git a/spec/client/ics-007-tendermint-client/TAO_V1_README.md b/spec/client/ics-007-tendermint-client/TAO_V1_README.md new file mode 100644 index 000000000..f481a42c1 --- /dev/null +++ b/spec/client/ics-007-tendermint-client/TAO_V1_README.md @@ -0,0 +1,459 @@ +--- +ics: 7 +title: Tendermint Client +stage: draft +category: IBC/TAO +kind: instantiation +implements: 2 +version compatibility: ibc-go v7.0.0 +author: Christopher Goes +created: 2019-12-10 +modified: 2019-12-19 +--- + +## Synopsis + +This specification document describes a client (verification algorithm) for a blockchain using Tendermint consensus. + +### Motivation + +State machines of various sorts replicated using the Tendermint consensus algorithm might like to interface with other replicated state machines or solo machines over IBC. + +### Definitions + +Functions & terms are as defined in [ICS 2](../../core/ics-002-client-semantics). + +`currentTimestamp` is as defined in [ICS 24](../../core/ics-024-host-requirements). + +The Tendermint light client uses the generalised Merkle proof format as defined in [ICS 23](../../core/ics-023-vector-commitments). + +`hash` is a generic collision-resistant hash function, and can easily be configured. + +### Desired Properties + +This specification must satisfy the client interface defined in ICS 2. + +#### Note on "would-have-been-fooled logic + +The basic idea of "would-have-been-fooled" detection is that it allows us to be a bit more conservative, and freeze our light client when we know that another light client somewhere else on the network with a slightly different update pattern could have been fooled, even though we weren't. + +Consider a topology of three chains - `A`, `B`, and `C`, and two clients for chain `A`, `A_1` and `A_2`, running on chains `B` and `C` respectively. The following sequence of events occurs: + +- Chain `A` produces a block at height `h_0` (correctly). +- Clients `A_1` and `A_2` are updated to the block at height `h_0`. +- Chain `A` produces a block at height `h_0 + n` (correctly). +- Client `A_1` is updated to the block at height `h_0 + n` (client `A_2` is not yet updated). +- Chain `A` produces a second (equivocating) block at height `h_0 + k`, where `k <= n`. + +*Without* "would-have-been-fooled", it will be possible to freeze client `A_2` (since there are two valid blocks at height `h_0 + k` which are newer than the latest header `A_2` knows), but it will *not* be possible to freeze `A_1`, since `A_1` has already progressed beyond `h_0 + k`. + +Arguably, this is disadvantageous, since `A_1` was just "lucky" in having been updated when `A_2` was not, and clearly some Byzantine fault has happened that should probably be dealt with by human or governance system intervention. The idea of "would-have-been-fooled" is to allow this to be detected by having `A_1` start from a configurable past header to detect misbehaviour (so in this case, `A_1` would be able to start from `h_0` and would also be frozen). + +There is a free parameter here - namely, how far back is `A_1` willing to go (how big can `n` be where `A_1` will still be willing to look up `h_0`, having been updated to `h_0 + n`)? There is also a countervailing concern, in and of that double-signing is presumed to be costless after the unbonding period has passed, and we don't want to open up a denial-of-service vector for IBC clients. + +The necessary condition is thus that `A_1` should be willing to look up headers as old as it has stored, but should also enforce the "unbonding period" check on the misbehaviour, and avoid freezing the client if the misbehaviour is older than the unbonding period (relative to the client's local timestamp). If there are concerns about clock skew a slight delta could be added. + +## Technical Specification + +This specification depends on correct instantiation of the [Tendermint consensus algorithm](https://github.com/tendermint/spec/blob/master/spec/consensus/consensus.md) and [light client algorithm](https://github.com/tendermint/spec/blob/master/spec/light-client). + +### Client state + +The Tendermint client state tracks the current revision, current validator set, trusting period, unbonding period, latest height, latest timestamp (block time), and a possible frozen height. + +```typescript +interface ClientState { + chainID: string + trustLevel: Rational + trustingPeriod: uint64 + unbondingPeriod: uint64 + latestHeight: Height + frozenHeight: Maybe + upgradePath: []string + maxClockDrift: uint64 + proofSpecs: []ProofSpec +} +``` + +### Consensus state + +The Tendermint client tracks the timestamp (block time), the hash of the next validator set, and commitment root for all previously verified consensus states (these can be pruned after the unbonding period has passed, but should not be pruned beforehand). + +```typescript +interface ConsensusState { + timestamp: uint64 + nextValidatorsHash: []byte + commitmentRoot: []byte +} +``` + +### Height + +The height of a Tendermint client consists of two `uint64`s: the revision number, and the height in the revision. + +```typescript +interface Height { + revisionNumber: uint64 + revisionHeight: uint64 +} +``` + +Comparison between heights is implemented as follows: + +```typescript +function compare(a: TendermintHeight, b: TendermintHeight): Ord { + if (a.revisionNumber < b.revisionNumber) + return LT + else if (a.revisionNumber === b.revisionNumber) + if (a.revisionHeight < b.revisionHeight) + return LT + else if (a.revisionHeight === b.revisionHeight) + return EQ + return GT +} +``` + +This is designed to allow the height to reset to `0` while the revision number increases by one in order to preserve timeouts through zero-height upgrades. + +### Headers + +The Tendermint headers include the height, the timestamp, the commitment root, the hash of the validator set, the hash of the next validator set, and the signatures by the validators who committed the block. The header submitted to the on-chain client also includes the entire validator set, and a trusted height and validator set to update from. This reduces the amount of state maintained by the on-chain client and prevents race conditions on relayer updates. + +```typescript +interface TendermintSignedHeader { + height: uint64 + timestamp: uint64 + commitmentRoot: []byte + validatorsHash: []byte + nextValidatorsHash: []byte + signatures: []Signature +} +``` + +```typescript +interface Header { + TendermintSignedHeader + identifier: string + validatorSet: List> + trustedHeight: Height + trustedValidatorSet: List> +} + +// GetHeight will return the header Height in the IBC ClientHeight +// format. +// Implementations may use the revision number to increment the height +// across height-resetting upgrades. See ibc-go for an example +func (Header) GetHeight() { + return Height{0, height} +} +``` + +Header implements `ClientMessage` interface. + +### `Misbehaviour` + +The `Misbehaviour` type is used for detecting misbehaviour and freezing the client - to prevent further packet flow - if applicable. +Tendermint client `Misbehaviour` consists of two headers at the same height both of which the light client would have considered valid. + +```typescript +interface Misbehaviour { + identifier: string + h1: Header + h2: Header +} +``` + +Misbehaviour implements `ClientMessage` interface. + +### Client initialisation + +Tendermint client initialisation requires a (subjectively chosen) latest consensus state, including the full validator set. + +```typescript +function initialise( + identifier: Identifier, + clientState: ClientState, + consensusState: ConsensusState +) { + assert(clientState.trustingPeriod < clientState.unbondingPeriod) + assert(clientState.height > 0) + assert(clientState.trustLevel >= 1/3 && clientState.trustLevel <= 1) + + provableStore.set("clients/{identifier}/clientState", clientState) + provableStore.set("clients/{identifier}/consensusStates/{height}", consensusState) +} +``` + +The Tendermint client `latestClientHeight` function returns the latest stored height, which is updated every time a new (more recent) header is validated. + +```typescript +function latestClientHeight(clientState: ClientState): Height { + return clientState.latestHeight +} +``` + +### Validity predicate + +Tendermint client validity checking uses the bisection algorithm described in the [Tendermint spec](https://github.com/tendermint/spec/tree/master/spec/consensus/light-client). If the provided header is valid, the client state is updated & the newly verified commitment written to the store. + +```typescript +function verifyClientMessage(clientMsg: ClientMessage) { + switch typeof(clientMsg) { + case Header: + verifyHeader(clientMsg) + case Misbehaviour: + verifyHeader(clientMsg.h1) + verifyHeader(clientMsg.h2) + } +} +``` + +Verify validity of regular update to the Tendermint client + +```typescript +function verifyHeader(header: Header) { + clientState = provableStore.get("clients/{header.identifier}/clientState") + // assert trusting period has not yet passed + assert(currentTimestamp() - clientState.latestTimestamp < clientState.trustingPeriod) + // assert header timestamp is less than trust period in the future. This should be resolved with an intermediate header. + assert(header.timestamp - clientState.latestTimeStamp < clientState.trustingPeriod) + // trusted height revision must be the same as header revision + // if revisions are different, use upgrade client instead + // trusted height must be less than header height + assert(header.GetHeight().revisionNumber == header.trustedHeight.revisionNumber) + assert(header.GetHeight().revisionHeight > header.trustedHeight.revisionHeight) + // fetch the consensus state at the trusted height + consensusState = provableStore.get("clients/{header.identifier}/consensusStates/{header.trustedHeight}") + // assert that header's trusted validator set hashes to consensus state's validator hash + assert(hash(header.trustedValidatorSet) == consensusState.nextValidatorsHash) + + // call the tendermint client's `verify` function + assert(tmClient.verify( + header.trustedValidatorSet, + clientState.latestHeight, + clientState.trustingPeriod, + clientState.maxClockDrift, + header.TendermintSignedHeader, + )) +} +``` + +### Misbehaviour predicate + +Function `checkForMisbehaviour` will check if an update contains evidence of Misbehaviour. If the ClientMessage is a header we check for implicit evidence of misbehaviour by checking if there already exists a conflicting consensus state in the store or if the header breaks time monotonicity. + +```typescript +function checkForMisbehaviour(clientMsg: clientMessage): boolean { + clientState = provableStore.get("clients/{clientMsg.identifier}/clientState") + switch typeof(clientMsg) { + case Header: + // fetch consensusstate at header height if it exists + consensusState = provableStore.get("clients/{clientMsg.identifier}/consensusStates/{header.GetHeight()}") + // if consensus state exists and conflicts with the header + // then the header is evidence of misbehaviour + if consensusState != nil && + !( + consensusState.timestamp == header.timestamp && + consensusState.commitmentRoot == header.commitmentRoot && + consensusState.nextValidatorsHash == header.nextValidatorsHash + ) { + return true + } + + // check for time monotonicity misbehaviour + // if header is not monotonically increasing with respect to neighboring consensus states + // then return true + // NOTE: implementation must have ability to iterate ascending/descending by height + prevConsState = getPreviousConsensusState(header.GetHeight()) + nextConsState = getNextConsensusState(header.GetHeight()) + if prevConsState.timestamp >= header.timestamp { + return true + } + if nextConsState != nil && nextConsState.timestamp <= header.timestamp { + return true + } + case Misbehaviour: + if (misbehaviour.h1.height < misbehaviour.h2.height) { + return false + } + // if heights are equal check that this is valid misbehaviour of a fork + if (misbehaviour.h1.height === misbehaviour.h2.height && misbehaviour.h1.commitmentRoot !== misbehaviour.h2.commitmentRoot) { + return true + } + // otherwise if heights are unequal check that this is valid misbehavior of BFT time violation + if (misbehaviour.h1.timestamp <= misbehaviour.h2.timestamp) { + return true + } + + return false + } +} +``` + +### Update state + +Function `updateState` will perform a regular update for the Tendermint client. It will add a consensus state to the client store. If the header is higher than the latest height on the `clientState`, then the `clientState` will be updated. + +```typescript +function updateState(clientMsg: clientMessage) { + clientState = provableStore.get("clients/{clientMsg.identifier}/clientState") + header = Header(clientMessage) + // only update the clientstate if the header height is higher + // than clientState latest height + if clientState.height < header.GetHeight() { + // update latest height + clientState.latestHeight = header.GetHeight() + + // save the client + provableStore.set("clients/{clientMsg.identifier}/clientState", clientState) + } + + // create recorded consensus state, save it + consensusState = ConsensusState{header.timestamp, header.nextValidatorsHash, header.commitmentRoot} + provableStore.set("clients/{clientMsg.identifier}/consensusStates/{header.GetHeight()}", consensusState) + + // these may be stored as private metadata within the client in order to verify + // that the delay period has passed in proof verification + provableStore.set("clients/{clientMsg.identifier}/processedTimes/{header.GetHeight()}", currentTimestamp()) + provableStore.set("clients/{clientMsg.identifier}/processedHeights/{header.GetHeight()}", currentHeight()) +} +``` + +### Update state on misbehaviour + +Function `updateStateOnMisbehaviour` will set the frozen height to a non-zero sentinel height to freeze the entire client. + +```typescript +function updateStateOnMisbehaviour(clientMsg: clientMessage) { + clientState = provableStore.get("clients/{clientMsg.identifier}/clientState") + clientState.frozenHeight = Height{0, 1} + provableStore.set("clients/{clientMsg.identifier}/clientState", clientState) +} +``` + +### Upgrades + +The chain which this light client is tracking can elect to write a special pre-determined key in state to allow the light client to update its client state (e.g. with a new chain ID or revision) in preparation for an upgrade. + +As the client state change will be performed immediately, once the new client state information is written to the predetermined key, the client will no longer be able to follow blocks on the old chain, so it must upgrade promptly. + +```typescript +function upgradeClientState( + clientState: ClientState, + newClientState: ClientState, + height: Height, + proof: CommitmentProof +) { + // assert trusting period has not yet passed + assert(currentTimestamp() - clientState.latestTimestamp < clientState.trustingPeriod) + // check that the revision has been incremented + assert(newClientState.latestHeight.revisionNumber > clientState.latestHeight.revisionNumber) + // check proof of updated client state in state at predetermined commitment prefix and key + path = applyPrefix(clientState.upgradeCommitmentPrefix, clientState.upgradeKey) + // check that the client is at a sufficient height + assert(clientState.latestHeight >= height) + // check that the client is unfrozen or frozen at a higher height + assert(clientState.frozenHeight === null || clientState.frozenHeight > height) + // fetch the previously verified commitment root & verify membership + // Implementations may choose how to pass in the identifier + // ibc-go provides the identifier-prefixed store to this method + // so that all state reads are for the client in question + consensusState = provableStore.get("clients/{clientIdentifier}/consensusStates/{height}") + // verify that the provided consensus state has been stored + assert(verifyMembership(consensusState.commitmentRoot, proof, path, newClientState)) + // update client state + clientState = newClientState + provableStore.set("clients/{clientIdentifier}/clientState", clientState) +} +``` + +### State verification functions + +Tendermint client state verification functions check a Merkle proof against a previously validated commitment root. + +These functions utilise the `proofSpecs` with which the client was initialised. + +```typescript +function verifyMembership( + clientState: ClientState, + height: Height, + delayTimePeriod: uint64, + delayBlockPeriod: uint64, + proof: CommitmentProof, + path: CommitmentPath, + value: []byte +): Error { + // check that the client is at a sufficient height + assert(clientState.latestHeight >= height) + // check that the client is unfrozen or frozen at a higher height + assert(clientState.frozenHeight === null || clientState.frozenHeight > height) + // assert that enough time has elapsed + assert(currentTimestamp() >= processedTime + delayPeriodTime) + // assert that enough blocks have elapsed + assert(currentHeight() >= processedHeight + delayPeriodBlocks) + // fetch the previously verified commitment root & verify membership + // Implementations may choose how to pass in the identifier + // ibc-go provides the identifier-prefixed store to this method + // so that all state reads are for the client in question + consensusState = provableStore.get("clients/{clientIdentifier}/consensusStates/{height}") + // verify that has been stored + if !verifyMembership(consensusState.commitmentRoot, proof, path, value) { + return error + } + return nil +} + +function verifyNonMembership( + clientState: ClientState, + height: Height, + delayTimePeriod: uint64, + delayBlockPeriod: uint64, + proof: CommitmentProof, + path: CommitmentPath +): Error { + // check that the client is at a sufficient height + assert(clientState.latestHeight >= height) + // check that the client is unfrozen or frozen at a higher height + assert(clientState.frozenHeight === null || clientState.frozenHeight > height) + // assert that enough time has elapsed + assert(currentTimestamp() >= processedTime + delayPeriodTime) + // assert that enough blocks have elapsed + assert(currentHeight() >= processedHeight + delayPeriodBlocks) + // fetch the previously verified commitment root & verify membership + // Implementations may choose how to pass in the identifier + // ibc-go provides the identifier-prefixed store to this method + // so that all state reads are for the client in question + consensusState = provableStore.get("clients/{clientIdentifier}/consensusStates/{height}") + // verify that nothing has been stored at path + if !verifyNonMembership(consensusState.commitmentRoot, proof, path) { + return error + } + return nil +} +``` + +### Properties & Invariants + +Correctness guarantees as provided by the Tendermint light client algorithm. + +## Backwards Compatibility + +Not applicable. + +## Forwards Compatibility + +Not applicable. Alterations to the client verification algorithm will require a new client standard. + +## Example Implementations + +- Implementation of ICS 07 in Go can be found in [ibc-go repository](https://github.com/cosmos/ibc-go). +- Implementation of ICS 07 in Rust can be found in [ibc-rs repository](https://github.com/cosmos/ibc-rs). + +## History + +December 10th, 2019 - Initial version +December 19th, 2019 - Final first draft + +## Copyright + +All content herein is licensed under [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0). diff --git a/spec/core/v2/ics-002-client-semantics/README.md b/spec/core/v2/ics-002-client-semantics/README.md index e1d1fd6de..7848a9c56 100644 --- a/spec/core/v2/ics-002-client-semantics/README.md +++ b/spec/core/v2/ics-002-client-semantics/README.md @@ -95,7 +95,7 @@ types may require additional properties. - `Height` specifies the order of the state updates of a state machine, e.g., a sequence number. This entails that each state update is mapped to a `Height`. -- `CommitmentRoot` is as defined in [ICS 23](../ics-023-vector-commitments). +- `CommitmentRoot` is as defined in [ICS 23]( ../../ics-023-vector-commitments). It provides an efficient way for higher-level protocol abstractions to verify whether a particular state transition has occurred on the remote state machine, i.e., it enables proofs of inclusion or non-inclusion of particular values at particular paths @@ -149,7 +149,7 @@ This specification outlines what each *client type* must define. A client type i of the data structures, initialisation logic, validity predicate, and misbehaviour predicate required to operate a light client. State machines implementing the IBC protocol can support any number of client types, and each client type can be instantiated with different initial consensus states in order to track -different consensus instances. In order to establish a connection between two state machines (see [ICS 3](../ics-003-connection-semantics)), +different consensus instances. In order to establish a connection between two state machines (see [ICS 3]( ../../ics-003-connection-semantics)), the state machines must each support the client type corresponding to the other state machine's consensus algorithm. Specific client types shall be defined in later versions of this specification and a canonical list shall exist in this repository. @@ -339,7 +339,7 @@ to intervene to unfreeze a frozen client & provide a new correct ClientMessage w #### `CommitmentProof` -`CommitmentProof` is an opaque data structure defined by a client type in accordance with [ICS 23](../ics-023-vector-commitments). +`CommitmentProof` is an opaque data structure defined by a client type in accordance with [ICS 23]( ../../ics-023-vector-commitments). It is utilised to verify presence or absence of a particular key/value pair in state at a particular finalised height (necessarily associated with a particular commitment root). @@ -512,8 +512,8 @@ security assumptions of proxy state machine correctness. ##### Merklized state trees -For clients of state machines with Merklized state trees, these functions can be implemented by calling the [ICS-23](../ics-023-vector-commitments/README.md) `verifyMembership` or `verifyNonMembership` methods, using a verified Merkle -root stored in the `ClientState`, to verify presence or absence of particular key/value pairs in state at particular heights in accordance with [ICS 23](../ics-023-vector-commitments). +For clients of state machines with Merklized state trees, these functions can be implemented by calling the [ICS-23]( ../../ics-023-vector-commitments/README.md) `verifyMembership` or `verifyNonMembership` methods, using a verified Merkle +root stored in the `ClientState`, to verify presence or absence of particular key/value pairs in state at particular heights in accordance with [ICS 23]( ../../ics-023-vector-commitments). ```typescript type verifyMembership = (ClientState, Height, CommitmentProof, Path, Value) => boolean diff --git a/spec/core/v2/ics-004-packet-semantics/README.md b/spec/core/v2/ics-004-packet-semantics/README.md index acd6ad7b6..843161551 100644 --- a/spec/core/v2/ics-004-packet-semantics/README.md +++ b/spec/core/v2/ics-004-packet-semantics/README.md @@ -31,7 +31,7 @@ In order to provide the desired ordering, exactly-once delivery, and module perm `ConsensusState` is as defined in [ICS 2](../ics-002-client-semantics). -`Connection` is as defined in [ICS 3](../ics-003-connection-semantics). +`Connection` is as defined in [ICS 3](../../ics-003-connection-semantics). `Port` and `authenticateCapability` are as defined in [ICS 5](../ics-005-port-allocation). @@ -39,7 +39,7 @@ In order to provide the desired ordering, exactly-once delivery, and module perm `Identifier`, `get`, `set`, `delete`, `getCurrentHeight`, and module-system related primitives are as defined in [ICS 24](../ics-024-host-requirements). -See [upgrades spec](./UPGRADES.md) for definition of `pendingInflightPackets` and `restoreChannel`. +See [upgrades spec](../../ics-004-channel-and-packet-semantics/UPGRADES.md) for definition of `pendingInflightPackets` and `restoreChannel`. A *channel* is a pipeline for exactly-once packet delivery between specific modules on separate blockchains, which has at least one end capable of sending packets and one end capable of receiving packets. @@ -91,7 +91,7 @@ interface ChannelEnd { - The `connectionHops` stores the list of connection identifiers ordered starting from the receiving end towards the sender. `connectionHops[0]` is the connection end on the receiving chain. More than one connection hop indicates a multi-hop channel. - The `version` string stores an opaque channel version, which is agreed upon during the handshake. This can determine module-level configuration such as which packet encoding is used for the channel. This version is not used by the core IBC protocol. If the version string contains structured metadata for the application to parse and interpret, then it is considered best practice to encode all metadata in a JSON struct and include the marshalled string in the version field. -See the [upgrade spec](./UPGRADES.md) for details on `upgradeSequence`. +See the [upgrade spec](../../ics-004-channel-and-packet-semantics/UPGRADES.md) for details on `upgradeSequence`. Channel ends have a *state*: @@ -111,7 +111,7 @@ enum ChannelState { - A channel end in `OPEN` state has completed the handshake and is ready to send and receive packets. - A channel end in `CLOSED` state has been closed and can no longer be used to send or receive packets. -See the [upgrade spec](./UPGRADES.md) for details on `FLUSHING` and `FLUSHCOMPLETE`. +See the [upgrade spec](../../ics-004-channel-and-packet-semantics/UPGRADES.md) for details on `FLUSHING` and `FLUSHCOMPLETE`. A `Packet`, in the interblockchain communication protocol, is a particular interface defined as follows: @@ -184,7 +184,7 @@ enum PacketReceipt { The architecture of clients, connections, channels and packets: -![Dataflow Visualisation](dataflow.png) +![Dataflow Visualisation](../../ics-004-channel-and-packet-semantics/dataflow.png) ### Preliminaries @@ -275,7 +275,7 @@ If not provided, the default `validateChannelIdentifier` function will always re #### Channel lifecycle management -![Channel State Machine](channel-state-machine.png) +![Channel State Machine](../../ics-004-channel-and-packet-semantics/channel-state-machine.png) | Initiator | Datagram | Chain acted upon | Prior state (A, B) | Posterior state (A, B) | | --------- | ---------------- | ---------------- | ------------------ | ---------------------- | @@ -313,7 +313,7 @@ function writeChannel( } ``` -See handler functions `handleChanOpenInit` and `handleChanOpenTry` in [Channel lifecycle management](../ics-026-routing-module/README.md#channel-lifecycle-management) for more details. +See handler functions `handleChanOpenInit` and `handleChanOpenTry` in [Channel lifecycle management](../../ics-026-routing-module/README.md#channel-lifecycle-management) for more details. The opening channel must provide the identifiers of the local channel identifier, local port, remote port, and remote channel identifier. @@ -707,13 +707,13 @@ function getCounterPartyHops(proof: CommitmentProof | MultihopProof, lastConnect #### Packet flow & handling -![Packet State Machine](packet-state-machine.png) +![Packet State Machine](../../ics-004-channel-and-packet-semantics/packet-state-machine.png) ##### A day in the life of a packet The following sequence of steps must occur for a packet to be sent from module *1* on machine *A* to module *2* on machine *B*, starting from scratch. -The module can interface with the IBC handler through [ICS 25](../ics-025-handler-interface) or [ICS 26](../ics-026-routing-module). +The module can interface with the IBC handler through [ICS 25]( ../../ics-025-handler-interface) or [ICS 26]( ../../ics-026-routing-module). 1. Initial client & port setup, in any order 1. Client created on *A* for *B* (see [ICS 2](../ics-002-client-semantics)) @@ -721,18 +721,18 @@ The module can interface with the IBC handler through [ICS 25](../ics-025-handle 1. Module *1* binds to a port (see [ICS 5](../ics-005-port-allocation)) 1. Module *2* binds to a port (see [ICS 5](../ics-005-port-allocation)), which is communicated out-of-band to module *1* 1. Establishment of a connection & channel, optimistic send, in order - 1. Connection opening handshake started from *A* to *B* by module *1* (see [ICS 3](../ics-003-connection-semantics)) + 1. Connection opening handshake started from *A* to *B* by module *1* (see [ICS 3](../../ics-003-connection-semantics)) 1. Channel opening handshake started from *1* to *2* using the newly created connection (this ICS) 1. Packet sent over the newly created channel from *1* to *2* (this ICS) 1. Successful completion of handshakes (if either handshake fails, the connection/channel can be closed & the packet timed-out) - 1. Connection opening handshake completes successfully (see [ICS 3](../ics-003-connection-semantics)) (this will require participation of a relayer process) + 1. Connection opening handshake completes successfully (see [ICS 3](../../ics-003-connection-semantics)) (this will require participation of a relayer process) 1. Channel opening handshake completes successfully (this ICS) (this will require participation of a relayer process) 1. Packet confirmation on machine *B*, module *2* (or packet timeout if the timeout height has passed) (this will require participation of a relayer process) 1. Acknowledgement (possibly) relayed back from module *2* on machine *B* to module *1* on machine *A* Represented spatially, packet transit between two machines can be rendered as follows: -![Packet Transit](packet-transit.png) +![Packet Transit](../../ics-004-channel-and-packet-semantics/packet-transit.png) ##### Sending packets diff --git a/spec/core/v2/ics-005-port-allocation/README.md b/spec/core/v2/ics-005-port-allocation/README.md index bb940eaa0..15dc677e2 100644 --- a/spec/core/v2/ics-005-port-allocation/README.md +++ b/spec/core/v2/ics-005-port-allocation/README.md @@ -33,7 +33,7 @@ port simultaneously. Channels are end-to-end between two ports, each of which mu Optionally, the host state machine can elect to expose port binding only to a specially-permissioned module manager, by generating a capability key specifically for the ability to bind ports. The module manager can then control which ports modules can bind to with a custom rule-set, and transfer ports to modules only when it -has validated the port name & module. This role can be played by the routing module (see [ICS 26](../ics-026-routing-module)). +has validated the port name & module. This role can be played by the routing module (see [ICS 26](../../ics-026-routing-module)). ### Definitions diff --git a/spec/core/v2/ics-024-host-requirements/README.md b/spec/core/v2/ics-024-host-requirements/README.md index a5daa2bb8..0605c971a 100644 --- a/spec/core/v2/ics-024-host-requirements/README.md +++ b/spec/core/v2/ics-024-host-requirements/README.md @@ -90,7 +90,7 @@ and a `privateStore` for storage local to the host, upon which `get` The `provableStore`: -- MUST write to a key/value store whose data can be externally proved with a vector commitment as defined in [ICS 23](../ics-023-vector-commitments). +- MUST write to a key/value store whose data can be externally proved with a vector commitment as defined in [ICS 23](../../ics-023-vector-commitments). - MUST use canonical data structure encodings provided in these specifications as proto3 files The `privateStore`: @@ -116,22 +116,22 @@ Parts of the private store MAY safely be used for other purposes as long as the Keys used in the private store MAY safely vary as long as there exists a bipartite mapping between the key formats defined herein and the ones actually used in the private store implementation. -Note that the client-related paths listed below reflect the Tendermint client as defined in [ICS 7](../../client/ics-007-tendermint-client) and may vary for other client types. +Note that the client-related paths listed below reflect the Tendermint client as defined in [ICS 7](../../../client/ics-007-tendermint-client) and may vary for other client types. | Store | Path format | Value type | Defined in | | -------------- | ------------------------------------------------------------------------------ | ----------------- | ---------------------- | | provableStore | "clients/{identifier}/clientState" | ClientState | [ICS 2](../ics-002-client-semantics) | -| provableStore | "clients/{identifier}/consensusStates/{height}" | ConsensusState | [ICS 7](../../client/ics-007-tendermint-client) | -| privateStore | "clients/{identifier}/connections | []Identifier | [ICS 3](../ics-003-connection-semantics) | -| provableStore | "connections/{identifier}" | ConnectionEnd | [ICS 3](../ics-003-connection-semantics) | +| provableStore | "clients/{identifier}/consensusStates/{height}" | ConsensusState | [ICS 7](../../../client/ics-007-tendermint-client) | +| privateStore | "clients/{identifier}/connections | []Identifier | [ICS 3](../../ics-003-connection-semantics) | +| provableStore | "connections/{identifier}" | ConnectionEnd | [ICS 3](../../ics-003-connection-semantics) | | privateStore | "ports/{identifier}" | CapabilityKey | [ICS 5](../ics-005-port-allocation) | -| provableStore | "channelEnds/ports/{identifier}/channels/{identifier}" | ChannelEnd | [ICS 4](../ics-004-channel-and-packet-semantics) | -| provableStore | "nextSequenceSend/ports/{identifier}/channels/{identifier}" | uint64 | [ICS 4](../ics-004-channel-and-packet-semantics) | -| provableStore | "nextSequenceRecv/ports/{identifier}/channels/{identifier}" | uint64 | [ICS 4](../ics-004-channel-and-packet-semantics) | -| provableStore | "nextSequenceAck/ports/{identifier}/channels/{identifier}" | uint64 | [ICS 4](../ics-004-channel-and-packet-semantics) | -| provableStore | "commitments/ports/{identifier}/channels/{identifier}/sequences/{sequence}" | bytes | [ICS 4](../ics-004-channel-and-packet-semantics) | -| provableStore | "receipts/ports/{identifier}/channels/{identifier}/sequences/{sequence}" | bytes | [ICS 4](../ics-004-channel-and-packet-semantics) | -| provableStore | "acks/ports/{identifier}/channels/{identifier}/sequences/{sequence}" | bytes | [ICS 4](../ics-004-channel-and-packet-semantics) | +| provableStore | "channelEnds/ports/{identifier}/channels/{identifier}" | ChannelEnd | [ICS 4](../ics-004-packet-semantics) | +| provableStore | "nextSequenceSend/ports/{identifier}/channels/{identifier}" | uint64 | [ICS 4](../ics-004-packet-semantics) | +| provableStore | "nextSequenceRecv/ports/{identifier}/channels/{identifier}" | uint64 | [ICS 4](../ics-004-packet-semantics) | +| provableStore | "nextSequenceAck/ports/{identifier}/channels/{identifier}" | uint64 | [ICS 4](../ics-004-packet-semantics) | +| provableStore | "commitments/ports/{identifier}/channels/{identifier}/sequences/{sequence}" | bytes | [ICS 4](../ics-004-packet-semantics) | +| provableStore | "receipts/ports/{identifier}/channels/{identifier}/sequences/{sequence}" | bytes | [ICS 4](../ics-004-packet-semantics) | +| provableStore | "acks/ports/{identifier}/channels/{identifier}/sequences/{sequence}" | bytes | [ICS 4](../ics-004-packet-semantics) | ### Module layout @@ -202,7 +202,7 @@ Host state machines MUST provide the ability to validate the `ClientState` of a type validateSelfClient = (counterpartyClientState: ClientState) => boolean ``` -`validateSelfClient` validates the client parameters for a client of the host chain. For example, below is the implementation for Tendermint hosts, using `ClientState` as defined in [ICS 7](../../client/ics-007-tendermint-client/): +`validateSelfClient` validates the client parameters for a client of the host chain. For example, below is the implementation for Tendermint hosts, using `ClientState` as defined in [ICS 7](../../../client/ics-007-tendermint-client/): ```typescript function validateSelfClient(counterpartyClientState: ClientState) { @@ -313,7 +313,7 @@ Modules that wish to make use of particular IBC features MAY implement certain h ### Datagram submission -Host state machines which implement the routing module MAY define a `submitDatagram` function to submit datagrams[1](#footnote1), which will be included in transactions, directly to the routing module (defined in [ICS 26](../ics-026-routing-module)): +Host state machines which implement the routing module MAY define a `submitDatagram` function to submit datagrams[1](#footnote1), which will be included in transactions, directly to the routing module (defined in [ICS 26](../../ics-026-routing-module)): ```typescript type submitDatagram = (datagram: Datagram) => void