diff --git a/spec/core/v2/ics-024-host-requirements/README.md b/spec/core/v2/ics-024-host-requirements/README.md index 0605c971a..c967197dc 100644 --- a/spec/core/v2/ics-024-host-requirements/README.md +++ b/spec/core/v2/ics-024-host-requirements/README.md @@ -5,16 +5,16 @@ stage: draft category: IBC/TAO kind: interface requires: 23 -required-by: 2, 3, 4, 5, 18 -version compatibility: ibc-go v7.0.0 -author: Christopher Goes -created: 2019-04-16 -modified: 2022-09-14 +required-by: 4 +version compatibility: ibc-go v10.0.0 +author: Aditya Sripal +created: 2024-08-21 +modified: 2024-08-21 --- ## Synopsis -This specification defines the minimal set of interfaces which must be provided and properties which must be fulfilled by a state machine hosting an implementation of the interblockchain communication protocol. +This specification defines the minimal set of interfaces which must be provided and properties which must be fulfilled by a state machine hosting an implementation of the interblockchain communication protocol. IBC relies on a key-value provable store for cross-chain communication. In version 2 of the specification, the expected key-value storage will only be for the keys that are relevant for packet processing. ### Motivation @@ -36,7 +36,7 @@ The IBC/TAO specifications define the implementations of two modules: the core " ### Paths, identifiers, separators -An `Identifier` is a bytestring used as a key for an object stored in state, such as a connection, channel, or light client. +An `Identifier` is a bytestring used as a key for an object stored in state, such as a packet commitment, acknowledgement, or receipt. Identifiers MUST be non-empty (of positive integer length). @@ -58,9 +58,9 @@ All identifiers, and all strings listed in this specification, must be encoded a By default, identifiers have the following minimum and maximum lengths in characters: -| Port identifier | Client identifier | Connection identifier | Channel identifier | -| --------------- | ----------------- | --------------------- | ------------------ | -| 2 - 128 | 9 - 64 | 10 - 64 | 8 - 64 | +| Port identifier | Client identifier | Channel identifier | +| --------------- | ----------------- | ------------------ | +| 2 - 128 | 9 - 64 | 8 - 64 | ### Key/value Store @@ -90,8 +90,8 @@ 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 use canonical data structure encodings provided in these specifications as proto3 files +- MUST write to a key/value store whose data can be externally proved with a vector commitment that can be proven by an IBC light client as defined in [ICS-002](../ics-002-client-semantics/README.md) +- MUST use canonical data structure encodings provided in these specifications. The `privateStore`: @@ -103,35 +103,61 @@ The `privateStore`: > > Note: this interface does not necessitate any particular storage backend or backend data layout. State machines may elect to use a storage backend configured in accordance with their needs, as long as the store on top fulfils the specified interface and provides commitment proofs. -### Path-space +### Provable Path-space -At present, IBC/TAO recommends the following path prefixes for the `provableStore` and `privateStore`. +IBC/TAO implementations MUST implement the following paths for the `provableStore` in the exact format specified. This is because counterparty IBC/TAO implementations will construct the paths according to this specification and send it to the light client to verify the IBC specified value stored under the IBC specified path. Future paths may be used in future versions of the protocol, so the entire key-space in the provable store MUST be reserved for the IBC handler. -Keys used in the provable store MAY safely vary on a per-client-type basis as long as there exists a bipartite mapping between the key formats -defined herein and the ones actually used in the machine's implementation. - -Parts of the private store MAY safely be used for other purposes as long as the IBC handler has exclusive access to the specific keys required. -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. - -| 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) | -| privateStore | "ports/{identifier}" | CapabilityKey | [ICS 5](../ics-005-port-allocation) | -| 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) | + +| Store | Path format | Value type | Defined in | +| -------------- | -------------------------------------------------------------------------- | ----------------- | ---------------------- | +| provableStore | "commitments/channels/{identifier}/sequences/{bigEndianUint64Sequence}" | bytes | [ICS 4](../ics-004-packet-semantics) | +| provableStore | "receipts/channels/{identifier}/sequences/{bigEndianUint64Sequence}" | bytes | [ICS 4](../ics-004-packet-semantics) | +| provableStore | "acks/channels/{identifier}/sequences/{bigEndianUint64Sequence}" | bytes | [ICS 4](../ics-004-packet-semantics) | + +### Provable Commitments + +In addition to specifying the paths that are expected across implementations, ICS-24 also standardizes the commitments stored under each standardized path. These commitments will also be constructed and checked by counterparties in order to enable packet flow. + +#### Packet Commitment + +When sending a packet, all IBC implementations are expected to store a packet commitment under the above specified packet commitment path. When receiving a packet, all IBC implementations will reconstruct the expected packet commitment and verify it was stored under the expected packet commitment path in order to verify the packet before sending it to the application. + +IBC standardizes the fields that a packet must have but does not standardize the structure containing them nor their on-chain encoding. Thus, different implementations may house these fields in different structs and encode the struct differently for their internal use so long as they create the same exact commitment when they store the packet under the packet commitment path. + +The structure of the packet and commitment of the packet timeout and application data is further specified in ICS4. + +```typescript +// commit packet hashes the destination identifier, the timeout and the data meant to be +// processed by the destination state machine +func commitPacket(destIdentifier: bytes, timeoutBytes: bytes, data: bytes): bytes { + buffer = sha256.Hash(destIdentifier) + buffer = append(buffer, sha256.hash(bigEndian(timeoutBytes))) + buffer = append(buffer, sha256.hash(data)) + return sha256.hash(buffer) +} +``` + +Since the packet commitment is keyed on the source identifier and sequence, with this key and value together; the receiving chain can prove that the sending chain has sent a packet with the given source and destination identifiers at a given sequence with the timeout and application data. + +#### Packet Acknowledgement + +The acknowledgement will be provided with the packet to the sending chain. Thus we only need to provably associate the acknowledgement with the original packet. This association is already accomplished by the acknowledgment path which contains the destination identifier and the sequence. Thus on the sending chain, we can prove the acknowledgement was indeed sent for the packet we sent. We prove the packet was sent by us by checking that we stored the packet commitment under the packet commitment path. We can retrieve the client from the source identifier and then prove the counterparty stored under the destination identifier and sequence. Thus, we can associate the acknowledgement stored under this path with the unique packet provided. The acknowledgement commitment can therefore simply consist of a hash of the acknowledgment data sent to the state machine. + +The creation of `ackData` for a given packet is further specified in ICS4. + +```typescript +func commitAcknowledgment(ackData: bytes): bytes { + return sha256.hash(ackData) +} +``` + +#### Packet Receipt + +A packet receipt will only tell the sending chain that the counterparty has successfully received the packet. Thus we just need a provable boolean flag uniquely associated with the sent packet. Thus, the receiver chain stores the packet receipt keyed on the destination identifier and the sequence to uniquely identify the packet. + +For chains that support nonexistence proofs of their own state, they can simply write a `SENTINEL_RECEIPT_VALUE` under the receipt path. This `SENTINEL_RECEIPT_PATH` can be any non-nil value so it is recommended to write a single byte. ### Module layout @@ -145,7 +171,7 @@ Represented spatially, the layout of modules & their included specifications on | +-------------------+ +--------------------+ +----------------------+ | | | Module Aardvark | <--> | IBC Routing Module | | IBC Handler Module | | | +-------------------+ | | | | | -| | Implements ICS 26. | | Implements ICS 2, 3, | | +| | Implements ICS 26. | | Implements ICS 2, | | | | | | 4, 5 internally. | | | +-------------------+ | | | | | | | Module Betazoid | <--> | | --> | Exposes interface | | @@ -160,133 +186,13 @@ Represented spatially, the layout of modules & their included specifications on ### Consensus state introspection -Host state machines MUST provide the ability to introspect their current height, with `getCurrentHeight`: +Host state machines MUST provide the ability to introspect their current height: ```typescript +// this will return the current height of the host state machine type getCurrentHeight = () => Height ``` -Host state machines MUST define a unique `ConsensusState` type fulfilling the requirements of [ICS 2](../ics-002-client-semantics), with a canonical binary serialisation. - -Host state machines MUST provide the ability to introspect their own consensus state, with `getConsensusState`: - -```typescript -type getConsensusState = (height: Height, proof?: bytes) => ConsensusState -``` - -`getConsensusState` MUST return the consensus state for at least some number `n` of contiguous recent heights, where `n` is constant for the host state machine. Heights older than `n` MAY be safely pruned (causing future calls to fail for those heights). - -We provide an optional proof data which comes from the `MsgConnectionOpenAck` or `MsgConnectionOpenTry` for host state machines which are unable to introspect their own `ConsensusState` and must rely on off-chain data. -
-In this case host state machines MUST maintain a map of `n` block numbers to header hashes where the proof would contain full header which can be hashed and compared with the on-chain record. - -Host state machines MUST provide the ability to introspect this stored recent consensus state count `n`, with `getStoredRecentConsensusStateCount`: - -```typescript -type getStoredRecentConsensusStateCount = () => Height -``` - -### Client state validation - -Host state machines MUST define a unique `ClientState` type fulfilling the requirements of [ICS 2](../ics-002-client-semantics). - -Host state machines MUST provide the ability to construct a `ClientState` representation of their own state for the purposes of client state validation, with `getHostClientState`: - -```typescript -type getHostClientState = (height: Height) => ClientState -``` - -Host state machines MUST provide the ability to validate the `ClientState` of a light client running on a counterparty chain, with `validateSelfClient`: - -```typescript -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/): - -```typescript -function validateSelfClient(counterpartyClientState: ClientState) { - hostClientState = getHostClientState() - - // assert that the counterparty client is not frozen - if counterpartyClientState.frozenHeight !== null { - return false - } - - // assert that the chain ids are the same - if counterpartyClientState.chainID !== hostClientState.chainID { - return false - } - - // assert that the counterparty client is in the same revision as the host chain - counterpartyRevisionNumber = parseRevisionNumber(counterpartyClientState.chainID) - if counterpartyRevisionNumber !== hostClientState.latestHeight.revisionNumber { - return false - } - - // assert that the counterparty client has a height less than the host height - if counterpartyClientState.latestHeight >= hostClientState.latestHeight { - return false - } - - // assert that the counterparty client has the same ProofSpec as the host - if counterpartyClientState.proofSpecs !== hostClientState.proofSpecs { - return false - } - - // assert that the trustLevel is within the allowed range. 1/3 is the minimum amount - // of trust needed which does not break the security model. - if counterpartyClientState.trustLevel < 1/3 || counterpartyClientState.trustLevel > 1 { - return false - } - - // assert that the unbonding periods are the same - if counterpartyClientState.unbondingPeriod != hostClientState.unbondingPeriod { - return false - } - - // assert that the unbonding period is greater than or equal to the trusting period - if counterpartyClientState.unbondingPeriod < counterpartyClientState.trustingPeriod { - return false - } - - // assert that the upgrade paths are the same - hostUpgradePath = applyPrefix(hostClientState.upgradeCommitmentPrefix, hostClientState.upgradeKey) - counterpartyUpgradePath = applyPrefix(counterpartyClientState.upgradeCommitmentPrefix, counterpartyClientState.upgradeKey) - if counterpartyUpgradePath !== hostUpgradePath { - return false - } - - return true -} -``` - -### Commitment path introspection - -Host chains MUST provide the ability to inspect their commitment path, with `getCommitmentPrefix`: - -```typescript -type getCommitmentPrefix = () => CommitmentPrefix -``` - -The result `CommitmentPrefix` is the prefix used by the host state machine's key-value store. -With the `CommitmentRoot root` and `CommitmentState state` of the host state machine, the following property MUST be preserved: - -```typescript -if provableStore.get(path) === value { - prefixedPath = applyPrefix(getCommitmentPrefix(), path) - if value !== nil { - proof = createMembershipProof(state, prefixedPath, value) - assert(verifyMembership(root, proof, prefixedPath, value)) - } else { - proof = createNonMembershipProof(state, prefixedPath) - assert(verifyNonMembership(root, proof, prefixedPath)) - } -} -``` - -For a host state machine, the return value of `getCommitmentPrefix` MUST be constant. - ### Timestamp access Host chains MUST provide a current Unix timestamp, accessible with `currentTimestamp()`: @@ -311,16 +217,6 @@ This permissioning can be implemented with unique references (object capabilitie Modules that wish to make use of particular IBC features MAY implement certain handler functions, e.g. to add additional logic to a channel handshake with an associated module on another state machine. -### 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)): - -```typescript -type submitDatagram = (datagram: Datagram) => void -``` - -`submitDatagram` allows relayer processes to submit IBC datagrams directly to the routing module on the host state machine. Host state machines MAY require that the relayer process submitting the datagram has an account to pay transaction fees, signs over the datagram in a larger transaction structure, etc — `submitDatagram` MUST define & construct any such packaging required. - ### Exception system Host state machines MUST support an exception system, whereby a transaction can abort execution and revert any previously made state changes (including state changes in other modules happening within the same transaction), excluding gas consumed & fee payments as appropriate, and a system invariant violation can halt the state machine. @@ -385,27 +281,10 @@ Key/value store functionality and consensus state type are unlikely to change du ## Example Implementations -- Implementation of ICS 24 in Go can be found in [ibc-go repository](https://github.com/cosmos/ibc-go). -- Implementation of ICS 24 in Rust can be found in [ibc-rs repository](https://github.com/cosmos/ibc-rs). - ## History -Apr 29, 2019 - Initial draft - -May 11, 2019 - Rename "RootOfTrust" to "ConsensusState" - -Jun 25, 2019 - Use "ports" instead of module names - -Aug 18, 2019 - Revisions to module system, definitions - -Jul 05, 2022 - Lower the minimal allowed length of a channel identifier to 8 - -Jul 27, 2022 - Move `ClientState` to the `provableStore`, and add "Client state validation" section +Aug 21, 2024 - [Initial draft](https://github.com/cosmos/ibc/pull/1144) ## Copyright All content herein is licensed under [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0). - ---- - -1: A datagram is an opaque bytestring transmitted over some physical network, and handled by the IBC routing module implemented in the ledger's state machine. In some implementations, the datagram may be a field in a ledger-specific transaction or message data structure which also contains other information (e.g. a fee for spam prevention, nonce for replay prevention, type identifier to route to the IBC handler, etc.). All IBC sub-protocols (such as opening a connection, creating a channel, sending a packet) are defined in terms of sets of datagrams and protocols for handling them through the routing module.