Skip to content

Commit

Permalink
Merge pull request #7 from omnia-network/release/0.4.0
Browse files Browse the repository at this point in the history
release: 0.4.0
  • Loading branch information
ilbertt authored Jul 21, 2024
2 parents 4f04afa + f8ddfc3 commit a4cc47e
Show file tree
Hide file tree
Showing 11 changed files with 63 additions and 221 deletions.
47 changes: 5 additions & 42 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,22 +1,13 @@
name: Release ic-websocket-cdk-mo
name: Publish ic-websocket-cdk-mo

# only run when the tests complete
# only run when a release is published
on:
workflow_run:
workflows: [ic-websocket-cdk-mo tests]
types:
- completed
branches:
- main
workflow_dispatch:
release:
types: [published]

jobs:
publish:
runs-on: ubuntu-latest
# only run if the tests were successful
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
outputs:
version: ${{ steps.npm-publish.outputs.version }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
Expand All @@ -25,7 +16,7 @@ jobs:

- uses: dfinity/setup-dfx@main
with:
dfx-version: 0.16.1
dfx-version: 0.21.0

- name: install mops
run: npm i ic-mops -g
Expand All @@ -37,33 +28,5 @@ jobs:
run: |
mops import-identity --no-encrypt -- "${DFX_IDENTITY_PEM}"
mops publish
echo "version=$(cat mops.toml | grep "version =" | cut -d\" -f2)" >> "$GITHUB_OUTPUT"
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
DFX_IDENTITY_PEM: ${{ secrets.DFX_IDENTITY_PEM }}

tag:
needs: publish
runs-on: ubuntu-latest
outputs:
version: ${{ steps.tag_version.outputs.new_tag }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Bump version and push tag
id: tag_version
uses: mathieudutour/[email protected]
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
custom_tag: ${{ needs.publish.outputs.version }}

release:
needs: tag
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ needs.tag.outputs.version }}
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:

- uses: dfinity/setup-dfx@main
with:
dfx-version: 0.16.1
dfx-version: 0.21.0

# rust toolchain is needed for integration tests
- uses: actions-rs/toolchain@v1
Expand Down
6 changes: 3 additions & 3 deletions mops.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ license = "MIT"

[dependencies]
ic-certification = "0.1.3"
sha2 = "0.0.4"
cbor = "0.1.3"
base = "0.10.4"
sha2 = "0.1.0"
cbor = "1.0.0"
base = "0.11.3"
13 changes: 8 additions & 5 deletions src/State.mo
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Text "mo:base/Text";
import Blob "mo:base/Blob";
import CertifiedData "mo:base/CertifiedData";
import Buffer "mo:base/Buffer";
import Nat8 "mo:base/Nat8";
import CertTree "mo:ic-certification/CertTree";
import Sha256 "mo:sha2/Sha256";

Expand Down Expand Up @@ -181,7 +182,7 @@ module {
public func is_registered_gateway(gateway_principal : GatewayPrincipal) : Bool {
switch (get_registered_gateway(gateway_principal)) {
case (#Ok(_)) { true };
case (#Err(err)) { false };
case (#Err(_)) { false };
};
};

Expand Down Expand Up @@ -366,7 +367,7 @@ module {
case (#Ok(registered_gateway)) {
registered_gateway.messages_queue;
};
case (#Err(error)) {
case (#Err(_)) {
// the value exists because we just checked that the gateway is registered
Prelude.unreachable();
};
Expand Down Expand Up @@ -454,15 +455,17 @@ module {
});
};

func labeledHash(l : Blob, content : CertTree.Hash) : CertTree.Hash {
func labeledHash(l : Blob, content : CertTree.Hash) : Blob {
let d = Sha256.Digest(#sha256);
d.writeBlob("\13ic-hashtree-labeled");
let domain_sep : Blob = "ic-hashtree-labeled";
d.writeArray([Nat8.fromNat(domain_sep.size())]);
d.writeBlob(domain_sep);
d.writeBlob(l);
d.writeBlob(content);
d.sum();
};

public func put_cert_for_message(key : Text, value : Blob) {
func put_cert_for_message(key : Text, value : Blob) {
let root_hash = do {
CERT_TREE.put([Text.encodeUtf8(key)], Sha256.fromBlob(#sha256, value));
labeledHash(Constants.LABEL_WEBSOCKET, CERT_TREE.treeHash());
Expand Down
14 changes: 7 additions & 7 deletions src/Timers.mo
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,13 @@ module {
///
/// The interval callback is [send_ack_to_clients_timer_callback]. After the callback is executed,
/// a timer is scheduled to check if the registered clients have sent a keep alive message.
public func schedule_send_ack_to_clients(ws_state : State.IcWebSocketState, ack_interval_ms : Nat64, handlers : Types.WsHandlers) {
let timer_id = Timer.recurringTimer(
public func schedule_send_ack_to_clients<system>(ws_state : State.IcWebSocketState, ack_interval_ms : Nat64, handlers : Types.WsHandlers) {
let timer_id = Timer.recurringTimer<system>(
#nanoseconds(Nat64.toNat(ack_interval_ms * 1_000_000)),
func() : async () {
send_ack_to_clients_timer_callback(ws_state, ack_interval_ms);
send_ack_to_clients_timer_callback(ws_state);

schedule_check_keep_alive(ws_state, handlers);
schedule_check_keep_alive<system>(ws_state, handlers);
},
);

Expand All @@ -67,8 +67,8 @@ module {
/// after receiving an acknowledgement message.
///
/// The timer callback is [check_keep_alive_timer_callback].
func schedule_check_keep_alive(ws_state : State.IcWebSocketState, handlers : Types.WsHandlers) {
let timer_id = Timer.setTimer(
func schedule_check_keep_alive<system>(ws_state : State.IcWebSocketState, handlers : Types.WsHandlers) {
let timer_id = Timer.setTimer<system>(
#nanoseconds(Nat64.toNat(Constants.Computed().CLIENT_KEEP_ALIVE_TIMEOUT_NS)),
func() : async () {
await check_keep_alive_timer_callback(ws_state, handlers);
Expand All @@ -81,7 +81,7 @@ module {
/// Sends an acknowledgement message to the client.
/// The message contains the current incoming message sequence number for that client,
/// so that the client knows that all the messages it sent have been received by the canister.
func send_ack_to_clients_timer_callback(ws_state : State.IcWebSocketState, ack_interval_ms : Nat64) {
func send_ack_to_clients_timer_callback(ws_state : State.IcWebSocketState) {
for (client_key in ws_state.REGISTERED_CLIENTS.keys()) {
// ignore the error, which shouldn't happen since the client is registered and the sequence number is initialized
switch (ws_state.get_expected_incoming_message_from_client_num(client_key)) {
Expand Down
116 changes: 2 additions & 114 deletions src/Types.mo
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,12 @@ import Hash "mo:base/Hash";
import Principal "mo:base/Principal";
import Text "mo:base/Text";
import Nat64 "mo:base/Nat64";
import Bool "mo:base/Bool";
import List "mo:base/List";
import Blob "mo:base/Blob";
import Array "mo:base/Array";
import Prelude "mo:base/Prelude";
import Iter "mo:base/Iter";
import Error "mo:base/Error";
import CborDecoder "mo:cbor/Decoder";
import CborEncoder "mo:cbor/Encoder";
import CborValue "mo:cbor/Value";
import CborTypes "mo:cbor/Types";

import Constants "Constants";
import Utils "Utils";
Expand Down Expand Up @@ -47,8 +43,6 @@ module {
public type CanisterWsGetMessagesResult = Result<CanisterOutputCertifiedMessages, Text>;
/// The result of [send].
public type CanisterSendResult = Result<(), Text>;
/// @deprecated Use [`CanisterSendResult`] instead.
public type CanisterWsSendResult = Result<(), Text>;
/// The result of [close].
public type CanisterCloseResult = Result<(), Text>;

Expand Down Expand Up @@ -84,7 +78,7 @@ module {
/// Encodes the `WebsocketMessage` into a CBOR blob.
public func encode_websocket_message(websocket_message : WebsocketMessage) : Result<Blob, Text> {
let principal_blob = Blob.toArray(Principal.toBlob(websocket_message.client_key.client_principal));
let cbor_value : CborValue.Value = #majorType5([
let cbor_value : CborTypes.Value = #majorType5([
(#majorType3("client_key"), #majorType5([(#majorType3("client_principal"), #majorType2(principal_blob)), (#majorType3("client_nonce"), #majorType0(websocket_message.client_key.client_nonce))])),
(#majorType3("sequence_num"), #majorType0(websocket_message.sequence_num)),
(#majorType3("timestamp"), #majorType0(websocket_message.timestamp)),
Expand All @@ -102,112 +96,6 @@ module {
};
};

/// Decodes the CBOR blob into a `WebsocketMessage`.
func decode_websocket_message(bytes : Blob) : Result<WebsocketMessage, Text> {
switch (CborDecoder.decode(bytes)) {
case (#err(err)) {
#Err("deserialization failed");
};
case (#ok(c)) {
switch (c) {
case (#majorType6({ tag; value })) {
switch (value) {
case (#majorType5(raw_content)) {
#Ok({
client_key = do {
let client_key_key_value = Array.find(raw_content, func((key, _) : (CborValue.Value, CborValue.Value)) : Bool = key == #majorType3("client_key"));
switch (client_key_key_value) {
case (?(_, #majorType5(raw_client_key))) {
let client_principal_value = Array.find(raw_client_key, func((key, _) : (CborValue.Value, CborValue.Value)) : Bool = key == #majorType3("client_principal"));
let client_principal = switch (client_principal_value) {
case (?(_, #majorType2(client_principal_blob))) {
Principal.fromBlob(
Blob.fromArray(client_principal_blob)
);
};
case (_) {
return #Err("missing field `client_key.client_principal`");
};
};
let client_nonce_value = Array.find(raw_client_key, func((key, _) : (CborValue.Value, CborValue.Value)) : Bool = key == #majorType3("client_nonce"));
let client_nonce = switch (client_nonce_value) {
case (?(_, #majorType0(client_nonce))) {
client_nonce;
};
case (_) {
return #Err("missing field `client_key.client_nonce`");
};
};

{
client_principal;
client_nonce;
};
};
case (_) {
return #Err("missing field `client_key`");
};
};
};
sequence_num = do {
let sequence_num_key_value = Array.find(raw_content, func((key, _) : (CborValue.Value, CborValue.Value)) : Bool = key == #majorType3("sequence_num"));
switch (sequence_num_key_value) {
case (?(_, #majorType0(sequence_num))) {
sequence_num;
};
case (_) {
return #Err("missing field `sequence_num`");
};
};
};
timestamp = do {
let timestamp_key_value = Array.find(raw_content, func((key, _) : (CborValue.Value, CborValue.Value)) : Bool = key == #majorType3("timestamp"));
switch (timestamp_key_value) {
case (?(_, #majorType0(timestamp))) {
timestamp;
};
case (_) {
return #Err("missing field `timestamp`");
};
};
};
is_service_message = do {
let is_service_message_key_value = Array.find(raw_content, func((key, _) : (CborValue.Value, CborValue.Value)) : Bool = key == #majorType3("is_service_message"));
switch (is_service_message_key_value) {
case (?(_, #majorType7(#bool(is_service_message)))) {
is_service_message;
};
case (_) {
return #Err("missing field `is_service_message`");
};
};
};
content = do {
let content_key_value = Array.find(raw_content, func((key, _) : (CborValue.Value, CborValue.Value)) : Bool = key == #majorType3("message"));
switch (content_key_value) {
case (?(_, #majorType2(content_blob))) {
Blob.fromArray(content_blob);
};
case (_) {
return #Err("missing field `content`");
};
};
};
});
};
case (_) {
#Err("invalid CBOR message content");
};
};
};
case (_) {
#Err("invalid CBOR message content");
};
};
};
};
};

// Element of the list of messages returned to the WS Gateway after polling.
public type CanisterOutputMessage = {
client_key : ClientKey; // The client that the gateway will forward the message to.
Expand Down
Loading

0 comments on commit a4cc47e

Please sign in to comment.