diff --git a/.circleci/config.yml b/.circleci/config.yml index 9f9f12124..33b6ca598 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -32,6 +32,13 @@ jobs: - run: sudo apt-get update && sudo apt-get -y install nodejs npm && sudo npm install codedown typescript tslint -g - run: make check_syntax + check_proto: + <<: *linux_defaults + steps: + - checkout + - run: sudo apt-get update && sudo apt-get -y install protobuf-compiler + - run: make check_proto + workflows: version: 2 test-suite: @@ -40,3 +47,4 @@ workflows: - check_links - check_sections - check_syntax + - check_proto diff --git a/Makefile b/Makefile index 5fe8bbacf..1394c9589 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ $(TOPTARGETS): $(SUBDIRS) $(SUBDIRS): $(MAKE) -C $@ $(MAKECMDGOALS) -check: check_links check_dependencies check_syntax check_sections +check: check_links check_dependencies check_syntax check_sections check_proto check_links: python ./scripts/check_links.py @@ -19,7 +19,10 @@ check_syntax: check_sections: python ./scripts/check_sections.py +check_proto: + $(MAKE) -C spec/ics-026-relayer-module check_proto + spec_pdf: pandoc --pdf-engine=xelatex --template eisvogel --filter pandoc-include --mathjax --toc --number-sections -o spec.pdf spec.pdc -.PHONY: $(TOPTARGETS) $(SUBDIRS) check check_links check_dependencies check_syntax check_sections spec_pdf +.PHONY: $(TOPTARGETS) $(SUBDIRS) check check_links check_dependencies check_syntax check_sections check_proto spec_pdf diff --git a/spec.pdf b/spec.pdf index 67689582a..62f07218b 100644 Binary files a/spec.pdf and b/spec.pdf differ diff --git a/spec/ics-002-consensus-verification/README.md b/spec/ics-002-consensus-verification/README.md index 5a8fef7cf..d9a57fe79 100644 --- a/spec/ics-002-consensus-verification/README.md +++ b/spec/ics-002-consensus-verification/README.md @@ -282,9 +282,9 @@ activity on the client. Frozen client SHOULD NOT be deleted from the state, as a method can be introduced in the future versions. ```typescript -function freezeClient(id: Identifier, h1: Header, h2: Header) { - consensusState = get(consensusStateKey(id)) - assert(consensusState.equivocationPredicate(h1, h2)) +function freezeClient(identifier: Identifier, firstHeader: Header, secondHeader: Header) { + consensusState = get(consensusStateKey(identifier)) + assert(consensusState.equivocationPredicate(firstHeader, secondHeader)) set(frozenKey(id), true) } ``` diff --git a/spec/ics-003-connection-semantics/README.md b/spec/ics-003-connection-semantics/README.md index 928db86b8..904cf88ca 100644 --- a/spec/ics-003-connection-semantics/README.md +++ b/spec/ics-003-connection-semantics/README.md @@ -96,6 +96,36 @@ function connectionKey(id: Identifier): Key { } ``` +A reverse mapping from clients to a set of connections (utilized to look up all connections using a client) is stored under a unique prefix per-client: + +```typescript +function clientConnectionsKey(clientIdentifier: Identifier): Key { + return "clients/{clientIdentifier}/connections" +} +``` + +### Helper functions + +`addConnectionToClient` is used to add a connection identifier to the set of connections associated with a client. + +```typescript +function addConnectionToClient(clientIdentifier: Identifier, connectionIdentifier: Identifier) { + conns = get(clientConnectionsKey(clientIdentifier, connectionIdentifier)) + conns.add(connectionIdentifier) + set(clientConnectionsKey(clientIdentifier, connectionIdentifier), conns) +} +``` + +`removeConnectionFromClient` is used to remove a connection identifier from the set of connections associated with a client. + +``` +function removeConnectionFromClient(clientIdentifier: Identifier, connectionIdentifier: Identifier) { + conns = get(clientConnectionsKey(clientIdentifier, connectionIdentifier)) + conns.remove(connectionIdentifier) + set(clientConnectionsKey(clientIdentifier, connectionIdentifier), conns) +} +``` + ### Subprotocols This ICS defines two subprotocols: opening handshake and closing handshake. Header tracking and closing-by-equivocation are defined in [ICS 2](../ics-002-consensus-verification). Datagrams defined herein are handled as external messages by the IBC relayer module defined in [ICS 26](../ics-026-relayer-module). @@ -134,6 +164,7 @@ function connOpenInit( connection = ConnectionEnd{state, desiredCounterpartyConnectionIdentifier, clientIdentifier, counterpartyClientIdentifier, nextTimeoutHeight} set(connectionKey(identifier), connection) + addConnectionToClient(clientIdentifier, identifier) } ``` @@ -160,6 +191,7 @@ function connOpenTry( connection = ConnectionEnd{state, counterpartyConnectionIdentifier, clientIdentifier, counterpartyClientIdentifier, nextTimeoutHeight} set(connectionKey(identifier), connection) + addConnectionToClient(clientIdentifier, identifier) } ``` @@ -242,6 +274,7 @@ function connOpenTimeout( )) } delete(connectionKey(identifier)) + removeConnectionFromClient(clientIdentifier, identifier) } ``` @@ -268,87 +301,31 @@ A correct protocol execution flows as follows (note that all calls are made thro *ConnCloseInit* initializes a close attempt on chain A. ```typescript -function connCloseInit(identifier: Identifier, nextTimeoutHeight: uint64) { +function connCloseInit(identifier: Identifier) { connection = get(connectionKey(identifier)) assert(connection.state === OPEN) - connection.state = CLOSETRY - connection.nextTimeoutHeight = nextTimeoutHeight - set(connectionKey(identifier), connection) -} -``` - -*ConnCloseTry* relays the intent to close a connection from chain A to chain B. - -```typescript -function connCloseTry( - identifier: Identifier, proofInit: CommitmentProof, proofHeight: uint64, - timeoutHeight: uint64, nextTimeoutHeight: uint64) { - assert(getConsensusState().getHeight() <= timeoutHeight) - connection = get(connectionKey(identifier)) - assert(connection.state === OPEN) - counterpartyStateRoot = get(rootKey(connection.clientIdentifier, proofHeight)) - expected = ConnectionEnd{CLOSETRY, identifier, connection.counterpartyClientIdentifier, - connection.clientIdentifier, timeoutHeight} - assert(verifyMembership(counterpartyStateRoot, proofInit, connectionKey(counterpartyConnectionIdentifier), expected)) connection.state = CLOSED - connection.nextTimeoutHeight = nextTimeoutHeight set(connectionKey(identifier), connection) } ``` -*ConnCloseAck* acknowledges a connection closure on chain B. +*ConnCloseConfirm* relays the intent to close a connection from chain A to chain B. ```typescript -function connCloseAck( - identifier: Identifier, proofTry: CommitmentProof, - proofHeight: uint64, timeoutHeight: uint64) { +function connCloseConfirm( + identifier: Identifier, proofInit: CommitmentProof, proofHeight: uint64) { assert(getConsensusState().getHeight() <= timeoutHeight) connection = get(connectionKey(identifier)) - assert(connection.state === CLOSETRY) + assert(connection.state === OPEN) counterpartyStateRoot = get(rootKey(connection.clientIdentifier, proofHeight)) expected = ConnectionEnd{CLOSED, identifier, connection.counterpartyClientIdentifier, - connection.clientIdentifier, timeoutHeight} - assert(verifyMembership(counterpartyStateRoot, proofTry, connectionKey(counterpartyConnectionIdentifier), expected)) + connection.clientIdentifier, 0} + assert(verifyMembership(counterpartyStateRoot, proofInit, connectionKey(counterpartyConnectionIdentifier), expected)) connection.state = CLOSED - connection.nextTimeoutHeight = 0 set(connectionKey(identifier), connection) } ``` -*ConnCloseTimeout* aborts a connection closing attempt due to a timeout on the other side and reopens the connection. - -```typescript -function connCloseTimeout( - identifier: Identifier, proofTimeout: CommitmentProof, - proofHeight: uint64, timeoutHeight: uint64) { - connection = get(connectionKey(identifier)) - counterpartyStateRoot = get(rootKey(connection.clientIdentifier, proofHeight)) - assert(proofHeight > connection.nextTimeoutHeight) - switch state { - case CLOSETRY: - expected = ConnectionEnd{OPEN, identifier, connection.counterpartyClientIdentifier, - connection.clientIdentifier, timeoutHeight} - assert(verifyMembership( - counterpartyStateRoot, proofTimeout, - connectionKey(counterpartyConnectionIdentifier), expected - )) - connection.state = OPEN - connection.nextTimeoutHeight = 0 - set(connectionKey(identifier), connection) - case CLOSED: - expected = ConnectionEnd{CLOSETRY, identifier, connection.counterpartyClientIdentifier, - connection.clientIdentifier, timeoutHeight} - assert(verifyMembership( - counterpartyStateRoot, proofTimeout, - connectionKey(counterpartyConnectionIdentifier), expected - )) - connection.state = OPEN - connection.nextTimeoutHeight = 0 - set(connectionKey(identifier), connection) - } -} -``` - #### Freezing by Equivocation The equivocation detection subprotocol is defined in [ICS 2](../ics-002-consensus-verification). If a client is frozen by equivocation, all associated connections are immediately frozen as well. @@ -365,6 +342,14 @@ function queryConnection(id: Identifier): ConnectionEnd | void { } ``` +Connections associated with a particular client can be queried by client identifier with `queryClientConnections`. + +```typescript +function queryClientConnections(id: Identifier): Set { + return get(clientConnectionsKey(id)) +} +``` + ## Backwards Compatibility Not applicable. @@ -389,6 +374,7 @@ Parts of this document were inspired by the [previous IBC specification](https:/ 29 March 2019 - Initial draft version submitted 17 May 2019 - Draft finalized +29 July 2019 - Revisions to track connection set associated with client ## Copyright diff --git a/spec/ics-004-channel-and-packet-semantics/README.md b/spec/ics-004-channel-and-packet-semantics/README.md index 6bedb8e8d..123534ecf 100644 --- a/spec/ics-004-channel-and-packet-semantics/README.md +++ b/spec/ics-004-channel-and-packet-semantics/README.md @@ -6,7 +6,7 @@ category: ibc-core requires: 2, 3, 5, 23, 24 author: Christopher Goes created: 2019-03-07 -modified: 2019-06-05 +modified: 2019-06-29 --- ## Synopsis @@ -367,13 +367,12 @@ function chanOpenTimeout( ##### Closing handshake -The `chanClose` function is called by either module to close their end of the channel. +The `chanCloseInit` function is called by either module to close their end of the channel. -Calling modules MAY atomically execute appropriate application logic in conjunction with calling `chanClose`. +Calling modules MAY atomically execute appropriate application logic in conjunction with calling `chanCloseInit`. ```typescript -function chanClose( - portIdentifier: Identifier, channelIdentifier: Identifier) { +function chanCloseInit(portIdentifier: Identifier, channelIdentifier: Identifier) { channel = get(channelKey(portIdentifier, channelIdentifier)) assert(channel.state === OPEN) connection = get(connectionKey(channel.connectionHops[0])) @@ -391,7 +390,7 @@ Calling modules MAY atomically execute appropriate application logic in conjunct ```typescript function chanCloseConfirm( portIdentifier: Identifier, channelIdentifier: Identifier, - proof: CommitmentProof, proofHeight: uint64) { + proofInit: CommitmentProof, proofHeight: uint64) { channel = get(channelKey(portIdentifier, channelIdentifier)) assert(channel.state === OPEN) connection = get(connectionKey(channel.connectionHops[0])) @@ -566,7 +565,7 @@ function timeoutPacketOrdered(packet: Packet, proof: CommitmentProof, proofHeigh assert(packet.destChannel === channel.counterpartyChannelIdentifier) connection = get(connectionKey(packet.connectionHops[0])) - assert(connection.state === OPEN) + // note: the connection may have been closed assert(packet.destPort === channel.counterpartyPortIdentifier) assert(packet.connectionHops === channel.connectionHops) @@ -614,7 +613,7 @@ function timeoutPacketUnordered(packet: Packet, proof: CommitmentProof, proofHei assert(packet.destChannel === channel.counterpartyChannelIdentifier) connection = get(connectionKey(packet.connectionHops[0])) - assert(connection.state === OPEN) + // note: the connection may have been closed assert(packet.destPort === channel.counterpartyPortIdentifier) assert(packet.connectionHops === channel.connectionHops) @@ -656,7 +655,7 @@ function timeoutClose(packet: Packet, proof: CommitmentProof, proofHeight: uint6 assert(packet.sourceChannel === channel.counterpartyChannelIdentifier) connection = get(connectionKey(channel.connectionHops[0])) - assert(connection.state === OPEN) + // note: the connection may have been closed assert(packet.sourcePort === channel.counterpartyPortIdentifier) assert(packet.connectionHops === channel.connectionHops) @@ -693,7 +692,7 @@ function cleanupPacketOrdered(packet: Packet, proof: CommitmentProof, proofHeigh assert(packet.destChannel === channel.counterpartyChannelIdentifier) connection = get(connectionKey(packet.connectionHops[0])) - assert(connection.state === OPEN) + // note: the connection may have been closed assert(packet.destPort === channel.counterpartyPortIdentifier) assert(packet.connectionHops === channel.connectionHops) @@ -730,7 +729,7 @@ function cleanupPacketUnordered(packet: Packet, proof: CommitmentProof, proofHei assert(packet.destChannel === channel.counterpartyChannelIdentifier) connection = get(connectionKey(packet.connectionHops[0])) - assert(connection.state === OPEN) + // note: the connection may have been closed assert(packet.destPort === channel.counterpartyPortIdentifier) assert(packet.connectionHops === channel.connectionHops) @@ -784,6 +783,7 @@ Coming soon. 5 June 2019 - Draft submitted 4 July 2019 - Modifications for unordered channels & acknowledgements 16 July 2019 - Alterations for multi-hop routing future compatibility +29 July 2019 - Revisions to handle timeouts after connection closure ## Copyright diff --git a/spec/ics-025-handler-interface/README.md b/spec/ics-025-handler-interface/README.md index 25772ea8f..1ca9b8634 100644 --- a/spec/ics-025-handler-interface/README.md +++ b/spec/ics-025-handler-interface/README.md @@ -88,7 +88,7 @@ function updateClient(id: Identifier, header; Header): error | void { The default IBC relayer module will allow external calls to `freezeClient`. ```typescript -function freezeClient(id: Identifier, headerOne: Header, headerTwo: Header): error | void { +function freezeClient(id: Identifier, firstHeader: Header, secondHeader: Header): error | void { // defined in ICS 2 } ``` @@ -166,34 +166,12 @@ function connCloseInit(identifier: Identifier, nextTimeoutHeight: uint64) { } ``` -`connCloseTry` continues the graceful connection closing process. It will fail if there are any open channels using the connection, if the proof is invalid, or if the identifier is invalid. +`connCloseConfirm` finalizes the graceful connection closing process. It will fail if the proof is invalid or if the identifier is invalid. -The default IBC relayer module will allow external calls to `connCloseTry`. +The default IBC relayer module will allow external calls to `connCloseConfirm`. ```typescript -function connCloseTry( - identifier: Identifier, proofInit: CommitmentProof, - timeoutHeight: uint64, nextTimeoutHeight: uint64) { - // defined in ICS 3 -} -``` - -`connCloseAck` finalizes the graceful connection closing process. It will fail if the proof is invalid or if the identifier is invalid. - -The default IBC relayer module will allow external calls to `connCloseAck`. - -```typescript -function connCloseAck(identifier: Identifier, proofTry: CommitmentProof, timeoutHeight: uint64) { - // defined in ICS 3 -} -``` - -`connCloseTimeout` proves that a connection closing handshake has timed-out and resets the process. - -The default IBC relayer module will allow external calls to `connCloseTimeout`. - -```typescript -function connCloseTimeout(identifier: Identifier, proofTimeout: CommitmentProof, timeoutHeight: uint64) { +function connCloseConfirm(identifier: Identifier, proofInit: CommitmentProof, proofHeight: uint64) { // defined in ICS 3 } ``` @@ -214,7 +192,7 @@ By default, channels are owned by the creating port, meaning only the module bou ```typescript function chanOpenInit( - connectionIdentifier: Identifier, channelIdentifier: Identifier, + connectionHops: [Identifier], portIdentifier: Identifier, channelIdentifier: Identifier, counterpartyChannelIdentifier: Identifier, counterpartyPortIdentifier: Identifier, nextTimeoutHeight: uint64) { // defined in ICS 4 } @@ -226,8 +204,8 @@ The default IBC relayer module will allow external calls to `chanOpenTry`. ```typescript function chanOpenTry( - connectionIdentifier: Identifier, channelIdentifier: Identifier, counterpartyChannelIdentifier: Identifier, - portIdentifier: Identifier, counterpartyPortIdentifier: Identifier, + connectionHops: [Identifier], portIdentifier: Identifier, channelIdentifier: Identifier, + counterpartyPortIdentifier: Identifier, counterpartyChannelIdentifier: Identifier, timeoutHeight: uint64, nextTimeoutHeight: uint64, proofInit: CommitmentProof) { // defined in ICS 4 } @@ -239,7 +217,7 @@ The default IBC relayer module will allow external calls to `chanOpenAck`. ```typescript function chanOpenAck( - connectionIdentifier: Identifier, channelIdentifier: Identifier, + portIdentifier: Identifier, channelIdentifier: Identifier, timeoutHeight: uint64, nextTimeoutHeight: uint64, proofTry: CommitmentProof) { // defined in ICS 4 } @@ -251,7 +229,7 @@ The default IBC relayer module will allow external calls to `chanOpenConfirm`. ```typescript function chanOpenConfirm( - connectionIdentifier: Identifier, channelIdentifier: Identifier, + portIdentifier: Identifier, channelIdentifier: Identifier, timeoutHeight: uint64, proofAck: CommitmentProof) { // defined in ICS 4 } @@ -263,7 +241,7 @@ The default IBC relayer module will allow external calls to `chanOpenTimeout`. ```typescript function chanOpenTimeout( - connectionIdentifier: Identifier, channelIdentifier: Identifier, + portIdentifier: Identifier, channelIdentifier: Identifier, timeoutHeight: uint64, proofTimeout: CommitmentProof) { // defined in ICS 4 } @@ -280,44 +258,19 @@ function queryChannel(connId: Identifier, chanId: Identifier): void { `chanCloseInit` initiates the channel closing handshake. ```typescript -function chanCloseInit( - connectionIdentifier: Identifier, channelIdentifier: Identifier, nextTimeoutHeight: uint64) { +function chanCloseInit(portIdentifier: Identifier, channelIdentifier: Identifier) { // defined in ICS 4 } ``` -`chanCloseTry` acknowledges the initialization of the channel closing handshake on the counterparty chain. +`chanCloseConfirm` acknowledges the closure of a channel on the counterparty chain and closes the corresponding end on this chain. -The default IBC relayer module will allow external calls to `chanCloseTry`. +The default IBC relayer module will allow external calls to `chanCloseConfirm`. ```typescript -function chanCloseTry( - connectionIdentifier: Identifier, channelIdentifier: Identifier, - timeoutHeight: uint64, nextTimeoutHeight: uint64, proofInit: CommitmentProof) { - // defined in ICS 4 -} -``` - -`chanCloseAck` acknowledges the acknowledgement and finalizes the channel closing handshake. - -The default IBC relayer module will allow external calls to `chanCloseAck`. - -```typescript -function chanCloseAck( - connectionIdentifier: Identifier, channelIdentifier: Identifier, - timeoutHeight: uint64, proofTry: CommitmentProof) { - // defined in ICS 4 -} -``` - -`chanCloseTimeout` proves that a channel closing handshake has timed-out and resets the process. - -The default IBC relayer module will allow external calls to `chanCloseTimeout`. - -```typescript -function chanCloseTimeout( - connectionIdentifier: Identifier, channelIdentifier: Identifier, - timeoutHeight: uint64, proofTimeout: CommitmentProof) { +function chanCloseConfirm( + portIdentifier: Identifier, channelIdentifier: Identifier, + proofInit: CommitmentProof, proofHeight: uint64) { // defined in ICS 4 } ``` diff --git a/spec/ics-026-relayer-module/Makefile b/spec/ics-026-relayer-module/Makefile new file mode 100644 index 000000000..e33283566 --- /dev/null +++ b/spec/ics-026-relayer-module/Makefile @@ -0,0 +1,4 @@ +check_proto: + /bin/bash -c 'TEMP=$$(mktemp); protoc datagrams.proto -o $$TEMP; rm -f $$TEMP' + +.PHONY: check_proto diff --git a/spec/ics-026-relayer-module/README.md b/spec/ics-026-relayer-module/README.md index 8bc8ba781..873b7f032 100644 --- a/spec/ics-026-relayer-module/README.md +++ b/spec/ics-026-relayer-module/README.md @@ -5,7 +5,7 @@ stage: Draft category: ibc-core author: Christopher Goes created: 2019-06-09 -modified: 2019-06-09 +modified: 2019-07-29 --- ## Synopsis @@ -22,23 +22,184 @@ but is a bit tricky to understand and may require extra work on the part of rela All functions provided by the IBC handler interface are defined as in [ICS 25](../ics-025-handler-interface). +The functions `generate` & `authenticate` are defined as in [ICS 5](../ics-005-port-allocation). + ### Desired Properties - Modules should be able to bind to ports and own channels through the relayer module. - No overhead should be added for packet sends and receives other than the layer of call indirection. +- The relayer module should call specified handler functions on modules when they need to act upon packets. ## Technical Specification -### Datagrams +### Module callback interface + +Modules must expose the following function signatures to the relayer module, which are called upon the receipt of various datagrams: + +```typescript +function onChanOpenInit() { + // defined by the module +} + +function onChanOpenTry() { + // defined by the module +} + +function onChanOpenAck() { + // defined by the module +} + +function onChanOpenConfirm() { + // defined by the module +} + +function onChanOpenTimeout() { + // defined by the module +} + +function onChanCloseConfirm() { + // defined by the module +} + +function onRecvPacket() { + // defined by the module +} + +function onTimeoutPacket() { + // defined by the module +} +``` + +These are combined together in a `ModuleCallbacks` interface: + +```typescript +interface ModuleCallbacks { + onChanOpenInit: () => boolean + onChanOpenTry: () => boolean + onChanOpenAck: () => boolean + onChanOpenConfirm: () => boolean + onChanOpenTimeout: () => boolean + onChanCloseConfirm: () => boolean + onRecvPacket: () => boolean + onTimeoutPacket: () => boolean +} +``` + +Callbacks are provided when the module binds to a port. + +```typescript +function callbackKey(portIdentifier: Identifier) { + return "callbacks/{portIdentifier}" +} +``` + +The calling module identifier is also stored for future authentication should the callbacks need to be altered. + +```typescript +function authenticationKey(portIdentifier: Identifier) { + return "authentication/{portIdentifier}" +} +``` + +### Port binding + +The IBC relayer module sits in-between the handler module ([ICS 25](../ics-025-handler-interface)) and individual modules on the host state machine. + +The function `bindPort` can be called by a module in order to bind to a port, through the relayer module, and set up callbacks. + +```typescript +function bindPort(id: Identifier, callbacks: Callbacks) { + assert(get(callbackKey(id)) === null) + handler.bindPort(id) + key = generate() + set(authenticationKey(id), key) + set(callbackKey(id), callbacks) +} +``` + +The function `updatePort` can be called by a module in order to alter the callbacks. + + +```typescript +function updatePort(id: Identifier, newCallbacks: Callbacks) { + assert(authenticate(get(authenticationKey(id)))) + set(callbackKey(id), newCallbacks) +} +``` + +The function `releasePort` can be called by a module in order to release a port previously in use. + +```typescript +function releasePort(id: Identifier) { + assert(authenticate(get(authenticationKey(id)))) + handler.releasePort(id) + del(callbackKey(id)) + del(authenticationKey(id)) +} +``` + +### Datagram handlers (write) *Datagrams* are external data blobs accepted as transactions by the relayer module. +This section defines a *handler function* for each datagram, which is executed when the associated datagram is submitted to the relayer module in a transaction. +All datagrams can also be safely submitted by other modules to the relayer module. +No message signatures or data validity checks are assumed beyond those which are explicitly indicated. #### Client lifecycle management -(todo) +`ClientCreate` creates a new light client with the specified identifier & consensus state. + +```typescript +interface ClientCreate { + identifier: Identifier + consensusState: ConsensusState +} +``` + +```typescript +function handleClientCreate(datagram: ClientCreate) { + handler.createClient(datagram.identifier, datagram.consensusState) +} +``` + +`ClientUpdate` updates an existing light client with the specified identifier & new header. + +```typescript +interface ClientUpdate { + identifier: Identifier + header: Header +} +``` + +```typescript +function handleClientUpdate(datagram: ClientUpdate) { + handler.updateClient(datagram.identifier, datagram.header) +} +``` + +`ClientFreeze` freezes an existing light client with the specified identifier by proving that an equivocation has occurred. + +```typescript +interface ClientFreeze { + identifier: Identifier + firstHeader: Header + secondHeader: Header +} +``` + +```typescript +function handleClientFreeze(datagram: ClientUpdate) { + handler.freezeClient(datagram.identifier, datagram.firstHeader, datagram.secondHeader) + + for (const identifier in handler.queryClientConnections(client)) + handler.handleConnCloseInit(identifier) +} +``` #### Connection lifecycle management +The `ConnOpenInit` datagram starts the connection handshake process with an IBC module on another chain. + ```typescript interface ConnOpenInit { identifier: Identifier @@ -49,6 +210,17 @@ interface ConnOpenInit { } ``` +```typescript +function handleConnOpenInit(datagram: ConnOpenInit) { + handler.connOpenInit( + datagram.identifier, datagram.desiredCounterpartyIdentifier, datagram.clientIdentifier, + datagram.counterpartyClientIdentifier, datagram.nextTimeoutHeight + ) +} +``` + +The `ConnOpenTry` datagram accepts a handshake request from an IBC module on another chain. + ```typescript interface ConnOpenTry { desiredIdentifier: Identifier @@ -61,6 +233,17 @@ interface ConnOpenTry { } ``` +```typescript +function handleConnOpenTry(datagram: ConnOpenTry) { + handler.connOpenTry( + datagram.desiredIdentifier, datagram.counterpartyConnectionIdentifier, datagram.counterpartyClientIdentifier, + datagram.clientIdentifier, datagram.proofInit, datagram.timeoutHeight, datagram.nextTimeoutHeight + ) +} +``` + +The `ConnOpenAck` datagram confirms a handshake acceptance by the IBC module on another chain. + ```typescript interface ConnOpenAck { identifier: Identifier @@ -70,6 +253,17 @@ interface ConnOpenAck { } ``` +```typescript +function handleConnOpenAck(datagram: ConnOpenAck) { + handler.connOpenAck( + datagram.identifier, datagram.proofTry, + datagram.timeoutHeight, datagram.nextTimeoutHeight + ) +} +``` + +The `ConnOpenConfirm` datagram acknowledges a handshake acknowledgement by an IBC module on another chain & finalizes the connection. + ```typescript interface ConnOpenConfirm { identifier: Identifier @@ -78,6 +272,16 @@ interface ConnOpenConfirm { } ``` +```typescript +function handleConnOpenConfirm(datagram: ConnOpenConfirm) { + handler.connOpenConfirm( + datagram.identifier, datagram.proofAck, datagram.timeoutHeight + ) +} +``` + +The `ConnOpenTimeout` datagram proves that a connection handshake has timed out prior to completion, resetting the state. + ```typescript interface ConnOpenTimeout { identifier: Identifier @@ -86,66 +290,99 @@ interface ConnOpenTimeout { } ``` +```typescript +function handleConnOpenTimeout(datagram: ConnOpenTimeout) { + handler.handleConnOpenTimeout( + datagram.identifier, datagram.proofTimeout, datagram.timeoutHeight + ) +} +``` + +The `ConnCloseInit` datagram closes an unused connection. + ```typescript interface ConnCloseInit { identifier: Identifier - nextTimeoutHeight: uint64 } ``` ```typescript -interface ConnCloseTry { - identifier: Identifier - proofInit: CommitmentProof - timeoutHeight: uint64 - nextTimeoutHeight: uint64 +function handleConnCloseInit(datagram: ConnCloseInit) { + handler.handleConnCloseInit(identifier) } ``` +The `ConnCloseConfirm` datagram acknowledges that a connection has been closed on the counterparty chain and closes the end on this chain. + ```typescript -interface ConnCloseAck { +interface ConnCloseConfirm { identifier: Identifier - proofTry: CommitmentProof - timeoutHeight: uint64 + proofInit: CommitmentProof + proofHeight: uint64 } ``` ```typescript -interface ConnCloseTimeout { - identifier: Identifier - proofTimeout: CommitmentProof - timeoutHeight: uint64 +function handleConnCloseConfirm(datagram: ConnCloseConfirm) { + handler.handleConnCloseConfirm( + datagram.identifier, datagram.proofInit, datagram.proofHeight + ) } ``` + #### Channel lifecycle management ```typescript interface ChanOpenInit { - connectionIdentifier: Identifier + portIdentifier: Identifier channelIdentifier: Identifier - counterpartyChannelIdentifier: Identifier counterpartyPortIdentifier: Identifier + counterpartyChannelIdentifier: Identifier + connectionHops: [Identifier] nextTimeoutHeight: uint64 } ``` +```typescript +function handleChanOpenInit(datagram: ChanOpenInit) { + module = lookupModule(datagram.portIdentifier) + module.onChanOpenInit(datagram) + handler.chanOpenInit( + datagram.portIdentifier, datagram.channelIdentifier, datagram.counterpartyPortIdentifier, + datagram.counterpartyChannelIdentifier, datagram.connectionHops, datagram.nextTimeoutHeight + ) +} +``` + ```typescript interface ChanOpenTry { - connectionIdentifier: Identifier - channelIdentifier: Identifier - counterpartyChannelIdentifier: Identifier portIdentifier: Identifier + channelIdentifier: Identifier counterpartyPortIdentifier: Identifier + counterpartyChannelIdentifier: Identifier + connectionHops: [Identifier] timeoutHeight: uint64 nextTimeoutHeight: uint64 proofInit: CommitmentProof } ``` +```typescript +function handleChanOpenTry(datagram: ChanOpenTry) { + module = lookupModule(datagram.portIdentifier) + module.onChanOpenTry(datagram) + handler.chanOpenTry( + datagram.portIdentifier, datagram.channelIdentifier, datagram.counterpartyPortIdentifier, + datagram.counterpartyChannelIdentifier, datagram.connectionHops, datagram.timeoutHeight, + datagram.nextTimeoutHeight, datagram.proofInit + ) +} +``` + ```typescript interface ChanOpenAck { - connectionIdentifier: Identifier + portIdentifier: Identifier channelIdentifier: Identifier timeoutHeight: uint64 nextTimeoutHeight: uint64 @@ -153,18 +390,38 @@ interface ChanOpenAck { } ``` +```typescript +function handleChanOpenAck(datagram: ChanOpenAck) { + module.onChanOpenAck(datagram) + handler.chanOpenAck( + datagram.portIdentifier, datagram.channelIdentifier, datagram.timeoutHeight, + datagram.nextTimeoutHeight, datagram.proofTry + ) +} +``` + ```typescript interface ChanOpenConfirm { - connectionIdentifier: Identifier + portIdentifier: Identifier channelIdentifier: Identifier timeoutHeight: uint64 proofAck: CommitmentProof } ``` +```typescript +function handleChanOpenConfirm(datagram: ChanOpenConfirm) { + module.onChanOpenConfirm(datagram) + handler.chanOpenConfirm( + datagram.portIdentifier, datagram.channelIdentifier, + datagram.timeoutHeight, datagram.proofAck + ) +} +``` + ```typescript interface ChanOpenTimeout { - connectionIdentifier: Identifier + portIdentifier: Identifier channelIdentifier: Identifier timeoutHeight: uint64 proofTimeout: CommitmentProof @@ -172,104 +429,165 @@ interface ChanOpenTimeout { ``` ```typescript -interface ChanCloseInit { - connectionIdentifier: Identifier - channelIdentifier: Identifier - nextTimeoutHeight: uint64 +function handleChanOpenTimeout(datagram: ChanOpenTimeout) { + module = lookupModule(datagram.portIdentifier) + module.onChanOpenTimeout(datagram) + handler.chanOpenTimeout( + datagram.portIdentifier, datagram.channelIdentifier, + datagram.timeoutHeight, datagram.proofTimeout + ) } ``` +Channel closure can only be initiated by the owning module directly (so there is no associated datagram). + ```typescript -interface ChanCloseTry { - connectionIdentifier: Identifier - channelIdentifier: Identifier - timeoutHeight: uint64 - nextTimeoutHeight: uint64 - proofInit: CommitmentProof +function handleChanCloseInit(portIdentifier: Identifier, channelIdentifier: Identifier) { + handler.chanCloseInit(portIdentifier, channelIdentifier) } ``` ```typescript -interface ChanCloseAck { - connectionIdentifier: Identifier +interface ChanCloseConfirm { + portIdentifier: Identifier channelIdentifier: Identifier - timeoutHeight: uint64 - proofTry: CommitmentProof + proofInit: CommitmentProof + proofHeight: uint64 } ``` ```typescript -interface ChanCloseTimeout { - connectionIdentifier: Identifier - channelIdentifier: Identifier - timeoutHeight: uint64 - proofTimeout: CommitmentProof +function handleChanCloseConfirm(datagram: ChanCloseConfirm) { + module = lookupModule(datagram.portIdentifier) + module.onChanCloseConfirm(datagram) + handler.chanCloseConfirm( + datagram.portIdentifier, datagram.channelIdentifier, + datagram.proofInit, datagram.proofHeight + ) } ``` #### Packet relay -### Subprotocols +`sendPacket` can only be invoked directly by the module owning the channel on which the packet is to be sent, so there is no associated datagram. -### Interface usage example +```typescript +function handlePacketSend(packet: Packet) { + // auth module + handler.sendPacket(packet) +} +``` -As a demonstration of interface usage, a simple module handling send/receive of a native asset could be implemented as follows: +```typescript +interface PacketRecv { + packet: Packet + proof: CommitmentProof + proofHeight: uint64 + acknowledgement: bytes +} +``` -```golang -type State struct { - channel string +```typescript +function handlePacketRecv(datagram: PacketRecv) { + module = lookupModule(datagram.portIdentifier) + module.onRecvPacket(datagram.packet) + handler.recvPacket(packet, proof, proofHeight, acknowledgement) } ``` -```golang -type PacketData struct { - asset string - amount integer - source address - destination address +```typescript +interface PacketTimeoutOrdered { + packet: Packet + proof: CommitmentProof + proofHeight: uint64 + nextSequenceRecv: uint64 } ``` -```coffeescript -function myModuleInit() - client = createClient(consensusState) - connection = createConnection(nil, client) - state.channel = createChannel(nil, connection, myModuleRecv, myModuleTimeout) +```typescript +function handlePacketTimeoutOrdered(datagram: PacketTimeoutOrdered) { + handler.timeoutPacketOrdered( + datagram.packet, datagram.proof, datagram.proofHeight, datagram.nextSequenceRecv + ) + module.onTimeoutPacket(datagram.packet) +} ``` -```coffeescript -function myModuleSend(string asset, integer amount, address source, address destination) - checkSignature(source) - deductBalance(source, asset, amount) - escrow(asset, amount) - sendPacket({ - channel: state.channel, - data: { - asset : asset, - amount : amount, - source : source, - destination : destination, - } - }) +```typescript +interface PacketTimeoutUnordered { + packet: Packet + proof: CommitmentProof + proofHeight: uint64 +} ``` -```coffeescript -function myModuleRecv(Packet packet) - recvPacket(packet) - assert(packet.channel == channel) - data = packet.data - unescrow(data.asset, data.amount) - increaseBalance(data.destination, data.asset, data.amount) +```typescript +function handlePacketTimeoutUnordered(datagram: PacketTimeoutUnordered) { + handler.timeoutPacketUnordered(datagram.packet, datagram.proof, datagram.proofHeight) + module.onTimeoutPacket(datagram.packet) +} ``` -```coffeescript -function myModuleTimeout(Packet packet) - timeoutPacket(packet) - data = packet.data - unescrow(packet.asset, packet.amount) - increaseBalance(packet.source, packet.asset, packet.amount) +```typescript +interface PacketTimeoutClose { + packet: Packet + proof: CommitmentProof + proofHeight: uint64 +} ``` +```typescript +function handlePacketTimeoutClose(datagram: PacketTimeoutClose) { + handler.timeoutPacketClose(datagram.packet, datagram.proof, datagram.proofHeight) +} +``` + +```typescript +interface PacketCleanupOrdered { + packet: Packet + proof: CommitmentProof + proofHeight: uint64 + nextSequenceRecv: uint64 +} +``` + +```typescript +function handlePacketCleanupOrdered(datagram: PacketCleanupOrdered) { + handler.cleanupPacketOrdered( + datagram.packet, datagram.proof, + datagram.proofHeight, datagram.nextSequenceRecv + ) +} +``` + +```typescript +interface PacketCleanupUnordered { + packet: Packet + proof: CommitmentProof + proofHeight: uint64 + acknowledgement: bytes +} +``` + +```typescript +function handlePacketCleanupUnordered(datagram: PacketCleanupUnordered) { + handler.cleanupPacketUnordered( + datagram.packet, datagram.proof, + datagram.proofHeight, datagram.acknowledgement + ) +} +``` + +### Query (read-only) functions + +- query clients +- query connections +- query channels + +### Interface usage example + +See ICS 20 for a usage example. + ## Backwards Compatibility Not applicable. @@ -289,6 +607,7 @@ Coming soon. ## History June 9 2019 - Draft submitted +July 28 2019 - Major revisions ## Copyright diff --git a/spec/ics-026-relayer-module/datagrams.proto b/spec/ics-026-relayer-module/datagrams.proto new file mode 100644 index 000000000..8d29cda23 --- /dev/null +++ b/spec/ics-026-relayer-module/datagrams.proto @@ -0,0 +1,166 @@ +syntax = 'proto3'; + +/* Client lifecycle management. */ + +message ClientCreate { + string identifier = 1; + string consensusState = 2; // TODO +} + +message ClientUpdate { + string identifier = 1; + string header = 2; // TODO +} + +message ClientFreeze { + string identifier = 1; + string firstHeader = 2; // TODO + string secondHeader = 3; // TODO +} + +/* Connection lifecycle management. */ + +message ConnOpenInit { + string identifier = 1; + string desiredCounterpartyIdentifier = 2; + string clientIdentifier = 3; + string counterpartyClientIdentifier = 4; + int64 nextTimeoutHeight = 5; +} + +message ConnOpenTry { + string desiredIdentifier = 1; + string counterpartyConnectionIdentifier = 2; + string counterpartyClientIdentifier = 3; + string clientIdentifier = 4; + string proofInit = 5; // TODO + uint64 timeoutHeight = 6; + uint64 nextTimeoutHeight = 7; +} + +message ConnOpenAck { + string identifier = 1; + string proofTry = 2; // TODO + uint64 timeoutHeight = 3; + uint64 nextTimeoutHeight = 4; +} + +message ConnOpenConfirm { + string identifier = 1; + string proofAck = 2; // TODO + uint64 timeoutHeight = 3; +} + +message ConnOpenTimeout { + string identifier = 1; + string proofTimeout = 2; // TODO + uint64 timeoutHeight = 3; +} + +message ConnCloseInit { + string identifier = 1; +} + +message ConnCloseConfirm { + string identifier = 1; + string proofInit = 2; // TODO + uint64 proofHeight = 3; +} + +/* Channel lifecycle management. */ + +message ChanOpenInit { + string portIdentifier = 1; + string channelIdentifier = 2; + string counterpartyPortIdentifier = 3; + string counterpartyChannelIdentifier = 4; + repeated string connectionHops = 5; + uint64 nextTimeoutHeight = 6; +} + +message ChanOpenTry { + string portIdentifier = 1; + string channelIdentifier = 2; + string counterpartyPortIdentifier = 3; + string counterpartyChannelIdentifier = 4; + repeated string connectionHops = 5; + uint64 timeoutHeight = 6; + uint64 nextTimeoutHeight = 7; + string proofInit = 8; // TODO +} + +message ChanOpenAck { + string portIdentifier = 1; + string channelIdentifier = 2; + uint64 timeoutHeight = 3; + uint64 nextTimeoutHeight = 4; + string proofTry = 5; // TODO +} + +message ChanOpenConfirm { + string portIdentifier = 1; + string channelIdentifier = 2; + uint64 timeoutHeight = 3; + string proofAck = 4; // TODO +} + +message ChanOpenTimeout { + string portIdentifier = 1; + string channelIdentifier = 2; + uint64 timeoutHeight = 3; + string proofTimeout = 4; // TODO +} + +message ChanCloseInit { + string portIdentifier = 1; + string channelIdentifier = 2; +} + +message ChanCloseConfirm { + string portIdentifier = 1; + string channelIdentifier = 2; + string proofInit = 3; // TODO + uint64 proofHeight = 4; +} + +/* Packet relay. */ + +message PacketRecv { + string packet = 1; // TODO + string proof = 2; // TODO + uint64 proofHeight = 3; + bytes acknowledgement = 4; +} + +message PacketTimeoutOrdered { + string packet = 1; // TODO + string proof = 2; // TODO + uint64 proofHeight = 3; + uint64 nextSequenceRecv = 4; +} + +message PacketTimeoutUnordered { + string packet = 1; // TODO + string proof = 2; // TODO + uint64 proofHeight = 3; +} + +message PacketTimeoutClose { + string packet = 1; // TODO + string proof = 2; // TODO + uint64 proofHeight = 3; +} + +message PacketCleanupOrdered { + string packet = 1; // TODO + string proof = 2; // TODO + uint64 proofHeight = 3; + uint64 nextSequenceRecv = 4; +} + +message PacketCleanupUnordered { + string packet = 1; // TODO + string proof = 2; // TODO + uint64 proofHeight = 3; + bytes acknowledgement = 4; +}