Union IBC
Overview
IBC
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 and ibc. This document is an attempt at specifying the implementation.
The semantic of the core protocol can be found in the ibc 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).
Protocol
ICS-002 Client
verifyMembership
no longer takes adelayPeriodTime
anddelayPeriodBlocks
, the specific light clients SHOULD implement such verification if necessary.verifyNonMembership
delayPeriodTime
has been removed.delayPeriodBlocks
has been removed.
clientId
is a uniqueuint32
. The client type MUST be stored and indexable with theclientId
.
Additions
registerClient
MUST be implemented and called before being able to callcreateClient
on a light client. Only one client type MUST exist for a given light client:
registerClient ∷ Implementations → ClientType → Address → ImplementationsregisterClient impls clientType lightClient = do assert (getImplementation impls clientType ≡ null) setImplementation impls clientType lightClient
ICS-003 Connection
connectionId
is no longer a string but a uniqueuint32
.ConnectionEnd
counterpartyPrefix
has been removed.version
has been removed.delayPeriodTime
has been removed.delayPeriodBlocks
has been removed.
type ClientId = Uint32type ConnectionId = Uint32
class ConnectionEnd where state ∷ ConnectionState counterpartyConnectionId ∷ ConnectionId clientId ∷ ClientId counterpartyClientId ∷ ClientId
ICS-004 Channel and Packet
Channel
channelId
is no longer a string but a uniqueuint32
.ChannelEnd
:connectionHops
has been renamed toconnectionId
and it’s type changed from[connectionIdentifer]
toconnectionId
.upgradeSequence
has been removed.
type ChannelId = Uint32data ChannelOrder = Ordered | Unordereddata ChannelState = Init | TryOpen | Open | Closed
class ChannelEnd where state ∷ ChannelState ordering ∷ ChannelOrder connectionId ∷ ConnectionId counterpartyChannelId ∷ ChannelId counterpartyPortId ∷ String version ∷ String
Packet
Packet
sourcePort
has been removed.destinationPort
has been removed.timeoutHeight
type has been changed from(uint64, uint64)
touint64
.
class Packet where sequence ∷ Uint64 sourceChannel ∷ ChannelId destinationChannel ∷ ChannelId payload ∷ Bytes timeoutHeight ∷ Uint64 timeoutTimestamp ∷ Uint64
ICS-024 Host
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.
type Prefix = Uint256
clientStatePrefix ∷ PrefixclientStatePrefix = 0x00
consensusStatePrefix ∷ PrefixconsensusStatePrefix = 0x01
connectionsPrefix ∷ PrefixconnectionsPrefix = 0x02
channelsPrefix ∷ PrefixchannelsPrefix = 0x03
packetsPrefix ∷ PrefixpacketsPrefix = 0x04
packetAcksPrefix ∷ PrefixpacketAcksPrefix = 0x05
nextSeqSendPrefix ∷ PrefixnextSeqSendPrefix = 0x06
nextSeqRecvPrefix ∷ PrefixnextSeqRecvPrefix = 0x07
nextSeqAckPrefix ∷ PrefixnextSeqAckPrefix = 0x08
commit ∷ AbiEncode a ⇒ a → Bytes32commit x = keccak256 (abiEncode x)
clientStateKey ∷ ClientId → Bytes32clientStateKey clientId = commit (clientStatePrefix, clientId)
consensusStateKey ∷ ClientId → Uint64 → Bytes32consensusStateKey clientId height = commit (consensusStatePrefix, clientId, height)
connectionKey ∷ ConnectionId → Bytes32connectionKey connectionId = commit (connectionsPrefix, connectionId)
channelKey ∷ ChannelId → Bytes32channelKey channelId = commit (channelsPrefix, channelId)
packetKey ∷ ChannelId → Bytes32 → Bytes32packetKey channelId packetHash = commit (packetsPrefix, channelId, packetHash)
packetReceiptKey ∷ ChannelId → Bytes32 → Bytes32packetReceiptKey channelId packetHash = commit (packetAcksPrefix, channelId, packetHash)
nextSequenceSendKey ∷ ChannelId → Bytes32nextSequenceSendKey channelId = commit (nextSeqSendPrefix, channelId)
nextSequenceRecvKey ∷ ChannelId → Bytes32nextSequenceRecvKey channelId = commit (nextSeqRecvPrefix, channelId)
nextSequenceAckKey ∷ ChannelId → Bytes32nextSequenceAckKey channelId = commit (nextSeqAckPrefix, channelId)
Commitments
After all preconditions are met, the protocol commits a succinct digest of the structures we need to prove on the counterparty. This commitments are encoded differently than in the canonical implementation, instead of protobuf, we use the solidity contract ABI encoding encoding.
Let’s define the setCommitment
and it’s associated commit
functions to update a store.
setCommitment ∷ Store → Bytes32 → Bytes32 → Store
commit ∷ AbiEncode a ⇒ a → Bytes32commit x = keccak256 (abiEncode x)
Client
commitClientState ∷ Store → ClientId → ClientState → StorecommitClientState store clientId clientState = setCommitment (clientStateKey clientId) (commit clientState)
commitConsensusState ∷ Store → ClientId → ConsensusState → StorecommitConsensusState store clientId consensusState = setCommitment (clientConsensusKey clientId) (commit consensusState)
Connection
commitConnection ∷ Store → ConnectionId → ConnectionEnd → StorecommitConnection store connectionId connection = setCommitment (connectionKey connectionId) (commit connection)
Channel
commitChannel ∷ Store → ChannelId → ChannelEnd → StorecommitChannel store channelId channel = setCommitment (channelKey channelId) (commit channel)
Packet
commitmentMagic ∷ Bytes32commitmentMagic = 0x0100000000000000000000000000000000000000000000000000000000000000
commitPacket ∷ Store → ChannelId → Packet → StorecommitPacket store channelId packet = setCommitment (packetKey channelId (commit packet)) commitmentMagic
mergeAck ∷ Bytes32 → Bytes32mergeAck ack = commitmentMagic | (ack & 0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
commitReceipt ∷ Store → ChannelId → Packet → StorecommitReceipt store channelId packet = setCommitment (packetReceiptKey channelId (commit packet)) commitmentMagic
type Acknowledgement = Bytes
commitAck ∷ Store → ChannelId → Packet → Acknowledgement → StorecommitAck store channelId packet ack = setCommitment (packetReceiptKey channelId (commit packet)) (mergeAck (keccak256 ack))
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.
commitmentMagic ∷ Bytes32commitmentMagic = 0x0100000000000000000000000000000000000000000000000000000000000000
setCommitment ∷ Store → Bytes32 → Bytes32 → StoregetCommitment ∷ Store → Bytes32 → Bytes32
mergeAck ∷ Bytes32 → Bytes32mergeAck ack = commitmentMagic | (ack & 0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
commitmentExist ∷ Store → Bytes32 → Packet → BoolcommitmentExist store expectedCommitment packet = getCommitment store (commit packet) ≡ exceptedCommitment
batchSend ∷ Store → [Packet] → StorebatchSend store packets = do assert (all (commitmentExist store commitmentMagic) packets) setCommitment store (packetKey channelId (commit packets)) commitmentMagic
batchAcks ∷ Store → [Packet] → [Acknowledgement] → StorebatchAcks store packets acks = do assert ( all (\(ack, packet) -> commitmentExist store (mergeAck (keccak256 ack)) packet) (zip acks packet) ) setCommitment store (packetReceiptKey channelId (commit packets)) (mergeAck (commit acks))
batchSend
is used to commit a batch of previously sent packets. It allows the relayer to provide a single membership proof for the whole batch at destination (recv).
batchAcks
is used to commit a batch of previously written acknowledgements. It allows the relayer to provide a single membership proof for the whole batch at destination (ack).
This functions can be used to trade execution gas on the source chain for the destination and vice versa. Committing a batch of sent packets will require an extra transaction on the source but will lower the execution gas on destination (single proof). Similarly, the batching of acknowledgements trade execution gas on the destination for the source. This further allow relayers and market makers to efficiently handle asset transfers based on gas price.