mirror of
https://github.com/fluencelabs/fluence-js.git
synced 2025-04-25 01:42:13 +00:00
Stepper integration impl (#955)
This commit is contained in:
parent
0a49f84d08
commit
2792dcfc93
@ -3,9 +3,11 @@
|
||||
node_modules
|
||||
types
|
||||
|
||||
src
|
||||
src/
|
||||
aqua/
|
||||
|
||||
tsconfig.json
|
||||
webpack.config.js
|
||||
|
||||
bundle
|
||||
pkg
|
||||
|
14
aqua/index.d.ts
vendored
Normal file
14
aqua/index.d.ts
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* @param wasm
|
||||
* @param {string} init_user_id
|
||||
* @param {string} aqua
|
||||
* @param {string} data
|
||||
* @returns {string}
|
||||
*/
|
||||
export function invoke(wasm: any, init_user_id: string, aqua: string, data: string): string;
|
||||
export function getStringFromWasm0(wasm: any, arg1: any, arg2: any): string
|
||||
export function getInt32Memory0(wasm: any): number[]
|
||||
export function passStringToWasm0(wasm: any, arg: any, malloc: any, realloc: any): number
|
||||
export let WASM_VECTOR_LEN: number;
|
1
aqua/index.js
Normal file
1
aqua/index.js
Normal file
@ -0,0 +1 @@
|
||||
export * from "./index_bg.js";
|
115
aqua/index_bg.js
Normal file
115
aqua/index_bg.js
Normal file
@ -0,0 +1,115 @@
|
||||
/**
|
||||
*
|
||||
* This is generated and patched code. All functions are using local wasm as an argument for now, not a global wasm file.
|
||||
*
|
||||
*/
|
||||
|
||||
export function main(wasm) {
|
||||
wasm.main();
|
||||
}
|
||||
|
||||
export let WASM_VECTOR_LEN = 0;
|
||||
|
||||
let cachegetUint8Memory0 = null;
|
||||
export function getUint8Memory0(wasm) {
|
||||
if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.memory.buffer) {
|
||||
cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachegetUint8Memory0;
|
||||
}
|
||||
|
||||
let cachedTextEncoder = new TextEncoder('utf-8');
|
||||
|
||||
const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
|
||||
? function (arg, view) {
|
||||
return cachedTextEncoder.encodeInto(arg, view);
|
||||
}
|
||||
: function (arg, view) {
|
||||
const buf = cachedTextEncoder.encode(arg);
|
||||
view.set(buf);
|
||||
return {
|
||||
read: arg.length,
|
||||
written: buf.length
|
||||
};
|
||||
});
|
||||
|
||||
export function passStringToWasm0(wasm, arg, malloc, realloc) {
|
||||
|
||||
if (realloc === undefined) {
|
||||
const buf = cachedTextEncoder.encode(arg);
|
||||
const ptr = malloc(buf.length);
|
||||
getUint8Memory0(wasm).subarray(ptr, ptr + buf.length).set(buf);
|
||||
WASM_VECTOR_LEN = buf.length;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
let len = arg.length;
|
||||
let ptr = malloc(len);
|
||||
|
||||
const mem = getUint8Memory0(wasm);
|
||||
|
||||
let offset = 0;
|
||||
|
||||
for (; offset < len; offset++) {
|
||||
const code = arg.charCodeAt(offset);
|
||||
if (code > 0x7F) break;
|
||||
mem[ptr + offset] = code;
|
||||
}
|
||||
|
||||
if (offset !== len) {
|
||||
if (offset !== 0) {
|
||||
arg = arg.slice(offset);
|
||||
}
|
||||
ptr = realloc(ptr, len, len = offset + arg.length * 3);
|
||||
const view = getUint8Memory0(wasm).subarray(ptr + offset, ptr + len);
|
||||
const ret = encodeString(arg, view);
|
||||
|
||||
offset += ret.written;
|
||||
}
|
||||
|
||||
WASM_VECTOR_LEN = offset;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
let cachegetInt32Memory0 = null;
|
||||
export function getInt32Memory0(wasm) {
|
||||
if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) {
|
||||
cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachegetInt32Memory0;
|
||||
}
|
||||
|
||||
let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
||||
|
||||
cachedTextDecoder.decode();
|
||||
|
||||
export function getStringFromWasm0(wasm, ptr, len) {
|
||||
return cachedTextDecoder.decode(getUint8Memory0(wasm).subarray(ptr, ptr + len));
|
||||
}
|
||||
/**
|
||||
* @param {any} wasm
|
||||
* @param {string} init_user_id
|
||||
* @param {string} aqua
|
||||
* @param {string} data
|
||||
* @returns {string}
|
||||
*/
|
||||
export function invoke(wasm, init_user_id, aqua, data) {
|
||||
try {
|
||||
const retptr = wasm.__wbindgen_export_0.value - 16;
|
||||
wasm.__wbindgen_export_0.value = retptr;
|
||||
var ptr0 = passStringToWasm0(wasm, init_user_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
var len0 = WASM_VECTOR_LEN;
|
||||
var ptr1 = passStringToWasm0(wasm, aqua, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
var len1 = WASM_VECTOR_LEN;
|
||||
var ptr2 = passStringToWasm0(wasm, data, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
var len2 = WASM_VECTOR_LEN;
|
||||
wasm.invoke(retptr, ptr0, len0, ptr1, len1, ptr2, len2);
|
||||
var r0 = getInt32Memory0(wasm)[retptr / 4 + 0];
|
||||
var r1 = getInt32Memory0(wasm)[retptr / 4 + 1];
|
||||
return getStringFromWasm0(wasm, r0, r1);
|
||||
} finally {
|
||||
wasm.__wbindgen_export_0.value += 16;
|
||||
wasm.__wbindgen_free(r0, r1);
|
||||
}
|
||||
}
|
||||
|
110
package-lock.json
generated
110
package-lock.json
generated
@ -196,6 +196,44 @@
|
||||
"watchpack": "^1.6.0"
|
||||
}
|
||||
},
|
||||
"@wasmer/wasi": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@wasmer/wasi/-/wasi-0.12.0.tgz",
|
||||
"integrity": "sha512-FJhLZKAfLWm/yjQI7eCRHNbA8ezmb7LSpUYFkHruZXs2mXk2+DaQtSElEtOoNrVQ4vApTyVaAd5/b7uEu8w6wQ==",
|
||||
"requires": {
|
||||
"browser-process-hrtime": "^1.0.0",
|
||||
"buffer-es6": "^4.9.3",
|
||||
"path-browserify": "^1.0.0",
|
||||
"randomfill": "^1.0.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"path-browserify": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
|
||||
"integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@wasmer/wasm-transformer": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@wasmer/wasm-transformer/-/wasm-transformer-0.12.0.tgz",
|
||||
"integrity": "sha512-pz4hvrJhqjxsmrMgxIH6jaEJuaL4/1iPNUg0lSIdpWeszgoCYjpQUimzh7ekKCIxl8WuqKOmm2f5hX+Vt8Sx8w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"wasm-feature-detect": "^1.2.2"
|
||||
}
|
||||
},
|
||||
"@wasmer/wasmfs": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@wasmer/wasmfs/-/wasmfs-0.12.0.tgz",
|
||||
"integrity": "sha512-m1ftchyQ1DfSenm5XbbdGIpb6KJHH5z0gODo3IZr6lATkj4WXfX/UeBTZ0aG9YVShBp+kHLdUHvOkqjy6p/GWw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"memfs": "3.0.4",
|
||||
"pako": "^1.0.11",
|
||||
"tar-stream": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/ast": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz",
|
||||
@ -876,6 +914,11 @@
|
||||
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
|
||||
"integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8="
|
||||
},
|
||||
"browser-process-hrtime": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz",
|
||||
"integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow=="
|
||||
},
|
||||
"browser-stdout": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
|
||||
@ -986,6 +1029,11 @@
|
||||
"ieee754": "^1.1.4"
|
||||
}
|
||||
},
|
||||
"buffer-es6": {
|
||||
"version": "4.9.3",
|
||||
"resolved": "https://registry.npmjs.org/buffer-es6/-/buffer-es6-4.9.3.tgz",
|
||||
"integrity": "sha1-8mNHuC33b9N+GLy1KIxJcM/VxAQ="
|
||||
},
|
||||
"buffer-from": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
|
||||
@ -2335,6 +2383,12 @@
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"dev": true
|
||||
},
|
||||
"fast-extend": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-extend/-/fast-extend-1.0.2.tgz",
|
||||
"integrity": "sha512-XXA9RmlPatkFKUzqVZAFth18R4Wo+Xug/S+C7YlYA3xrXwfPlW3dqNwOb4hvQo7wZJ2cNDYhrYuPzVOfHy5/uQ==",
|
||||
"dev": true
|
||||
},
|
||||
"fast-fifo": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.0.0.tgz",
|
||||
@ -2690,6 +2744,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"fs-constants": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
|
||||
"dev": true
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz",
|
||||
@ -2701,6 +2761,12 @@
|
||||
"universalify": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"fs-monkey": {
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-0.3.3.tgz",
|
||||
"integrity": "sha512-FNUvuTAJ3CqCQb5ELn+qCbGR/Zllhf2HtwsdAtBi59s1WeCjKMT81fHcSu7dwIskqGVK+MmOrb7VOBlq3/SItw==",
|
||||
"dev": true
|
||||
},
|
||||
"fs-write-stream-atomic": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
|
||||
@ -4824,6 +4890,16 @@
|
||||
"p-is-promise": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"memfs": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/memfs/-/memfs-3.0.4.tgz",
|
||||
"integrity": "sha512-OcZEzwX9E5AoY8SXjuAvw0DbIAYwUzV/I236I8Pqvrlv7sL/Y0E9aRCon05DhaV8pg1b32uxj76RgW0s5xjHBA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-extend": "1.0.2",
|
||||
"fs-monkey": "0.3.3"
|
||||
}
|
||||
},
|
||||
"memory-fs": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz",
|
||||
@ -6170,7 +6246,6 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.0"
|
||||
}
|
||||
@ -6179,7 +6254,6 @@
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz",
|
||||
"integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"randombytes": "^2.0.5",
|
||||
"safe-buffer": "^5.1.0"
|
||||
@ -7257,6 +7331,32 @@
|
||||
"integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
|
||||
"dev": true
|
||||
},
|
||||
"tar-stream": {
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.4.tgz",
|
||||
"integrity": "sha512-o3pS2zlG4gxr67GmFYBLlq+dM8gyRGUOvsrHclSkvtVtQbjV0s/+ZE8OpICbaj8clrX3tjeHngYGP7rweaBnuw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bl": "^4.0.3",
|
||||
"end-of-stream": "^1.4.1",
|
||||
"fs-constants": "^1.0.0",
|
||||
"inherits": "^2.0.3",
|
||||
"readable-stream": "^3.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"bl": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz",
|
||||
"integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"buffer": "^5.5.0",
|
||||
"inherits": "^2.0.4",
|
||||
"readable-stream": "^3.4.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"terser": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-4.7.0.tgz",
|
||||
@ -7823,6 +7923,12 @@
|
||||
"integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==",
|
||||
"dev": true
|
||||
},
|
||||
"wasm-feature-detect": {
|
||||
"version": "1.2.9",
|
||||
"resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.2.9.tgz",
|
||||
"integrity": "sha512-2E9/gtLVLpv2wnZDyYv8WY2dR9gHbmyv5uhZsnOcMSzqc78aGZpKQORPNcnrPwAU23nFUo7GAwKuoTAWRgsJ7Q==",
|
||||
"dev": true
|
||||
},
|
||||
"watchpack": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.2.tgz",
|
||||
|
@ -1,13 +1,14 @@
|
||||
{
|
||||
"name": "fluence",
|
||||
"version": "0.7.24",
|
||||
"version": "0.7.38",
|
||||
"description": "the browser js-libp2p client for the Fluence network",
|
||||
"main": "./dist/fluence.js",
|
||||
"typings": "./dist/fluence.d.ts",
|
||||
"scripts": {
|
||||
"test": "mocha -r ts-node/register src/**/*.spec.ts",
|
||||
"test-ts": "ts-mocha -p tsconfig.json src/**/*.spec.ts",
|
||||
"package:build": "NODE_ENV=production webpack && tsc",
|
||||
"package:build": "NODE_ENV=production webpack && npm run package",
|
||||
"package": "tsc && cp -r aqua dist",
|
||||
"compile": "tsc",
|
||||
"start": "webpack-dev-server -p",
|
||||
"build": "webpack"
|
||||
@ -16,7 +17,6 @@
|
||||
"license": "Apache 2.0",
|
||||
"dependencies": {
|
||||
"async": "3.2.0",
|
||||
"loglevel": "1.7.0",
|
||||
"base64-js": "1.3.1",
|
||||
"bs58": "4.0.1",
|
||||
"cids": "0.8.1",
|
||||
@ -26,6 +26,7 @@
|
||||
"libp2p-mplex": "0.9.5",
|
||||
"libp2p-secio": "0.12.5",
|
||||
"libp2p-websockets": "0.13.6",
|
||||
"loglevel": "1.7.0",
|
||||
"peer-id": "0.13.12",
|
||||
"uuid": "8.3.0"
|
||||
},
|
||||
|
93
src/callService.ts
Normal file
93
src/callService.ts
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
import {getService, registerService} from "./globalState";
|
||||
|
||||
export interface CallServiceResult {
|
||||
ret_code: number,
|
||||
result: string
|
||||
}
|
||||
|
||||
export class Service {
|
||||
|
||||
serviceId: string;
|
||||
functions: Map<string, (args: any[]) => object> = new Map();
|
||||
|
||||
constructor(serviceId: string) {
|
||||
this.serviceId = serviceId;
|
||||
}
|
||||
|
||||
registerFunction(fnName: string, fn: (args: any[]) => object) {
|
||||
this.functions.set(fnName, fn);
|
||||
}
|
||||
|
||||
call(fnName: string, args: any[]): CallServiceResult {
|
||||
let fn = this.functions.get(fnName)
|
||||
if (fn) {
|
||||
try {
|
||||
let result = fn(args)
|
||||
return {
|
||||
ret_code: 0,
|
||||
result: JSON.stringify(result)
|
||||
}
|
||||
} catch (err) {
|
||||
return {
|
||||
ret_code: 1,
|
||||
result: JSON.stringify(err)
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
let errorMsg = `Error. There is no function ${fnName}`
|
||||
return {
|
||||
ret_code: 1,
|
||||
result: JSON.stringify(errorMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let logger = new Service("")
|
||||
logger.registerFunction("", (args: any[]) => {
|
||||
console.log("logger service: " + args)
|
||||
return { result: "done" }
|
||||
})
|
||||
registerService(logger);
|
||||
|
||||
export function callService(service_id: string, fn_name: string, args: string): CallServiceResult {
|
||||
try {
|
||||
let argsObject = JSON.parse(args)
|
||||
if (!Array.isArray(argsObject)) {
|
||||
throw new Error("args is not an array")
|
||||
}
|
||||
let service = getService(service_id)
|
||||
if (service) {
|
||||
return service.call(fn_name, argsObject)
|
||||
} else {
|
||||
return {
|
||||
result: JSON.stringify(`Error. There is no service: ${service_id}`),
|
||||
ret_code: 0
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Cannot parse arguments: " + JSON.stringify(err))
|
||||
return {
|
||||
result: JSON.stringify("Cannot parse arguments: " + JSON.stringify(err)),
|
||||
ret_code: 1
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
import {getService, registerService} from "./globalState";
|
||||
|
||||
interface CallServiceResult {
|
||||
ret_code: number,
|
||||
result: string
|
||||
}
|
||||
|
||||
export class Service {
|
||||
|
||||
serviceId: string;
|
||||
functions: Map<string, (args: string) => string> = new Map();
|
||||
|
||||
constructor(serviceId: string) {
|
||||
this.serviceId = serviceId;
|
||||
}
|
||||
|
||||
registerFunction(fnName: string, fn: (args: string) => string) {
|
||||
this.functions.set(fnName, fn);
|
||||
}
|
||||
|
||||
call(fnName: string, args: string): CallServiceResult {
|
||||
let fn = this.functions.get(fnName)
|
||||
if (fn) {
|
||||
try {
|
||||
let result = fn(args)
|
||||
return {
|
||||
ret_code: 0,
|
||||
result
|
||||
}
|
||||
} catch (err) {
|
||||
return {
|
||||
ret_code: 1,
|
||||
result: JSON.stringify(err)
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
let errorMsg = `Error. There is no function ${fnName}`
|
||||
return {
|
||||
ret_code: 1,
|
||||
result: errorMsg
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let logger = new Service("logger")
|
||||
logger.registerFunction("logger", (args: string) => {
|
||||
console.log("logger service: " + args)
|
||||
return ""
|
||||
})
|
||||
registerService(logger);
|
||||
|
||||
export function call_service(service_id: string, fn_name: string, args: string): string {
|
||||
let service = getService(service_id)
|
||||
if (service) {
|
||||
return JSON.stringify(service.call(fn_name, args))
|
||||
} else {
|
||||
return `Error. There is no service ${service_id}`
|
||||
}
|
||||
}
|
@ -48,4 +48,12 @@ export default class Fluence {
|
||||
|
||||
return client;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
Fluence: Fluence;
|
||||
}
|
||||
}
|
||||
|
||||
window.Fluence = Fluence;
|
||||
|
@ -16,13 +16,14 @@
|
||||
|
||||
|
||||
import {Particle} from "./particle";
|
||||
import {StepperOutcome} from "./stepperOutcome";
|
||||
import * as PeerId from "peer-id";
|
||||
import Multiaddr from "multiaddr"
|
||||
import {FluenceConnection} from "./fluenceConnection";
|
||||
import {Subscriptions} from "./subscriptions";
|
||||
import * as stepper from "../stepper";
|
||||
|
||||
const WASM = stepper.loadWasm();
|
||||
import {addParticle, getCurrentParticleId, popParticle, setCurrentParticleId} from "./globalState";
|
||||
import {instantiateStepper, Stepper} from "./stepper";
|
||||
import log from "loglevel";
|
||||
|
||||
export class FluenceClient {
|
||||
readonly selfPeerId: PeerId;
|
||||
@ -30,6 +31,7 @@ export class FluenceClient {
|
||||
|
||||
private nodePeerIdStr: string;
|
||||
private subscriptions = new Subscriptions();
|
||||
private stepper: Stepper = undefined;
|
||||
|
||||
connection: FluenceConnection;
|
||||
|
||||
@ -39,38 +41,63 @@ export class FluenceClient {
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits a response that match the predicate.
|
||||
*
|
||||
* @param id
|
||||
* @param ttl
|
||||
* Pass a particle to a stepper and send a result to other services.
|
||||
*/
|
||||
waitResponse(id: string, ttl: number): Promise<Particle> {
|
||||
return new Promise((resolve, reject) => {
|
||||
// subscribe for responses, to handle response
|
||||
// TODO if there's no conn, reject
|
||||
this.subscriptions.subscribe(id, (particle: Particle) => {
|
||||
resolve(particle);
|
||||
}, ttl);
|
||||
})
|
||||
private handleParticle(particle: Particle): void {
|
||||
|
||||
// if a current particle is processing, add new particle to the queue
|
||||
if (getCurrentParticleId() !== undefined) {
|
||||
addParticle(particle);
|
||||
} else {
|
||||
if (this.stepper === undefined) {
|
||||
throw new Error("Undefined. Stepper is not initialized. User 'Fluence.connect' to create a client.")
|
||||
}
|
||||
// start particle processing if queue is empty
|
||||
try {
|
||||
let stepperOutcomeStr = this.stepper(particle.init_peer_id, particle.script, JSON.stringify(particle.data))
|
||||
let stepperOutcome: StepperOutcome = JSON.parse(stepperOutcomeStr);
|
||||
|
||||
log.info("inner stepper outcome:");
|
||||
log.info(stepperOutcome);
|
||||
|
||||
// do nothing if there is no `next_peer_pks`
|
||||
if (stepperOutcome.next_peer_pks.length > 0) {
|
||||
let newParticle: Particle = {...particle};
|
||||
newParticle.data = JSON.parse(stepperOutcome.data);
|
||||
|
||||
this.connection.sendParticle(newParticle).catch((reason) => {
|
||||
console.error(`Error on sending particle with id ${particle.id}: ${reason}`)
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
// get last particle from the queue
|
||||
let nextParticle = popParticle();
|
||||
// start the processing of a new particle if it exists
|
||||
if (nextParticle) {
|
||||
// update current particle
|
||||
setCurrentParticleId(nextParticle.id);
|
||||
this.handleParticle(nextParticle)
|
||||
} else {
|
||||
// wait for a new call (do nothing) if there is no new particle in a queue
|
||||
setCurrentParticleId(undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle incoming call.
|
||||
* If FunctionCall returns - we should send it as a response.
|
||||
* Handle incoming particle from a relay.
|
||||
*/
|
||||
handleParticle(): (particle: Particle) => void {
|
||||
private handleExternalParticle(): (particle: Particle) => void {
|
||||
|
||||
let _this = this;
|
||||
|
||||
return (particle: Particle) => {
|
||||
// call all subscriptions for a new call
|
||||
if (!_this.subscriptions.applyToSubscriptions(particle)) {
|
||||
// if there is no subscription, use Stepper
|
||||
WASM.then((w) => {
|
||||
let stepperOutcomeStr = w.invoke(particle.init_peer_id, particle.script, JSON.stringify(particle.data))
|
||||
let stepperOutcome: StepperOutcome = JSON.parse(stepperOutcomeStr);
|
||||
console.log(stepperOutcome)
|
||||
})
|
||||
let now = Date.now();
|
||||
if (particle.timestamp + particle.ttl > now) {
|
||||
_this.handleParticle(particle);
|
||||
} else {
|
||||
console.log(`Particle expired. Now: ${now}, ttl: ${particle.ttl}, ts: ${particle.timestamp}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -102,15 +129,19 @@ export class FluenceClient {
|
||||
}
|
||||
|
||||
let peerId = PeerId.createFromB58String(nodePeerId);
|
||||
let connection = new FluenceConnection(multiaddr, peerId, this.selfPeerId, this.handleParticle());
|
||||
|
||||
this.stepper = await instantiateStepper(this.selfPeerId);
|
||||
|
||||
let connection = new FluenceConnection(multiaddr, peerId, this.selfPeerId, this.handleExternalParticle());
|
||||
|
||||
await connection.connect();
|
||||
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
async sendParticle(particle: Particle): Promise<Particle> {
|
||||
await this.connection.sendParticle(particle);
|
||||
return this.waitResponse(particle.id, particle.ttl);
|
||||
sendParticle(particle: Particle): string {
|
||||
this.handleParticle(particle);
|
||||
this.subscriptions.subscribe(particle.id, particle.ttl);
|
||||
return particle.id
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,6 @@ enum Status {
|
||||
export class FluenceConnection {
|
||||
|
||||
private readonly selfPeerId: PeerId;
|
||||
readonly relay: PeerId;
|
||||
private node: LibP2p;
|
||||
private readonly address: Multiaddr;
|
||||
readonly nodePeerId: PeerId;
|
||||
@ -91,8 +90,9 @@ export class FluenceConnection {
|
||||
async function (source: AsyncIterable<string>) {
|
||||
for await (const msg of source) {
|
||||
try {
|
||||
log.debug(_this.selfPeerIdStr);
|
||||
let particle = parseParticle(msg);
|
||||
log.debug("Particle is received:");
|
||||
log.debug(JSON.stringify(particle, undefined, 2));
|
||||
_this.handleCall(particle);
|
||||
} catch(e) {
|
||||
log.error("error on handling a new incoming message: " + e);
|
||||
@ -123,7 +123,7 @@ export class FluenceConnection {
|
||||
this.checkConnectedOrThrow();
|
||||
|
||||
let particleStr = stringifyParticle(particle);
|
||||
log.debug("send function call: \n" + JSON.stringify(particle, undefined, 2));
|
||||
log.debug("send particle: \n" + JSON.stringify(particle, undefined, 2));
|
||||
|
||||
// create outgoing substream
|
||||
const conn = await this.node.dialProtocol(this.address, PROTOCOL_NAME) as {stream: Stream; protocol: string};
|
||||
|
@ -1,7 +1,42 @@
|
||||
import {Service} from "./call_service";
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
import {Service} from "./callService";
|
||||
import {Particle} from "./particle";
|
||||
|
||||
// TODO put state with wasm file in each created FluenceClient
|
||||
let services: Map<string, Service> = new Map();
|
||||
let particlesQueue: Particle[] = [];
|
||||
let currentParticle: string | undefined = undefined;
|
||||
|
||||
export function getCurrentParticleId(): string | undefined {
|
||||
return currentParticle;
|
||||
}
|
||||
|
||||
export function setCurrentParticleId(particle: string | undefined) {
|
||||
currentParticle = particle;
|
||||
}
|
||||
|
||||
export function addParticle(particle: Particle): void {
|
||||
particlesQueue.push(particle);
|
||||
}
|
||||
|
||||
export function popParticle(): Particle | undefined {
|
||||
return particlesQueue.pop();
|
||||
}
|
||||
|
||||
export function registerService(service: Service) {
|
||||
services.set(service.serviceId, service)
|
||||
|
@ -35,7 +35,7 @@ export async function build(peerId: PeerId, script: string, data: object, ttl?:
|
||||
let id = genUUID();
|
||||
let currentTime = (new Date()).getTime();
|
||||
|
||||
if (ttl) {
|
||||
if (ttl === undefined) {
|
||||
ttl = DEFAULT_TTL
|
||||
}
|
||||
|
||||
|
86
src/stepper.ts
Normal file
86
src/stepper.ts
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
import {wasmBs64} from "../wasmBs64";
|
||||
import {toByteArray} from "base64-js";
|
||||
import * as aqua from "../aqua"
|
||||
|
||||
import {callService} from "./callService";
|
||||
import {getInt32Memory0, getStringFromWasm0, passStringToWasm0, WASM_VECTOR_LEN} from "../aqua";
|
||||
import PeerId from "peer-id";
|
||||
import log from "loglevel";
|
||||
|
||||
export type Stepper = (init_user_id: string, script: string, data: string) => string
|
||||
|
||||
export async function instantiateStepper(pid: PeerId): Promise<Stepper> {
|
||||
// Fetch our Wasm File
|
||||
const arr = toByteArray(wasmBs64)
|
||||
|
||||
let wasm: any = undefined;
|
||||
|
||||
const importObject = {
|
||||
// __wbg_callserviceimpl_c0ca292e3c8c0c97 this is a function generated by bindgen. Could be changed.
|
||||
// If so, an error with a new name will be occurred after wasm initialization.
|
||||
"./index_bg.js": { __wbg_callserviceimpl_c0ca292e3c8c0c97: (arg0: any, arg1: any, arg2: any, arg3: any, arg4: any, arg5: any, arg6: any) => {
|
||||
try {
|
||||
let serviceId = getStringFromWasm0(wasm, arg1, arg2)
|
||||
let fnName = getStringFromWasm0(wasm, arg3, arg4)
|
||||
let args = getStringFromWasm0(wasm, arg5, arg6);
|
||||
var ret = callService(serviceId, fnName, args);
|
||||
let retStr = JSON.stringify(ret)
|
||||
var ptr0 = passStringToWasm0(wasm, retStr, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
var len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0(wasm)[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0(wasm)[arg0 / 4 + 0] = ptr0;
|
||||
} finally {
|
||||
wasm.__wbindgen_free(arg1, arg2);
|
||||
wasm.__wbindgen_free(arg3, arg4);
|
||||
wasm.__wbindgen_free(arg5, arg6);
|
||||
}
|
||||
},
|
||||
__wbg_getcurrentpeeridimpl_a04b7c07e1108952: (arg0: any) => {
|
||||
var ret = pid.toB58String();
|
||||
var ptr0 = passStringToWasm0(wasm, ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
var len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0(wasm)[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0(wasm)[arg0 / 4 + 0] = ptr0;
|
||||
}
|
||||
},
|
||||
"host": { log_utf8_string: (arg0: any, arg1: any) => {
|
||||
try {
|
||||
let str = getStringFromWasm0(wasm, arg0, arg1)
|
||||
log.debug(str)
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let module = await WebAssembly.compile(arr);
|
||||
let webAssemblyInstantiatedSource = await WebAssembly.instantiate(module, {
|
||||
...importObject
|
||||
});
|
||||
|
||||
wasm = webAssemblyInstantiatedSource.exports
|
||||
|
||||
wasm.main();
|
||||
|
||||
let func = (init_user_id: string, script: string, data: string) => {
|
||||
return aqua.invoke(wasm, init_user_id, script, data)
|
||||
}
|
||||
|
||||
return func
|
||||
}
|
@ -1,4 +1,20 @@
|
||||
interface StepperOutcome {
|
||||
/*
|
||||
* Copyright 2020 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 interface StepperOutcome {
|
||||
ret_code: number,
|
||||
data: string,
|
||||
next_peer_pks: string[]
|
||||
|
@ -15,9 +15,10 @@
|
||||
*/
|
||||
|
||||
import {Particle} from "./particle";
|
||||
import log from "loglevel";
|
||||
|
||||
export class Subscriptions {
|
||||
private subscriptions: Map<string, (particle: Particle) => void> = new Map();
|
||||
private subscriptions: Set<string> = new Set();
|
||||
|
||||
constructor() {}
|
||||
|
||||
@ -25,35 +26,18 @@ export class Subscriptions {
|
||||
* Subscriptions will be applied by outside message if id will be the same.
|
||||
*
|
||||
* @param id message identificator
|
||||
* @param f function to use with outside message
|
||||
* @param ttl time to live, subscription will be deleted after this time
|
||||
*/
|
||||
subscribe(id: string, f: (particle: Particle) => void, ttl: number) {
|
||||
subscribe(id: string, ttl: number) {
|
||||
let _this = this;
|
||||
setTimeout(() => {
|
||||
_this.subscriptions.delete(id)
|
||||
console.log(`Particle with id ${id} deleted by timeout`)
|
||||
log.info(`Particle with id ${id} deleted by timeout`)
|
||||
}, ttl)
|
||||
this.subscriptions.set(id, f);
|
||||
this.subscriptions.add(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* A particle will be applied if id of the particle was subscribed earlier.
|
||||
* @param particle
|
||||
*/
|
||||
applyToSubscriptions(particle: Particle): boolean {
|
||||
let callback = this.subscriptions.get(particle.id)
|
||||
if (callback) {
|
||||
callback(particle);
|
||||
return true;
|
||||
} else {
|
||||
if (Number(particle.timestamp) + particle.ttl > Date.now()) {
|
||||
console.log("Old particle received. 'ttl' is ended.");
|
||||
} else {
|
||||
console.log("External particle received. 'Stepper' needed on client. Unimplemented.");
|
||||
}
|
||||
console.log(particle);
|
||||
return false;
|
||||
}
|
||||
hasSubscription(particle: Particle): boolean {
|
||||
return this.subscriptions.has(particle.id)
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import {certificateFromString, certificateToString, issue} from "../trust/certif
|
||||
import {TrustGraph} from "../trust/trust_graph";
|
||||
import {nodeRootCert} from "../trust/misc";
|
||||
import {peerIdToSeed, seedToPeerId} from "../seed";
|
||||
import {build, Particle} from "../particle";
|
||||
import {build} from "../particle";
|
||||
|
||||
describe("Typescript usage suite", () => {
|
||||
|
||||
@ -42,10 +42,15 @@ describe("Typescript usage suite", () => {
|
||||
let key1 = await Fluence.generatePeerId();
|
||||
let key2 = await Fluence.generatePeerId();
|
||||
|
||||
// connect to two different nodes
|
||||
let cl1 = await Fluence.connect("/dns4/134.209.186.43/tcp/9003/ws/p2p/12D3KooWBUJifCTgaxAUrcM9JysqCcS4CS8tiYH5hExbdWCAoNwb", key1);
|
||||
let nodePeerId = "12D3KooWQ8x4SMBmSSUrMzY2m13uzC7UoSyvHaDhTKx7hH8aXxpt"
|
||||
let addr = '/ip4/127.0.0.1/tcp/9001/ws/p2p/' + nodePeerId
|
||||
|
||||
let particle = await build(key1, "123", {a: 777, b: "567"})
|
||||
// connect to two different nodes
|
||||
let cl1 = await Fluence.connect(addr, key1);
|
||||
|
||||
let script = "((call (%current% (local_service_id local_fn_name) () result_name)) (call (remote_peer_id (service_id fn_name) () g)))"
|
||||
|
||||
let particle = await build(key1, script, {})
|
||||
|
||||
let result = await cl1.sendParticle(particle)
|
||||
console.log(result)
|
||||
|
1
stepper.d.ts
vendored
1
stepper.d.ts
vendored
@ -1 +0,0 @@
|
||||
export function loadWasm(): Promise<typeof import('./pkg')>
|
@ -1,3 +0,0 @@
|
||||
export async function loadWasm() {
|
||||
return import('./pkg')
|
||||
}
|
1
wasmBs64.ts
Normal file
1
wasmBs64.ts
Normal file
File diff suppressed because one or more lines are too long
@ -32,7 +32,7 @@ const config = {
|
||||
new HtmlWebpackPlugin(),
|
||||
new WasmPackPlugin({
|
||||
// TODO use another path somehow
|
||||
crateDirectory: path.resolve(__dirname, "../../../aquamarine"),
|
||||
crateDirectory: path.resolve(__dirname, "../../../aquamarine/stepper"),
|
||||
outDir:path.resolve(__dirname, "./pkg")
|
||||
}),
|
||||
new webpack.ProvidePlugin({
|
||||
|
Loading…
x
Reference in New Issue
Block a user