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
This commit is contained in:
Akim 2023-11-23 04:18:10 +07:00 committed by GitHub
parent f4a550dd22
commit 98462bfdf6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 2413 additions and 585 deletions

View File

@ -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"
}

View File

@ -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<TestResult> => {
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<TestResult> => {
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();

View File

@ -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",

View File

@ -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<string[]>;
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<number>;
clear_state: (callParams: ParticleContext$$) => void | Promise<void>;
test_logs: (callParams: ParticleContext$$) => void | Promise<void>;
multiply: (num: number, callParams: ParticleContext$$) => number | Promise<number>;
add: (num: number, callParams: ParticleContext$$) => number | Promise<number>;
state: (callParams: ParticleContext$$) => number | Promise<number>;
subtract: (num: number, callParams: ParticleContext$$) => number | Promise<number>;
}
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<string>;
}
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<ResourceTestResultType>;
export type HelloTestParams = [config?: {ttl?: number}] | [peer: IFluenceClient$$, config?: {ttl?: number}];
export type HelloTestResult = Promise<string>;
export type Demo_calculationParams = [service_id: string, config?: {ttl?: number}] | [peer: IFluenceClient$$, service_id: string, config?: {ttl?: number}];
export type Demo_calculationResult = Promise<number>;
export type MarineTestParams = [wasm64: string, config?: {ttl?: number}] | [peer: IFluenceClient$$, wasm64: string, config?: {ttl?: number}];
export type MarineTestResult = Promise<number>;

View File

@ -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
);
}

View File

@ -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<string[]>;
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<number>;
clear_state: (callParams: ParticleContext$$) => void | Promise<void>;
test_logs: (callParams: ParticleContext$$) => void | Promise<void>;
multiply: (num: number, callParams: ParticleContext$$) => number | Promise<number>;
add: (num: number, callParams: ParticleContext$$) => number | Promise<number>;
state: (callParams: ParticleContext$$) => number | Promise<number>;
subtract: (num: number, callParams: ParticleContext$$) => number | Promise<number>;
}
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<string>;
}
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<ResourceTestResultType>;
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<string>;
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<number>;
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<number>;
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
);
}

View File

@ -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,

View File

@ -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<string, ServiceDef>,
@ -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<never, JSONValue> = service;
return JSON.stringify(recursiveRenameLaquaProps(record), null, 4);
}

View File

@ -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<T extends OutputType>(
export default async function aquaToJs(
res: CompilationResult,
outputType: T,
): Promise<LanguageOutput[T] | NothingToGenerate> {
outputType: "js",
): Promise<JsOutput | NothingToGenerate>;
export default async function aquaToJs(
res: CompilationResult,
outputType: "ts",
): Promise<TsOutput | NothingToGenerate>;
export default async function aquaToJs(
res: CompilationResult,
outputType: "js" | "ts",
): Promise<JsOutput | TsOutput | NothingToGenerate> {
if (
Object.keys(res.services).length === 0 &&
Object.keys(res.functions).length === 0
@ -48,13 +52,14 @@ export default async function aquaToJs<T extends OutputType>(
const packageJson = await getPackageJsonContent();
return outputType === "js"
? {
if (outputType === "js") {
return {
sources: generateSources(res, "js", packageJson),
types: generateTypes(res, packageJson),
};
}
: // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
({
return {
sources: generateSources(res, "ts", packageJson),
} as LanguageOutput[T]);
};
}

View File

@ -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()

View File

@ -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",

View File

@ -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

View File

@ -90,10 +90,6 @@ export class ClientPeer extends FluencePeer implements IFluenceClient {
);
}
getPeerId(): string {
return this.keyPair.getPeerId();
}
getPeerSecretKey(): Uint8Array {
return this.keyPair.toEd25519PrivateKey();
}

View File

@ -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<typeof relaySchema>;
/**
@ -51,7 +51,13 @@ export type RelayOptions = z.infer<typeof relaySchema>;
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)]),
});

View File

@ -49,7 +49,6 @@ export type CallAquaFunctionArgs = {
config: CallAquaFunctionConfig | undefined;
peer: FluencePeer;
args: { [key: string]: JSONValue | ArgCallbackFunction };
fireAndForget?: boolean | undefined;
};
export type CallAquaFunctionConfig = {

View File

@ -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<LabeledProductType<SimpleTypes> | 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;

View File

@ -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,

View File

@ -21,6 +21,16 @@ import { ParticleContext } from "../jsServiceHost/interfaces.js";
export type MaybePromise<T> = T | Promise<T>;
export type ServiceImpl = Record<
string,
(args: {
args: JSONArray;
context: ParticleContext;
}) => MaybePromise<JSONValue>
>;
export type UserServiceImpl = Record<
string,
(...args: [...JSONArray, ParticleContext]) => MaybePromise<JSONValue>
>;
export type ServiceFnArgs<T> = { args: T; context: ParticleContext };

View File

@ -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() {

View File

@ -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);
}
}

View File

@ -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() {

View File

@ -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<void> {
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<ParticleQueueItem>();
private _timeouts: Array<NodeJS.Timeout> = [];
private timeouts: Record<string, NodeJS.Timeout> = {};
private _particleSourceSubscription?: Unsubscribable;
private _incomingParticlePromise?: Promise<void>;
@ -334,67 +339,142 @@ export abstract class FluencePeer {
registerTracing(this, "tracingSrv", this._classServices.tracing);
}
// TODO: too long, refactor
private _startParticleProcessing() {
this._particleSourceSubscription = this.connection.particleSource.subscribe(
{
next: (p) => {
private async sendParticleToRelay(
item: ParticleQueueItem & { result: InterpreterResult },
) {
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(),
);
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);
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({
particle: p,
callResults: [],
onSuccess: () => {},
onError: () => {},
...item,
particle: newParticle,
callResults: [[key, serviceResult]],
});
},
},
}),
);
}
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,
});
private async execCallRequest(
req: CallServiceData,
): Promise<CallServiceResult> {
const particleId = req.particleContext.particleId;
log_particle.trace(
"id %s. script: %s",
item.particle.id,
item.particle.script,
"id %s. executing call service handler %j",
particleId,
req,
);
log_particle.trace(
"id %s. call results: %j",
item.particle.id,
item.callResults,
if (await this.marineHost.hasService(req.serviceId)) {
// TODO build correct CallParameters instead of default ones
const result = await this.marineHost.callService(
req.serviceId,
req.fnName,
req.args,
);
}),
filterExpiredParticles(this._expireParticle.bind(this)),
groupBy((item) => {
return fromUint8Array(item.particle.signature);
}),
mergeMap((group$) => {
return {
retCode: ResultCodes.success,
result: result,
};
}
let res = await this.jsServiceHost.callService(req);
if (res === null) {
res = {
retCode: ResultCodes.error,
result: `No service found for service call: serviceId='${
req.serviceId
}', fnName='${req.fnName}' args='${jsonify(req.args)}'`,
};
}
log_particle.trace(
"id %s. executed call service handler, req: %j, res: %j ",
particleId,
req,
res,
);
return res;
}
private mapParticleGroup(
group$: GroupedObservable<string, ParticleQueueItem>,
) {
let prevData: Uint8Array = Buffer.from([]);
let firstRun = true;
return group$.pipe(
concatMap(async (item) => {
if (firstRun) {
const timeout = setTimeout(() => {
this._expireParticle(item);
if (!(group$.key in this.timeouts)) {
this.timeouts[group$.key] = 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!
@ -432,21 +512,14 @@ export abstract class FluencePeer {
let avmCallResult: InterpreterResult | Error;
try {
const res = await this.marineHost.callService(
"avm",
"invoke",
args,
);
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
) {
if (!(avmCallResult instanceof Error) && avmCallResult.retCode === 0) {
const newData = Buffer.from(avmCallResult.data);
prevData = newData;
}
@ -456,20 +529,16 @@ export abstract class FluencePeer {
result: avmCallResult,
};
}),
filter((item): item is NonNullable<typeof item> => {
return item !== null;
}),
filterExpiredParticles<
ParticleQueueItem & {
result: Error | InterpreterResult;
}
>(this._expireParticle.bind(this)),
mergeMap(async (item) => {
>(this.expireParticle.bind(this)),
filter(() => {
// If peer was stopped, do not proceed further
if (!this.isInitialized) {
return;
}
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(
@ -521,113 +590,74 @@ export abstract class FluencePeer {
// 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})`,
),
);
// 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) {
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]],
});
});
}
// 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) {
private expireParticle(item: ParticleQueueItem) {
const particleId = item.particle.id;
log_particle.debug(
@ -649,55 +679,9 @@ export abstract class FluencePeer {
return new TextDecoder().decode(data.buffer);
}
private async _execSingleCallRequest(
req: CallServiceData,
): Promise<CallServiceResult> {
const particleId = req.particleContext.particleId;
log_particle.trace(
"id %s. executing call service handler %j",
particleId,
req,
);
if (await this.marineHost.hasService(req.serviceId)) {
// TODO build correct CallParameters instead of default ones
const result = await this.marineHost.callService(
req.serviceId,
req.fnName,
req.args,
);
return {
retCode: ResultCodes.success,
result: result,
};
}
let res = await this.jsServiceHost.callService(req);
if (res === null) {
res = {
retCode: ResultCodes.error,
result: `No service found for service call: serviceId='${
req.serviceId
}', fnName='${req.fnName}' args='${jsonify(req.args)}'`,
};
}
log_particle.trace(
"id %s. executed call service handler, req: %j, res: %j ",
particleId,
req,
res,
);
return res;
}
private _stopParticleProcessing() {
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);
});
}

View File

@ -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 {}

View File

@ -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);
});
});
}

View File

@ -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"],

View File

@ -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<SignReturnType> {
async sign({
args: [data],
context,
}: ServiceFnArgs<[number[]]>): Promise<SignReturnType> {
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<boolean> {
verify({
args: [signature, data],
}: ServiceFnArgs<[number[], number[]]>): Promise<boolean> {
return this.keyPair.verify(
Uint8Array.from(data),
Uint8Array.from(signature),

View File

@ -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,

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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(
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(
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);
});

View File

@ -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<void>;
}
export function registerTracing(
peer: FluencePeer,
serviceId: string,

View File

@ -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]) => {

View File

@ -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<CompiledFile> => {
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: "",
},
],
],
};
};

39
pnpm-lock.yaml generated
View File

@ -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