From 1304a23ec33837178c15e189c5b5f461a0a8a2fe Mon Sep 17 00:00:00 2001 From: Hussein Ait Lahcen Date: Mon, 4 Nov 2024 19:09:43 +0100 Subject: [PATCH] feat(docs): union ibc spec --- docs/astro.config.ts | 6 + .../docs/protocol/specifications/ibc.mdx | 254 ++++++++++++++++++ evm/contracts/core/24-host/IBCCommitment.sol | 14 - 3 files changed, 260 insertions(+), 14 deletions(-) create mode 100644 docs/src/content/docs/protocol/specifications/ibc.mdx diff --git a/docs/astro.config.ts b/docs/astro.config.ts index 84e67a23d3..efad92b2a2 100644 --- a/docs/astro.config.ts +++ b/docs/astro.config.ts @@ -155,6 +155,12 @@ export default defineConfig({ label: "Overview", link: "/protocol/overview" }, + { + label: "Specifications", + autogenerate: { + directory: "/protocol/specifications" + } + }, { label: "Channels", autogenerate: { diff --git a/docs/src/content/docs/protocol/specifications/ibc.mdx b/docs/src/content/docs/protocol/specifications/ibc.mdx new file mode 100644 index 0000000000..82b4963ca3 --- /dev/null +++ b/docs/src/content/docs/protocol/specifications/ibc.mdx @@ -0,0 +1,254 @@ +--- +title: "Union IBC" +sidebar: + badge: + text: live + variant: note +--- + +import Mermaid from "#/components/Mermaid.astro"; + +# Overview + +[`IBC`](https://ibcprotocol.org) is a blockchain interoperability protocol for secure general message passing between blockchains. At Union, we use a specialized in-house version (more EVM friendly) that slightly deviates from the _canonical_ [ibc-go](https://github.com/cosmos/ibc-go) and [ibc](https://github.com/cosmos/ibc). This document is an attempt at specifying the implementation. + +The semantic of the core protocol can be found in the [ibc](https://github.com/cosmos/ibc/tree/41a8caa54d8691b7fa98795d31f401e1df31e18c/spec/core) repository. Our implementation is deviating from the semantic in few places that we will describe. Most of the changes are specializations/optimizations targeting the EVM. + +The protocol assumes the execution happens within a smart contract engine (contract addresses **MUST** be unique). + +## ICS-002 Client + +:::note + +[Read more on the canonical specification](https://github.com/cosmos/ibc/tree/41a8caa54d8691b7fa98795d31f401e1df31e18c/spec/core/ics-002-client-semantics) + +::: + +- [`verifyMembership`](https://github.com/cosmos/ibc/tree/41a8caa54d8691b7fa98795d31f401e1df31e18c/spec/core/ics-002-client-semantics#state-verification) no longer takes a `delayPeriodTime` and `delayPeriodBlocks`, the specific light clients SHOULD implement such verification if necessary. +- [`verifyNonMembership`](https://github.com/cosmos/ibc/tree/41a8caa54d8691b7fa98795d31f401e1df31e18c/spec/core/ics-002-client-semantics#state-verification) + - `delayPeriodTime` has been removed. + - `delayPeriodBlocks` has been removed. +- [`clientIdentifier`](https://github.com/cosmos/ibc/tree/41a8caa54d8691b7fa98795d31f401e1df31e18c/spec/core/ics-002-client-semantics#identifier-validation) is a unique `uint32`. The client type **MUST** be stored and indexable with the `clientIdentifier` see. + +### Additions + +- `registerClient` **MUST** be implemented and called before being able to call `createClient` on a light client. Only one client type **MUST** exist for a given light client: +```haskell +registerClient ∷ ClientType → Address → IO () +registerClient clientType lightClient = do + assert (getImplementation clientType ≡ null) + setImplementation clientType lightClient +``` + +## ICS-003 Connection + +:::note + +[Read more on the canonical specification](https://github.com/cosmos/ibc/blob/41a8caa54d8691b7fa98795d31f401e1df31e18c/spec/core/ics-003-connection-semantics/README.md). + +::: + +:::caution + +The protocol version is considered hardcoded. We support the `Ordered` and `Unordered` features for channels only. + +::: + +- [`connectionIdentifier`](https://github.com/cosmos/ibc/tree/41a8caa54d8691b7fa98795d31f401e1df31e18c/spec/core/ics-003-connection-semantics#identifier-validation) is no longer a string but a unique `uint32`. +- [`ConnectionEnd`](https://github.com/cosmos/ibc/tree/41a8caa54d8691b7fa98795d31f401e1df31e18c/spec/core/ics-003-connection-semantics#data-structures) + - `counterpartyPrefix` has been removed. + - `version` has been removed. + - `delayPeriodTime` has been removed. + - `delayPeriodBlocks` has been removed. +```haskell +type ClientIdentifier = Uint32 +type ConnectionIdentifier = Uint32 + +class ConnectionEnd where + state ∷ ConnectionState + counterpartyConnectionIdentifier ∷ ConnectionIdentifier + clientIdentifier ∷ ClientIdentifier + counterpartyClientIdentifier ∷ ClientIdentifier +``` + + +## ICS-004 Channel and Packet + +:::note + +[Read more on the canonical specification](https://github.com/cosmos/ibc/blob/41a8caa54d8691b7fa98795d31f401e1df31e18c/spec/core/ics-004-channel-and-packet-semantics/README.md) + +::: + +### Channel + +:::caution + +We do not support the [Channel Upgrade](https://github.com/cosmos/ibc/blob/41a8caa54d8691b7fa98795d31f401e1df31e18c/spec/core/ics-004-channel-and-packet-semantics/UPGRADES.md) feature. +We only support `Ordered` and `Unordered` channels. + +::: + +- [`channelIdentifier`](https://github.com/cosmos/ibc/blob/41a8caa54d8691b7fa98795d31f401e1df31e18c/spec/core/ics-004-channel-and-packet-semantics/README.md#identifier-validation) is no longer a string but a unique `uint32`. +- [`ChannelEnd`](https://github.com/cosmos/ibc/blob/41a8caa54d8691b7fa98795d31f401e1df31e18c/spec/core/ics-004-channel-and-packet-semantics/README.md#definitions): + - `connectionHops` has been renamed to `connectionId` and it's type changed from `[connectionIdentifer]` to `connectionIdentifier`. + - `upgradeSequence` has been removed. +```haskell +type ChannelIdentifier = Uint32 +data ChannelOrder = Ordered | Unordered +data ChannelState = Init | TryOpen | Open | Closed + + +class ChannelEnd where + state ∷ ChannelState + ordering ∷ ChannelOrder + connectionId ∷ ConnectionIdentifier + counterpartyChannelId ∷ ChannelIdentifier + counterpartyPortId ∷ String + version ∷ String +``` + +### Packet + +:::caution + +The [ICS-05 port allocation](https://github.com/cosmos/ibc/blob/41a8caa54d8691b7fa98795d31f401e1df31e18c/spec/core/ics-005-port-allocation/README.md) module has been removed, channel identifiers uniquely identify protocols and the implementor **MUST** ensure there is an existing indirection from `channelIdentifier -> protocolAddress`. + +::: + +- [`Packet`](https://github.com/cosmos/ibc/blob/41a8caa54d8691b7fa98795d31f401e1df31e18c/spec/core/ics-004-channel-and-packet-semantics/README.md#definitions) + - `sourcePort` has been removed. + - `destinationPort` has been removed. + - `timeoutHeight` type has been changed from `(uint64, uint64)` to `uint64`. +```haskell +class Packet where + sequence ∷ Uint64 + sourceChannel ∷ ChannelIdentifier + destinationChannel ∷ ChannelIdentifier + payload ∷ [Uint8] + timeoutHeight ∷ Uint64 + timeoutTimestamp ∷ Uint64 +``` + +## ICS-024 Host + +:::note + +[Read more on the canonical specification](https://github.com/cosmos/ibc/blob/41a8caa54d8691b7fa98795d31f401e1df31e18c/spec/core/ics-024-host-requirements/README.md#synopsis) + +::: + +### Path-space + +Union approach to the path-space is deviating from the canonical implementation. Since we no longer use string for client/connection/channel identifiers, the commitment paths are binary. + +```haskell +type Prefix = Uint256 + +clientStatePrefix ∷ Prefix +clientStatePrefix = 0x00 + +consensusStatePrefix ∷ Prefix +consensusStatePrefix = 0x01 + +connectionsPrefix ∷ Prefix +connectionsPrefix = 0x02 + +channelsPrefix ∷ Prefix +channelsPrefix = 0x03 + +packetsPrefix ∷ Prefix +packetsPrefix = 0x04 + +packetAcksPrefix ∷ Prefix +packetAcksPrefix = 0x05 + +nextSeqSendPrefix ∷ Prefix +nextSeqSendPrefix = 0x06 + +nextSeqRecvPrefix ∷ Prefix +nextSeqRecvPrefix = 0x07 + +nextSeqAckPrefix ∷ Prefix +nextSeqAckPrefix = 0x08 + +commit ∷ AbiEncode a ⇒ a → Bytes32 +commit x = keccak256 (abiEncode x) + +clientStateKey ∷ ClientIdentifier → Bytes32 +clientStateKey clientId = commit (clientStatePrefix, clientId) + +consensusStateKey ∷ ClientIdentifier → Uint64 → Bytes32 +consensusStateKey clientId height = commit (consensusStatePrefix, clientId, height) + +connectionKey ∷ ConnectionIdentifier → Bytes32 +connectionKey connectionId = commit (connectionsPrefix, connectionId) + +channelKey ∷ ChannelIdentifier → Bytes32 +channelKey channelId = commit (channelsPrefix, channelId) + +packetKey ∷ ChannelIdentifier → Bytes32 → Bytes32 +packetKey channelId packetHash = commit (packetsPrefix, channelId, packetHash) + +packetReceiptKey ∷ ChannelIdentifier → Bytes32 → Bytes32 +packetReceiptKey channelId packetHash = commit (packetAcksPrefix, channelId, packetHash) + +nextSequenceSendKey ∷ ChannelIdentifier → Bytes32 +nextSequenceSendKey channelId = commit (nextSeqSendPrefix, channelId) + +nextSequenceRecvKey ∷ ChannelIdentifier → Bytes32 +nextSequenceRecvKey channelId = commit (nextSeqRecvPrefix, channelId) + +nextSequenceAckKey ∷ ChannelIdentifier → Bytes32 +nextSequenceAckKey channelId = commit (nextSeqAckPrefix, channelId) +``` + + +## Extensions + +### ICS-004 Packet + +The `packetKey` and `packetReceiptKey` are special commitments that no longer takes a `sequence` but the whole packet hash. This allows us to extend the protocol with batching for sent packets and written acknowledgements. + +```haskell +setCommitment ∷ Store → Bytes32 → Bytes32 → Store +getCommitment ∷ Store → Bytes32 → Bytes32 + +commitmentMagic ∷ Bytes32 +commitmentMagic = 0x0100000000000000000000000000000000000000000000000000000000000000 + +mergeAck ∷ Bytes32 → Bytes32 +mergeAck ack = + commitmentMagic | + (ack & 0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) + +commitmentExist ∷ Store → Bytes32 → Packet → Bool +commitmentExist store expectedCommitment packet = + getCommitment store (commit packet) ≡ exceptedCommitment + +batchSend ∷ Store → [Packet] → Store +batchSend store packets = do + assert (all (commitmentExist store commitmentMagic) packets) + setCommitment store (packetKey channelId (commit packets)) commitmentMagic + +batchAcks ∷ Store → [Packet] → [Acknowledgement] → Store +batchAcks store packets acks = do + assert ( + all + (\(ack, packet) -> commitmentExist store (mergeAck ack) packet) + (zip acks packet) + ) + setCommitment + store + (packetReceiptKey channelId (commit packets)) + (mergeAck (commit acks)) +``` + + +:::tip + +The `commitmentMagic` value and `mergeAck` function are present to reuse the same slot to commit the receipt and the acknowledgement hash. This drastically lower the gas cost on EVM. + +Receipts are written when a packet is received. Acknowledgements when the execution of a received packet completes. Because of the asynchronous nature of the acknowledgement, we must store a receipt to avoid replay attacks. + +::: diff --git a/evm/contracts/core/24-host/IBCCommitment.sol b/evm/contracts/core/24-host/IBCCommitment.sol index 49e99d4784..46e265362c 100644 --- a/evm/contracts/core/24-host/IBCCommitment.sol +++ b/evm/contracts/core/24-host/IBCCommitment.sol @@ -36,13 +36,6 @@ library IBCCommitment { return abi.encode(CHANNELS, channelId); } - function packetCommitmentPath( - uint32 channelId, - uint64 sequence - ) internal pure returns (bytes memory) { - return abi.encode(PACKETS, channelId, sequence); - } - function batchPacketsCommitmentPath( uint32 channelId, bytes32 batchHash @@ -102,13 +95,6 @@ library IBCCommitment { return keccak256(channelPath(channelId)); } - function packetCommitmentKey( - uint32 channelId, - uint64 sequence - ) internal pure returns (bytes32) { - return keccak256(packetCommitmentPath(channelId, sequence)); - } - function batchPacketsCommitmentKey( uint32 channelId, bytes32 batchHash