mirror of
https://github.com/fluencelabs/fluence-js.git
synced 2025-05-16 11:21:19 +00:00
Remove schema from js-client
This commit is contained in:
parent
2f316cc8fb
commit
426f154377
@ -14,60 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { SecurityTetraplet } from "@fluencelabs/avm";
|
||||
|
||||
import { InterfaceToType, MaybePromise } from "./utils.js";
|
||||
|
||||
/**
|
||||
* Peer ID's id as a base58 string (multihash/CIDv0).
|
||||
*/
|
||||
export type PeerIdB58 = string;
|
||||
|
||||
/**
|
||||
* Additional information about a service call
|
||||
* @typeparam ArgName
|
||||
*/
|
||||
export type CallParams<ArgName extends string | null> = {
|
||||
/**
|
||||
* The identifier of particle which triggered the call
|
||||
*/
|
||||
particleId: string;
|
||||
|
||||
/**
|
||||
* The peer id which created the particle
|
||||
*/
|
||||
initPeerId: PeerIdB58;
|
||||
|
||||
/**
|
||||
* Particle's timestamp when it was created
|
||||
*/
|
||||
timestamp: number;
|
||||
|
||||
/**
|
||||
* Time to live in milliseconds. The time after the particle should be expired
|
||||
*/
|
||||
ttl: number;
|
||||
|
||||
/**
|
||||
* Particle's signature
|
||||
*/
|
||||
signature?: string;
|
||||
|
||||
/**
|
||||
* Security tetraplets
|
||||
*/
|
||||
tetraplets: ArgName extends string
|
||||
? Record<ArgName, InterfaceToType<SecurityTetraplet>[]>
|
||||
: Record<string, never>;
|
||||
};
|
||||
|
||||
export type ServiceImpl = Record<
|
||||
string,
|
||||
(
|
||||
...args: [...JSONArray, CallParams<string>]
|
||||
) => MaybePromise<JSONValue | undefined>
|
||||
>;
|
||||
|
||||
export type JSONValue =
|
||||
| string
|
||||
| number
|
||||
|
@ -17,8 +17,8 @@
|
||||
import { JSONValue } from "@fluencelabs/interfaces";
|
||||
import { it, describe, expect } from "vitest";
|
||||
|
||||
import { SendError } from "../../jsPeer/errors.js";
|
||||
import { CallServiceData } from "../../jsServiceHost/interfaces.js";
|
||||
import { doNothing } from "../../jsServiceHost/serviceUtils.js";
|
||||
import { handleTimeout } from "../../particle/Particle.js";
|
||||
import { registerHandlersHelper, withClient } from "../../util/testUtils.js";
|
||||
import { checkConnection } from "../checkConnection.js";
|
||||
@ -71,7 +71,11 @@ describe("FluenceClient usage test suite", () => {
|
||||
},
|
||||
});
|
||||
|
||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||
peer.internals.initiateParticle(
|
||||
particle,
|
||||
() => {},
|
||||
handleTimeout(reject),
|
||||
);
|
||||
});
|
||||
|
||||
expect(result).toBe("hello world!");
|
||||
@ -124,7 +128,11 @@ describe("FluenceClient usage test suite", () => {
|
||||
throw particle;
|
||||
}
|
||||
|
||||
peer1.internals.initiateParticle(particle, doNothing);
|
||||
peer1.internals.initiateParticle(
|
||||
particle,
|
||||
() => {},
|
||||
() => {},
|
||||
);
|
||||
|
||||
expect(await res).toEqual("test");
|
||||
});
|
||||
@ -172,13 +180,13 @@ describe("FluenceClient usage test suite", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("With connection options: defaultTTL", async () => {
|
||||
it.only("With connection options: defaultTTL", async () => {
|
||||
await withClient(RELAY, { defaultTtlMs: 1 }, async (peer) => {
|
||||
const isConnected = await checkConnection(peer);
|
||||
|
||||
expect(isConnected).toBeFalsy();
|
||||
});
|
||||
});
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
it.skip("Should throw correct error when the client tries to send a particle not to the relay", async () => {
|
||||
@ -206,11 +214,15 @@ describe("FluenceClient usage test suite", () => {
|
||||
},
|
||||
});
|
||||
|
||||
peer.internals.initiateParticle(particle, (stage) => {
|
||||
if (stage.stage === "sendingError") {
|
||||
reject(stage.errorMessage);
|
||||
}
|
||||
});
|
||||
peer.internals.initiateParticle(
|
||||
particle,
|
||||
() => {},
|
||||
(error: Error) => {
|
||||
if (error instanceof SendError) {
|
||||
reject(error.message);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
await promise;
|
||||
|
@ -110,6 +110,7 @@ export const checkConnection = async (
|
||||
|
||||
peer.internals.initiateParticle(
|
||||
particle,
|
||||
() => {},
|
||||
handleTimeout(() => {
|
||||
reject("particle timed out");
|
||||
}),
|
||||
|
@ -16,13 +16,7 @@
|
||||
|
||||
import assert from "assert";
|
||||
|
||||
import {
|
||||
FnConfig,
|
||||
FunctionCallDef,
|
||||
getArgumentTypes,
|
||||
isReturnTypeVoid,
|
||||
PassedArgs,
|
||||
} from "@fluencelabs/interfaces";
|
||||
import { FnConfig, JSONValue } from "@fluencelabs/interfaces";
|
||||
|
||||
import { FluencePeer } from "../jsPeer/FluencePeer.js";
|
||||
import { logger } from "../util/logger.js";
|
||||
@ -36,6 +30,7 @@ import {
|
||||
ServiceDescription,
|
||||
userHandlerService,
|
||||
} from "./services.js";
|
||||
import { ServiceImpl } from "./types.js";
|
||||
|
||||
const log = logger("aqua");
|
||||
|
||||
@ -52,43 +47,35 @@ const log = logger("aqua");
|
||||
*/
|
||||
|
||||
type CallAquaFunctionArgs = {
|
||||
def: FunctionCallDef;
|
||||
script: string;
|
||||
config: FnConfig;
|
||||
peer: FluencePeer;
|
||||
args: PassedArgs;
|
||||
args: { [key: string]: JSONValue | ServiceImpl[string] };
|
||||
};
|
||||
|
||||
export const callAquaFunction = async ({
|
||||
def,
|
||||
script,
|
||||
config,
|
||||
peer,
|
||||
args,
|
||||
}: CallAquaFunctionArgs) => {
|
||||
// TODO: this function should be rewritten. We can remove asserts if we wont check definition there
|
||||
log.trace("calling aqua function %j", { def, script, config, args });
|
||||
const argumentTypes = getArgumentTypes(def);
|
||||
log.trace("calling aqua function %j", { script, config, args });
|
||||
|
||||
const particle = await peer.internals.createNewParticle(script, config.ttl);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
for (const [name, argVal] of Object.entries(args)) {
|
||||
const type = argumentTypes[name];
|
||||
let service: ServiceDescription;
|
||||
|
||||
if (type.tag === "arrow") {
|
||||
if (typeof argVal === "function") {
|
||||
// TODO: Add validation here
|
||||
assert(
|
||||
typeof argVal === "function",
|
||||
"Should not be possible, bad types",
|
||||
);
|
||||
|
||||
service = userHandlerService(
|
||||
def.names.callbackSrv,
|
||||
[name, type],
|
||||
argVal,
|
||||
);
|
||||
service = userHandlerService("callbackSrv", name, argVal);
|
||||
} else {
|
||||
// TODO: Add validation here
|
||||
assert(
|
||||
@ -96,50 +83,31 @@ export const callAquaFunction = async ({
|
||||
"Should not be possible, bad types",
|
||||
);
|
||||
|
||||
service = injectValueService(def.names.getDataSrv, name, type, argVal);
|
||||
console.log("inject service", name, argVal);
|
||||
service = injectValueService("getDataSrv", name, argVal);
|
||||
}
|
||||
|
||||
registerParticleScopeService(peer, particle, service);
|
||||
}
|
||||
|
||||
registerParticleScopeService(peer, particle, responseService(def, resolve));
|
||||
registerParticleScopeService(peer, particle, responseService(resolve));
|
||||
|
||||
registerParticleScopeService(peer, particle, injectRelayService(def, peer));
|
||||
registerParticleScopeService(peer, particle, injectRelayService(peer));
|
||||
|
||||
registerParticleScopeService(
|
||||
peer,
|
||||
particle,
|
||||
errorHandlingService(def, reject),
|
||||
);
|
||||
registerParticleScopeService(peer, particle, errorHandlingService(reject));
|
||||
// If function is void, then it's completed when one of the two conditions is met:
|
||||
// 1. The particle is sent to the network (state 'sent')
|
||||
// 2. All CallRequests are executed, e.g., all variable loading and local function calls are completed (state 'localWorkDone')
|
||||
|
||||
peer.internals.initiateParticle(particle, (stage) => {
|
||||
// If function is void, then it's completed when one of the two conditions is met:
|
||||
// 1. The particle is sent to the network (state 'sent')
|
||||
// 2. All CallRequests are executed, e.g., all variable loading and local function calls are completed (state 'localWorkDone')
|
||||
if (
|
||||
isReturnTypeVoid(def) &&
|
||||
(stage.stage === "sent" || stage.stage === "localWorkDone")
|
||||
) {
|
||||
resolve(undefined);
|
||||
}
|
||||
// TODO: make test
|
||||
// if (
|
||||
// isReturnTypeVoid(def) &&
|
||||
// (stage.stage === "sent" || stage.stage === "localWorkDone")
|
||||
// ) {
|
||||
// resolve(undefined);
|
||||
// }
|
||||
// },
|
||||
|
||||
if (stage.stage === "sendingError") {
|
||||
reject(
|
||||
`Could not send particle for ${def.functionName}: not connected (particle id: ${particle.id})`,
|
||||
);
|
||||
}
|
||||
|
||||
if (stage.stage === "expired") {
|
||||
reject(
|
||||
`Particle expired after ttl of ${particle.ttl}ms for function ${def.functionName} (particle id: ${particle.id})`,
|
||||
);
|
||||
}
|
||||
|
||||
if (stage.stage === "interpreterError") {
|
||||
reject(
|
||||
`Script interpretation failed for ${def.functionName}: ${stage.errorMessage} (particle id: ${particle.id})`,
|
||||
);
|
||||
}
|
||||
});
|
||||
peer.internals.initiateParticle(particle, resolve, reject);
|
||||
});
|
||||
};
|
||||
|
@ -14,66 +14,64 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { ServiceDef, ServiceImpl } from "@fluencelabs/interfaces";
|
||||
|
||||
import { FluencePeer } from "../jsPeer/FluencePeer.js";
|
||||
import { logger } from "../util/logger.js";
|
||||
|
||||
import { registerGlobalService, userHandlerService } from "./services.js";
|
||||
import { ServiceImpl } from "./types.js";
|
||||
|
||||
const log = logger("aqua");
|
||||
|
||||
interface RegisterServiceArgs {
|
||||
peer: FluencePeer;
|
||||
def: ServiceDef;
|
||||
serviceId: string | undefined;
|
||||
service: ServiceImpl;
|
||||
}
|
||||
|
||||
const findAllPossibleServiceMethods = (service: ServiceImpl): Set<string> => {
|
||||
let prototype: Record<string, unknown> = service;
|
||||
const serviceMethods = new Set<string>();
|
||||
|
||||
do {
|
||||
Object.getOwnPropertyNames(prototype)
|
||||
.filter((prop) => {
|
||||
return typeof prototype[prop] === "function" && prop !== "constructor";
|
||||
})
|
||||
.forEach((prop) => {
|
||||
return serviceMethods.add(prop);
|
||||
});
|
||||
|
||||
// Satisfying typescript here
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
prototype = Object.getPrototypeOf(prototype) as Record<string, unknown>;
|
||||
} while (prototype.constructor !== Object);
|
||||
|
||||
return serviceMethods;
|
||||
};
|
||||
|
||||
export const registerService = ({
|
||||
peer,
|
||||
def,
|
||||
serviceId = def.defaultServiceId,
|
||||
serviceId,
|
||||
service,
|
||||
}: RegisterServiceArgs) => {
|
||||
// TODO: Need to refactor this. We can compute function types from service implementation, making func more type safe
|
||||
log.trace("registering aqua service %o", { def, serviceId, service });
|
||||
|
||||
// Checking for missing keys
|
||||
const requiredKeys =
|
||||
def.functions.tag === "nil" ? [] : Object.keys(def.functions.fields);
|
||||
|
||||
const incorrectServiceDefinitions = requiredKeys.filter((f) => {
|
||||
return !(f in service);
|
||||
});
|
||||
log.trace("registering aqua service %o", { serviceId, service });
|
||||
|
||||
if (serviceId == null) {
|
||||
throw new Error("Service ID must be specified");
|
||||
}
|
||||
|
||||
if (incorrectServiceDefinitions.length > 0) {
|
||||
throw new Error(
|
||||
`Error registering service ${serviceId}: missing functions: ` +
|
||||
incorrectServiceDefinitions
|
||||
.map((d) => {
|
||||
return "'" + d + "'";
|
||||
})
|
||||
.join(", "),
|
||||
);
|
||||
}
|
||||
const serviceMethods = findAllPossibleServiceMethods(service);
|
||||
|
||||
const singleFunctions =
|
||||
def.functions.tag === "nil" ? [] : Object.entries(def.functions.fields);
|
||||
|
||||
for (const singleFunction of singleFunctions) {
|
||||
const [name] = singleFunction;
|
||||
// The function has type of (arg1, arg2, arg3, ... , callParams) => CallServiceResultType | void
|
||||
for (const method of serviceMethods) {
|
||||
// The function has type of (arg1, arg2, arg3, ... , ParticleContext) => CallServiceResultType | void
|
||||
// Account for the fact that user service might be defined as a class - .bind(...)
|
||||
const userDefinedHandler = service[name].bind(service);
|
||||
const handler = service[method];
|
||||
const userDefinedHandler = handler.bind(service);
|
||||
|
||||
const serviceDescription = userHandlerService(
|
||||
serviceId,
|
||||
singleFunction,
|
||||
method,
|
||||
userDefinedHandler,
|
||||
);
|
||||
|
||||
|
@ -14,32 +14,18 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { SecurityTetraplet } from "@fluencelabs/avm";
|
||||
import {
|
||||
CallParams,
|
||||
ArrowWithoutCallbacks,
|
||||
FunctionCallDef,
|
||||
NonArrowType,
|
||||
ServiceImpl,
|
||||
JSONValue,
|
||||
} from "@fluencelabs/interfaces";
|
||||
import { fromUint8Array } from "js-base64";
|
||||
import { match } from "ts-pattern";
|
||||
import { JSONValue } from "@fluencelabs/interfaces";
|
||||
|
||||
import { FluencePeer } from "../jsPeer/FluencePeer.js";
|
||||
import {
|
||||
CallServiceData,
|
||||
GenericCallServiceHandler,
|
||||
ParticleContext,
|
||||
ResultCodes,
|
||||
} from "../jsServiceHost/interfaces.js";
|
||||
import { Particle } from "../particle/Particle.js";
|
||||
|
||||
import {
|
||||
aquaArgs2Ts,
|
||||
responseServiceValue2ts,
|
||||
returnType2Aqua,
|
||||
ts2aqua,
|
||||
} from "./conversions.js";
|
||||
import { ServiceImpl } from "./types.js";
|
||||
|
||||
export interface ServiceDescription {
|
||||
serviceId: string;
|
||||
@ -50,10 +36,10 @@ export interface ServiceDescription {
|
||||
/**
|
||||
* Creates a service which injects relay's peer id into aqua space
|
||||
*/
|
||||
export const injectRelayService = (def: FunctionCallDef, peer: FluencePeer) => {
|
||||
export const injectRelayService = (peer: FluencePeer) => {
|
||||
return {
|
||||
serviceId: def.names.getDataSrv,
|
||||
fnName: def.names.relay,
|
||||
serviceId: "getDataSrv",
|
||||
fnName: "-relay-",
|
||||
handler: () => {
|
||||
return {
|
||||
retCode: ResultCodes.success,
|
||||
@ -69,7 +55,6 @@ export const injectRelayService = (def: FunctionCallDef, peer: FluencePeer) => {
|
||||
export const injectValueService = (
|
||||
serviceId: string,
|
||||
fnName: string,
|
||||
valueType: NonArrowType,
|
||||
value: JSONValue,
|
||||
) => {
|
||||
return {
|
||||
@ -78,7 +63,7 @@ export const injectValueService = (
|
||||
handler: () => {
|
||||
return {
|
||||
retCode: ResultCodes.success,
|
||||
result: ts2aqua(value, valueType),
|
||||
result: value,
|
||||
};
|
||||
},
|
||||
};
|
||||
@ -87,15 +72,17 @@ export const injectValueService = (
|
||||
/**
|
||||
* Creates a service which is used to return value from aqua function into typescript space
|
||||
*/
|
||||
export const responseService = (
|
||||
def: FunctionCallDef,
|
||||
resolveCallback: (val: JSONValue) => void,
|
||||
) => {
|
||||
export const responseService = (resolveCallback: (val: JSONValue) => void) => {
|
||||
return {
|
||||
serviceId: def.names.responseSrv,
|
||||
fnName: def.names.responseFnName,
|
||||
serviceId: "callbackSrv",
|
||||
fnName: "response",
|
||||
handler: (req: CallServiceData) => {
|
||||
const userFunctionReturn = responseServiceValue2ts(req, def.arrow);
|
||||
const userFunctionReturn =
|
||||
req.args.length === 0
|
||||
? null
|
||||
: req.args.length === 1
|
||||
? req.args[0]
|
||||
: req.args;
|
||||
|
||||
setTimeout(() => {
|
||||
resolveCallback(userFunctionReturn);
|
||||
@ -113,12 +100,11 @@ export const responseService = (
|
||||
* Creates a service which is used to return errors from aqua function into typescript space
|
||||
*/
|
||||
export const errorHandlingService = (
|
||||
def: FunctionCallDef,
|
||||
rejectCallback: (err: JSONValue) => void,
|
||||
) => {
|
||||
return {
|
||||
serviceId: def.names.errorHandlingSrv,
|
||||
fnName: def.names.errorFnName,
|
||||
serviceId: "errorHandlingSrv",
|
||||
fnName: "error",
|
||||
handler: (req: CallServiceData) => {
|
||||
const [err] = req.args;
|
||||
|
||||
@ -139,21 +125,21 @@ export const errorHandlingService = (
|
||||
*/
|
||||
export const userHandlerService = (
|
||||
serviceId: string,
|
||||
arrowType: [string, ArrowWithoutCallbacks],
|
||||
fnName: string,
|
||||
userHandler: ServiceImpl[string],
|
||||
) => {
|
||||
const [fnName, type] = arrowType;
|
||||
return {
|
||||
serviceId,
|
||||
fnName,
|
||||
handler: async (req: CallServiceData) => {
|
||||
const args: [...JSONValue[], CallParams<string>] = [
|
||||
...aquaArgs2Ts(req, type),
|
||||
extractCallParams(req, type),
|
||||
const args: [...JSONValue[], ParticleContext] = [
|
||||
...req.args,
|
||||
req.particleContext,
|
||||
];
|
||||
|
||||
const rawResult = await userHandler.bind(null)(...args);
|
||||
const result = returnType2Aqua(rawResult, type);
|
||||
const result = await userHandler.bind(null)(...args);
|
||||
|
||||
console.log(result, "userHandlerService result", serviceId, fnName);
|
||||
|
||||
return {
|
||||
retCode: ResultCodes.success,
|
||||
@ -163,46 +149,6 @@ export const userHandlerService = (
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Extracts call params from from call service data according to aqua type definition
|
||||
*/
|
||||
const extractCallParams = (
|
||||
req: CallServiceData,
|
||||
arrow: ArrowWithoutCallbacks,
|
||||
): CallParams<string> => {
|
||||
const names: (string | undefined)[] = match(arrow.domain)
|
||||
.with({ tag: "nil" }, () => {
|
||||
return [];
|
||||
})
|
||||
.with({ tag: "unlabeledProduct" }, (x) => {
|
||||
return x.items.map((_, index) => {
|
||||
return "arg" + index;
|
||||
});
|
||||
})
|
||||
.with({ tag: "labeledProduct" }, (x) => {
|
||||
return Object.keys(x.fields);
|
||||
})
|
||||
.exhaustive();
|
||||
|
||||
const tetraplets: Record<string, SecurityTetraplet[]> = {};
|
||||
|
||||
for (let i = 0; i < req.args.length; i++) {
|
||||
const name = names[i];
|
||||
|
||||
if (name != null) {
|
||||
tetraplets[name] = req.tetraplets[i];
|
||||
}
|
||||
}
|
||||
|
||||
const callParams = {
|
||||
...req.particleContext,
|
||||
signature: fromUint8Array(req.particleContext.signature),
|
||||
tetraplets,
|
||||
};
|
||||
|
||||
return callParams;
|
||||
};
|
||||
|
||||
export const registerParticleScopeService = (
|
||||
peer: FluencePeer,
|
||||
particle: Particle,
|
||||
|
@ -22,7 +22,5 @@ export type MaybePromise<T> = T | Promise<T>;
|
||||
|
||||
export type ServiceImpl = Record<
|
||||
string,
|
||||
(
|
||||
...args: [...JSONArray, ParticleContext]
|
||||
) => MaybePromise<JSONValue | undefined>
|
||||
(...args: [...JSONArray, ParticleContext]) => MaybePromise<JSONValue>
|
||||
>;
|
||||
|
@ -22,6 +22,7 @@ import {
|
||||
KeyPairFormat,
|
||||
serializeAvmArgs,
|
||||
} from "@fluencelabs/avm";
|
||||
import { JSONValue } from "@fluencelabs/interfaces";
|
||||
import { fromUint8Array } from "js-base64";
|
||||
import {
|
||||
concatMap,
|
||||
@ -55,7 +56,6 @@ import {
|
||||
getActualTTL,
|
||||
hasExpired,
|
||||
Particle,
|
||||
ParticleExecutionStage,
|
||||
ParticleQueueItem,
|
||||
} from "../particle/Particle.js";
|
||||
import { registerSig } from "../services/_aqua/services.js";
|
||||
@ -67,6 +67,8 @@ import { Tracing } from "../services/Tracing.js";
|
||||
import { logger } from "../util/logger.js";
|
||||
import { jsonify, isString, getErrorMessage } from "../util/utils.js";
|
||||
|
||||
import { ExpirationError, InterpreterError, SendError } from "./errors.js";
|
||||
|
||||
const log_particle = logger("particle");
|
||||
const log_peer = logger("peer");
|
||||
|
||||
@ -247,11 +249,13 @@ export abstract class FluencePeer {
|
||||
/**
|
||||
* Initiates a new particle execution starting from local peer
|
||||
* @param particle - particle to start execution of
|
||||
* @param onStageChange - callback for reacting on particle state changes
|
||||
* @param onSuccess - callback which is called when particle execution succeed
|
||||
* @param onError - callback which is called when particle execution fails
|
||||
*/
|
||||
initiateParticle: (
|
||||
particle: IParticle,
|
||||
onStageChange: (stage: ParticleExecutionStage) => void,
|
||||
onSuccess: (result: JSONValue) => void,
|
||||
onError: (error: Error) => void,
|
||||
): void => {
|
||||
if (!this.isInitialized) {
|
||||
throw new Error(
|
||||
@ -268,7 +272,8 @@ export abstract class FluencePeer {
|
||||
this._incomingParticles.next({
|
||||
particle: particle,
|
||||
callResults: [],
|
||||
onStageChange: onStageChange,
|
||||
onSuccess,
|
||||
onError,
|
||||
});
|
||||
},
|
||||
|
||||
@ -336,7 +341,8 @@ export abstract class FluencePeer {
|
||||
this._incomingParticles.next({
|
||||
particle: p,
|
||||
callResults: [],
|
||||
onStageChange: () => {},
|
||||
onSuccess: () => {},
|
||||
onError: () => {},
|
||||
});
|
||||
},
|
||||
},
|
||||
@ -471,10 +477,11 @@ export abstract class FluencePeer {
|
||||
item.result.message,
|
||||
);
|
||||
|
||||
item.onStageChange({
|
||||
stage: "interpreterError",
|
||||
errorMessage: item.result.message,
|
||||
});
|
||||
item.onError(
|
||||
new InterpreterError(
|
||||
`Script interpretation failed: ${item.result.message} (particle id: ${item.particle.id})`,
|
||||
),
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -493,10 +500,11 @@ export abstract class FluencePeer {
|
||||
this.decodeAvmData(item.result.data),
|
||||
);
|
||||
|
||||
item.onStageChange({
|
||||
stage: "interpreterError",
|
||||
errorMessage: item.result.errorMessage,
|
||||
});
|
||||
item.onError(
|
||||
new InterpreterError(
|
||||
`Script interpretation failed: ${item.result.errorMessage} (particle id: ${item.particle.id})`,
|
||||
),
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -508,10 +516,6 @@ export abstract class FluencePeer {
|
||||
this.decodeAvmData(item.result.data),
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
item.onStageChange({ stage: "interpreted" });
|
||||
}, 0);
|
||||
|
||||
let connectionPromise: Promise<void> = Promise.resolve();
|
||||
|
||||
// send particle further if requested
|
||||
@ -527,6 +531,8 @@ export abstract class FluencePeer {
|
||||
item.result.nextPeerPks.toString(),
|
||||
);
|
||||
|
||||
const interpreterResult = item.result;
|
||||
|
||||
connectionPromise = this.connection
|
||||
.sendParticle(item.result.nextPeerPks, newParticle)
|
||||
.then(() => {
|
||||
@ -535,7 +541,10 @@ export abstract class FluencePeer {
|
||||
newParticle.id,
|
||||
);
|
||||
|
||||
item.onStageChange({ stage: "sent" });
|
||||
if (interpreterResult.callRequests.length === 0) {
|
||||
// Nothing to call, just fire-and-forget behavior
|
||||
item.onSuccess(null);
|
||||
}
|
||||
})
|
||||
.catch((e: unknown) => {
|
||||
log_particle.error(
|
||||
@ -544,10 +553,13 @@ export abstract class FluencePeer {
|
||||
e,
|
||||
);
|
||||
|
||||
item.onStageChange({
|
||||
stage: "sendingError",
|
||||
errorMessage: getErrorMessage(e),
|
||||
});
|
||||
const message = getErrorMessage(e);
|
||||
|
||||
item.onError(
|
||||
new SendError(
|
||||
`Could not send particle: (particle id: ${item.particle.id}, message: ${message})`,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@ -560,7 +572,10 @@ export abstract class FluencePeer {
|
||||
args: cr.arguments,
|
||||
serviceId: cr.serviceId,
|
||||
tetraplets: cr.tetraplets,
|
||||
particleContext: getParticleContext(item.particle),
|
||||
particleContext: getParticleContext(
|
||||
item.particle,
|
||||
cr.tetraplets,
|
||||
),
|
||||
};
|
||||
|
||||
void this._execSingleCallRequest(req)
|
||||
@ -582,6 +597,14 @@ export abstract class FluencePeer {
|
||||
};
|
||||
})
|
||||
.then((res) => {
|
||||
if (
|
||||
req.serviceId === "callbackSrv" &&
|
||||
req.fnName === "response"
|
||||
) {
|
||||
// Particle already processed
|
||||
return;
|
||||
}
|
||||
|
||||
const serviceResult = {
|
||||
result: jsonify(res.result),
|
||||
retCode: res.retCode,
|
||||
@ -600,7 +623,8 @@ export abstract class FluencePeer {
|
||||
});
|
||||
}
|
||||
} else {
|
||||
item.onStageChange({ stage: "localWorkDone" });
|
||||
// Every air instruction executed or particle will go to relay
|
||||
item.onSuccess(null);
|
||||
}
|
||||
|
||||
return connectionPromise;
|
||||
@ -613,6 +637,7 @@ export abstract class FluencePeer {
|
||||
}
|
||||
|
||||
private _expireParticle(item: ParticleQueueItem) {
|
||||
console.log(item);
|
||||
const particleId = item.particle.id;
|
||||
|
||||
log_particle.debug(
|
||||
@ -623,7 +648,11 @@ export abstract class FluencePeer {
|
||||
|
||||
this.jsServiceHost.removeParticleScopeHandlers(particleId);
|
||||
|
||||
item.onStageChange({ stage: "expired" });
|
||||
item.onError(
|
||||
new ExpirationError(
|
||||
`Particle expired after ttl of ${item.particle.ttl}ms (particle id: ${item.particle.id})`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
private decodeAvmData(data: Uint8Array) {
|
||||
|
@ -44,7 +44,11 @@ describe("Basic AVM functionality in Fluence Peer tests", () => {
|
||||
},
|
||||
});
|
||||
|
||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||
peer.internals.initiateParticle(
|
||||
particle,
|
||||
() => {},
|
||||
handleTimeout(reject),
|
||||
);
|
||||
});
|
||||
|
||||
expect(res).toBe("1");
|
||||
@ -85,7 +89,11 @@ describe("Basic AVM functionality in Fluence Peer tests", () => {
|
||||
},
|
||||
});
|
||||
|
||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||
peer.internals.initiateParticle(
|
||||
particle,
|
||||
() => {},
|
||||
handleTimeout(reject),
|
||||
);
|
||||
});
|
||||
|
||||
expect(res).toStrictEqual(["1", "2"]);
|
||||
@ -126,7 +134,11 @@ describe("Basic AVM functionality in Fluence Peer tests", () => {
|
||||
},
|
||||
});
|
||||
|
||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||
peer.internals.initiateParticle(
|
||||
particle,
|
||||
() => {},
|
||||
handleTimeout(reject),
|
||||
);
|
||||
});
|
||||
|
||||
expect(res).toBe("fast_result");
|
||||
@ -178,7 +190,11 @@ describe("Basic AVM functionality in Fluence Peer tests", () => {
|
||||
},
|
||||
});
|
||||
|
||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||
peer.internals.initiateParticle(
|
||||
particle,
|
||||
() => {},
|
||||
handleTimeout(reject),
|
||||
);
|
||||
});
|
||||
|
||||
expect(res).toBe("failed_with_timeout");
|
||||
|
@ -94,7 +94,11 @@ describe("FluencePeer flow tests", () => {
|
||||
},
|
||||
});
|
||||
|
||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||
peer.internals.initiateParticle(
|
||||
particle,
|
||||
() => {},
|
||||
handleTimeout(reject),
|
||||
);
|
||||
});
|
||||
|
||||
expect(res).toEqual(expect.arrayContaining(["test1", "test1"]));
|
||||
|
@ -72,7 +72,11 @@ describe("FluencePeer usage test suite", () => {
|
||||
},
|
||||
});
|
||||
|
||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||
peer.internals.initiateParticle(
|
||||
particle,
|
||||
() => {},
|
||||
handleTimeout(reject),
|
||||
);
|
||||
});
|
||||
|
||||
expect(res).toBe("test");
|
||||
@ -130,7 +134,11 @@ describe("FluencePeer usage test suite", () => {
|
||||
},
|
||||
});
|
||||
|
||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||
peer.internals.initiateParticle(
|
||||
particle,
|
||||
() => {},
|
||||
handleTimeout(reject),
|
||||
);
|
||||
});
|
||||
|
||||
expect(res).toBe(null);
|
||||
@ -167,7 +175,11 @@ describe("FluencePeer usage test suite", () => {
|
||||
},
|
||||
});
|
||||
|
||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||
peer.internals.initiateParticle(
|
||||
particle,
|
||||
() => {},
|
||||
handleTimeout(reject),
|
||||
);
|
||||
});
|
||||
|
||||
await expect(promise).rejects.toMatchObject({
|
||||
@ -205,6 +217,6 @@ async function callIncorrectService(peer: FluencePeer) {
|
||||
},
|
||||
});
|
||||
|
||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||
peer.internals.initiateParticle(particle, () => {}, handleTimeout(reject));
|
||||
});
|
||||
}
|
||||
|
21
packages/core/js-client/src/jsPeer/errors.ts
Normal file
21
packages/core/js-client/src/jsPeer/errors.ts
Normal file
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright 2023 Fluence Labs Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export class ExpirationError extends Error {}
|
||||
|
||||
export class InterpreterError extends Error {}
|
||||
|
||||
export class SendError extends Error {}
|
@ -79,7 +79,7 @@ export enum ResultCodes {
|
||||
/**
|
||||
* Particle context. Contains additional information about particle which triggered `call` air instruction from AVM
|
||||
*/
|
||||
export interface ParticleContext {
|
||||
export type ParticleContext = {
|
||||
/**
|
||||
* The identifier of particle which triggered the call
|
||||
*/
|
||||
@ -104,7 +104,12 @@ export interface ParticleContext {
|
||||
* Particle's signature
|
||||
*/
|
||||
signature: Uint8Array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Security Tetraplets received from AVM and copied here
|
||||
*/
|
||||
tetraplets: SecurityTetraplet[][];
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents the information passed from AVM when a `call` air instruction is executed on the local peer
|
||||
|
@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { SecurityTetraplet } from "@fluencelabs/avm";
|
||||
import { JSONArray } from "@fluencelabs/interfaces";
|
||||
|
||||
import { FluencePeer } from "../jsPeer/FluencePeer.js";
|
||||
@ -28,10 +29,6 @@ import {
|
||||
ResultCodes,
|
||||
} from "./interfaces.js";
|
||||
|
||||
export const doNothing = () => {
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const WrapFnIntoServiceCall = (
|
||||
fn: (args: JSONArray) => CallServiceResultType | undefined,
|
||||
) => {
|
||||
@ -51,13 +48,17 @@ export class ServiceError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
export const getParticleContext = (particle: IParticle): ParticleContext => {
|
||||
export const getParticleContext = (
|
||||
particle: IParticle,
|
||||
tetraplets: SecurityTetraplet[][],
|
||||
): ParticleContext => {
|
||||
return {
|
||||
particleId: particle.id,
|
||||
initPeerId: particle.initPeerId,
|
||||
timestamp: particle.timestamp,
|
||||
ttl: particle.ttl,
|
||||
signature: particle.signature,
|
||||
tetraplets,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -24,6 +24,8 @@ import { KeyPair } from "../keypair/index.js";
|
||||
import { numberToLittleEndianBytes } from "../util/bytes.js";
|
||||
|
||||
import { IParticle } from "./interfaces.js";
|
||||
import { JSONValue } from "@fluencelabs/interfaces";
|
||||
import { ExpirationError } from "../jsPeer/errors.js";
|
||||
|
||||
const particleSchema = z.object({
|
||||
id: z.string(),
|
||||
@ -183,15 +185,16 @@ export type ParticleExecutionStage =
|
||||
export interface ParticleQueueItem {
|
||||
particle: IParticle;
|
||||
callResults: CallResultsArray;
|
||||
onStageChange: (state: ParticleExecutionStage) => void;
|
||||
onSuccess: (result: JSONValue) => void;
|
||||
onError: (error: Error) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to handle particle at expired stage
|
||||
*/
|
||||
export const handleTimeout = (fn: () => void) => {
|
||||
return (stage: ParticleExecutionStage) => {
|
||||
if (stage.stage === "expired") {
|
||||
return (error: Error) => {
|
||||
if (error instanceof ExpirationError) {
|
||||
fn();
|
||||
}
|
||||
};
|
||||
|
@ -14,58 +14,45 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Buffer } from "buffer";
|
||||
import * as fs from "fs";
|
||||
|
||||
import { CallParams } from "@fluencelabs/interfaces";
|
||||
import { readFile } from "fs/promises";
|
||||
|
||||
import { FluencePeer } from "../jsPeer/FluencePeer.js";
|
||||
import { ParticleContext } from "../jsServiceHost/interfaces.js";
|
||||
import { getErrorMessage } from "../util/utils.js";
|
||||
|
||||
import { NodeUtilsDef, registerNodeUtils } from "./_aqua/node-utils.js";
|
||||
import { registerNodeUtils } from "./_aqua/node-utils.js";
|
||||
import { SecurityGuard } from "./securityGuard.js";
|
||||
import { defaultGuard } from "./SingleModuleSrv.js";
|
||||
|
||||
export class NodeUtils implements NodeUtilsDef {
|
||||
export class NodeUtils {
|
||||
constructor(private peer: FluencePeer) {
|
||||
this.securityGuard_readFile = defaultGuard(this.peer);
|
||||
}
|
||||
|
||||
securityGuard_readFile: SecurityGuard<"path">;
|
||||
securityGuard_readFile: SecurityGuard;
|
||||
|
||||
async read_file(path: string, callParams: CallParams<"path">) {
|
||||
async read_file(path: string, callParams: ParticleContext) {
|
||||
if (!this.securityGuard_readFile(callParams)) {
|
||||
return {
|
||||
success: false,
|
||||
error: "Security guard validation failed",
|
||||
error: ["Security guard validation failed"],
|
||||
content: null,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
// Strange enough, but Buffer type works here, while reading with encoding 'utf-8' doesn't
|
||||
const data = await new Promise<Buffer>((resolve, reject) => {
|
||||
fs.readFile(path, (err, data) => {
|
||||
if (err != null) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
const data = await readFile(path, "base64");
|
||||
|
||||
return {
|
||||
success: true,
|
||||
// TODO: this is strange bug.
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
content: data as unknown as string,
|
||||
content: [data],
|
||||
error: null,
|
||||
};
|
||||
} catch (err: unknown) {
|
||||
return {
|
||||
success: false,
|
||||
error: getErrorMessage(err),
|
||||
error: [getErrorMessage(err)],
|
||||
content: null,
|
||||
};
|
||||
}
|
||||
|
@ -14,11 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { CallParams, PeerIdB58 } from "@fluencelabs/interfaces";
|
||||
import { PeerIdB58 } from "@fluencelabs/interfaces";
|
||||
|
||||
import { ParticleContext } from "../jsServiceHost/interfaces.js";
|
||||
import { KeyPair } from "../keypair/index.js";
|
||||
|
||||
import { SigDef } from "./_aqua/services.js";
|
||||
import {
|
||||
allowOnlyParticleOriginatedAt,
|
||||
allowServiceFn,
|
||||
@ -28,7 +28,7 @@ import {
|
||||
} from "./securityGuard.js";
|
||||
|
||||
export const defaultSigGuard = (peerId: PeerIdB58) => {
|
||||
return and<"data">(
|
||||
return and(
|
||||
allowOnlyParticleOriginatedAt(peerId),
|
||||
or(
|
||||
allowServiceFn("trust-graph", "get_trust_bytes"),
|
||||
@ -43,23 +43,23 @@ export const defaultSigGuard = (peerId: PeerIdB58) => {
|
||||
|
||||
type SignReturnType =
|
||||
| {
|
||||
error: null;
|
||||
signature: number[];
|
||||
error: [];
|
||||
signature: [number[]];
|
||||
success: true;
|
||||
}
|
||||
| {
|
||||
error: string;
|
||||
signature: null;
|
||||
error: [string];
|
||||
signature: [];
|
||||
success: false;
|
||||
};
|
||||
|
||||
export class Sig implements SigDef {
|
||||
export class Sig {
|
||||
constructor(private keyPair: KeyPair) {}
|
||||
|
||||
/**
|
||||
* Configurable security guard for sign method
|
||||
*/
|
||||
securityGuard: SecurityGuard<"data"> = () => {
|
||||
securityGuard: SecurityGuard = () => {
|
||||
return true;
|
||||
};
|
||||
|
||||
@ -75,13 +75,13 @@ export class Sig implements SigDef {
|
||||
*/
|
||||
async sign(
|
||||
data: number[],
|
||||
callParams: CallParams<"data">,
|
||||
context: ParticleContext,
|
||||
): Promise<SignReturnType> {
|
||||
if (!this.securityGuard(callParams)) {
|
||||
if (!this.securityGuard(context)) {
|
||||
return {
|
||||
success: false,
|
||||
error: "Security guard validation failed",
|
||||
signature: null,
|
||||
error: ["Security guard validation failed"],
|
||||
signature: [],
|
||||
};
|
||||
}
|
||||
|
||||
@ -89,8 +89,8 @@ export class Sig implements SigDef {
|
||||
|
||||
return {
|
||||
success: true,
|
||||
error: null,
|
||||
signature: Array.from(signedData),
|
||||
error: [],
|
||||
signature: [Array.from(signedData)],
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -16,13 +16,12 @@
|
||||
|
||||
import { Buffer } from "buffer";
|
||||
|
||||
import { CallParams } from "@fluencelabs/interfaces";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
import { FluencePeer } from "../jsPeer/FluencePeer.js";
|
||||
import { ParticleContext } from "../jsServiceHost/interfaces.js";
|
||||
import { getErrorMessage } from "../util/utils.js";
|
||||
|
||||
import { SrvDef } from "./_aqua/single-module-srv.js";
|
||||
import {
|
||||
allowOnlyParticleOriginatedAt,
|
||||
SecurityGuard,
|
||||
@ -32,7 +31,7 @@ export const defaultGuard = (peer: FluencePeer) => {
|
||||
return allowOnlyParticleOriginatedAt(peer.keyPair.getPeerId());
|
||||
};
|
||||
|
||||
export class Srv implements SrvDef {
|
||||
export class Srv {
|
||||
private services: Set<string> = new Set();
|
||||
|
||||
constructor(private peer: FluencePeer) {
|
||||
@ -40,16 +39,13 @@ export class Srv implements SrvDef {
|
||||
this.securityGuard_remove = defaultGuard(this.peer);
|
||||
}
|
||||
|
||||
securityGuard_create: SecurityGuard<"wasm_b64_content">;
|
||||
securityGuard_create: SecurityGuard;
|
||||
|
||||
async create(
|
||||
wasm_b64_content: string,
|
||||
callParams: CallParams<"wasm_b64_content">,
|
||||
) {
|
||||
async create(wasm_b64_content: string, callParams: ParticleContext) {
|
||||
if (!this.securityGuard_create(callParams)) {
|
||||
return {
|
||||
success: false,
|
||||
error: "Security guard validation failed",
|
||||
error: ["Security guard validation failed"],
|
||||
service_id: null,
|
||||
};
|
||||
}
|
||||
@ -66,25 +62,25 @@ export class Srv implements SrvDef {
|
||||
|
||||
return {
|
||||
success: true,
|
||||
service_id: newServiceId,
|
||||
service_id: [newServiceId],
|
||||
error: null,
|
||||
};
|
||||
} catch (err: unknown) {
|
||||
return {
|
||||
success: true,
|
||||
service_id: null,
|
||||
error: getErrorMessage(err),
|
||||
error: [getErrorMessage(err)],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
securityGuard_remove: SecurityGuard<"service_id">;
|
||||
securityGuard_remove: SecurityGuard;
|
||||
|
||||
async remove(service_id: string, callParams: CallParams<"service_id">) {
|
||||
async remove(service_id: string, callParams: ParticleContext) {
|
||||
if (!this.securityGuard_remove(callParams)) {
|
||||
return {
|
||||
success: false,
|
||||
error: "Security guard validation failed",
|
||||
error: ["Security guard validation failed"],
|
||||
service_id: null,
|
||||
};
|
||||
}
|
||||
@ -92,7 +88,7 @@ export class Srv implements SrvDef {
|
||||
if (!this.services.has(service_id)) {
|
||||
return {
|
||||
success: false,
|
||||
error: `Service with id ${service_id} not found`,
|
||||
error: [`Service with id ${service_id} not found`],
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -16,11 +16,14 @@
|
||||
|
||||
import assert from "assert";
|
||||
|
||||
import { CallParams, JSONArray } from "@fluencelabs/interfaces";
|
||||
import { JSONArray } from "@fluencelabs/interfaces";
|
||||
import { toUint8Array } from "js-base64";
|
||||
import { it, describe, expect, test } from "vitest";
|
||||
|
||||
import { CallServiceData } from "../../jsServiceHost/interfaces.js";
|
||||
import {
|
||||
CallServiceData,
|
||||
ParticleContext,
|
||||
} from "../../jsServiceHost/interfaces.js";
|
||||
import { KeyPair } from "../../keypair/index.js";
|
||||
import { builtInServices } from "../builtins.js";
|
||||
import { allowServiceFn } from "../securityGuard.js";
|
||||
@ -149,6 +152,7 @@ describe("Tests for default handler", () => {
|
||||
timestamp: 595951200,
|
||||
ttl: 595961200,
|
||||
signature: new Uint8Array([]),
|
||||
tetraplets: [],
|
||||
},
|
||||
};
|
||||
|
||||
@ -185,6 +189,7 @@ describe("Tests for default handler", () => {
|
||||
timestamp: 595951200,
|
||||
ttl: 595961200,
|
||||
signature: new Uint8Array([]),
|
||||
tetraplets: [],
|
||||
},
|
||||
};
|
||||
|
||||
@ -243,14 +248,15 @@ const makeTestTetraplet = (
|
||||
initPeerId: string,
|
||||
serviceId: string,
|
||||
fnName: string,
|
||||
): CallParams<"data"> => {
|
||||
): ParticleContext => {
|
||||
return {
|
||||
particleId: "",
|
||||
timestamp: 0,
|
||||
ttl: 0,
|
||||
initPeerId: initPeerId,
|
||||
tetraplets: {
|
||||
data: [
|
||||
signature: new Uint8Array([]),
|
||||
tetraplets: [
|
||||
[
|
||||
{
|
||||
peer_pk: initPeerId,
|
||||
function_name: fnName,
|
||||
@ -258,7 +264,7 @@ const makeTestTetraplet = (
|
||||
json_path: "",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
@ -273,7 +279,7 @@ describe("Sig service tests", () => {
|
||||
);
|
||||
|
||||
expect(res.success).toBe(true);
|
||||
expect(res.signature).toStrictEqual(testDataSig);
|
||||
expect(res.signature).toStrictEqual([testDataSig]);
|
||||
});
|
||||
|
||||
it("sig.verify should return true for the correct signature", async () => {
|
||||
@ -305,7 +311,7 @@ describe("Sig service tests", () => {
|
||||
|
||||
expect(signature.success).toBe(true);
|
||||
assert(signature.success);
|
||||
const res = await sig.verify(signature.signature, testData);
|
||||
const res = await sig.verify(signature.signature[0], testData);
|
||||
|
||||
expect(res).toBe(true);
|
||||
});
|
||||
@ -334,7 +340,7 @@ describe("Sig service tests", () => {
|
||||
);
|
||||
|
||||
expect(res.success).toBe(false);
|
||||
expect(res.error).toBe("Security guard validation failed");
|
||||
expect(res.error).toStrictEqual(["Security guard validation failed"]);
|
||||
});
|
||||
|
||||
it("sig.sign with defaultSigGuard should not allow particles initiated from other peers", async () => {
|
||||
@ -352,7 +358,7 @@ describe("Sig service tests", () => {
|
||||
);
|
||||
|
||||
expect(res.success).toBe(false);
|
||||
expect(res.error).toBe("Security guard validation failed");
|
||||
expect(res.error).toStrictEqual(["Security guard validation failed"]);
|
||||
});
|
||||
|
||||
it("changing securityGuard should work", async () => {
|
||||
|
@ -17,7 +17,6 @@
|
||||
import { it, describe, expect, beforeEach, afterEach } from "vitest";
|
||||
|
||||
import { FluencePeer } from "../../jsPeer/FluencePeer.js";
|
||||
import { doNothing } from "../../jsServiceHost/serviceUtils.js";
|
||||
import { mkTestPeer } from "../../util/testUtils.js";
|
||||
|
||||
let peer: FluencePeer;
|
||||
@ -72,7 +71,11 @@ describe("Sig service test suite", () => {
|
||||
});
|
||||
|
||||
const p = await peer.internals.createNewParticle(script);
|
||||
peer.internals.initiateParticle(p, doNothing);
|
||||
peer.internals.initiateParticle(
|
||||
p,
|
||||
() => {},
|
||||
() => {},
|
||||
);
|
||||
|
||||
const [
|
||||
nestedFirst,
|
||||
|
@ -17,10 +17,10 @@
|
||||
import * as path from "path";
|
||||
import * as url from "url";
|
||||
|
||||
import { ServiceDef, ServiceImpl } from "@fluencelabs/interfaces";
|
||||
import { it, describe, expect, beforeAll } from "vitest";
|
||||
|
||||
import { registerService } from "../../compilerSupport/registerService.js";
|
||||
import { ServiceImpl } from "../../compilerSupport/types.js";
|
||||
import { KeyPair } from "../../keypair/index.js";
|
||||
import { compileAqua, CompiledFnCall, withPeer } from "../../util/testUtils.js";
|
||||
import { allowServiceFn } from "../securityGuard.js";
|
||||
@ -29,8 +29,6 @@ import { Sig } from "../Sig.js";
|
||||
const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
|
||||
|
||||
let aqua: Record<string, CompiledFnCall>;
|
||||
let sigDef: ServiceDef;
|
||||
let dataProviderDef: ServiceDef;
|
||||
|
||||
describe("Sig service test suite", () => {
|
||||
beforeAll(async () => {
|
||||
@ -39,14 +37,12 @@ describe("Sig service test suite", () => {
|
||||
"../../../aqua_test/sigService.aqua",
|
||||
);
|
||||
|
||||
const { services, functions } = await compileAqua(pathToAquaFiles);
|
||||
const { functions } = await compileAqua(pathToAquaFiles);
|
||||
|
||||
aqua = functions;
|
||||
sigDef = services["Sig"];
|
||||
dataProviderDef = services["DataProvider"];
|
||||
});
|
||||
|
||||
it("Use custom sig service, success path", async () => {
|
||||
it.only("Use custom sig service, success path", async () => {
|
||||
await withPeer(async (peer) => {
|
||||
const customKeyPair = await KeyPair.randomEd25519();
|
||||
const customSig = new Sig(customKeyPair);
|
||||
@ -54,7 +50,6 @@ describe("Sig service test suite", () => {
|
||||
|
||||
registerService({
|
||||
peer,
|
||||
def: sigDef,
|
||||
serviceId: "CustomSig",
|
||||
// TODO: fix this after changing registerService signature
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
@ -63,7 +58,6 @@ describe("Sig service test suite", () => {
|
||||
|
||||
registerService({
|
||||
peer,
|
||||
def: dataProviderDef,
|
||||
serviceId: "data",
|
||||
service: {
|
||||
provide_data: () => {
|
||||
@ -81,7 +75,7 @@ describe("Sig service test suite", () => {
|
||||
const isSigCorrect = await customSig.verify(
|
||||
// TODO: Use compiled ts wrappers
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
(result as { signature: number[] }).signature,
|
||||
(result as { signature: [number[]] }).signature[0],
|
||||
data,
|
||||
);
|
||||
|
||||
@ -97,7 +91,6 @@ describe("Sig service test suite", () => {
|
||||
|
||||
registerService({
|
||||
peer,
|
||||
def: sigDef,
|
||||
serviceId: "CustomSig",
|
||||
// TODO: fix this after changing registerService signature
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
@ -106,7 +99,6 @@ describe("Sig service test suite", () => {
|
||||
|
||||
registerService({
|
||||
peer,
|
||||
def: dataProviderDef,
|
||||
serviceId: "data",
|
||||
service: {
|
||||
provide_data: () => {
|
||||
@ -122,7 +114,7 @@ describe("Sig service test suite", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("Default sig service should be resolvable by peer id", async () => {
|
||||
it.only("Default sig service should be resolvable by peer id", async () => {
|
||||
await withPeer(async (peer) => {
|
||||
const sig = peer.internals.getServices().sig;
|
||||
|
||||
@ -130,7 +122,6 @@ describe("Sig service test suite", () => {
|
||||
|
||||
registerService({
|
||||
peer: peer,
|
||||
def: dataProviderDef,
|
||||
serviceId: "data",
|
||||
service: {
|
||||
provide_data: () => {
|
||||
@ -146,6 +137,11 @@ describe("Sig service test suite", () => {
|
||||
});
|
||||
|
||||
expect(callAsSigRes).toHaveProperty("success", false);
|
||||
|
||||
expect(callAsPeerIdRes).toHaveProperty("error", [
|
||||
"Security guard validation failed",
|
||||
]);
|
||||
|
||||
expect(callAsPeerIdRes).toHaveProperty("success", false);
|
||||
|
||||
sig.securityGuard = () => {
|
||||
@ -167,7 +163,8 @@ describe("Sig service test suite", () => {
|
||||
const isValid = await sig.verify(
|
||||
// TODO: Use compiled ts wrappers
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
(callAsSigResAfterGuardChange as { signature: number[] }).signature,
|
||||
(callAsSigResAfterGuardChange as { signature: [number[]] })
|
||||
.signature[0],
|
||||
data,
|
||||
);
|
||||
|
||||
|
@ -14,30 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This compiled aqua file was modified to make it work in monorepo
|
||||
*/
|
||||
import { CallParams, ServiceImpl } from "@fluencelabs/interfaces";
|
||||
|
||||
import { registerService } from "../../compilerSupport/registerService.js";
|
||||
import { ServiceImpl } from "../../compilerSupport/types.js";
|
||||
import { FluencePeer } from "../../jsPeer/FluencePeer.js";
|
||||
import { NodeUtils } from "../NodeUtils.js";
|
||||
|
||||
// Services
|
||||
|
||||
export interface NodeUtilsDef {
|
||||
read_file: (
|
||||
path: string,
|
||||
callParams: CallParams<"path">,
|
||||
) =>
|
||||
| { content: string | null; error: string | null; success: boolean }
|
||||
| Promise<{
|
||||
content: string | null;
|
||||
error: string | null;
|
||||
success: boolean;
|
||||
}>;
|
||||
}
|
||||
|
||||
export function registerNodeUtils(
|
||||
peer: FluencePeer,
|
||||
serviceId: string,
|
||||
@ -49,55 +30,6 @@ export function registerNodeUtils(
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
service: service as unknown as ServiceImpl,
|
||||
serviceId,
|
||||
def: {
|
||||
defaultServiceId: "node_utils",
|
||||
functions: {
|
||||
tag: "labeledProduct",
|
||||
fields: {
|
||||
read_file: {
|
||||
tag: "arrow",
|
||||
domain: {
|
||||
tag: "labeledProduct",
|
||||
fields: {
|
||||
path: {
|
||||
tag: "scalar",
|
||||
name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
codomain: {
|
||||
tag: "unlabeledProduct",
|
||||
items: [
|
||||
{
|
||||
tag: "struct",
|
||||
name: "ReadFileResult",
|
||||
fields: {
|
||||
content: {
|
||||
tag: "option",
|
||||
type: {
|
||||
tag: "scalar",
|
||||
name: "string",
|
||||
},
|
||||
},
|
||||
error: {
|
||||
tag: "option",
|
||||
type: {
|
||||
tag: "scalar",
|
||||
name: "string",
|
||||
},
|
||||
},
|
||||
success: {
|
||||
tag: "scalar",
|
||||
name: "bool",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -14,33 +14,30 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This compiled aqua file was modified to make it work in monorepo
|
||||
*/
|
||||
import { CallParams, ServiceImpl } from "@fluencelabs/interfaces";
|
||||
|
||||
import { registerService } from "../../compilerSupport/registerService.js";
|
||||
import { ServiceImpl } from "../../compilerSupport/types.js";
|
||||
import { FluencePeer } from "../../jsPeer/FluencePeer.js";
|
||||
import { ParticleContext } from "../../jsServiceHost/interfaces.js";
|
||||
import { Sig } from "../Sig.js";
|
||||
|
||||
// Services
|
||||
|
||||
export interface SigDef {
|
||||
get_peer_id: (callParams: CallParams<null>) => string | Promise<string>;
|
||||
get_peer_id: (callParams: ParticleContext) => string | Promise<string>;
|
||||
sign: (
|
||||
data: number[],
|
||||
callParams: CallParams<"data">,
|
||||
callParams: ParticleContext,
|
||||
) =>
|
||||
| { error: string | null; signature: number[] | null; success: boolean }
|
||||
| { error: [string?]; signature: [number[]?]; success: boolean }
|
||||
| Promise<{
|
||||
error: string | null;
|
||||
signature: number[] | null;
|
||||
error: [string?];
|
||||
signature: [number[]?];
|
||||
success: boolean;
|
||||
}>;
|
||||
verify: (
|
||||
signature: number[],
|
||||
data: number[],
|
||||
callParams: CallParams<"signature" | "data">,
|
||||
callParams: ParticleContext,
|
||||
) => boolean | Promise<boolean>;
|
||||
}
|
||||
|
||||
@ -55,107 +52,6 @@ export function registerSig(
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
service: service as unknown as ServiceImpl,
|
||||
serviceId,
|
||||
def: {
|
||||
defaultServiceId: "sig",
|
||||
functions: {
|
||||
tag: "labeledProduct",
|
||||
fields: {
|
||||
get_peer_id: {
|
||||
tag: "arrow",
|
||||
domain: {
|
||||
tag: "nil",
|
||||
},
|
||||
codomain: {
|
||||
tag: "unlabeledProduct",
|
||||
items: [
|
||||
{
|
||||
tag: "scalar",
|
||||
name: "string",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
sign: {
|
||||
tag: "arrow",
|
||||
domain: {
|
||||
tag: "labeledProduct",
|
||||
fields: {
|
||||
data: {
|
||||
tag: "array",
|
||||
type: {
|
||||
tag: "scalar",
|
||||
name: "u8",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
codomain: {
|
||||
tag: "unlabeledProduct",
|
||||
items: [
|
||||
{
|
||||
tag: "struct",
|
||||
name: "SignResult",
|
||||
fields: {
|
||||
error: {
|
||||
tag: "option",
|
||||
type: {
|
||||
tag: "scalar",
|
||||
name: "string",
|
||||
},
|
||||
},
|
||||
signature: {
|
||||
tag: "option",
|
||||
type: {
|
||||
tag: "array",
|
||||
type: {
|
||||
tag: "scalar",
|
||||
name: "u8",
|
||||
},
|
||||
},
|
||||
},
|
||||
success: {
|
||||
tag: "scalar",
|
||||
name: "bool",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
verify: {
|
||||
tag: "arrow",
|
||||
domain: {
|
||||
tag: "labeledProduct",
|
||||
fields: {
|
||||
signature: {
|
||||
tag: "array",
|
||||
type: {
|
||||
tag: "scalar",
|
||||
name: "u8",
|
||||
},
|
||||
},
|
||||
data: {
|
||||
tag: "array",
|
||||
type: {
|
||||
tag: "scalar",
|
||||
name: "u8",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
codomain: {
|
||||
tag: "unlabeledProduct",
|
||||
items: [
|
||||
{
|
||||
tag: "scalar",
|
||||
name: "bool",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -14,37 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This compiled aqua file was modified to make it work in monorepo
|
||||
*/
|
||||
import { CallParams, ServiceImpl } from "@fluencelabs/interfaces";
|
||||
|
||||
import { registerService } from "../../compilerSupport/registerService.js";
|
||||
import { ServiceImpl } from "../../compilerSupport/types.js";
|
||||
import { FluencePeer } from "../../jsPeer/FluencePeer.js";
|
||||
import { Srv } from "../SingleModuleSrv.js";
|
||||
|
||||
// Services
|
||||
|
||||
export interface SrvDef {
|
||||
create: (
|
||||
wasm_b64_content: string,
|
||||
callParams: CallParams<"wasm_b64_content">,
|
||||
) =>
|
||||
| { error: string | null; service_id: string | null; success: boolean }
|
||||
| Promise<{
|
||||
error: string | null;
|
||||
service_id: string | null;
|
||||
success: boolean;
|
||||
}>;
|
||||
list: (callParams: CallParams<null>) => string[] | Promise<string[]>;
|
||||
remove: (
|
||||
service_id: string,
|
||||
callParams: CallParams<"service_id">,
|
||||
) =>
|
||||
| { error: string | null; success: boolean }
|
||||
| Promise<{ error: string | null; success: boolean }>;
|
||||
}
|
||||
|
||||
export function registerSrv(
|
||||
peer: FluencePeer,
|
||||
serviceId: string,
|
||||
@ -56,107 +30,6 @@ export function registerSrv(
|
||||
// TODO: fix this after changing registerService signature
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
service: service as unknown as ServiceImpl,
|
||||
def: {
|
||||
defaultServiceId: "single_module_srv",
|
||||
functions: {
|
||||
tag: "labeledProduct",
|
||||
fields: {
|
||||
create: {
|
||||
tag: "arrow",
|
||||
domain: {
|
||||
tag: "labeledProduct",
|
||||
fields: {
|
||||
wasm_b64_content: {
|
||||
tag: "scalar",
|
||||
name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
codomain: {
|
||||
tag: "unlabeledProduct",
|
||||
items: [
|
||||
{
|
||||
tag: "struct",
|
||||
name: "ServiceCreationResult",
|
||||
fields: {
|
||||
error: {
|
||||
tag: "option",
|
||||
type: {
|
||||
tag: "scalar",
|
||||
name: "string",
|
||||
},
|
||||
},
|
||||
service_id: {
|
||||
tag: "option",
|
||||
type: {
|
||||
tag: "scalar",
|
||||
name: "string",
|
||||
},
|
||||
},
|
||||
success: {
|
||||
tag: "scalar",
|
||||
name: "bool",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
list: {
|
||||
tag: "arrow",
|
||||
domain: {
|
||||
tag: "nil",
|
||||
},
|
||||
codomain: {
|
||||
tag: "unlabeledProduct",
|
||||
items: [
|
||||
{
|
||||
tag: "array",
|
||||
type: {
|
||||
tag: "scalar",
|
||||
name: "string",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
remove: {
|
||||
tag: "arrow",
|
||||
domain: {
|
||||
tag: "labeledProduct",
|
||||
fields: {
|
||||
service_id: {
|
||||
tag: "scalar",
|
||||
name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
codomain: {
|
||||
tag: "unlabeledProduct",
|
||||
items: [
|
||||
{
|
||||
tag: "struct",
|
||||
name: "RemoveResult",
|
||||
fields: {
|
||||
error: {
|
||||
tag: "option",
|
||||
type: {
|
||||
tag: "scalar",
|
||||
name: "string",
|
||||
},
|
||||
},
|
||||
success: {
|
||||
tag: "scalar",
|
||||
name: "bool",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -15,25 +15,23 @@
|
||||
*/
|
||||
|
||||
import { SecurityTetraplet } from "@fluencelabs/avm";
|
||||
import { CallParams, PeerIdB58 } from "@fluencelabs/interfaces";
|
||||
import { PeerIdB58 } from "@fluencelabs/interfaces";
|
||||
|
||||
type ArgName = string | null;
|
||||
import { ParticleContext } from "../jsServiceHost/interfaces.js";
|
||||
|
||||
/**
|
||||
* A predicate of call params for sig service's sign method which determines whether signing operation is allowed or not
|
||||
*/
|
||||
export type SecurityGuard<T extends ArgName> = (
|
||||
params: CallParams<T>,
|
||||
) => boolean;
|
||||
export type SecurityGuard = (params: ParticleContext) => boolean;
|
||||
|
||||
/**
|
||||
* Only allow calls when tetraplet for 'data' argument satisfies the predicate
|
||||
*/
|
||||
export const allowTetraplet = <T extends ArgName>(
|
||||
export const allowTetraplet = (
|
||||
pred: (tetraplet: SecurityTetraplet) => boolean,
|
||||
): SecurityGuard<T> => {
|
||||
): SecurityGuard => {
|
||||
return (params) => {
|
||||
const t = params.tetraplets["data"][0];
|
||||
const t = params.tetraplets[0][0];
|
||||
return pred(t);
|
||||
};
|
||||
};
|
||||
@ -41,10 +39,10 @@ export const allowTetraplet = <T extends ArgName>(
|
||||
/**
|
||||
* Only allow data which comes from the specified serviceId and fnName
|
||||
*/
|
||||
export const allowServiceFn = <T extends ArgName>(
|
||||
export const allowServiceFn = (
|
||||
serviceId: string,
|
||||
fnName: string,
|
||||
): SecurityGuard<T> => {
|
||||
): SecurityGuard => {
|
||||
return allowTetraplet((t) => {
|
||||
return t.service_id === serviceId && t.function_name === fnName;
|
||||
});
|
||||
@ -53,9 +51,7 @@ export const allowServiceFn = <T extends ArgName>(
|
||||
/**
|
||||
* Only allow data originated from the specified json_path
|
||||
*/
|
||||
export const allowExactJsonPath = <T extends ArgName>(
|
||||
jsonPath: string,
|
||||
): SecurityGuard<T> => {
|
||||
export const allowExactJsonPath = (jsonPath: string): SecurityGuard => {
|
||||
return allowTetraplet((t) => {
|
||||
return t.json_path === jsonPath;
|
||||
});
|
||||
@ -64,9 +60,9 @@ export const allowExactJsonPath = <T extends ArgName>(
|
||||
/**
|
||||
* Only allow signing when particle is initiated at the specified peer
|
||||
*/
|
||||
export const allowOnlyParticleOriginatedAt = <T extends ArgName>(
|
||||
export const allowOnlyParticleOriginatedAt = (
|
||||
peerId: PeerIdB58,
|
||||
): SecurityGuard<T> => {
|
||||
): SecurityGuard => {
|
||||
return (params) => {
|
||||
return params.initPeerId === peerId;
|
||||
};
|
||||
@ -76,9 +72,7 @@ export const allowOnlyParticleOriginatedAt = <T extends ArgName>(
|
||||
* Only allow signing when all of the predicates are satisfied.
|
||||
* Useful for predicates reuse
|
||||
*/
|
||||
export const and = <T extends ArgName>(
|
||||
...predicates: SecurityGuard<T>[]
|
||||
): SecurityGuard<T> => {
|
||||
export const and = (...predicates: SecurityGuard[]): SecurityGuard => {
|
||||
return (params) => {
|
||||
return predicates.every((x) => {
|
||||
return x(params);
|
||||
@ -90,9 +84,7 @@ export const and = <T extends ArgName>(
|
||||
* Only allow signing when any of the predicates are satisfied.
|
||||
* Useful for predicates reuse
|
||||
*/
|
||||
export const or = <T extends ArgName>(
|
||||
...predicates: SecurityGuard<T>[]
|
||||
): SecurityGuard<T> => {
|
||||
export const or = (...predicates: SecurityGuard[]): SecurityGuard => {
|
||||
return (params) => {
|
||||
return predicates.some((x) => {
|
||||
return x(params);
|
||||
|
@ -20,7 +20,7 @@ import { Path, Aqua } from "@fluencelabs/aqua-api/aqua-api.js";
|
||||
import {
|
||||
FunctionCallDef,
|
||||
JSONArray,
|
||||
PassedArgs,
|
||||
JSONValue,
|
||||
ServiceDef,
|
||||
} from "@fluencelabs/interfaces";
|
||||
import { Subject, Subscribable } from "rxjs";
|
||||
@ -30,7 +30,10 @@ import { ClientConfig, RelayOptions } from "../clientPeer/types.js";
|
||||
import { callAquaFunction } from "../compilerSupport/callFunction.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";
|
||||
@ -73,6 +76,18 @@ interface FunctionInfo {
|
||||
funcDef: FunctionCallDef;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type for callback passed as aqua function argument
|
||||
*/
|
||||
export type ArgCallbackFunction = (
|
||||
...args: [...JSONValue[], ParticleContext]
|
||||
) => JSONValue | Promise<JSONValue>;
|
||||
|
||||
/**
|
||||
* Arguments passed to Aqua function
|
||||
*/
|
||||
export type PassedArgs = { [key: string]: JSONValue | ArgCallbackFunction };
|
||||
|
||||
export const compileAqua = async (aquaFile: string): Promise<CompiledFile> => {
|
||||
await fs.access(aquaFile);
|
||||
|
||||
@ -88,11 +103,12 @@ export const compileAqua = async (aquaFile: string): Promise<CompiledFile> => {
|
||||
);
|
||||
}
|
||||
|
||||
console.log(compilationResult);
|
||||
|
||||
const functions = Object.entries(compilationResult.functions)
|
||||
.map(([name, fnInfo]: [string, FunctionInfo]) => {
|
||||
const callFn = (peer: FluencePeer, args: PassedArgs) => {
|
||||
return callAquaFunction({
|
||||
def: fnInfo.funcDef,
|
||||
script: fnInfo.script,
|
||||
config: {},
|
||||
peer: peer,
|
||||
|
Loading…
x
Reference in New Issue
Block a user