Skip to content

Commit

Permalink
Merge pull request #7 from omnia-network/docs
Browse files Browse the repository at this point in the history
message flow docs
  • Loading branch information
massimoalbarello authored Oct 18, 2023
2 parents 8636ffc + 2ecfc18 commit a5c6339
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 2 deletions.
117 changes: 115 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ To make it easier to run both the unit and integration tests, a script is provid
## Overview
![](./docs/images/image2.png)
![](./docs/images/high_level_view.png)
In order to enable WebSockets for a dapp running on the IC, we use a trustless intermediary, called WS Gateway, that provides a WebSocket endpoint for the frontend of the dapp, running in the user’s browser and interacts with the canister backend.
Expand Down Expand Up @@ -192,7 +192,7 @@ The gateway is needed as a WebSocket is a one-to-one connection between client a
WS Gateway accepts WebSocket connections with multiple clients in order to relay their messages to and from the canister.
Upon receiving a signed envelope from a client, the WS Gateway relays the message to the `/<canister_id>/call` endpoint of the Internet Computer. This way, the WS Gateway is transparent to the canister, which receives the request as if sent directly by the client which signed it.
Upon receiving a signed envelope from a client, the WS Gateway relays the message to the `/canister/<canister_id>/call` endpoint of the Internet Computer. This way, the WS Gateway is transparent to the canister, which receives the request as if sent directly by the client which signed it.
In order to get updates from the canister, the WS Gateway polls the caniser by sending periodic queries to the `ws_get_messages` method. Upon receiving a response to a query, the WS Gateway relays the contained message and certificate to the corresponding client using the WebSocket.
Expand All @@ -204,6 +204,119 @@ The gateway is needed as a WebSocket is a one-to-one connection between client a
In order to send an update to one of its clients, the canister calls the `ws_send` method of the CDK specifying the message that it wants to be delivered to the client. The CDK pushes this message in a FIFO queue together with all the other clients' messages. Upon receiving a query call to the `ws_get_messages` method, the CDK returns the messages in this queue (up to a certain limit), together with a certificate which proves to the clients that the messages are actually the ones sent from the canister even if relayed by the WS Gateway.
## Message Flow
### Establishment
![](./docs/images/establishment.png)
To establish a new IC WebSocket connection, the client (via the [IC WebSocket Frontend SDK](https://github.com/omnia-network/ic-websocket-sdk-js)):
- chooses a WS Gateway among the available ones and creates a new instance of `IcWebSocket` and passes the client’s identity to it (if any, otherwise a random one is generated by the SDK).
- opens a WebSocket connection to the specified Gateway.
- once the WebSocket connection is open, it creates a WebSocket Actor which is used to send requests, signed with the provided identity, to the Gateway. Each request specifies the canister and the method which the request is for.
- the first request sent is a singed envelope with content of `Call` variant. The content contains, among other things, the principal of the canister the client is connecting to, `ws_open` as the method name, and the argument of type `CanisterWsOpenArguments`.
- sends the envelope to the WS Gateway.
- once it receives the response containing the result of type `CanisterWsOpenResultValue`, triggers the `onWsOpen` callback.
The Gateway:
- receives the envelope with content of variant `Call` from the client and relays it to the `canister/<canister_id>/call` endpoint of the Internet Computer.
- receives the HTTP response from the IC containing `Ok(())` in the body and relays it to the client. This response is not enough for the client to trigger `onWsOpen`.
- creates a mapping between the `client_id` assigned to the WebSocket connection and the client’s key composed of a principal (either corresponding to the client’s identity or to the one randomly generated by the SDK) and the nonce specified by the client SDK.
- if the client is the first connecting to the specified canister via this WS Gateway, the latter starts polling the canister by querying the `ws_get_messages` endpoint, otherwise the Gateway is already polling the canister.
- once the poller returns certified messages from the canister, it relays each of them to the clients via their corresponding WebSocket connection, together with the certificate.
The canister (via the [IC WebSocket Backend CDK](https://github.com/omnia-network/ic-websocket-cdk-rs)):
- receives a request on the `ws_open` method from the client (relayed by the Gateway in a way that is transparent to the canister).
- if the client is the first connecting via this Gateway, it creates a message queue where it stores all the messages of clients connected via that Gateway, and which only this can poll. Otherwise, the queue already exists.
- once the canister processes the request to the `ws_open` method, it puts the message containing the result of type `CanisterWsOpenResultValue` in the respective message queue which the Gateway fetches in the next polling iteration.
- triggers the `on_open` callback.
Types:
- `CanisterWsOpenArguments`
- client nonce used by the canister to distinguish two different connections from the same client.
- `CanisterWsOpenResult`
- result with empty `Ok` value. Needed only to let the client know that the IC WebSocket connection has been opened.
### Relay Client Messages
![](./docs/images/client_message.png)
Once the connection is established, the client can send WebSocket messages to the canister. In order to do so, the client (via the [IC WebSocket Frontend SDK](https://github.com/omnia-network/ic-websocket-sdk-js)):
- creates a signed envelope with content of `Call` variant. The content contains, among other things, the principal of the canister the client is connected to, `ws_message` as the method name, and the argument of type `CanisterWsMessageArguments`.
- sends the envelope to the WS Gateway.
The Gateway:
- receives the envelope from the client and relays it to the `canister/<canister_id>/call` endpoint of the Internet Computer.
- receives the HTTP response from the canister containing `Ok(())` in the body and relays it to the client. This is not enough to acknowledge the client’s message.
The canister (via the [IC WebSocket Backend CDK](https://github.com/omnia-network/ic-websocket-cdk-rs)):
- receives a request on the `ws_message` method from the client (relayed by the Gateway in a way that is transparent to the canister).
- checks whether the sequence number of the `WebSocketMessage` (included automatically by the client’s SDK) corresponds to the next expected sequence number from the respective client.
- triggers the `on_message` callback.
Types:
- `CanisterWsMessageArguments`
- message of type `WebSocketMessage`
- sequence number used to identify the client message
- serialized content
- client key composed of client’s principal and nonce specified during the opening of the connection
- timestamp
- `is_service_message` flag used to determine whether the message is only used by the CDK and SDK to detect eventual bad behaviour of the WS Gateway. Messages flagged as true are not passed to the client.
### Relay Canister Messages
![](./docs/images/canister_message.png)
Once the connection is established, the canister can send WebSocket messages to the client. In order to do so, the canister (via the [IC WebSocket Backend CDK](https://github.com/omnia-network/ic-websocket-cdk-rs)):
- calls the `ws_send` method, specifying the client key of the client it wants to send the message to and the serialized message to be sent.
- the message of type `CanisterOutputMessage` is stored in the message queue corresponding to the WS Gateway which the client is connected to.
The Gateway:
- fetches the messages of type `CanisterOutputMessage` from the respective queue of the canister in the next polling iteration by querying the `ws_get_messages` method with the argument of type `CanisterWsGetMessagesArguments`. The list of fetched messages is of type `CanisterOutputCertifiedMessages`.
- for each message of type `CanisterOutputMessage`, gets the `client_id` corresponding to client key specified in the message.
- constructs a message of type `CanisterToClientMessage` from the one of type `CanisterOutputMessage`.
- relays the message of type `CanisterToClientMessage` to the client via the WebSocket connection identified by the `client_id`.
The client (via the [IC WebSocket Frontend SDK](https://github.com/omnia-network/ic-websocket-sdk-js)):
- receives the message of type `CanisterToClientMessage` from the Gateway via WebSocket.
- verifies the certificate which proves that the message has been created by the canister.
- checks whether the sequence number of the `WebSocketMessage` corresponds to the next expected sequence number from the canister.
- triggers the `onWsMessage` callback.
Types:
- `CanisterWsGetMessagesArguments`
- nonce used by the WS Gateway to let the CDK know which was the last polled message. This way the CDK does not return messages that have already been relayed to the clients.
- `CanisterOutputCertifiedMessages`
- vector of messages of type `CanisterOutputMessage`
- certificate of all the messages
- certified state tree
- `CanisterOutputMessage`
- client key of the client which the message is for. This is used by the WS Gateway to get the `client_id` corresponding to the WebSocket connection with the respective client.
- content of type `WebSocketMessage`
- sequence number used to identify the client message
- serialized content
- client key composed of client’s principal and nonce specified during the opening of the connection
- timestamp
- `is_service_message` flag used to determine whether the message is only used by the CDK and SDK to detect eventual bad behaviour of the WS Gateway. Messages flagged as true are not passed to the client.
- key constructed by appending the next outgoing message nonce to the gateway principal. This key is used by the client to verify the certificate of the response and by the Gateway to determine the nonce to poll from in the next polling iteration.
- `CanisterToClientMessage`
- content of type `WebSocketMessage` (same as in `CanisterOutputMessage`).
- key constructed by appending the next outgoing message nonce to the gateway principal (same as in `CanisterOutputMessage`).
- certificate of all the messages
- certified state tree, containing that message
# License
TODO: add a license
Expand Down
Binary file added docs/images/acknowledgement.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/canister_message.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/client_message.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/establishment.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
Binary file removed docs/images/image1.png
Binary file not shown.

0 comments on commit a5c6339

Please sign in to comment.