From 98462bfdf62d52c91371a11dc97d4eb1d1740b0c Mon Sep 17 00:00:00 2001 From: Akim <59872966+akim-bow@users.noreply.github.com> Date: Thu, 23 Nov 2023 04:18:10 +0700 Subject: [PATCH] chore: Review fixes at #378 (#383) * Review fixes * remove logs * Fixes * Todo to remove prefix later * Refactor service signatures * Fixes * Update lock file * Fix lockfile * Update deps * More fixes and renames * Fix compiler * Peer refactoring and cutting onConnectionChange API * Revert deleted API --- packages/@tests/aqua/package.json | 4 +- packages/@tests/aqua/src/index.ts | 6 - packages/core/aqua-to-js/package.json | 4 +- .../__test__/__snapshots__/generate.snap.d.ts | 47 + .../__test__/__snapshots__/generate.snap.js | 880 +++++++++++++++++ .../__test__/__snapshots__/generate.snap.ts | 918 ++++++++++++++++++ .../aqua-to-js/src/generate/interfaces.ts | 10 +- .../core/aqua-to-js/src/generate/service.ts | 26 +- packages/core/aqua-to-js/src/index.ts | 41 +- .../core/js-client/aqua_test/marine-js.aqua | 6 +- packages/core/js-client/package.json | 2 +- packages/core/js-client/src/api.ts | 16 +- .../js-client/src/clientPeer/ClientPeer.ts | 4 - .../core/js-client/src/clientPeer/types.ts | 18 +- .../src/compilerSupport/callFunction.ts | 1 - .../src/compilerSupport/conversions.ts | 24 +- .../js-client/src/compilerSupport/services.ts | 11 +- .../js-client/src/compilerSupport/types.ts | 10 + .../core/js-client/src/ephemeral/client.ts | 2 +- .../core/js-client/src/ephemeral/network.ts | 4 +- packages/core/js-client/src/index.ts | 2 +- .../core/js-client/src/jsPeer/FluencePeer.ts | 612 ++++++------ packages/core/js-client/src/jsPeer/errors.ts | 9 + .../src/jsServiceHost/serviceUtils.ts | 18 +- .../core/js-client/src/services/NodeUtils.ts | 6 +- packages/core/js-client/src/services/Sig.ts | 14 +- .../js-client/src/services/SingleModuleSrv.ts | 22 +- .../core/js-client/src/services/Tracing.ts | 17 +- .../services/__test__/builtInHandler.spec.ts | 122 +-- .../src/services/__test__/sigService.spec.ts | 43 +- .../js-client/src/services/_aqua/tracing.ts | 9 - .../core/js-client/src/services/builtins.ts | 8 +- packages/core/js-client/src/util/testUtils.ts | 43 +- pnpm-lock.yaml | 39 +- 34 files changed, 2413 insertions(+), 585 deletions(-) diff --git a/packages/@tests/aqua/package.json b/packages/@tests/aqua/package.json index 3d1e174d..6a9ffe6a 100644 --- a/packages/@tests/aqua/package.json +++ b/packages/@tests/aqua/package.json @@ -20,11 +20,11 @@ "base64-js": "1.5.1" }, "devDependencies": { - "@fluencelabs/aqua-api": "0.12.4-main-cee4448-2196-1", + "@fluencelabs/aqua-api": "0.13.0", "@fluencelabs/aqua-lib": "0.6.0", "@fluencelabs/aqua-to-js": "workspace:*", "@fluencelabs/js-client": "workspace:*", - "@fluencelabs/registry": "0.8.8-1", + "@fluencelabs/registry": "0.9.0", "@fluencelabs/trust-graph": "3.1.2", "ts-node": "10.9.1" } diff --git a/packages/@tests/aqua/src/index.ts b/packages/@tests/aqua/src/index.ts index 3cecd6ba..c5a47205 100644 --- a/packages/@tests/aqua/src/index.ts +++ b/packages/@tests/aqua/src/index.ts @@ -15,7 +15,6 @@ */ import { Fluence, type ClientConfig } from "@fluencelabs/js-client"; -import { fromByteArray } from "base64-js"; import { test as particleTest } from "./_aqua/finalize_particle.js"; import { @@ -56,10 +55,6 @@ export type TestResult = export const runTest = async (): Promise => { try { - Fluence.onConnectionStateChange((state) => { - console.info("connection state changed: ", state); - }); - console.log("connecting to Fluence Network..."); console.log("multiaddr: ", relay.multiaddr); @@ -82,7 +77,6 @@ export const runTest = async (): Promise => { const client = Fluence.getClient(); console.log("my peer id: ", client.getPeerId()); - console.log("my sk id: ", fromByteArray(client.getPeerSecretKey())); console.log("running hello test..."); const hello = await helloTest(); diff --git a/packages/core/aqua-to-js/package.json b/packages/core/aqua-to-js/package.json index c949ee9f..fa8911f2 100644 --- a/packages/core/aqua-to-js/package.json +++ b/packages/core/aqua-to-js/package.json @@ -18,11 +18,11 @@ "ts-pattern": "5.0.5" }, "devDependencies": { - "@fluencelabs/aqua-api": "0.12.4-main-cee4448-2196-1", + "@fluencelabs/aqua-api": "0.13.0", "@fluencelabs/aqua-lib": "0.7.3", "@fluencelabs/interfaces": "workspace:*", "@fluencelabs/js-client": "workspace:^", - "@fluencelabs/registry": "0.8.7", + "@fluencelabs/registry": "0.9.0", "@fluencelabs/spell": "0.5.20", "@fluencelabs/trust-graph": "0.4.7", "vitest": "0.34.6", diff --git a/packages/core/aqua-to-js/src/generate/__test__/__snapshots__/generate.snap.d.ts b/packages/core/aqua-to-js/src/generate/__test__/__snapshots__/generate.snap.d.ts index e3e51fda..439cba25 100644 --- a/packages/core/aqua-to-js/src/generate/__test__/__snapshots__/generate.snap.d.ts +++ b/packages/core/aqua-to-js/src/generate/__test__/__snapshots__/generate.snap.d.ts @@ -17,4 +17,51 @@ import { v5_registerService as registerService$$ } from '@fluencelabs/js-client'; +// Services +export interface SrvDef { + create: (wasm_b64_content: string, callParams: ParticleContext$$) => { error: string | null; service_id: string | null; success: boolean; } | Promise<{ error: string | null; service_id: string | null; success: boolean; }>; + list: (callParams: ParticleContext$$) => string[] | Promise; + remove: (service_id: string, callParams: ParticleContext$$) => { error: string | null; success: boolean; } | Promise<{ error: string | null; success: boolean; }>; +} +export function registerSrv(service: SrvDef): void; +export function registerSrv(serviceId: string, service: SrvDef): void; +export function registerSrv(peer: IFluenceClient$$, service: SrvDef): void; +export function registerSrv(peer: IFluenceClient$$, serviceId: string, service: SrvDef): void; +export interface CalcServiceDef { + divide: (num: number, callParams: ParticleContext$$) => number | Promise; + clear_state: (callParams: ParticleContext$$) => void | Promise; + test_logs: (callParams: ParticleContext$$) => void | Promise; + multiply: (num: number, callParams: ParticleContext$$) => number | Promise; + add: (num: number, callParams: ParticleContext$$) => number | Promise; + state: (callParams: ParticleContext$$) => number | Promise; + subtract: (num: number, callParams: ParticleContext$$) => number | Promise; +} +export function registerCalcService(serviceId: string, service: CalcServiceDef): void; +export function registerCalcService(peer: IFluenceClient$$, serviceId: string, service: CalcServiceDef): void; +export interface HelloWorldDef { + hello: (str: string, callParams: ParticleContext$$) => string | Promise; +} +export function registerHelloWorld(service: HelloWorldDef): void; +export function registerHelloWorld(serviceId: string, service: HelloWorldDef): void; +export function registerHelloWorld(peer: IFluenceClient$$, service: HelloWorldDef): void; +export function registerHelloWorld(peer: IFluenceClient$$, serviceId: string, service: HelloWorldDef): void; + +// Functions +export type ResourceTestResultType = [string | null, string[]] + +export type ResourceTestParams = [label: string, config?: {ttl?: number}] | [peer: IFluenceClient$$, label: string, config?: {ttl?: number}]; + +export type ResourceTestResult = Promise; + +export type HelloTestParams = [config?: {ttl?: number}] | [peer: IFluenceClient$$, config?: {ttl?: number}]; + +export type HelloTestResult = Promise; + +export type Demo_calculationParams = [service_id: string, config?: {ttl?: number}] | [peer: IFluenceClient$$, service_id: string, config?: {ttl?: number}]; + +export type Demo_calculationResult = Promise; + +export type MarineTestParams = [wasm64: string, config?: {ttl?: number}] | [peer: IFluenceClient$$, wasm64: string, config?: {ttl?: number}]; + +export type MarineTestResult = Promise; diff --git a/packages/core/aqua-to-js/src/generate/__test__/__snapshots__/generate.snap.js b/packages/core/aqua-to-js/src/generate/__test__/__snapshots__/generate.snap.js index 2fd03f35..7b344124 100644 --- a/packages/core/aqua-to-js/src/generate/__test__/__snapshots__/generate.snap.js +++ b/packages/core/aqua-to-js/src/generate/__test__/__snapshots__/generate.snap.js @@ -17,4 +17,884 @@ import { v5_registerService as registerService$$ } from '@fluencelabs/js-client'; +// Services +export function registerSrv(...args) { + registerService$$( + args, + { + "defaultServiceId": "single_module_srv", + "functions": { + "fields": { + "create": { + "domain": { + "fields": { + "wasm_b64_content": { + "name": "string", + "tag": "scalar" + } + }, + "tag": "labeledProduct" + }, + "codomain": { + "items": [ + { + "name": "ServiceCreationResult", + "fields": { + "error": { + "type": { + "name": "string", + "tag": "scalar" + }, + "tag": "option" + }, + "service_id": { + "type": { + "name": "string", + "tag": "scalar" + }, + "tag": "option" + }, + "success": { + "name": "bool", + "tag": "scalar" + } + }, + "tag": "struct" + } + ], + "tag": "unlabeledProduct" + }, + "tag": "arrow" + }, + "list": { + "domain": { + "tag": "nil" + }, + "codomain": { + "items": [ + { + "type": { + "name": "string", + "tag": "scalar" + }, + "tag": "array" + } + ], + "tag": "unlabeledProduct" + }, + "tag": "arrow" + }, + "remove": { + "domain": { + "fields": { + "service_id": { + "name": "string", + "tag": "scalar" + } + }, + "tag": "labeledProduct" + }, + "codomain": { + "items": [ + { + "name": "RemoveResult", + "fields": { + "error": { + "type": { + "name": "string", + "tag": "scalar" + }, + "tag": "option" + }, + "success": { + "name": "bool", + "tag": "scalar" + } + }, + "tag": "struct" + } + ], + "tag": "unlabeledProduct" + }, + "tag": "arrow" + } + }, + "tag": "labeledProduct" + } +} + ); +} + + +export function registerCalcService(...args) { + registerService$$( + args, + { + "functions": { + "fields": { + "divide": { + "domain": { + "fields": { + "num": { + "name": "f64", + "tag": "scalar" + } + }, + "tag": "labeledProduct" + }, + "codomain": { + "items": [ + { + "name": "f64", + "tag": "scalar" + } + ], + "tag": "unlabeledProduct" + }, + "tag": "arrow" + }, + "clear_state": { + "domain": { + "tag": "nil" + }, + "codomain": { + "tag": "nil" + }, + "tag": "arrow" + }, + "test_logs": { + "domain": { + "tag": "nil" + }, + "codomain": { + "tag": "nil" + }, + "tag": "arrow" + }, + "multiply": { + "domain": { + "fields": { + "num": { + "name": "f64", + "tag": "scalar" + } + }, + "tag": "labeledProduct" + }, + "codomain": { + "items": [ + { + "name": "f64", + "tag": "scalar" + } + ], + "tag": "unlabeledProduct" + }, + "tag": "arrow" + }, + "add": { + "domain": { + "fields": { + "num": { + "name": "f64", + "tag": "scalar" + } + }, + "tag": "labeledProduct" + }, + "codomain": { + "items": [ + { + "name": "f64", + "tag": "scalar" + } + ], + "tag": "unlabeledProduct" + }, + "tag": "arrow" + }, + "state": { + "domain": { + "tag": "nil" + }, + "codomain": { + "items": [ + { + "name": "f64", + "tag": "scalar" + } + ], + "tag": "unlabeledProduct" + }, + "tag": "arrow" + }, + "subtract": { + "domain": { + "fields": { + "num": { + "name": "f64", + "tag": "scalar" + } + }, + "tag": "labeledProduct" + }, + "codomain": { + "items": [ + { + "name": "f64", + "tag": "scalar" + } + ], + "tag": "unlabeledProduct" + }, + "tag": "arrow" + } + }, + "tag": "labeledProduct" + } +} + ); +} + + +export function registerHelloWorld(...args) { + registerService$$( + args, + { + "defaultServiceId": "hello-world", + "functions": { + "fields": { + "hello": { + "domain": { + "fields": { + "str": { + "name": "string", + "tag": "scalar" + } + }, + "tag": "labeledProduct" + }, + "codomain": { + "items": [ + { + "name": "string", + "tag": "scalar" + } + ], + "tag": "unlabeledProduct" + }, + "tag": "arrow" + } + }, + "tag": "labeledProduct" + } +} + ); +} + + +// Functions +export const resourceTest_script = ` +(xor + (seq + (seq + (seq + (seq + (call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-) + (call %init_peer_id% ("getDataSrv" "label") [] -label-arg-) + ) + (new $resource_id + (seq + (seq + (seq + (call %init_peer_id% ("peer" "timestamp_sec") [] ret) + (xor + (seq + (seq + (call -relay- ("registry" "get_key_bytes") [-label-arg- [] ret [] ""] ret-0) + (xor + (call %init_peer_id% ("sig" "sign") [ret-0] ret-1) + (fail :error:) + ) + ) + (new -if-else-error- + (new -else-error- + (new -if-error- + (xor + (match ret-1.$.success false + (ap ret-1.$.error.[0] $error) + ) + (seq + (ap :error: -if-error-) + (xor + (match :error:.$.error_code 10001 + (new $successful + (seq + (seq + (seq + (seq + (seq + (seq + (ap ret-1.$.signature ret-1_flat) + (call -relay- ("registry" "get_key_id") [-label-arg- %init_peer_id%] ret-2) + ) + (call -relay- ("op" "string_to_b58") [ret-2] ret-3) + ) + (call -relay- ("kad" "neighborhood") [ret-3 [] []] ret-4) + ) + (par + (fold ret-4 n-0 + (par + (xor + (xor + (seq + (seq + (seq + (call n-0 ("peer" "timestamp_sec") [] ret-5) + (call n-0 ("trust-graph" "get_weight") [%init_peer_id% ret-5] ret-6) + ) + (call n-0 ("registry" "register_key") [-label-arg- [] ret [] "" ret-1_flat.$.[0] ret-6 ret-5] ret-7) + ) + (new -if-else-error- + (new -else-error- + (new -if-error- + (xor + (seq + (match ret-7.$.success true + (ap true $successful) + ) + (new $-ephemeral-stream- + (new #-ephemeral-canon- + (canon -relay- $-ephemeral-stream- #-ephemeral-canon-) + ) + ) + ) + (seq + (ap :error: -if-error-) + (xor + (seq + (match :error:.$.error_code 10001 + (ap ret-7.$.error $error) + ) + (new $-ephemeral-stream- + (new #-ephemeral-canon- + (canon -relay- $-ephemeral-stream- #-ephemeral-canon-) + ) + ) + ) + (seq + (seq + (seq + (ap :error: -else-error-) + (xor + (seq + (match :error:.$.error_code 10001 + (ap -if-error- -if-else-error-) + ) + (new $-ephemeral-stream- + (new #-ephemeral-canon- + (canon -relay- $-ephemeral-stream- #-ephemeral-canon-) + ) + ) + ) + (seq + (ap -else-error- -if-else-error-) + (new $-ephemeral-stream- + (new #-ephemeral-canon- + (canon -relay- $-ephemeral-stream- #-ephemeral-canon-) + ) + ) + ) + ) + ) + (fail -if-else-error-) + ) + (new $-ephemeral-stream- + (new #-ephemeral-canon- + (canon -relay- $-ephemeral-stream- #-ephemeral-canon-) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + (null) + ) + (fail :error:) + ) + (next n-0) + ) + (never) + ) + (null) + ) + ) + (new $status + (new $result-1 + (seq + (seq + (seq + (par + (seq + (new $successful_test + (seq + (seq + (fold $successful successful_fold_var + (seq + (seq + (ap successful_fold_var $successful_test) + (canon -relay- $successful_test #successful_iter_canon) + ) + (xor + (match #successful_iter_canon.length 1 + (null) + ) + (next successful_fold_var) + ) + ) + (never) + ) + (canon -relay- $successful_test #successful_result_canon) + ) + (ap #successful_result_canon successful_gate) + ) + ) + (ap "ok" $status) + ) + (seq + (call -relay- ("peer" "timeout") [6000 "timeout"] ret-8) + (ap ret-8 $status) + ) + ) + (new $status_test + (seq + (seq + (fold $status status_fold_var + (seq + (seq + (ap status_fold_var $status_test) + (canon -relay- $status_test #status_iter_canon) + ) + (xor + (match #status_iter_canon.length 1 + (null) + ) + (next status_fold_var) + ) + ) + (never) + ) + (canon -relay- $status_test #status_result_canon) + ) + (ap #status_result_canon status_gate) + ) + ) + ) + (new -if-else-error- + (new -else-error- + (new -if-error- + (xor + (match status_gate.$.[0] "ok" + (ap true $result-1) + ) + (seq + (ap :error: -if-error-) + (xor + (match :error:.$.error_code 10001 + (ap false $result-1) + ) + (seq + (seq + (ap :error: -else-error-) + (xor + (match :error:.$.error_code 10001 + (ap -if-error- -if-else-error-) + ) + (ap -else-error- -if-else-error-) + ) + ) + (fail -if-else-error-) + ) + ) + ) + ) + ) + ) + ) + ) + (new $result-1_test + (seq + (seq + (fold $result-1 result-1_fold_var + (seq + (seq + (ap result-1_fold_var $result-1_test) + (canon -relay- $result-1_test #result-1_iter_canon) + ) + (xor + (match #result-1_iter_canon.length 1 + (null) + ) + (next result-1_fold_var) + ) + ) + (never) + ) + (canon -relay- $result-1_test #result-1_result_canon) + ) + (ap #result-1_result_canon result-1_gate) + ) + ) + ) + ) + ) + ) + (new -if-else-error- + (new -else-error- + (new -if-error- + (xor + (match result-1_gate.$.[0] false + (ap "resource wasn't created: timeout exceeded" $error) + ) + (seq + (ap :error: -if-error-) + (xor + (match :error:.$.error_code 10001 + (ap ret-2 $resource_id) + ) + (seq + (seq + (ap :error: -else-error-) + (xor + (seq + (match :error:.$.error_code 10001 + (ap -if-error- -if-else-error-) + ) + (new $-ephemeral-stream- + (new #-ephemeral-canon- + (canon -relay- $-ephemeral-stream- #-ephemeral-canon-) + ) + ) + ) + (seq + (ap -else-error- -if-else-error-) + (new $-ephemeral-stream- + (new #-ephemeral-canon- + (canon -relay- $-ephemeral-stream- #-ephemeral-canon-) + ) + ) + ) + ) + ) + (fail -if-else-error-) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + (seq + (seq + (ap :error: -else-error-) + (xor + (seq + (match :error:.$.error_code 10001 + (ap -if-error- -if-else-error-) + ) + (new $-ephemeral-stream- + (new #-ephemeral-canon- + (canon -relay- $-ephemeral-stream- #-ephemeral-canon-) + ) + ) + ) + (seq + (ap -else-error- -if-else-error-) + (new $-ephemeral-stream- + (new #-ephemeral-canon- + (canon -relay- $-ephemeral-stream- #-ephemeral-canon-) + ) + ) + ) + ) + ) + (fail -if-else-error-) + ) + ) + ) + ) + ) + ) + ) + ) + (fail :error:) + ) + ) + (canon %init_peer_id% $resource_id #-resource_id-fix-0) + ) + (ap #-resource_id-fix-0 -resource_id-flat-0) + ) + ) + ) + (canon %init_peer_id% $error #error_canon) + ) + (call %init_peer_id% ("callbackSrv" "response") [-resource_id-flat-0 #error_canon]) + ) + (call %init_peer_id% ("errorHandlingSrv" "error") [:error: 0]) +) +`; + + +export function resourceTest(...args) { + return callFunction$$( + args, + { + "functionName": "resourceTest", + "arrow": { + "domain": { + "fields": { + "label": { + "name": "string", + "tag": "scalar" + } + }, + "tag": "labeledProduct" + }, + "codomain": { + "items": [ + { + "type": { + "name": "string", + "tag": "scalar" + }, + "tag": "option" + }, + { + "type": { + "name": "string", + "tag": "scalar" + }, + "tag": "array" + } + ], + "tag": "unlabeledProduct" + }, + "tag": "arrow" + }, + "names": { + "relay": "-relay-", + "getDataSrv": "getDataSrv", + "callbackSrv": "callbackSrv", + "responseSrv": "callbackSrv", + "responseFnName": "response", + "errorHandlingSrv": "errorHandlingSrv", + "errorFnName": "error" + } +}, + resourceTest_script + ); +} + +export const helloTest_script = ` +(xor + (seq + (seq + (call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-) + (call %init_peer_id% ("hello-world" "hello") ["Fluence user"] ret) + ) + (call %init_peer_id% ("callbackSrv" "response") [ret]) + ) + (call %init_peer_id% ("errorHandlingSrv" "error") [:error: 0]) +) +`; + + +export function helloTest(...args) { + return callFunction$$( + args, + { + "functionName": "helloTest", + "arrow": { + "domain": { + "fields": {}, + "tag": "labeledProduct" + }, + "codomain": { + "items": [ + { + "name": "string", + "tag": "scalar" + } + ], + "tag": "unlabeledProduct" + }, + "tag": "arrow" + }, + "names": { + "relay": "-relay-", + "getDataSrv": "getDataSrv", + "callbackSrv": "callbackSrv", + "responseSrv": "callbackSrv", + "responseFnName": "response", + "errorHandlingSrv": "errorHandlingSrv", + "errorFnName": "error" + } +}, + helloTest_script + ); +} + +export const demo_calculation_script = ` +(xor + (seq + (seq + (seq + (seq + (seq + (seq + (seq + (seq + (call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-) + (call %init_peer_id% ("getDataSrv" "service_id") [] -service_id-arg-) + ) + (call %init_peer_id% (-service_id-arg- "test_logs") []) + ) + (call %init_peer_id% (-service_id-arg- "add") [10] ret) + ) + (call %init_peer_id% (-service_id-arg- "multiply") [5] ret-0) + ) + (call %init_peer_id% (-service_id-arg- "subtract") [8] ret-1) + ) + (call %init_peer_id% (-service_id-arg- "divide") [6] ret-2) + ) + (call %init_peer_id% (-service_id-arg- "state") [] ret-3) + ) + (call %init_peer_id% ("callbackSrv" "response") [ret-3]) + ) + (call %init_peer_id% ("errorHandlingSrv" "error") [:error: 0]) +) +`; + + +export function demo_calculation(...args) { + return callFunction$$( + args, + { + "functionName": "demo_calculation", + "arrow": { + "domain": { + "fields": { + "service_id": { + "name": "string", + "tag": "scalar" + } + }, + "tag": "labeledProduct" + }, + "codomain": { + "items": [ + { + "name": "f64", + "tag": "scalar" + } + ], + "tag": "unlabeledProduct" + }, + "tag": "arrow" + }, + "names": { + "relay": "-relay-", + "getDataSrv": "getDataSrv", + "callbackSrv": "callbackSrv", + "responseSrv": "callbackSrv", + "responseFnName": "response", + "errorHandlingSrv": "errorHandlingSrv", + "errorFnName": "error" + } +}, + demo_calculation_script + ); +} + +export const marineTest_script = ` +(xor + (seq + (seq + (seq + (seq + (seq + (seq + (seq + (seq + (seq + (call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-) + (call %init_peer_id% ("getDataSrv" "wasm64") [] -wasm64-arg-) + ) + (call %init_peer_id% ("single_module_srv" "create") [-wasm64-arg-] ret) + ) + (call %init_peer_id% (ret.$.service_id.[0] "test_logs") []) + ) + (call %init_peer_id% (ret.$.service_id.[0] "add") [10] ret-0) + ) + (call %init_peer_id% (ret.$.service_id.[0] "multiply") [5] ret-1) + ) + (call %init_peer_id% (ret.$.service_id.[0] "subtract") [8] ret-2) + ) + (call %init_peer_id% (ret.$.service_id.[0] "divide") [6] ret-3) + ) + (call %init_peer_id% (ret.$.service_id.[0] "state") [] ret-4) + ) + (call %init_peer_id% ("callbackSrv" "response") [ret-4]) + ) + (call %init_peer_id% ("errorHandlingSrv" "error") [:error: 0]) +) +`; + + +export function marineTest(...args) { + return callFunction$$( + args, + { + "functionName": "marineTest", + "arrow": { + "domain": { + "fields": { + "wasm64": { + "name": "string", + "tag": "scalar" + } + }, + "tag": "labeledProduct" + }, + "codomain": { + "items": [ + { + "name": "f64", + "tag": "scalar" + } + ], + "tag": "unlabeledProduct" + }, + "tag": "arrow" + }, + "names": { + "relay": "-relay-", + "getDataSrv": "getDataSrv", + "callbackSrv": "callbackSrv", + "responseSrv": "callbackSrv", + "responseFnName": "response", + "errorHandlingSrv": "errorHandlingSrv", + "errorFnName": "error" + } +}, + marineTest_script + ); +} diff --git a/packages/core/aqua-to-js/src/generate/__test__/__snapshots__/generate.snap.ts b/packages/core/aqua-to-js/src/generate/__test__/__snapshots__/generate.snap.ts index e3e51fda..258b5609 100644 --- a/packages/core/aqua-to-js/src/generate/__test__/__snapshots__/generate.snap.ts +++ b/packages/core/aqua-to-js/src/generate/__test__/__snapshots__/generate.snap.ts @@ -17,4 +17,922 @@ import { v5_registerService as registerService$$ } from '@fluencelabs/js-client'; +// Services +export interface SrvDef { + create: (wasm_b64_content: string, callParams: ParticleContext$$) => { error: string | null; service_id: string | null; success: boolean; } | Promise<{ error: string | null; service_id: string | null; success: boolean; }>; + list: (callParams: ParticleContext$$) => string[] | Promise; + remove: (service_id: string, callParams: ParticleContext$$) => { error: string | null; success: boolean; } | Promise<{ error: string | null; success: boolean; }>; +} +export function registerSrv(service: SrvDef): void; +export function registerSrv(serviceId: string, service: SrvDef): void; +export function registerSrv(peer: IFluenceClient$$, service: SrvDef): void; +export function registerSrv(peer: IFluenceClient$$, serviceId: string, service: SrvDef): void; +export function registerSrv(...args: any[]) { + registerService$$( + args, + { + "defaultServiceId": "single_module_srv", + "functions": { + "fields": { + "create": { + "domain": { + "fields": { + "wasm_b64_content": { + "name": "string", + "tag": "scalar" + } + }, + "tag": "labeledProduct" + }, + "codomain": { + "items": [ + { + "name": "ServiceCreationResult", + "fields": { + "error": { + "type": { + "name": "string", + "tag": "scalar" + }, + "tag": "option" + }, + "service_id": { + "type": { + "name": "string", + "tag": "scalar" + }, + "tag": "option" + }, + "success": { + "name": "bool", + "tag": "scalar" + } + }, + "tag": "struct" + } + ], + "tag": "unlabeledProduct" + }, + "tag": "arrow" + }, + "list": { + "domain": { + "tag": "nil" + }, + "codomain": { + "items": [ + { + "type": { + "name": "string", + "tag": "scalar" + }, + "tag": "array" + } + ], + "tag": "unlabeledProduct" + }, + "tag": "arrow" + }, + "remove": { + "domain": { + "fields": { + "service_id": { + "name": "string", + "tag": "scalar" + } + }, + "tag": "labeledProduct" + }, + "codomain": { + "items": [ + { + "name": "RemoveResult", + "fields": { + "error": { + "type": { + "name": "string", + "tag": "scalar" + }, + "tag": "option" + }, + "success": { + "name": "bool", + "tag": "scalar" + } + }, + "tag": "struct" + } + ], + "tag": "unlabeledProduct" + }, + "tag": "arrow" + } + }, + "tag": "labeledProduct" + } +} + ); +} +export interface CalcServiceDef { + divide: (num: number, callParams: ParticleContext$$) => number | Promise; + clear_state: (callParams: ParticleContext$$) => void | Promise; + test_logs: (callParams: ParticleContext$$) => void | Promise; + multiply: (num: number, callParams: ParticleContext$$) => number | Promise; + add: (num: number, callParams: ParticleContext$$) => number | Promise; + state: (callParams: ParticleContext$$) => number | Promise; + subtract: (num: number, callParams: ParticleContext$$) => number | Promise; +} +export function registerCalcService(serviceId: string, service: CalcServiceDef): void; +export function registerCalcService(peer: IFluenceClient$$, serviceId: string, service: CalcServiceDef): void; +export function registerCalcService(...args: any[]) { + registerService$$( + args, + { + "functions": { + "fields": { + "divide": { + "domain": { + "fields": { + "num": { + "name": "f64", + "tag": "scalar" + } + }, + "tag": "labeledProduct" + }, + "codomain": { + "items": [ + { + "name": "f64", + "tag": "scalar" + } + ], + "tag": "unlabeledProduct" + }, + "tag": "arrow" + }, + "clear_state": { + "domain": { + "tag": "nil" + }, + "codomain": { + "tag": "nil" + }, + "tag": "arrow" + }, + "test_logs": { + "domain": { + "tag": "nil" + }, + "codomain": { + "tag": "nil" + }, + "tag": "arrow" + }, + "multiply": { + "domain": { + "fields": { + "num": { + "name": "f64", + "tag": "scalar" + } + }, + "tag": "labeledProduct" + }, + "codomain": { + "items": [ + { + "name": "f64", + "tag": "scalar" + } + ], + "tag": "unlabeledProduct" + }, + "tag": "arrow" + }, + "add": { + "domain": { + "fields": { + "num": { + "name": "f64", + "tag": "scalar" + } + }, + "tag": "labeledProduct" + }, + "codomain": { + "items": [ + { + "name": "f64", + "tag": "scalar" + } + ], + "tag": "unlabeledProduct" + }, + "tag": "arrow" + }, + "state": { + "domain": { + "tag": "nil" + }, + "codomain": { + "items": [ + { + "name": "f64", + "tag": "scalar" + } + ], + "tag": "unlabeledProduct" + }, + "tag": "arrow" + }, + "subtract": { + "domain": { + "fields": { + "num": { + "name": "f64", + "tag": "scalar" + } + }, + "tag": "labeledProduct" + }, + "codomain": { + "items": [ + { + "name": "f64", + "tag": "scalar" + } + ], + "tag": "unlabeledProduct" + }, + "tag": "arrow" + } + }, + "tag": "labeledProduct" + } +} + ); +} + +export interface HelloWorldDef { + hello: (str: string, callParams: ParticleContext$$) => string | Promise; +} +export function registerHelloWorld(service: HelloWorldDef): void; +export function registerHelloWorld(serviceId: string, service: HelloWorldDef): void; +export function registerHelloWorld(peer: IFluenceClient$$, service: HelloWorldDef): void; +export function registerHelloWorld(peer: IFluenceClient$$, serviceId: string, service: HelloWorldDef): void; +export function registerHelloWorld(...args: any[]) { + registerService$$( + args, + { + "defaultServiceId": "hello-world", + "functions": { + "fields": { + "hello": { + "domain": { + "fields": { + "str": { + "name": "string", + "tag": "scalar" + } + }, + "tag": "labeledProduct" + }, + "codomain": { + "items": [ + { + "name": "string", + "tag": "scalar" + } + ], + "tag": "unlabeledProduct" + }, + "tag": "arrow" + } + }, + "tag": "labeledProduct" + } +} + ); +} + + +// Functions +export const resourceTest_script = ` +(xor + (seq + (seq + (seq + (seq + (call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-) + (call %init_peer_id% ("getDataSrv" "label") [] -label-arg-) + ) + (new $resource_id + (seq + (seq + (seq + (call %init_peer_id% ("peer" "timestamp_sec") [] ret) + (xor + (seq + (seq + (call -relay- ("registry" "get_key_bytes") [-label-arg- [] ret [] ""] ret-0) + (xor + (call %init_peer_id% ("sig" "sign") [ret-0] ret-1) + (fail :error:) + ) + ) + (new -if-else-error- + (new -else-error- + (new -if-error- + (xor + (match ret-1.$.success false + (ap ret-1.$.error.[0] $error) + ) + (seq + (ap :error: -if-error-) + (xor + (match :error:.$.error_code 10001 + (new $successful + (seq + (seq + (seq + (seq + (seq + (seq + (ap ret-1.$.signature ret-1_flat) + (call -relay- ("registry" "get_key_id") [-label-arg- %init_peer_id%] ret-2) + ) + (call -relay- ("op" "string_to_b58") [ret-2] ret-3) + ) + (call -relay- ("kad" "neighborhood") [ret-3 [] []] ret-4) + ) + (par + (fold ret-4 n-0 + (par + (xor + (xor + (seq + (seq + (seq + (call n-0 ("peer" "timestamp_sec") [] ret-5) + (call n-0 ("trust-graph" "get_weight") [%init_peer_id% ret-5] ret-6) + ) + (call n-0 ("registry" "register_key") [-label-arg- [] ret [] "" ret-1_flat.$.[0] ret-6 ret-5] ret-7) + ) + (new -if-else-error- + (new -else-error- + (new -if-error- + (xor + (seq + (match ret-7.$.success true + (ap true $successful) + ) + (new $-ephemeral-stream- + (new #-ephemeral-canon- + (canon -relay- $-ephemeral-stream- #-ephemeral-canon-) + ) + ) + ) + (seq + (ap :error: -if-error-) + (xor + (seq + (match :error:.$.error_code 10001 + (ap ret-7.$.error $error) + ) + (new $-ephemeral-stream- + (new #-ephemeral-canon- + (canon -relay- $-ephemeral-stream- #-ephemeral-canon-) + ) + ) + ) + (seq + (seq + (seq + (ap :error: -else-error-) + (xor + (seq + (match :error:.$.error_code 10001 + (ap -if-error- -if-else-error-) + ) + (new $-ephemeral-stream- + (new #-ephemeral-canon- + (canon -relay- $-ephemeral-stream- #-ephemeral-canon-) + ) + ) + ) + (seq + (ap -else-error- -if-else-error-) + (new $-ephemeral-stream- + (new #-ephemeral-canon- + (canon -relay- $-ephemeral-stream- #-ephemeral-canon-) + ) + ) + ) + ) + ) + (fail -if-else-error-) + ) + (new $-ephemeral-stream- + (new #-ephemeral-canon- + (canon -relay- $-ephemeral-stream- #-ephemeral-canon-) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + (null) + ) + (fail :error:) + ) + (next n-0) + ) + (never) + ) + (null) + ) + ) + (new $status + (new $result-1 + (seq + (seq + (seq + (par + (seq + (new $successful_test + (seq + (seq + (fold $successful successful_fold_var + (seq + (seq + (ap successful_fold_var $successful_test) + (canon -relay- $successful_test #successful_iter_canon) + ) + (xor + (match #successful_iter_canon.length 1 + (null) + ) + (next successful_fold_var) + ) + ) + (never) + ) + (canon -relay- $successful_test #successful_result_canon) + ) + (ap #successful_result_canon successful_gate) + ) + ) + (ap "ok" $status) + ) + (seq + (call -relay- ("peer" "timeout") [6000 "timeout"] ret-8) + (ap ret-8 $status) + ) + ) + (new $status_test + (seq + (seq + (fold $status status_fold_var + (seq + (seq + (ap status_fold_var $status_test) + (canon -relay- $status_test #status_iter_canon) + ) + (xor + (match #status_iter_canon.length 1 + (null) + ) + (next status_fold_var) + ) + ) + (never) + ) + (canon -relay- $status_test #status_result_canon) + ) + (ap #status_result_canon status_gate) + ) + ) + ) + (new -if-else-error- + (new -else-error- + (new -if-error- + (xor + (match status_gate.$.[0] "ok" + (ap true $result-1) + ) + (seq + (ap :error: -if-error-) + (xor + (match :error:.$.error_code 10001 + (ap false $result-1) + ) + (seq + (seq + (ap :error: -else-error-) + (xor + (match :error:.$.error_code 10001 + (ap -if-error- -if-else-error-) + ) + (ap -else-error- -if-else-error-) + ) + ) + (fail -if-else-error-) + ) + ) + ) + ) + ) + ) + ) + ) + (new $result-1_test + (seq + (seq + (fold $result-1 result-1_fold_var + (seq + (seq + (ap result-1_fold_var $result-1_test) + (canon -relay- $result-1_test #result-1_iter_canon) + ) + (xor + (match #result-1_iter_canon.length 1 + (null) + ) + (next result-1_fold_var) + ) + ) + (never) + ) + (canon -relay- $result-1_test #result-1_result_canon) + ) + (ap #result-1_result_canon result-1_gate) + ) + ) + ) + ) + ) + ) + (new -if-else-error- + (new -else-error- + (new -if-error- + (xor + (match result-1_gate.$.[0] false + (ap "resource wasn't created: timeout exceeded" $error) + ) + (seq + (ap :error: -if-error-) + (xor + (match :error:.$.error_code 10001 + (ap ret-2 $resource_id) + ) + (seq + (seq + (ap :error: -else-error-) + (xor + (seq + (match :error:.$.error_code 10001 + (ap -if-error- -if-else-error-) + ) + (new $-ephemeral-stream- + (new #-ephemeral-canon- + (canon -relay- $-ephemeral-stream- #-ephemeral-canon-) + ) + ) + ) + (seq + (ap -else-error- -if-else-error-) + (new $-ephemeral-stream- + (new #-ephemeral-canon- + (canon -relay- $-ephemeral-stream- #-ephemeral-canon-) + ) + ) + ) + ) + ) + (fail -if-else-error-) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + (seq + (seq + (ap :error: -else-error-) + (xor + (seq + (match :error:.$.error_code 10001 + (ap -if-error- -if-else-error-) + ) + (new $-ephemeral-stream- + (new #-ephemeral-canon- + (canon -relay- $-ephemeral-stream- #-ephemeral-canon-) + ) + ) + ) + (seq + (ap -else-error- -if-else-error-) + (new $-ephemeral-stream- + (new #-ephemeral-canon- + (canon -relay- $-ephemeral-stream- #-ephemeral-canon-) + ) + ) + ) + ) + ) + (fail -if-else-error-) + ) + ) + ) + ) + ) + ) + ) + ) + (fail :error:) + ) + ) + (canon %init_peer_id% $resource_id #-resource_id-fix-0) + ) + (ap #-resource_id-fix-0 -resource_id-flat-0) + ) + ) + ) + (canon %init_peer_id% $error #error_canon) + ) + (call %init_peer_id% ("callbackSrv" "response") [-resource_id-flat-0 #error_canon]) + ) + (call %init_peer_id% ("errorHandlingSrv" "error") [:error: 0]) +) +`; + +export type ResourceTestResultType = [string | null, string[]] + +export type ResourceTestParams = [label: string, config?: {ttl?: number}] | [peer: IFluenceClient$$, label: string, config?: {ttl?: number}]; + +export type ResourceTestResult = Promise; + +export function resourceTest(...args: ResourceTestParams): ResourceTestResult { + return callFunction$$( + args, + { + "functionName": "resourceTest", + "arrow": { + "domain": { + "fields": { + "label": { + "name": "string", + "tag": "scalar" + } + }, + "tag": "labeledProduct" + }, + "codomain": { + "items": [ + { + "type": { + "name": "string", + "tag": "scalar" + }, + "tag": "option" + }, + { + "type": { + "name": "string", + "tag": "scalar" + }, + "tag": "array" + } + ], + "tag": "unlabeledProduct" + }, + "tag": "arrow" + }, + "names": { + "relay": "-relay-", + "getDataSrv": "getDataSrv", + "callbackSrv": "callbackSrv", + "responseSrv": "callbackSrv", + "responseFnName": "response", + "errorHandlingSrv": "errorHandlingSrv", + "errorFnName": "error" + } +}, + resourceTest_script + ); +} + +export const helloTest_script = ` +(xor + (seq + (seq + (call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-) + (call %init_peer_id% ("hello-world" "hello") ["Fluence user"] ret) + ) + (call %init_peer_id% ("callbackSrv" "response") [ret]) + ) + (call %init_peer_id% ("errorHandlingSrv" "error") [:error: 0]) +) +`; + +export type HelloTestParams = [config?: {ttl?: number}] | [peer: IFluenceClient$$, config?: {ttl?: number}]; + +export type HelloTestResult = Promise; + +export function helloTest(...args: HelloTestParams): HelloTestResult { + return callFunction$$( + args, + { + "functionName": "helloTest", + "arrow": { + "domain": { + "fields": {}, + "tag": "labeledProduct" + }, + "codomain": { + "items": [ + { + "name": "string", + "tag": "scalar" + } + ], + "tag": "unlabeledProduct" + }, + "tag": "arrow" + }, + "names": { + "relay": "-relay-", + "getDataSrv": "getDataSrv", + "callbackSrv": "callbackSrv", + "responseSrv": "callbackSrv", + "responseFnName": "response", + "errorHandlingSrv": "errorHandlingSrv", + "errorFnName": "error" + } +}, + helloTest_script + ); +} + +export const demo_calculation_script = ` +(xor + (seq + (seq + (seq + (seq + (seq + (seq + (seq + (seq + (call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-) + (call %init_peer_id% ("getDataSrv" "service_id") [] -service_id-arg-) + ) + (call %init_peer_id% (-service_id-arg- "test_logs") []) + ) + (call %init_peer_id% (-service_id-arg- "add") [10] ret) + ) + (call %init_peer_id% (-service_id-arg- "multiply") [5] ret-0) + ) + (call %init_peer_id% (-service_id-arg- "subtract") [8] ret-1) + ) + (call %init_peer_id% (-service_id-arg- "divide") [6] ret-2) + ) + (call %init_peer_id% (-service_id-arg- "state") [] ret-3) + ) + (call %init_peer_id% ("callbackSrv" "response") [ret-3]) + ) + (call %init_peer_id% ("errorHandlingSrv" "error") [:error: 0]) +) +`; + +export type Demo_calculationParams = [service_id: string, config?: {ttl?: number}] | [peer: IFluenceClient$$, service_id: string, config?: {ttl?: number}]; + +export type Demo_calculationResult = Promise; + +export function demo_calculation(...args: Demo_calculationParams): Demo_calculationResult { + return callFunction$$( + args, + { + "functionName": "demo_calculation", + "arrow": { + "domain": { + "fields": { + "service_id": { + "name": "string", + "tag": "scalar" + } + }, + "tag": "labeledProduct" + }, + "codomain": { + "items": [ + { + "name": "f64", + "tag": "scalar" + } + ], + "tag": "unlabeledProduct" + }, + "tag": "arrow" + }, + "names": { + "relay": "-relay-", + "getDataSrv": "getDataSrv", + "callbackSrv": "callbackSrv", + "responseSrv": "callbackSrv", + "responseFnName": "response", + "errorHandlingSrv": "errorHandlingSrv", + "errorFnName": "error" + } +}, + demo_calculation_script + ); +} + +export const marineTest_script = ` +(xor + (seq + (seq + (seq + (seq + (seq + (seq + (seq + (seq + (seq + (call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-) + (call %init_peer_id% ("getDataSrv" "wasm64") [] -wasm64-arg-) + ) + (call %init_peer_id% ("single_module_srv" "create") [-wasm64-arg-] ret) + ) + (call %init_peer_id% (ret.$.service_id.[0] "test_logs") []) + ) + (call %init_peer_id% (ret.$.service_id.[0] "add") [10] ret-0) + ) + (call %init_peer_id% (ret.$.service_id.[0] "multiply") [5] ret-1) + ) + (call %init_peer_id% (ret.$.service_id.[0] "subtract") [8] ret-2) + ) + (call %init_peer_id% (ret.$.service_id.[0] "divide") [6] ret-3) + ) + (call %init_peer_id% (ret.$.service_id.[0] "state") [] ret-4) + ) + (call %init_peer_id% ("callbackSrv" "response") [ret-4]) + ) + (call %init_peer_id% ("errorHandlingSrv" "error") [:error: 0]) +) +`; + +export type MarineTestParams = [wasm64: string, config?: {ttl?: number}] | [peer: IFluenceClient$$, wasm64: string, config?: {ttl?: number}]; + +export type MarineTestResult = Promise; + +export function marineTest(...args: MarineTestParams): MarineTestResult { + return callFunction$$( + args, + { + "functionName": "marineTest", + "arrow": { + "domain": { + "fields": { + "wasm64": { + "name": "string", + "tag": "scalar" + } + }, + "tag": "labeledProduct" + }, + "codomain": { + "items": [ + { + "name": "f64", + "tag": "scalar" + } + ], + "tag": "unlabeledProduct" + }, + "tag": "arrow" + }, + "names": { + "relay": "-relay-", + "getDataSrv": "getDataSrv", + "callbackSrv": "callbackSrv", + "responseSrv": "callbackSrv", + "responseFnName": "response", + "errorHandlingSrv": "errorHandlingSrv", + "errorFnName": "error" + } +}, + marineTest_script + ); +} diff --git a/packages/core/aqua-to-js/src/generate/interfaces.ts b/packages/core/aqua-to-js/src/generate/interfaces.ts index 73f9910f..575b9324 100644 --- a/packages/core/aqua-to-js/src/generate/interfaces.ts +++ b/packages/core/aqua-to-js/src/generate/interfaces.ts @@ -20,8 +20,6 @@ import { genTypeName, typeToTs } from "../common.js"; import { CLIENT } from "../constants.js"; import { capitalize, getFuncArgs } from "../utils.js"; -import { DefaultServiceId } from "./service.js"; - export interface TypeGenerator { type(field: string, type: string): string; generic(field: string, type: string): string; @@ -134,11 +132,9 @@ export class TSTypeGenerator implements TypeGenerator { ]; const registerServiceArgs = - // This wrong type comes from aqua team. We need to discuss fix with them - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - (srvDef.defaultServiceId as DefaultServiceId).s_Some__f_value != null - ? functionOverloadsWithDefaultServiceId - : functionOverloadsWithoutDefaultServiceId; + srvDef.defaultServiceId == null + ? functionOverloadsWithoutDefaultServiceId + : functionOverloadsWithDefaultServiceId; return [ interfaces, diff --git a/packages/core/aqua-to-js/src/generate/service.ts b/packages/core/aqua-to-js/src/generate/service.ts index c58d3fd1..13cb89ab 100644 --- a/packages/core/aqua-to-js/src/generate/service.ts +++ b/packages/core/aqua-to-js/src/generate/service.ts @@ -14,17 +14,12 @@ * limitations under the License. */ -import { ServiceDef } from "@fluencelabs/interfaces"; +import { JSONValue, ServiceDef } from "@fluencelabs/interfaces"; import { recursiveRenameLaquaProps } from "../utils.js"; import { TypeGenerator } from "./interfaces.js"; -// Actual value of defaultServiceId which comes from aqua-api -export interface DefaultServiceId { - s_Some__f_value?: string; -} - export function generateServices( typeGenerator: TypeGenerator, services: Record, @@ -68,21 +63,6 @@ function generateRegisterServiceOverload( } function serviceToJson(service: ServiceDef): string { - return JSON.stringify( - { - // This assertion is required because aqua-api gives bad types - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - ...((service.defaultServiceId as DefaultServiceId).s_Some__f_value != null - ? { - defaultServiceId: - // This assertion is required because aqua-api gives bad types - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - (service.defaultServiceId as DefaultServiceId).s_Some__f_value, - } - : {}), - functions: recursiveRenameLaquaProps(service.functions), - }, - null, - 4, - ); + const record: Record = service; + return JSON.stringify(recursiveRenameLaquaProps(record), null, 4); } diff --git a/packages/core/aqua-to-js/src/index.ts b/packages/core/aqua-to-js/src/index.ts index fd14c282..cacfd03d 100644 --- a/packages/core/aqua-to-js/src/index.ts +++ b/packages/core/aqua-to-js/src/index.ts @@ -27,18 +27,22 @@ interface TsOutput { sources: string; } -type LanguageOutput = { - js: JsOutput; - ts: TsOutput; -}; - type NothingToGenerate = null; -type OutputType = "js" | "ts"; -export default async function aquaToJs( +export default async function aquaToJs( res: CompilationResult, - outputType: T, -): Promise { + outputType: "js", +): Promise; + +export default async function aquaToJs( + res: CompilationResult, + outputType: "ts", +): Promise; + +export default async function aquaToJs( + res: CompilationResult, + outputType: "js" | "ts", +): Promise { if ( Object.keys(res.services).length === 0 && Object.keys(res.functions).length === 0 @@ -48,13 +52,14 @@ export default async function aquaToJs( const packageJson = await getPackageJsonContent(); - return outputType === "js" - ? { - sources: generateSources(res, "js", packageJson), - types: generateTypes(res, packageJson), - } - : // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - ({ - sources: generateSources(res, "ts", packageJson), - } as LanguageOutput[T]); + if (outputType === "js") { + return { + sources: generateSources(res, "js", packageJson), + types: generateTypes(res, packageJson), + }; + } + + return { + sources: generateSources(res, "ts", packageJson), + }; } diff --git a/packages/core/js-client/aqua_test/marine-js.aqua b/packages/core/js-client/aqua_test/marine-js.aqua index 2de7507f..860f90d4 100644 --- a/packages/core/js-client/aqua_test/marine-js.aqua +++ b/packages/core/js-client/aqua_test/marine-js.aqua @@ -1,10 +1,10 @@ -data GreetingRecord: +data GreetingRecordData: str: string num: i32 service Greeting("greeting"): greeting(name: string) -> string - greeting_record() -> GreetingRecord + greeting_record() -> GreetingRecordData func call(arg: string) -> string: res1 <- Greeting.greeting(arg) @@ -13,7 +13,7 @@ func call(arg: string) -> string: <- res3 service GreetingRecord: - greeting_record() -> GreetingRecord + greeting_record() -> GreetingRecordData log_debug() log_error() log_info() diff --git a/packages/core/js-client/package.json b/packages/core/js-client/package.json index a4ad25b7..2d0232cd 100644 --- a/packages/core/js-client/package.json +++ b/packages/core/js-client/package.json @@ -58,7 +58,7 @@ "zod": "3.22.4" }, "devDependencies": { - "@fluencelabs/aqua-api": "0.9.3", + "@fluencelabs/aqua-api": "0.13.0", "@rollup/plugin-inject": "5.0.3", "@types/bs58": "4.0.1", "@types/debug": "4.1.7", diff --git a/packages/core/js-client/src/api.ts b/packages/core/js-client/src/api.ts index 2b831f30..5648f7c7 100644 --- a/packages/core/js-client/src/api.ts +++ b/packages/core/js-client/src/api.ts @@ -30,7 +30,7 @@ import { js2aqua, wrapJsFunction, } from "./compilerSupport/conversions.js"; -import { ServiceImpl } from "./compilerSupport/types.js"; +import { ServiceImpl, UserServiceImpl } from "./compilerSupport/types.js"; import { FluencePeer } from "./jsPeer/FluencePeer.js"; import { callAquaFunction, Fluence, registerService } from "./index.js"; @@ -46,6 +46,8 @@ function validateAquaConfig( ]).parse(config); } +// TODO: remove v5 prefix from functions + /** * Convenience function to support Aqua `func` generation backend * The compiler only need to generate a call the function and provide the corresponding definitions and the air script @@ -56,8 +58,8 @@ function validateAquaConfig( */ export const v5_callFunction = async ( args: [ - client: FluencePeer | (JSONValue | ServiceImpl[string]), - ...args: (JSONValue | ServiceImpl[string])[], + client: FluencePeer | (JSONValue | UserServiceImpl[string]), + ...args: (JSONValue | UserServiceImpl[string])[], ], def: FunctionCallDef, script: string, @@ -161,10 +163,10 @@ const getDefaultServiceId = (def: ServiceDef) => { }; type RegisterServiceType = - | [ServiceImpl] - | [string, ServiceImpl] - | [FluencePeer, ServiceImpl] - | [FluencePeer, string, ServiceImpl]; + | [UserServiceImpl] + | [string, UserServiceImpl] + | [FluencePeer, UserServiceImpl] + | [FluencePeer, string, UserServiceImpl]; /** * Convenience function to support Aqua `service` generation backend diff --git a/packages/core/js-client/src/clientPeer/ClientPeer.ts b/packages/core/js-client/src/clientPeer/ClientPeer.ts index 57397731..9f7ce987 100644 --- a/packages/core/js-client/src/clientPeer/ClientPeer.ts +++ b/packages/core/js-client/src/clientPeer/ClientPeer.ts @@ -90,10 +90,6 @@ export class ClientPeer extends FluencePeer implements IFluenceClient { ); } - getPeerId(): string { - return this.keyPair.getPeerId(); - } - getPeerSecretKey(): Uint8Array { return this.keyPair.toEd25519PrivateKey(); } diff --git a/packages/core/js-client/src/clientPeer/types.ts b/packages/core/js-client/src/clientPeer/types.ts index 9dbe2a86..3dd5c1f9 100644 --- a/packages/core/js-client/src/clientPeer/types.ts +++ b/packages/core/js-client/src/clientPeer/types.ts @@ -29,12 +29,6 @@ export type Node = { multiaddr: string; }; -/** - * A node in Fluence network a client can connect to. - * Can be in the form of: - * - string: multiaddr in string format - * - Node: node structure, @see Node - */ export const relaySchema = z.union([ z.string(), z.object({ @@ -43,6 +37,12 @@ export const relaySchema = z.union([ }), ]); +/** + * A node in Fluence network a client can connect to. + * Can be in the form of: + * - string: multiaddr in string format + * - Node: node structure, @see Node + */ export type RelayOptions = z.infer; /** @@ -51,7 +51,13 @@ export type RelayOptions = z.infer; export type KeyTypes = "RSA" | "Ed25519" | "secp256k1"; const keyPairOptionsSchema = z.object({ + /** + * Key pair type. Only Ed25519 is supported for now. + */ type: z.literal("Ed25519"), + /** + * Key pair source. Could be byte array or generated randomly. + */ source: z.union([z.literal("random"), z.instanceof(Uint8Array)]), }); diff --git a/packages/core/js-client/src/compilerSupport/callFunction.ts b/packages/core/js-client/src/compilerSupport/callFunction.ts index 6d8f177e..f53d1807 100644 --- a/packages/core/js-client/src/compilerSupport/callFunction.ts +++ b/packages/core/js-client/src/compilerSupport/callFunction.ts @@ -49,7 +49,6 @@ export type CallAquaFunctionArgs = { config: CallAquaFunctionConfig | undefined; peer: FluencePeer; args: { [key: string]: JSONValue | ArgCallbackFunction }; - fireAndForget?: boolean | undefined; }; export type CallAquaFunctionConfig = { diff --git a/packages/core/js-client/src/compilerSupport/conversions.ts b/packages/core/js-client/src/compilerSupport/conversions.ts index 348c8d06..bae59f1a 100644 --- a/packages/core/js-client/src/compilerSupport/conversions.ts +++ b/packages/core/js-client/src/compilerSupport/conversions.ts @@ -25,16 +25,14 @@ import { UnlabeledProductType, } from "@fluencelabs/interfaces"; -import { ParticleContext } from "../jsServiceHost/interfaces.js"; - -import { ServiceImpl } from "./types.js"; +import { ServiceImpl, UserServiceImpl } from "./types.js"; export class SchemaValidationError extends Error { constructor( public path: string[], schema: NonArrowSimpleType | ArrowWithoutCallbacks, expected: string, - provided: JSONValue | ServiceImpl[string], + provided: JSONValue | UserServiceImpl[string], ) { const given = provided === null @@ -205,18 +203,12 @@ export function js2aqua( // Wrapping function, converting its arguments to aqua before call and back to js after call. // It makes callbacks and service functions defined by user operate on js types seamlessly export const wrapJsFunction = ( - func: ServiceImpl[string], + func: UserServiceImpl[string], schema: | ArrowWithoutCallbacks | ArrowType | UnlabeledProductType>, ): ServiceImpl[string] => { - return async (...args) => { - // These assertions used to correctly destructure tuple. It's impossible to do without asserts due to ts limitations. - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - const jsonArgs = args.slice(0, args.length - 1) as JSONValue[]; - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - const context = args[args.length - 1] as ParticleContext; - + return async ({ args, context }) => { const schemaArgs = schema.domain.tag === "nil" ? [] @@ -224,13 +216,13 @@ export const wrapJsFunction = ( ? schema.domain.items : Object.values(schema.domain.fields); - if (schemaArgs.length !== jsonArgs.length) { + if (schemaArgs.length !== args.length) { throw new Error( - `Schema and generated air doesn't match. Air has been called with ${jsonArgs.length} args and schema contains ${schemaArgs.length} args`, + `Schema and generated air doesn't match. Air has been called with ${args.length} args and schema contains ${schemaArgs.length} args`, ); } - const tsArgs = jsonArgs.map((arg, i) => { + const jsArgs = args.map((arg, i) => { return aqua2js(arg, schemaArgs[i]); }); @@ -243,7 +235,7 @@ export const wrapJsFunction = ( ? schema.codomain.items[0] : schema.codomain; - let result = await func(...tsArgs, context); + let result = await func(...jsArgs, context); if (returnTypeVoid) { result = null; diff --git a/packages/core/js-client/src/compilerSupport/services.ts b/packages/core/js-client/src/compilerSupport/services.ts index e9294eb1..16ac01e7 100644 --- a/packages/core/js-client/src/compilerSupport/services.ts +++ b/packages/core/js-client/src/compilerSupport/services.ts @@ -20,7 +20,6 @@ import { FluencePeer } from "../jsPeer/FluencePeer.js"; import { CallServiceData, GenericCallServiceHandler, - ParticleContext, ResultCodes, } from "../jsServiceHost/interfaces.js"; import { Particle } from "../particle/Particle.js"; @@ -132,12 +131,12 @@ export const userHandlerService = ( serviceId, fnName, handler: async (req: CallServiceData) => { - const args: [...JSONValue[], ParticleContext] = [ - ...req.args, - req.particleContext, - ]; + const { args, particleContext: context } = req; - const result = await userHandler.bind(null)(...args); + const result = await userHandler.bind(null)({ + args, + context, + }); return { retCode: ResultCodes.success, diff --git a/packages/core/js-client/src/compilerSupport/types.ts b/packages/core/js-client/src/compilerSupport/types.ts index fd0361d5..6ad2a283 100644 --- a/packages/core/js-client/src/compilerSupport/types.ts +++ b/packages/core/js-client/src/compilerSupport/types.ts @@ -21,6 +21,16 @@ import { ParticleContext } from "../jsServiceHost/interfaces.js"; export type MaybePromise = T | Promise; export type ServiceImpl = Record< + string, + (args: { + args: JSONArray; + context: ParticleContext; + }) => MaybePromise +>; + +export type UserServiceImpl = Record< string, (...args: [...JSONArray, ParticleContext]) => MaybePromise >; + +export type ServiceFnArgs = { args: T; context: ParticleContext }; diff --git a/packages/core/js-client/src/ephemeral/client.ts b/packages/core/js-client/src/ephemeral/client.ts index a9efe08d..bac36dd7 100644 --- a/packages/core/js-client/src/ephemeral/client.ts +++ b/packages/core/js-client/src/ephemeral/client.ts @@ -43,7 +43,7 @@ export class EphemeralNetworkClient extends FluencePeer { const marine = new MarineBackgroundRunner( { async getValue() { - // TODO: load worker with avm and marine, test that it works + // TODO: load worker in parallel with avm and marine, test that it works return getWorker("@fluencelabs/marine-worker", "/"); }, start() { diff --git a/packages/core/js-client/src/ephemeral/network.ts b/packages/core/js-client/src/ephemeral/network.ts index 13202c08..f4b42122 100644 --- a/packages/core/js-client/src/ephemeral/network.ts +++ b/packages/core/js-client/src/ephemeral/network.ts @@ -249,7 +249,7 @@ export class EphemeralNetwork { const marine = new MarineBackgroundRunner( { async getValue() { - // TODO: load worker with avm and marine, test that it works + // TODO: load worker in parallel with avm and marine, test that it works return getWorker("@fluencelabs/marine-worker", "/"); }, start() { @@ -315,7 +315,7 @@ export class EphemeralNetwork { await Promise.all(startPromises); for (const p of peers) { - this.peers.set(p.keyPair.getPeerId(), p); + this.peers.set(p.getPeerId(), p); } } diff --git a/packages/core/js-client/src/index.ts b/packages/core/js-client/src/index.ts index 1eb0ebb9..97ac0e98 100644 --- a/packages/core/js-client/src/index.ts +++ b/packages/core/js-client/src/index.ts @@ -63,7 +63,7 @@ const createClient = async ( const marine = new MarineBackgroundRunner( { async getValue() { - // TODO: load worker with avm and marine, test that it works + // TODO: load worker in parallel with avm and marine, test that it works return getWorker("@fluencelabs/marine-worker", CDNUrl); }, start() { diff --git a/packages/core/js-client/src/jsPeer/FluencePeer.ts b/packages/core/js-client/src/jsPeer/FluencePeer.ts index 20cc88cc..c2cce60c 100644 --- a/packages/core/js-client/src/jsPeer/FluencePeer.ts +++ b/packages/core/js-client/src/jsPeer/FluencePeer.ts @@ -28,6 +28,7 @@ import { concatMap, filter, groupBy, + GroupedObservable, lastValueFrom, mergeMap, pipe, @@ -65,7 +66,7 @@ import { defaultSigGuard, Sig } from "../services/Sig.js"; import { Srv } from "../services/SingleModuleSrv.js"; import { Tracing } from "../services/Tracing.js"; import { logger } from "../util/logger.js"; -import { jsonify, isString, getErrorMessage } from "../util/utils.js"; +import { getErrorMessage, isString, jsonify } from "../util/utils.js"; import { ExpirationError, InterpreterError, SendError } from "./errors.js"; @@ -106,7 +107,7 @@ export const DEFAULT_CONFIG: PeerConfig = { export abstract class FluencePeer { constructor( protected readonly config: PeerConfig, - readonly keyPair: KeyPair, + protected readonly keyPair: KeyPair, protected readonly marineHost: IMarineHost, protected readonly jsServiceHost: IJsServiceHost, protected readonly connection: IConnection, @@ -114,6 +115,10 @@ export abstract class FluencePeer { this._initServices(); } + getPeerId(): string { + return this.keyPair.getPeerId(); + } + async start(): Promise { log_peer.trace("starting Fluence peer"); @@ -123,7 +128,7 @@ export abstract class FluencePeer { await this.marineHost.start(); - this._startParticleProcessing(); + this.startParticleProcessing(); this.isInitialized = true; await this.connection.start(); log_peer.trace("started Fluence peer"); @@ -143,7 +148,7 @@ export abstract class FluencePeer { await this._incomingParticlePromise; log_peer.trace("All particles finished execution"); - this._stopParticleProcessing(); + this.stopParticleProcessing(); await this.marineHost.stop(); await this.connection.stop(); this.isInitialized = false; @@ -300,7 +305,7 @@ export abstract class FluencePeer { // Queues for incoming and outgoing particles private _incomingParticles = new Subject(); - private _timeouts: Array = []; + private timeouts: Record = {}; private _particleSourceSubscription?: Unsubscribable; private _incomingParticlePromise?: Promise; @@ -334,322 +339,86 @@ export abstract class FluencePeer { registerTracing(this, "tracingSrv", this._classServices.tracing); } - // TODO: too long, refactor - private _startParticleProcessing() { - this._particleSourceSubscription = this.connection.particleSource.subscribe( - { - next: (p) => { - this._incomingParticles.next({ - particle: p, - callResults: [], - onSuccess: () => {}, - onError: () => {}, - }); - }, - }, + private async sendParticleToRelay( + item: ParticleQueueItem & { result: InterpreterResult }, + ) { + const newParticle = cloneWithNewData( + item.particle, + Buffer.from(item.result.data), ); - this._incomingParticlePromise = lastValueFrom( - this._incomingParticles.pipe( - tap((item) => { - log_particle.debug("id %s. received:", item.particle.id); - - log_particle.trace("id %s. data: %j", item.particle.id, { - initPeerId: item.particle.initPeerId, - timestamp: item.particle.timestamp, - ttl: item.particle.ttl, - signature: item.particle.signature, - }); - - log_particle.trace( - "id %s. script: %s", - item.particle.id, - item.particle.script, - ); - - log_particle.trace( - "id %s. call results: %j", - item.particle.id, - item.callResults, - ); - }), - filterExpiredParticles(this._expireParticle.bind(this)), - groupBy((item) => { - return fromUint8Array(item.particle.signature); - }), - mergeMap((group$) => { - let prevData: Uint8Array = Buffer.from([]); - let firstRun = true; - - return group$.pipe( - concatMap(async (item) => { - if (firstRun) { - const timeout = setTimeout(() => { - this._expireParticle(item); - }, getActualTTL(item.particle)); - - this._timeouts.push(timeout); - firstRun = false; - } - - if (!this.isInitialized) { - // If `.stop()` was called return null to stop particle processing immediately - return null; - } - - // IMPORTANT! - // AVM runner execution and prevData <-> newData swapping - // MUST happen sequentially (in a critical section). - // Otherwise the race might occur corrupting the prevData - - log_particle.debug( - "id %s. sending particle to interpreter", - item.particle.id, - ); - - log_particle.trace( - "id %s. prevData: %s", - item.particle.id, - this.decodeAvmData(prevData).slice(0, 50), - ); - - const args = serializeAvmArgs( - { - initPeerId: item.particle.initPeerId, - currentPeerId: this.keyPair.getPeerId(), - timestamp: item.particle.timestamp, - ttl: item.particle.ttl, - keyFormat: KeyPairFormat.Ed25519, - particleId: item.particle.id, - secretKeyBytes: this.keyPair.toEd25519PrivateKey(), - }, - item.particle.script, - prevData, - item.particle.data, - item.callResults, - ); - - let avmCallResult: InterpreterResult | Error; - - try { - const res = await this.marineHost.callService( - "avm", - "invoke", - args, - ); - - avmCallResult = deserializeAvmResult(res); - } catch (e) { - avmCallResult = e instanceof Error ? e : new Error(String(e)); - } - - if ( - !(avmCallResult instanceof Error) && - avmCallResult.retCode === 0 - ) { - const newData = Buffer.from(avmCallResult.data); - prevData = newData; - } - - return { - ...item, - result: avmCallResult, - }; - }), - filter((item): item is NonNullable => { - return item !== null; - }), - filterExpiredParticles< - ParticleQueueItem & { - result: Error | InterpreterResult; - } - >(this._expireParticle.bind(this)), - mergeMap(async (item) => { - // If peer was stopped, do not proceed further - if (!this.isInitialized) { - return; - } - - // Do not continue if there was an error in particle interpretation - if (item.result instanceof Error) { - log_particle.error( - "id %s. interpreter failed: %s", - item.particle.id, - item.result.message, - ); - - item.onError( - new InterpreterError( - `Script interpretation failed: ${item.result.message} (particle id: ${item.particle.id})`, - ), - ); - - return; - } - - if (item.result.retCode !== 0) { - log_particle.error( - "id %s. interpreter failed: retCode: %d, message: %s", - item.particle.id, - item.result.retCode, - item.result.errorMessage, - ); - - log_particle.trace( - "id %s. avm data: %s", - item.particle.id, - this.decodeAvmData(item.result.data), - ); - - item.onError( - new InterpreterError( - `Script interpretation failed: ${item.result.errorMessage} (particle id: ${item.particle.id})`, - ), - ); - - return; - } - - log_particle.trace( - "id %s. interpreter result: retCode: %d, avm data: %s", - item.particle.id, - item.result.retCode, - this.decodeAvmData(item.result.data), - ); - - let connectionPromise: Promise = Promise.resolve(); - - // send particle further if requested - if (item.result.nextPeerPks.length > 0) { - const newParticle = cloneWithNewData( - item.particle, - Buffer.from(item.result.data), - ); - - log_particle.debug( - "id %s. sending particle into network. Next peer ids: %s", - newParticle.id, - item.result.nextPeerPks.toString(), - ); - - connectionPromise = this.connection - .sendParticle(item.result.nextPeerPks, newParticle) - .then(() => { - log_particle.trace( - "id %s. send successful", - newParticle.id, - ); - }) - .catch((e: unknown) => { - log_particle.error( - "id %s. send failed %j", - newParticle.id, - e, - ); - - const message = getErrorMessage(e); - - item.onError( - new SendError( - `Could not send particle: (particle id: ${item.particle.id}, message: ${message})`, - ), - ); - }); - } - - // execute call requests if needed - // and put particle with the results back to queue - if (item.result.callRequests.length > 0) { - for (const [key, cr] of item.result.callRequests) { - const req = { - fnName: cr.functionName, - args: cr.arguments, - serviceId: cr.serviceId, - tetraplets: cr.tetraplets, - particleContext: getParticleContext( - item.particle, - cr.tetraplets, - ), - }; - - void this._execSingleCallRequest(req) - .catch((err): CallServiceResult => { - if (err instanceof ServiceError) { - return { - retCode: ResultCodes.error, - result: err.message, - }; - } - - return { - retCode: ResultCodes.error, - result: `Service call failed. fnName="${ - req.fnName - }" serviceId="${ - req.serviceId - }" error: ${getErrorMessage(err)}`, - }; - }) - .then((res) => { - if ( - req.serviceId === "callbackSrv" && - req.fnName === "response" - ) { - // Particle already processed - return; - } - - const serviceResult = { - result: jsonify(res.result), - retCode: res.retCode, - }; - - const newParticle = cloneWithNewData( - item.particle, - Buffer.from([]), - ); - - this._incomingParticles.next({ - ...item, - particle: newParticle, - callResults: [[key, serviceResult]], - }); - }); - } - } - - return connectionPromise; - }), - ); - }), - ), - { defaultValue: undefined }, - ); - } - - private _expireParticle(item: ParticleQueueItem) { - const particleId = item.particle.id; - log_particle.debug( - "id %s. particle has expired after %d. Deleting particle-related queues and handlers", - item.particle.id, - item.particle.ttl, + "id %s. sending particle into network. Next peer ids: %s", + newParticle.id, + item.result.nextPeerPks.toString(), ); - this.jsServiceHost.removeParticleScopeHandlers(particleId); + try { + await this.connection.sendParticle(item.result.nextPeerPks, newParticle); + log_particle.trace("id %s. send successful", newParticle.id); + } catch (e) { + log_particle.error("id %s. send failed %j", newParticle.id, e); - item.onError( - new ExpirationError( - `Particle expired after ttl of ${item.particle.ttl}ms (particle id: ${item.particle.id})`, - ), + const message = getErrorMessage(e); + + item.onError( + new SendError( + `Could not send particle: (particle id: ${item.particle.id}, message: ${message})`, + ), + ); + } + } + + private async execCallRequests( + item: ParticleQueueItem & { result: InterpreterResult }, + ) { + return Promise.all( + item.result.callRequests.map(async ([key, cr]) => { + const req = { + fnName: cr.functionName, + args: cr.arguments, + serviceId: cr.serviceId, + tetraplets: cr.tetraplets, + particleContext: getParticleContext(item.particle, cr.tetraplets), + }; + + let res: CallServiceResult; + + try { + res = await this.execCallRequest(req); + } catch (err) { + if (err instanceof ServiceError) { + res = { + retCode: ResultCodes.error, + result: err.message, + }; + } + + res = { + retCode: ResultCodes.error, + result: `Service call failed. fnName="${req.fnName}" serviceId="${ + req.serviceId + }" error: ${getErrorMessage(err)}`, + }; + } + + const serviceResult = { + result: jsonify(res.result), + retCode: res.retCode, + }; + + const newParticle = cloneWithNewData(item.particle, Buffer.from([])); + + this._incomingParticles.next({ + ...item, + particle: newParticle, + callResults: [[key, serviceResult]], + }); + }), ); } - private decodeAvmData(data: Uint8Array) { - return new TextDecoder().decode(data.buffer); - } - - private async _execSingleCallRequest( + private async execCallRequest( req: CallServiceData, ): Promise { const particleId = req.particleContext.particleId; @@ -695,9 +464,224 @@ export abstract class FluencePeer { return res; } - private _stopParticleProcessing() { + private mapParticleGroup( + group$: GroupedObservable, + ) { + let prevData: Uint8Array = Buffer.from([]); + + return group$.pipe( + concatMap(async (item) => { + if (!(group$.key in this.timeouts)) { + this.timeouts[group$.key] = setTimeout(() => { + this.expireParticle(item); + }, getActualTTL(item.particle)); + } + + // IMPORTANT! + // AVM runner execution and prevData <-> newData swapping + // MUST happen sequentially (in a critical section). + // Otherwise the race might occur corrupting the prevData + + log_particle.debug( + "id %s. sending particle to interpreter", + item.particle.id, + ); + + log_particle.trace( + "id %s. prevData: %s", + item.particle.id, + this.decodeAvmData(prevData).slice(0, 50), + ); + + const args = serializeAvmArgs( + { + initPeerId: item.particle.initPeerId, + currentPeerId: this.keyPair.getPeerId(), + timestamp: item.particle.timestamp, + ttl: item.particle.ttl, + keyFormat: KeyPairFormat.Ed25519, + particleId: item.particle.id, + secretKeyBytes: this.keyPair.toEd25519PrivateKey(), + }, + item.particle.script, + prevData, + item.particle.data, + item.callResults, + ); + + let avmCallResult: InterpreterResult | Error; + + try { + const res = await this.marineHost.callService("avm", "invoke", args); + + avmCallResult = deserializeAvmResult(res); + } catch (e) { + avmCallResult = e instanceof Error ? e : new Error(String(e)); + } + + if (!(avmCallResult instanceof Error) && avmCallResult.retCode === 0) { + const newData = Buffer.from(avmCallResult.data); + prevData = newData; + } + + return { + ...item, + result: avmCallResult, + }; + }), + filterExpiredParticles< + ParticleQueueItem & { + result: Error | InterpreterResult; + } + >(this.expireParticle.bind(this)), + filter(() => { + // If peer was stopped, do not proceed further + return this.isInitialized; + }), + mergeMap(async (item) => { + // Do not continue if there was an error in particle interpretation + if (item.result instanceof Error) { + log_particle.error( + "id %s. interpreter failed: %s", + item.particle.id, + item.result.message, + ); + + item.onError( + new InterpreterError( + `Script interpretation failed: ${item.result.message} (particle id: ${item.particle.id})`, + ), + ); + + return; + } + + if (item.result.retCode !== 0) { + log_particle.error( + "id %s. interpreter failed: retCode: %d, message: %s", + item.particle.id, + item.result.retCode, + item.result.errorMessage, + ); + + log_particle.trace( + "id %s. avm data: %s", + item.particle.id, + this.decodeAvmData(item.result.data), + ); + + item.onError( + new InterpreterError( + `Script interpretation failed: ${item.result.errorMessage} (particle id: ${item.particle.id})`, + ), + ); + + return; + } + + log_particle.trace( + "id %s. interpreter result: retCode: %d, avm data: %s", + item.particle.id, + item.result.retCode, + this.decodeAvmData(item.result.data), + ); + + let connectionPromise: Promise = Promise.resolve(); + + // send particle further if requested + if (item.result.nextPeerPks.length > 0) { + // TS doesn't allow to pass just 'item' + connectionPromise = this.sendParticleToRelay({ + ...item, + result: item.result, + }); + } + + // execute call requests if needed + // and put particle with the results back to queue + if (item.result.callRequests.length > 0) { + // TS doesn't allow to pass just 'item' + void this.execCallRequests({ ...item, result: item.result }); + } + + return connectionPromise; + }), + ); + } + + private startParticleProcessing() { + this._particleSourceSubscription = this.connection.particleSource.subscribe( + { + next: (particle) => { + this._incomingParticles.next({ + particle, + callResults: [], + onSuccess: () => {}, + onError: () => {}, + }); + }, + }, + ); + + this._incomingParticlePromise = lastValueFrom( + this._incomingParticles.pipe( + tap((item) => { + log_particle.debug("id %s. received:", item.particle.id); + + log_particle.trace("id %s. data: %j", item.particle.id, { + initPeerId: item.particle.initPeerId, + timestamp: item.particle.timestamp, + ttl: item.particle.ttl, + signature: item.particle.signature, + }); + + log_particle.trace( + "id %s. script: %s", + item.particle.id, + item.particle.script, + ); + + log_particle.trace( + "id %s. call results: %j", + item.particle.id, + item.callResults, + ); + }), + filterExpiredParticles(this.expireParticle.bind(this)), + groupBy((item) => { + return fromUint8Array(item.particle.signature); + }), + mergeMap(this.mapParticleGroup.bind(this)), + ), + { defaultValue: undefined }, + ); + } + + private expireParticle(item: ParticleQueueItem) { + const particleId = item.particle.id; + + log_particle.debug( + "id %s. particle has expired after %d. Deleting particle-related queues and handlers", + item.particle.id, + item.particle.ttl, + ); + + this.jsServiceHost.removeParticleScopeHandlers(particleId); + + item.onError( + new ExpirationError( + `Particle expired after ttl of ${item.particle.ttl}ms (particle id: ${item.particle.id})`, + ), + ); + } + + private decodeAvmData(data: Uint8Array) { + return new TextDecoder().decode(data.buffer); + } + + private stopParticleProcessing() { // do not hang if the peer has been stopped while some of the timeouts are still being executed - this._timeouts.forEach((timeout) => { + Object.values(this.timeouts).forEach((timeout) => { clearTimeout(timeout); }); } diff --git a/packages/core/js-client/src/jsPeer/errors.ts b/packages/core/js-client/src/jsPeer/errors.ts index f49d86e3..710a262e 100644 --- a/packages/core/js-client/src/jsPeer/errors.ts +++ b/packages/core/js-client/src/jsPeer/errors.ts @@ -14,8 +14,17 @@ * limitations under the License. */ +/** + * Throw when particle times out, e.g. haven't been resolved after TTL is expired + */ export class ExpirationError extends Error {} +/** + * Throws when AquaVM interpreter returns an error while executing air script. It could be badly written air or internal bug. + */ export class InterpreterError extends Error {} +/** + * Throws when network error occurs while sending particle to relay peer. + */ export class SendError extends Error {} diff --git a/packages/core/js-client/src/jsServiceHost/serviceUtils.ts b/packages/core/js-client/src/jsServiceHost/serviceUtils.ts index 42e9b8ef..2e6fcfe7 100644 --- a/packages/core/js-client/src/jsServiceHost/serviceUtils.ts +++ b/packages/core/js-client/src/jsServiceHost/serviceUtils.ts @@ -65,7 +65,23 @@ export const getParticleContext = ( export function registerDefaultServices(peer: FluencePeer) { Object.entries(builtInServices).forEach(([serviceId, service]) => { Object.entries(service).forEach(([fnName, fn]) => { - peer.internals.regHandler.common(serviceId, fnName, fn); + const wrapped = async (req: CallServiceData) => { + const res = await fn(req); + + if ( + res.retCode === ResultCodes.error && + typeof res.result === "string" + ) { + return { + retCode: ResultCodes.error, + result: `("${serviceId}" "${fnName}") ${res.result}`, + }; + } + + return res; + }; + + peer.internals.regHandler.common(serviceId, fnName, wrapped); }); }); } diff --git a/packages/core/js-client/src/services/NodeUtils.ts b/packages/core/js-client/src/services/NodeUtils.ts index a5cdc015..cd60deac 100644 --- a/packages/core/js-client/src/services/NodeUtils.ts +++ b/packages/core/js-client/src/services/NodeUtils.ts @@ -16,8 +16,8 @@ import { readFile } from "fs/promises"; +import { ServiceFnArgs } from "../compilerSupport/types.js"; import { FluencePeer } from "../jsPeer/FluencePeer.js"; -import { ParticleContext } from "../jsServiceHost/interfaces.js"; import { getErrorMessage } from "../util/utils.js"; import { registerNodeUtils } from "./_aqua/node-utils.js"; @@ -31,8 +31,8 @@ export class NodeUtils { securityGuard_readFile: SecurityGuard; - async read_file(path: string, callParams: ParticleContext) { - if (!this.securityGuard_readFile(callParams)) { + async read_file({ args: [path], context }: ServiceFnArgs<[string]>) { + if (!this.securityGuard_readFile(context)) { return { success: false, error: ["Security guard validation failed"], diff --git a/packages/core/js-client/src/services/Sig.ts b/packages/core/js-client/src/services/Sig.ts index e021e71b..3aa85f5e 100644 --- a/packages/core/js-client/src/services/Sig.ts +++ b/packages/core/js-client/src/services/Sig.ts @@ -16,7 +16,7 @@ import { PeerIdB58 } from "@fluencelabs/interfaces"; -import { ParticleContext } from "../jsServiceHost/interfaces.js"; +import { ServiceFnArgs } from "../compilerSupport/types.js"; import { KeyPair } from "../keypair/index.js"; import { @@ -73,10 +73,10 @@ export class Sig { /** * Signs the data using key pair's private key. Required by aqua */ - async sign( - data: number[], - context: ParticleContext, - ): Promise { + async sign({ + args: [data], + context, + }: ServiceFnArgs<[number[]]>): Promise { if (!this.securityGuard(context)) { return { success: false, @@ -97,7 +97,9 @@ export class Sig { /** * Verifies the signature. Required by aqua */ - verify(signature: number[], data: number[]): Promise { + verify({ + args: [signature, data], + }: ServiceFnArgs<[number[], number[]]>): Promise { return this.keyPair.verify( Uint8Array.from(data), Uint8Array.from(signature), diff --git a/packages/core/js-client/src/services/SingleModuleSrv.ts b/packages/core/js-client/src/services/SingleModuleSrv.ts index cef5b25e..4260175e 100644 --- a/packages/core/js-client/src/services/SingleModuleSrv.ts +++ b/packages/core/js-client/src/services/SingleModuleSrv.ts @@ -18,8 +18,8 @@ import { Buffer } from "buffer"; import { v4 as uuidv4 } from "uuid"; +import { ServiceFnArgs } from "../compilerSupport/types.js"; import { FluencePeer } from "../jsPeer/FluencePeer.js"; -import { ParticleContext } from "../jsServiceHost/interfaces.js"; import { getErrorMessage } from "../util/utils.js"; import { @@ -28,7 +28,7 @@ import { } from "./securityGuard.js"; export const defaultGuard = (peer: FluencePeer) => { - return allowOnlyParticleOriginatedAt(peer.keyPair.getPeerId()); + return allowOnlyParticleOriginatedAt(peer.getPeerId()); }; // Service for registering marine modules in js-client's marine runtime @@ -42,8 +42,8 @@ export class Srv { securityGuard_create: SecurityGuard; - async create(wasm_b64_content: string, callParams: ParticleContext) { - if (!this.securityGuard_create(callParams)) { + async create({ args: [wasmContent], context }: ServiceFnArgs<[string]>) { + if (!this.securityGuard_create(context)) { return { success: false, error: ["Marine services could be registered on %init_peer_id% only"], @@ -53,7 +53,7 @@ export class Srv { try { const newServiceId = uuidv4(); - const buffer = Buffer.from(wasm_b64_content, "base64"); + const buffer = Buffer.from(wasmContent, "base64"); // TODO:: figure out why SharedArrayBuffer is not working here // const sab = new SharedArrayBuffer(buffer.length); // const tmp = new Uint8Array(sab); @@ -77,8 +77,8 @@ export class Srv { securityGuard_remove: SecurityGuard; - async remove(service_id: string, callParams: ParticleContext) { - if (!this.securityGuard_remove(callParams)) { + async remove({ args: [serviceId], context }: ServiceFnArgs<[string]>) { + if (!this.securityGuard_remove(context)) { return { success: false, error: ["Marine services could be remove on %init_peer_id% only"], @@ -86,15 +86,15 @@ export class Srv { }; } - if (!this.services.has(service_id)) { + if (!this.services.has(serviceId)) { return { success: false, - error: [`Service with id ${service_id} not found`], + error: [`Service with id ${serviceId} not found`], }; } - await this.peer.removeMarineService(service_id); - this.services.delete(service_id); + await this.peer.removeMarineService(serviceId); + this.services.delete(serviceId); return { success: true, diff --git a/packages/core/js-client/src/services/Tracing.ts b/packages/core/js-client/src/services/Tracing.ts index a6f84d53..2b372f64 100644 --- a/packages/core/js-client/src/services/Tracing.ts +++ b/packages/core/js-client/src/services/Tracing.ts @@ -14,18 +14,15 @@ * limitations under the License. */ -import { ParticleContext } from "../jsServiceHost/interfaces.js"; +import { ServiceFnArgs } from "../compilerSupport/types.js"; -import { TracingDef } from "./_aqua/tracing.js"; - -export class Tracing implements TracingDef { - tracingEvent( - arrowName: string, - event: string, - callParams: ParticleContext, - ): void { +export class Tracing { + tracingEvent({ + args: [arrowName, event], + context, + }: ServiceFnArgs<[string, string]>): void { // This console log is intentional // eslint-disable-next-line no-console - console.log("[%s] (%s) %s", callParams.particleId, arrowName, event); + console.log("[%s] (%s) %s", context.particleId, arrowName, event); } } diff --git a/packages/core/js-client/src/services/__test__/builtInHandler.spec.ts b/packages/core/js-client/src/services/__test__/builtInHandler.spec.ts index c8c4d948..ee931eed 100644 --- a/packages/core/js-client/src/services/__test__/builtInHandler.spec.ts +++ b/packages/core/js-client/src/services/__test__/builtInHandler.spec.ts @@ -18,16 +18,14 @@ import assert from "assert"; import { JSONArray } from "@fluencelabs/interfaces"; import { toUint8Array } from "js-base64"; -import { it, describe, expect, test } from "vitest"; +import { describe, expect, it, test } from "vitest"; -import { - CallServiceData, - ParticleContext, -} from "../../jsServiceHost/interfaces.js"; +import { CallServiceData } from "../../jsServiceHost/interfaces.js"; import { KeyPair } from "../../keypair/index.js"; +import { makeTestTetraplet } from "../../util/testUtils.js"; import { builtInServices } from "../builtins.js"; import { allowServiceFn } from "../securityGuard.js"; -import { Sig, defaultSigGuard } from "../Sig.js"; +import { defaultSigGuard, Sig } from "../Sig.js"; const a10b20 = `{ "a": 10, @@ -244,39 +242,15 @@ const testDataWrongSig = [ 159, 25, 109, 95, 160, 181, 65, 254, 238, 47, 156, 240, 151, 58, 14, ]; -const makeTestTetraplet = ( - initPeerId: string, - serviceId: string, - fnName: string, -): ParticleContext => { - return { - particleId: "", - timestamp: 0, - ttl: 0, - initPeerId: initPeerId, - signature: new Uint8Array([]), - tetraplets: [ - [ - { - peer_pk: initPeerId, - function_name: fnName, - service_id: serviceId, - json_path: "", - }, - ], - ], - }; -}; - describe("Sig service tests", () => { it("sig.sign should create the correct signature", async () => { const ctx = await context; const sig = new Sig(ctx.peerKeyPair); - const res = await sig.sign( - testData, - makeTestTetraplet(ctx.peerId, "any_service", "any_func"), - ); + const res = await sig.sign({ + args: [testData], + context: makeTestTetraplet(ctx.peerId, "any_service", "any_func"), + }); expect(res.success).toBe(true); expect(res.signature).toStrictEqual([testDataSig]); @@ -286,7 +260,10 @@ describe("Sig service tests", () => { const ctx = await context; const sig = new Sig(ctx.peerKeyPair); - const res = await sig.verify(testDataSig, testData); + const res = await sig.verify({ + args: [testDataSig, testData], + context: makeTestTetraplet(ctx.peerId, "any_service", "any_func"), + }); expect(res).toBe(true); }); @@ -295,7 +272,10 @@ describe("Sig service tests", () => { const ctx = await context; const sig = new Sig(ctx.peerKeyPair); - const res = await sig.verify(testDataWrongSig, testData); + const res = await sig.verify({ + args: [testDataWrongSig, testData], + context: makeTestTetraplet(ctx.peerId, "any_service", "any_func"), + }); expect(res).toBe(false); }); @@ -304,14 +284,18 @@ describe("Sig service tests", () => { const ctx = await context; const sig = new Sig(ctx.peerKeyPair); - const signature = await sig.sign( - testData, - makeTestTetraplet(ctx.peerId, "any_service", "any_func"), - ); + const signature = await sig.sign({ + args: [testData], + context: makeTestTetraplet(ctx.peerId, "any_service", "any_func"), + }); expect(signature.success).toBe(true); assert(signature.success); - const res = await sig.verify(signature.signature[0], testData); + + const res = await sig.verify({ + args: [signature.signature[0], testData], + context: makeTestTetraplet(ctx.peerId, "any_service", "any_func"), + }); expect(res).toBe(true); }); @@ -321,10 +305,10 @@ describe("Sig service tests", () => { const sig = new Sig(ctx.peerKeyPair); sig.securityGuard = defaultSigGuard(ctx.peerId); - const signature = await sig.sign( - testData, - makeTestTetraplet(ctx.peerId, "registry", "get_route_bytes"), - ); + const signature = await sig.sign({ + args: [testData], + context: makeTestTetraplet(ctx.peerId, "registry", "get_route_bytes"), + }); expect(signature).toBeDefined(); }); @@ -334,10 +318,10 @@ describe("Sig service tests", () => { const sig = new Sig(ctx.peerKeyPair); sig.securityGuard = defaultSigGuard(ctx.peerId); - const res = await sig.sign( - testData, - makeTestTetraplet(ctx.peerId, "other_service", "other_fn"), - ); + const res = await sig.sign({ + args: [testData], + context: makeTestTetraplet(ctx.peerId, "other_service", "other_fn"), + }); expect(res.success).toBe(false); expect(res.error).toStrictEqual(["Security guard validation failed"]); @@ -348,14 +332,14 @@ describe("Sig service tests", () => { const sig = new Sig(ctx.peerKeyPair); sig.securityGuard = defaultSigGuard(ctx.peerId); - const res = await sig.sign( - testData, - makeTestTetraplet( + const res = await sig.sign({ + args: [testData], + context: makeTestTetraplet( (await KeyPair.randomEd25519()).getPeerId(), "registry", "get_key_bytes", ), - ); + }); expect(res.success).toBe(false); expect(res.error).toStrictEqual(["Security guard validation failed"]); @@ -366,27 +350,27 @@ describe("Sig service tests", () => { const sig = new Sig(ctx.peerKeyPair); sig.securityGuard = allowServiceFn("test", "test"); - const successful1 = await sig.sign( - testData, - makeTestTetraplet(ctx.peerId, "test", "test"), - ); + const successful1 = await sig.sign({ + args: [testData], + context: makeTestTetraplet(ctx.peerId, "test", "test"), + }); - const unSuccessful1 = await sig.sign( - testData, - makeTestTetraplet(ctx.peerId, "wrong", "wrong"), - ); + const unSuccessful1 = await sig.sign({ + args: [testData], + context: makeTestTetraplet(ctx.peerId, "wrong", "wrong"), + }); sig.securityGuard = allowServiceFn("wrong", "wrong"); - const successful2 = await sig.sign( - testData, - makeTestTetraplet(ctx.peerId, "wrong", "wrong"), - ); + const successful2 = await sig.sign({ + args: [testData], + context: makeTestTetraplet(ctx.peerId, "wrong", "wrong"), + }); - const unSuccessful2 = await sig.sign( - testData, - makeTestTetraplet(ctx.peerId, "test", "test"), - ); + const unSuccessful2 = await sig.sign({ + args: [testData], + context: makeTestTetraplet(ctx.peerId, "test", "test"), + }); expect(successful1.success).toBe(true); expect(successful2.success).toBe(true); diff --git a/packages/core/js-client/src/services/__test__/sigService.spec.ts b/packages/core/js-client/src/services/__test__/sigService.spec.ts index ed0ade8b..ad80de14 100644 --- a/packages/core/js-client/src/services/__test__/sigService.spec.ts +++ b/packages/core/js-client/src/services/__test__/sigService.spec.ts @@ -21,7 +21,12 @@ import { it, describe, expect, beforeAll } from "vitest"; import { registerService } from "../../compilerSupport/registerService.js"; import { KeyPair } from "../../keypair/index.js"; -import { compileAqua, CompiledFnCall, withPeer } from "../../util/testUtils.js"; +import { + compileAqua, + CompiledFnCall, + makeTestTetraplet, + withPeer, +} from "../../util/testUtils.js"; import { allowServiceFn } from "../securityGuard.js"; import { Sig } from "../Sig.js"; @@ -71,12 +76,15 @@ describe("Sig service test suite", () => { expect(result).toHaveProperty("success", true); - const isSigCorrect = await customSig.verify( - // TODO: Use compiled ts wrappers - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - (result as { signature: [number[]] }).signature[0], - data, - ); + const isSigCorrect = await customSig.verify({ + args: [ + // TODO: Use compiled ts wrappers + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + (result as { signature: [number[]] }).signature[0], + data, + ], + context: makeTestTetraplet(peer.getPeerId(), "any_service", "any_func"), + }); expect(isSigCorrect).toBe(true); }); @@ -132,7 +140,7 @@ describe("Sig service test suite", () => { const callAsSigRes = await aqua["callSig"](peer, { sigId: "sig" }); const callAsPeerIdRes = await aqua["callSig"](peer, { - sigId: peer.keyPair.getPeerId(), + sigId: peer.getPeerId(), }); expect(callAsSigRes).toHaveProperty("success", false); @@ -152,20 +160,23 @@ describe("Sig service test suite", () => { }); const callAsPeerIdResAfterGuardChange = await aqua["callSig"](peer, { - sigId: peer.keyPair.getPeerId(), + sigId: peer.getPeerId(), }); expect(callAsSigResAfterGuardChange).toHaveProperty("success", true); expect(callAsPeerIdResAfterGuardChange).toHaveProperty("success", true); - const isValid = await sig.verify( - // TODO: Use compiled ts wrappers - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - (callAsSigResAfterGuardChange as { signature: [number[]] }) - .signature[0], - data, - ); + const isValid = await sig.verify({ + args: [ + // TODO: Use compiled ts wrappers + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + (callAsSigResAfterGuardChange as { signature: [number[]] }) + .signature[0], + data, + ], + context: makeTestTetraplet(peer.getPeerId(), "any_service", "any_func"), + }); expect(isValid).toBe(true); }); diff --git a/packages/core/js-client/src/services/_aqua/tracing.ts b/packages/core/js-client/src/services/_aqua/tracing.ts index 96ce332e..534528fc 100644 --- a/packages/core/js-client/src/services/_aqua/tracing.ts +++ b/packages/core/js-client/src/services/_aqua/tracing.ts @@ -20,19 +20,10 @@ import { registerService } from "../../compilerSupport/registerService.js"; import { FluencePeer } from "../../jsPeer/FluencePeer.js"; -import { ParticleContext } from "../../jsServiceHost/interfaces.js"; import { Tracing } from "../Tracing.js"; // Services -export interface TracingDef { - tracingEvent: ( - arrowName: string, - event: string, - callParams: ParticleContext, - ) => void | Promise; -} - export function registerTracing( peer: FluencePeer, serviceId: string, diff --git a/packages/core/js-client/src/services/builtins.ts b/packages/core/js-client/src/services/builtins.ts index 3dc6a781..01459fa0 100644 --- a/packages/core/js-client/src/services/builtins.ts +++ b/packages/core/js-client/src/services/builtins.ts @@ -311,12 +311,8 @@ export const builtInServices: Record< return success(args.length === 0 ? {} : args[0]); }), - concat: withSchema(z.array(z.array(z.unknown())))((args) => { - // Schema is used with unknown type to prevent useless runtime check - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - const arr = args as never[][]; - - return success(arr.flat()); + concat: withSchema(z.array(z.array(z.any())))((args) => { + return success(args.flat()); }), string_to_b58: withSchema(z.tuple([z.string()]))(([input]) => { diff --git a/packages/core/js-client/src/util/testUtils.ts b/packages/core/js-client/src/util/testUtils.ts index 9c0e0820..e2f983c4 100644 --- a/packages/core/js-client/src/util/testUtils.ts +++ b/packages/core/js-client/src/util/testUtils.ts @@ -16,7 +16,7 @@ import { promises as fs } from "fs"; -import { Path, Aqua } from "@fluencelabs/aqua-api/aqua-api.js"; +import { compileFromPath } from "@fluencelabs/aqua-api"; import { FunctionCallDef, JSONArray, @@ -33,7 +33,10 @@ import { callAquaFunction } from "../compilerSupport/callFunction.js"; import { ServiceImpl } from "../compilerSupport/types.js"; import { IConnection } from "../connection/interfaces.js"; import { DEFAULT_CONFIG, FluencePeer } from "../jsPeer/FluencePeer.js"; -import { CallServiceResultType } from "../jsServiceHost/interfaces.js"; +import { + CallServiceResultType, + ParticleContext, +} from "../jsServiceHost/interfaces.js"; import { JsServiceHost } from "../jsServiceHost/JsServiceHost.js"; import { WrapFnIntoServiceCall } from "../jsServiceHost/serviceUtils.js"; import { KeyPair } from "../keypair/index.js"; @@ -87,11 +90,9 @@ export type PassedArgs = { [key: string]: JSONValue | ArgCallbackFunction }; export const compileAqua = async (aquaFile: string): Promise => { await fs.access(aquaFile); - const compilationResult = await Aqua.compile( - new Path(aquaFile), - [], - undefined, - ); + const compilationResult = await compileFromPath({ + filePath: aquaFile, + }); if (compilationResult.errors.length > 0) { throw new Error( @@ -154,7 +155,7 @@ export class TestPeer extends FluencePeer { const marine = new MarineBackgroundRunner( { async getValue() { - // TODO: load worker with avm and marine, test that it works + // TODO: load worker in parallel with avm and marine, test that it works return getWorker("@fluencelabs/marine-worker", "/"); }, start() { @@ -237,7 +238,7 @@ export const withClient = async ( const marine = new MarineBackgroundRunner( { async getValue() { - // TODO: load worker with avm and marine, test that it works + // TODO: load worker in parallel with avm and marine, test that it works return getWorker("@fluencelabs/marine-worker", "/"); }, start() { @@ -292,3 +293,27 @@ export const withClient = async ( await client.disconnect(); } }; + +export const makeTestTetraplet = ( + initPeerId: string, + serviceId: string, + fnName: string, +): ParticleContext => { + return { + particleId: "", + timestamp: 0, + ttl: 0, + initPeerId: initPeerId, + signature: new Uint8Array([]), + tetraplets: [ + [ + { + peer_pk: initPeerId, + function_name: fnName, + service_id: serviceId, + json_path: "", + }, + ], + ], + }; +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3aaa5f05..ae4c8a2d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -61,8 +61,8 @@ importers: version: 1.5.1 devDependencies: '@fluencelabs/aqua-api': - specifier: 0.12.4-main-cee4448-2196-1 - version: 0.12.4-main-cee4448-2196-1 + specifier: 0.13.0 + version: 0.13.0 '@fluencelabs/aqua-lib': specifier: 0.6.0 version: 0.6.0 @@ -73,8 +73,8 @@ importers: specifier: workspace:* version: link:../../core/js-client '@fluencelabs/registry': - specifier: 0.8.8-1 - version: 0.8.8-1 + specifier: 0.9.0 + version: 0.9.0 '@fluencelabs/trust-graph': specifier: 3.1.2 version: 3.1.2 @@ -167,8 +167,8 @@ importers: version: 5.0.5 devDependencies: '@fluencelabs/aqua-api': - specifier: 0.12.4-main-cee4448-2196-1 - version: 0.12.4-main-cee4448-2196-1 + specifier: 0.13.0 + version: 0.13.0 '@fluencelabs/aqua-lib': specifier: 0.7.3 version: 0.7.3 @@ -179,8 +179,8 @@ importers: specifier: workspace:^ version: link:../js-client '@fluencelabs/registry': - specifier: 0.8.7 - version: 0.8.7 + specifier: 0.9.0 + version: 0.9.0 '@fluencelabs/spell': specifier: 0.5.20 version: 0.5.20 @@ -288,8 +288,8 @@ importers: version: 3.22.4 devDependencies: '@fluencelabs/aqua-api': - specifier: 0.9.3 - version: 0.9.3 + specifier: 0.13.0 + version: 0.13.0 '@rollup/plugin-inject': specifier: 5.0.3 version: 5.0.3 @@ -3677,12 +3677,8 @@ packages: resolution: {integrity: sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - /@fluencelabs/aqua-api@0.12.4-main-cee4448-2196-1: - resolution: {registry: https://registry.npmjs.org/, tarball: https://npm.fluence.dev/@fluencelabs/aqua-api/-/aqua-api-0.12.4-main-cee4448-2196-1.tgz} - dev: true - - /@fluencelabs/aqua-api@0.9.3: - resolution: {integrity: sha512-ieM2e7qMXgm9BPSSd2fxVbqLlYkR/a/aVTAQXO8gdx2rKKFqnTgFX4gpSOTxrrCMshi8OnXfd2OZi1hsJHTnKA==} + /@fluencelabs/aqua-api@0.13.0: + resolution: {integrity: sha512-AY6pXoK6xsFfgQHxhv6Lj+uPZKqiL3qPc2EVIZcl1RFX0Q+S0o1SmFlAVX2PrdA+31gbk9aAOtTXt+40GadooA==} dev: true /@fluencelabs/aqua-lib@0.5.2: @@ -3712,15 +3708,8 @@ packages: default-import: 1.1.5 dev: false - /@fluencelabs/registry@0.8.7: - resolution: {integrity: sha512-43bmb1v4p5ORvaiLBrUAl+hRPo3luxxBVrJgqTvipJa2OEg2wCRA/Wo9s4M7Lchnv3NoYLOyNTzNyFopQRKILA==} - dependencies: - '@fluencelabs/aqua-lib': 0.7.0 - '@fluencelabs/trust-graph': 0.4.1 - dev: true - - /@fluencelabs/registry@0.8.8-1: - resolution: {integrity: sha512-zdkn/JiMXAozn43/nrF+Cvq6/heSIUS1e3tOb8AFRMoI2Czd3o8p6fEwdJwa7QE8IkD1NOln/C/bWUVwfEDi9w==} + /@fluencelabs/registry@0.9.0: + resolution: {integrity: sha512-PGyoH6AtBKR9ieQgt2ZM6Ehk68PIxwtqLhr4hpphiU36Yl+Qo2aRVgQMSK944dtV31nZQC8hTssU+NqVZOEs/w==} dependencies: '@fluencelabs/aqua-lib': 0.7.0 '@fluencelabs/trust-graph': 0.4.1