|
| 1 | +--- |
| 2 | +title: "Union IBC" |
| 3 | +sidebar: |
| 4 | + badge: |
| 5 | + text: live |
| 6 | + variant: note |
| 7 | +--- |
| 8 | + |
| 9 | +import Mermaid from "#/components/Mermaid.astro"; |
| 10 | + |
| 11 | +# Overview |
| 12 | + |
| 13 | +[`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. |
| 14 | + |
| 15 | +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. |
| 16 | + |
| 17 | +The protocol assumes the execution happens within a smart contract engine (contract addresses **MUST** be unique). |
| 18 | + |
| 19 | +## ICS-002 Client |
| 20 | + |
| 21 | +:::note |
| 22 | + |
| 23 | +[Read more on the canonical specification](https://github.com/cosmos/ibc/tree/41a8caa54d8691b7fa98795d31f401e1df31e18c/spec/core/ics-002-client-semantics) |
| 24 | + |
| 25 | +::: |
| 26 | + |
| 27 | +- [`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. |
| 28 | +- [`verifyNonMembership`](https://github.com/cosmos/ibc/tree/41a8caa54d8691b7fa98795d31f401e1df31e18c/spec/core/ics-002-client-semantics#state-verification) |
| 29 | + - `delayPeriodTime` has been removed. |
| 30 | + - `delayPeriodBlocks` has been removed. |
| 31 | +- [`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. |
| 32 | + |
| 33 | +### Additions |
| 34 | + |
| 35 | +- `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: |
| 36 | +```haskell |
| 37 | +registerClient ∷ ClientType → Address → IO () |
| 38 | +registerClient clientType lightClient = do |
| 39 | + assert (getImplementation clientType ≡ null) |
| 40 | + setImplementation clientType lightClient |
| 41 | +``` |
| 42 | + |
| 43 | +## ICS-003 Connection |
| 44 | + |
| 45 | +:::note |
| 46 | + |
| 47 | +[Read more on the canonical specification](https://github.com/cosmos/ibc/blob/41a8caa54d8691b7fa98795d31f401e1df31e18c/spec/core/ics-003-connection-semantics/README.md). |
| 48 | + |
| 49 | +::: |
| 50 | + |
| 51 | +:::caution |
| 52 | + |
| 53 | +The protocol version is considered hardcoded. We support the `Ordered` and `Unordered` features for channels only. |
| 54 | + |
| 55 | +::: |
| 56 | + |
| 57 | +- [`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`. |
| 58 | +- [`ConnectionEnd`](https://github.com/cosmos/ibc/tree/41a8caa54d8691b7fa98795d31f401e1df31e18c/spec/core/ics-003-connection-semantics#data-structures) |
| 59 | + - `counterpartyPrefix` has been removed. |
| 60 | + - `version` has been removed. |
| 61 | + - `delayPeriodTime` has been removed. |
| 62 | + - `delayPeriodBlocks` has been removed. |
| 63 | +```haskell |
| 64 | +type ClientIdentifier = Uint32 |
| 65 | +type ConnectionIdentifier = Uint32 |
| 66 | + |
| 67 | +class ConnectionEnd where |
| 68 | + state ∷ ConnectionState |
| 69 | + counterpartyConnectionIdentifier ∷ ConnectionIdentifier |
| 70 | + clientIdentifier ∷ ClientIdentifier |
| 71 | + counterpartyClientIdentifier ∷ ClientIdentifier |
| 72 | +``` |
| 73 | + |
| 74 | + |
| 75 | +## ICS-004 Channel and Packet |
| 76 | + |
| 77 | +:::note |
| 78 | + |
| 79 | +[Read more on the canonical specification](https://github.com/cosmos/ibc/blob/41a8caa54d8691b7fa98795d31f401e1df31e18c/spec/core/ics-004-channel-and-packet-semantics/README.md) |
| 80 | + |
| 81 | +::: |
| 82 | + |
| 83 | +### Channel |
| 84 | + |
| 85 | +:::caution |
| 86 | + |
| 87 | +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. |
| 88 | +We only support `Ordered` and `Unordered` channels. |
| 89 | + |
| 90 | +::: |
| 91 | + |
| 92 | +- [`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`. |
| 93 | +- [`ChannelEnd`](https://github.com/cosmos/ibc/blob/41a8caa54d8691b7fa98795d31f401e1df31e18c/spec/core/ics-004-channel-and-packet-semantics/README.md#definitions): |
| 94 | + - `connectionHops` has been renamed to `connectionId` and it's type changed from `[connectionIdentifer]` to `connectionIdentifier`. |
| 95 | + - `upgradeSequence` has been removed. |
| 96 | +```haskell |
| 97 | +type ChannelIdentifier = Uint32 |
| 98 | +data ChannelOrder = Ordered | Unordered |
| 99 | +data ChannelState = Init | TryOpen | Open | Closed |
| 100 | + |
| 101 | + |
| 102 | +class ChannelEnd where |
| 103 | + state ∷ ChannelState |
| 104 | + ordering ∷ ChannelOrder |
| 105 | + connectionId ∷ ConnectionIdentifier |
| 106 | + counterpartyChannelId ∷ ChannelIdentifier |
| 107 | + counterpartyPortId ∷ String |
| 108 | + version ∷ String |
| 109 | +``` |
| 110 | + |
| 111 | +### Packet |
| 112 | + |
| 113 | +:::caution |
| 114 | + |
| 115 | +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`. |
| 116 | + |
| 117 | +::: |
| 118 | + |
| 119 | +- [`Packet`](https://github.com/cosmos/ibc/blob/41a8caa54d8691b7fa98795d31f401e1df31e18c/spec/core/ics-004-channel-and-packet-semantics/README.md#definitions) |
| 120 | + - `sourcePort` has been removed. |
| 121 | + - `destinationPort` has been removed. |
| 122 | + - `timeoutHeight` type has been changed from `(uint64, uint64)` to `uint64`. |
| 123 | +```haskell |
| 124 | +class Packet where |
| 125 | + sequence ∷ Uint64 |
| 126 | + sourceChannel ∷ ChannelIdentifier |
| 127 | + destinationChannel ∷ ChannelIdentifier |
| 128 | + payload ∷ [Uint8] |
| 129 | + timeoutHeight ∷ Uint64 |
| 130 | + timeoutTimestamp ∷ Uint64 |
| 131 | +``` |
| 132 | + |
| 133 | +## ICS-024 Host |
| 134 | + |
| 135 | +:::note |
| 136 | + |
| 137 | +[Read more on the canonical specification](https://github.com/cosmos/ibc/blob/41a8caa54d8691b7fa98795d31f401e1df31e18c/spec/core/ics-024-host-requirements/README.md#synopsis) |
| 138 | + |
| 139 | +::: |
| 140 | + |
| 141 | +### Path-space |
| 142 | + |
| 143 | +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. |
| 144 | + |
| 145 | +```haskell |
| 146 | +type Prefix = Uint256 |
| 147 | + |
| 148 | +clientStatePrefix ∷ Prefix |
| 149 | +clientStatePrefix = 0x00 |
| 150 | + |
| 151 | +consensusStatePrefix ∷ Prefix |
| 152 | +consensusStatePrefix = 0x01 |
| 153 | + |
| 154 | +connectionsPrefix ∷ Prefix |
| 155 | +connectionsPrefix = 0x02 |
| 156 | + |
| 157 | +channelsPrefix ∷ Prefix |
| 158 | +channelsPrefix = 0x03 |
| 159 | + |
| 160 | +packetsPrefix ∷ Prefix |
| 161 | +packetsPrefix = 0x04 |
| 162 | + |
| 163 | +packetAcksPrefix ∷ Prefix |
| 164 | +packetAcksPrefix = 0x05 |
| 165 | + |
| 166 | +nextSeqSendPrefix ∷ Prefix |
| 167 | +nextSeqSendPrefix = 0x06 |
| 168 | + |
| 169 | +nextSeqRecvPrefix ∷ Prefix |
| 170 | +nextSeqRecvPrefix = 0x07 |
| 171 | + |
| 172 | +nextSeqAckPrefix ∷ Prefix |
| 173 | +nextSeqAckPrefix = 0x08 |
| 174 | + |
| 175 | +commit ∷ AbiEncode a ⇒ a → Bytes32 |
| 176 | +commit x = keccak256 (abiEncode x) |
| 177 | + |
| 178 | +clientStateKey ∷ ClientIdentifier → Bytes32 |
| 179 | +clientStateKey clientId = commit (clientStatePrefix, clientId) |
| 180 | + |
| 181 | +consensusStateKey ∷ ClientIdentifier → Uint64 → Bytes32 |
| 182 | +consensusStateKey clientId height = commit (consensusStatePrefix, clientId, height) |
| 183 | + |
| 184 | +connectionKey ∷ ConnectionIdentifier → Bytes32 |
| 185 | +connectionKey connectionId = commit (connectionsPrefix, connectionId) |
| 186 | + |
| 187 | +channelKey ∷ ChannelIdentifier → Bytes32 |
| 188 | +channelKey channelId = commit (channelsPrefix, channelId) |
| 189 | + |
| 190 | +packetKey ∷ ChannelIdentifier → Bytes32 → Bytes32 |
| 191 | +packetKey channelId packetHash = commit (packetsPrefix, channelId, packetHash) |
| 192 | + |
| 193 | +packetReceiptKey ∷ ChannelIdentifier → Bytes32 → Bytes32 |
| 194 | +packetReceiptKey channelId packetHash = commit (packetAcksPrefix, channelId, packetHash) |
| 195 | + |
| 196 | +nextSequenceSendKey ∷ ChannelIdentifier → Bytes32 |
| 197 | +nextSequenceSendKey channelId = commit (nextSeqSendPrefix, channelId) |
| 198 | + |
| 199 | +nextSequenceRecvKey ∷ ChannelIdentifier → Bytes32 |
| 200 | +nextSequenceRecvKey channelId = commit (nextSeqRecvPrefix, channelId) |
| 201 | + |
| 202 | +nextSequenceAckKey ∷ ChannelIdentifier → Bytes32 |
| 203 | +nextSequenceAckKey channelId = commit (nextSeqAckPrefix, channelId) |
| 204 | +``` |
| 205 | + |
| 206 | + |
| 207 | +## Extensions |
| 208 | + |
| 209 | +### ICS-004 Packet |
| 210 | + |
| 211 | +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. |
| 212 | + |
| 213 | +```haskell |
| 214 | +setCommitment ∷ Store → Bytes32 → Bytes32 → Store |
| 215 | +getCommitment ∷ Store → Bytes32 → Bytes32 |
| 216 | + |
| 217 | +commitmentMagic ∷ Bytes32 |
| 218 | +commitmentMagic = 0x0100000000000000000000000000000000000000000000000000000000000000 |
| 219 | + |
| 220 | +mergeAck ∷ Bytes32 → Bytes32 |
| 221 | +mergeAck ack = |
| 222 | + commitmentMagic | |
| 223 | + (ack & 0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) |
| 224 | + |
| 225 | +commitmentExist ∷ Store → Bytes32 → Packet → Bool |
| 226 | +commitmentExist store expectedCommitment packet = |
| 227 | + getCommitment store (commit packet) ≡ exceptedCommitment |
| 228 | + |
| 229 | +batchSend ∷ Store → [Packet] → Store |
| 230 | +batchSend store packets = do |
| 231 | + assert (all (commitmentExist store commitmentMagic) packets) |
| 232 | + setCommitment store (packetKey channelId (commit packets)) commitmentMagic |
| 233 | + |
| 234 | +batchAcks ∷ Store → [Packet] → [Ackowledgement] → Store |
| 235 | +batchAcks store packets acks = do |
| 236 | + assert ( |
| 237 | + all |
| 238 | + (\(ack, packet) -> commitmentExist store (mergeAck ack) packet) |
| 239 | + (zip acks packet) |
| 240 | + ) |
| 241 | + setCommitment |
| 242 | + store |
| 243 | + (packetReceiptKey channelId (commit packets)) |
| 244 | + (mergeAck (commit acks)) |
| 245 | +``` |
| 246 | + |
| 247 | + |
| 248 | +:::tip |
| 249 | + |
| 250 | +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. |
| 251 | + |
| 252 | +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. |
| 253 | + |
| 254 | +::: |
0 commit comments