mirror of
https://github.com/fluencelabs/fluence-js.git
synced 2025-06-28 07:11:33 +00:00
feat: Update Libp2p to latest version. Add standalone bundled JS Client (#239)
This commit is contained in:
@ -1,8 +0,0 @@
|
||||
module.exports = {
|
||||
semi: true,
|
||||
trailingComma: "all",
|
||||
singleQuote: true,
|
||||
printWidth: 120,
|
||||
tabWidth: 4,
|
||||
useTabs: false
|
||||
};
|
@ -1,11 +0,0 @@
|
||||
# FluenceJS connection
|
||||
|
||||
This package is a part of FluenceJS, the official implementation of the Fluence Peer in typescript. See the [FluenceJS repo](https://github.com/fluencelabs/fluence-js) for more info
|
||||
|
||||
## Contributing
|
||||
|
||||
While the project is still in the early stages of development, you are welcome to track progress and contribute. As the project is undergoing rapid changes, interested contributors should contact the team before embarking on larger pieces of work. All contributors should consult with and agree to our [basic contributing rules](CONTRIBUTING.md).
|
||||
|
||||
## License
|
||||
|
||||
[Apache 2.0](LICENSE)
|
@ -1,35 +0,0 @@
|
||||
{
|
||||
"name": "@fluencelabs/connection",
|
||||
"version": "0.2.0",
|
||||
"description": "Fluence connection",
|
||||
"main": "./dist/index.js",
|
||||
"typings": "./dist/index.d.ts",
|
||||
"engines": {
|
||||
"node": ">=10",
|
||||
"pnpm": ">=3"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc"
|
||||
},
|
||||
"repository": "https://github.com/fluencelabs/fluence-js",
|
||||
"author": "Fluence Labs",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@fluencelabs/interfaces": "workspace:0.2.0",
|
||||
"peer-id": "0.16.0",
|
||||
"it-length-prefixed": "5.0.3",
|
||||
"it-pipe": "1.1.0",
|
||||
"@chainsafe/libp2p-noise": "4.1.1",
|
||||
"libp2p": "0.36.2",
|
||||
"libp2p-interfaces": "4.0.6",
|
||||
"libp2p-mplex": "0.10.7",
|
||||
"libp2p-websockets": "0.16.2",
|
||||
"loglevel": "1.8.1",
|
||||
"multiaddr": "10.0.1",
|
||||
"browser-or-node": "2.0.0",
|
||||
"buffer": "6.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "4.6.4"
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 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 { isBrowser } from 'browser-or-node';
|
||||
import { Buffer as BufferPolyfill } from 'buffer';
|
||||
|
||||
export default isBrowser ? BufferPolyfill : Buffer;
|
@ -1,14 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist/",
|
||||
"lib": ["ES2015"],
|
||||
"target": "ES5",
|
||||
"module": "commonjs",
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"declaration": true,
|
||||
"esModuleInterop": true,
|
||||
},
|
||||
"exclude": ["node_modules", "dist"],
|
||||
"include": ["src/**/*"]
|
||||
}
|
21
packages/core/interfaces/.gitignore
vendored
21
packages/core/interfaces/.gitignore
vendored
@ -1,21 +0,0 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
bundle/
|
||||
|
||||
dist
|
||||
esm
|
||||
types
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
.idea
|
@ -1,12 +0,0 @@
|
||||
.idea
|
||||
.gitignore
|
||||
node_modules
|
||||
types
|
||||
|
||||
src/
|
||||
|
||||
tsconfig.json
|
||||
webpack.config.js
|
||||
|
||||
bundle
|
||||
pkg
|
@ -1,8 +0,0 @@
|
||||
module.exports = {
|
||||
semi: true,
|
||||
trailingComma: "all",
|
||||
singleQuote: true,
|
||||
printWidth: 120,
|
||||
tabWidth: 4,
|
||||
useTabs: false
|
||||
};
|
@ -1,13 +0,0 @@
|
||||
## Contribute Code
|
||||
|
||||
You are welcome to contribute to Fluence.
|
||||
|
||||
Things you need to know:
|
||||
|
||||
1. You need to **agree to the Contributors License Agreement**. This is a common practice in all major Open Source projects. At the current moment we are unable to accept contributions made on behalf of a company. Only individual contributions will be accepted.
|
||||
2. **Not all proposed contributions can be accepted**. Some features may e.g. just fit a third-party add-on better. The contribution must fit the overall direction of Fluence and really improve it. The more effort you invest, the better you should clarify in advance whether the contribution fits: the best way would be to just open an issue to discuss the contribution you plan to make.
|
||||
|
||||
### Contributor License Agreement
|
||||
|
||||
When you contribute, you have to be aware that your contribution is covered by **Apache License 2.0**, but might relicensed under few other software licenses mentioned in the **Contributor License Agreement**.
|
||||
In particular you need to agree to the [Contributor License Agreement](https://gist.github.com/fluencelabs-org/3f4cbb3cc14c1c0fb9ad99d8f7316ed7). If you agree to its content, you simply have to click on the link posted by the CLA assistant as a comment to the pull request. Click it to check the CLA, then accept it on the following screen if you agree to it. CLA assistant will save this decision for upcoming contributions and will notify you if there is any change to the CLA in the meantime.
|
@ -1,11 +0,0 @@
|
||||
# FluenceJS interfaces
|
||||
|
||||
This package is a part of FluenceJS, the official implementation of the Fluence Peer in typescript. See the [FluenceJS repo](https://github.com/fluencelabs/fluence-js) for more info
|
||||
|
||||
## Contributing
|
||||
|
||||
While the project is still in the early stages of development, you are welcome to track progress and contribute. As the project is undergoing rapid changes, interested contributors should contact the team before embarking on larger pieces of work. All contributors should consult with and agree to our [basic contributing rules](CONTRIBUTING.md).
|
||||
|
||||
## License
|
||||
|
||||
[Apache 2.0](LICENSE)
|
@ -1,25 +0,0 @@
|
||||
{
|
||||
"name": "@fluencelabs/interfaces",
|
||||
"version": "0.2.0",
|
||||
"description": "Fluence interfaces",
|
||||
"main": "./dist/index.js",
|
||||
"typings": "./dist/index.d.ts",
|
||||
"engines": {
|
||||
"node": ">=10",
|
||||
"pnpm": ">=3"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc"
|
||||
},
|
||||
"repository": "https://github.com/fluencelabs/fluence-js",
|
||||
"author": "Fluence Labs",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"typescript": "4.6.4",
|
||||
"@fluencelabs/avm": "0.31.10",
|
||||
"@fluencelabs/marine-js": "0.3.38",
|
||||
"@types/node": "16.11.59",
|
||||
"threads": "^1.7.0"
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist/",
|
||||
"lib": ["ES2017"],
|
||||
"target": "ES5",
|
||||
"module": "commonjs",
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"declaration": true,
|
||||
"esModuleInterop": true
|
||||
},
|
||||
"exclude": ["node_modules", "dist"],
|
||||
"include": ["src/**/*"]
|
||||
}
|
16
packages/core/jest.config.cjs
Normal file
16
packages/core/jest.config.cjs
Normal file
@ -0,0 +1,16 @@
|
||||
/** @type {import('ts-jest').JestConfigWithTsJest} */
|
||||
module.exports = {
|
||||
extensionsToTreatAsEsm: ['.ts'],
|
||||
moduleNameMapper: {
|
||||
'^(\\.{1,2}/.*)\\.js$': '$1',
|
||||
},
|
||||
testPathIgnorePatterns: ['dist'],
|
||||
transform: {
|
||||
'^.+\\.tsx?$': [
|
||||
'ts-jest',
|
||||
{
|
||||
useESM: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
21
packages/core/js-peer/.gitignore
vendored
21
packages/core/js-peer/.gitignore
vendored
@ -1,21 +0,0 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
bundle/
|
||||
|
||||
dist
|
||||
esm
|
||||
types
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
.idea
|
@ -1,12 +0,0 @@
|
||||
.idea
|
||||
.gitignore
|
||||
node_modules
|
||||
types
|
||||
|
||||
src/
|
||||
|
||||
tsconfig.json
|
||||
webpack.config.js
|
||||
|
||||
bundle
|
||||
pkg
|
@ -1,8 +0,0 @@
|
||||
module.exports = {
|
||||
semi: true,
|
||||
trailingComma: "all",
|
||||
singleQuote: true,
|
||||
printWidth: 120,
|
||||
tabWidth: 4,
|
||||
useTabs: false
|
||||
};
|
@ -1,13 +0,0 @@
|
||||
## Contribute Code
|
||||
|
||||
You are welcome to contribute to Fluence.
|
||||
|
||||
Things you need to know:
|
||||
|
||||
1. You need to **agree to the Contributors License Agreement**. This is a common practice in all major Open Source projects. At the current moment we are unable to accept contributions made on behalf of a company. Only individual contributions will be accepted.
|
||||
2. **Not all proposed contributions can be accepted**. Some features may e.g. just fit a third-party add-on better. The contribution must fit the overall direction of Fluence and really improve it. The more effort you invest, the better you should clarify in advance whether the contribution fits: the best way would be to just open an issue to discuss the contribution you plan to make.
|
||||
|
||||
### Contributor License Agreement
|
||||
|
||||
When you contribute, you have to be aware that your contribution is covered by **Apache License 2.0**, but might relicensed under few other software licenses mentioned in the **Contributor License Agreement**.
|
||||
In particular you need to agree to the [Contributor License Agreement](https://gist.github.com/fluencelabs-org/3f4cbb3cc14c1c0fb9ad99d8f7316ed7). If you agree to its content, you simply have to click on the link posted by the CLA assistant as a comment to the pull request. Click it to check the CLA, then accept it on the following screen if you agree to it. CLA assistant will save this decision for upcoming contributions and will notify you if there is any change to the CLA in the meantime.
|
@ -1,6 +0,0 @@
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
testTimeout: 10000,
|
||||
testPathIgnorePatterns: ['dist'],
|
||||
};
|
@ -1,59 +0,0 @@
|
||||
{
|
||||
"name": "@fluencelabs/js-peer",
|
||||
"version": "0.1.0",
|
||||
"description": "TypeScript implementation of Fluence Peer",
|
||||
"main": "./dist/index.js",
|
||||
"typings": "./dist/index.d.ts",
|
||||
"engines": {
|
||||
"node": ">=10",
|
||||
"pnpm": ">=3"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"compile-aqua": "aqua -i ./aqua/ -o ./src/internal/_aqua",
|
||||
"test": "jest",
|
||||
"test:unit": "jest --testPathPattern=src/__test__/unit",
|
||||
"test:integration": "jest --testPathPattern=src/__test__/integration"
|
||||
},
|
||||
"repository": "https://github.com/fluencelabs/fluence-js",
|
||||
"author": "Fluence Labs",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@fluencelabs/avm": "0.32.1",
|
||||
"@fluencelabs/connection": "workspace:0.2.0",
|
||||
"@fluencelabs/interfaces": "workspace:0.2.0",
|
||||
"@fluencelabs/keypair": "workspace:0.2.0",
|
||||
"@fluencelabs/marine-js": "0.3.38",
|
||||
"@fluencelabs/marine.background-runner": "workspace:0.1.0",
|
||||
"async": "3.2.4",
|
||||
"base64-js": "^1.5.1",
|
||||
"bs58": "5.0.0",
|
||||
"buffer": "6.0.3",
|
||||
"cids": "1.1.9",
|
||||
"loglevel": "1.8.1",
|
||||
"multiformats": "9.9.0",
|
||||
"peer-id": "0.16.0",
|
||||
"platform": "1.3.6",
|
||||
"rxjs": "7.5.5",
|
||||
"ts-pattern": "3.3.3",
|
||||
"uuid": "8.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fluencelabs/marine.deps-loader.node": "workspace:0.1.0",
|
||||
"@fluencelabs/aqua": "0.7.7-362",
|
||||
"@fluencelabs/aqua-api": "0.9.1-373",
|
||||
"@fluencelabs/aqua-lib": "0.6.0",
|
||||
"@fluencelabs/fluence-network-environment": "1.0.13",
|
||||
"@types/bs58": "4.0.1",
|
||||
"@types/jest": "28.1.0",
|
||||
"@types/platform": "1.3.4",
|
||||
"@types/uuid": "8.3.2",
|
||||
"jest": "28.1.0",
|
||||
"jest-each": "28.1.3",
|
||||
"js-base64": "3.7.2",
|
||||
"multiaddr": "10.0.1",
|
||||
"ts-jest": "28.0.2",
|
||||
"ts-node": "10.9.1",
|
||||
"typescript": "4.6.4"
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
import * as bs58 from 'bs58';
|
||||
import { KeyPair } from '@fluencelabs/keypair';
|
||||
|
||||
describe('KeyPair tests', () => {
|
||||
it('generate keypair from seed', async function () {
|
||||
// arrange
|
||||
const random = await KeyPair.randomEd25519();
|
||||
const privateKey = random.toEd25519PrivateKey();
|
||||
|
||||
// act
|
||||
const keyPair = await KeyPair.fromEd25519SK(privateKey);
|
||||
const privateKey2 = keyPair.toEd25519PrivateKey();
|
||||
|
||||
// assert
|
||||
expect(privateKey).toStrictEqual(privateKey2);
|
||||
});
|
||||
|
||||
it('create keypair from ed25519 private key', async function () {
|
||||
// arrange
|
||||
const rustSK = 'jDaxLJzYtzgwTMrELJCAqavtmx85ktQNfB2rLcK7MhH';
|
||||
const sk = bs58.decode(rustSK);
|
||||
|
||||
// act
|
||||
const keyPair = await KeyPair.fromEd25519SK(sk);
|
||||
|
||||
// assert
|
||||
const expectedPeerId = '12D3KooWH1W3VznVZ87JH4FwABK4mkntcspTVWJDta6c2xg9Pzbp';
|
||||
expect(keyPair.Libp2pPeerId.toB58String()).toStrictEqual(expectedPeerId);
|
||||
});
|
||||
|
||||
it('create keypair from a seed phrase', async function () {
|
||||
// arrange
|
||||
const seedArray = new Uint8Array(32).fill(1);
|
||||
|
||||
// act
|
||||
const keyPair = await KeyPair.fromEd25519SK(seedArray);
|
||||
|
||||
// assert
|
||||
const expectedPeerId = '12D3KooWK99VoVxNE7XzyBwXEzW7xhK7Gpv85r9F3V3fyKSUKPH5';
|
||||
expect(keyPair.Libp2pPeerId.toB58String()).toStrictEqual(expectedPeerId);
|
||||
});
|
||||
});
|
@ -1,335 +0,0 @@
|
||||
import { CallParams, CallServiceData } from '../../commonTypes';
|
||||
import each from 'jest-each';
|
||||
import { builtInServices } from '../../builtins/common';
|
||||
import { KeyPair } from '@fluencelabs/keypair';
|
||||
import { Sig, defaultSigGuard } from '../../builtins/Sig';
|
||||
import { toUint8Array } from 'js-base64';
|
||||
import { allowServiceFn } from '../../builtins/securityGuard';
|
||||
|
||||
const a10b20 = `{
|
||||
"a": 10,
|
||||
"b": 20
|
||||
}`;
|
||||
|
||||
const oneTwoThreeFour = `[
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4
|
||||
]`;
|
||||
|
||||
describe('Tests for default handler', () => {
|
||||
// prettier-ignore
|
||||
each`
|
||||
serviceId | fnName | args | retCode | result
|
||||
${'op'} | ${'identity'} | ${[]} | ${0} | ${{}}
|
||||
${'op'} | ${'identity'} | ${[1]} | ${0} | ${1}
|
||||
${'op'} | ${'identity'} | ${[1, 2]} | ${1} | ${'identity accepts up to 1 arguments, received 2 arguments'}
|
||||
|
||||
${'op'} | ${'noop'} | ${[1, 2]} | ${0} | ${{}}
|
||||
|
||||
${'op'} | ${'array'} | ${[1, 2, 3]} | ${0} | ${[1, 2, 3]}
|
||||
|
||||
${'op'} | ${'array_length'} | ${[[1, 2, 3]]} | ${0} | ${3}
|
||||
${'op'} | ${'array_length'} | ${[]} | ${1} | ${'array_length accepts exactly one argument, found: 0'}
|
||||
|
||||
${'op'} | ${'concat'} | ${[[1, 2], [3, 4], [5, 6]]} | ${0} | ${[1, 2, 3, 4, 5, 6]}
|
||||
${'op'} | ${'concat'} | ${[[1, 2]]} | ${0} | ${[1, 2]}
|
||||
${'op'} | ${'concat'} | ${[]} | ${0} | ${[]}
|
||||
${'op'} | ${'concat'} | ${[1, [1, 2], 1]} | ${1} | ${"All arguments of 'concat' must be arrays: arguments 0, 2 are not"}
|
||||
|
||||
${'op'} | ${'string_to_b58'} | ${["test"]} | ${0} | ${"3yZe7d"}
|
||||
${'op'} | ${'string_to_b58'} | ${["test", 1]} | ${1} | ${"string_to_b58 accepts only one string argument"}
|
||||
|
||||
${'op'} | ${'string_from_b58'} | ${["3yZe7d"]} | ${0} | ${"test"}
|
||||
${'op'} | ${'string_from_b58'} | ${["3yZe7d", 1]} | ${1} | ${"string_from_b58 accepts only one string argument"}
|
||||
|
||||
${'op'} | ${'bytes_to_b58'} | ${[[116, 101, 115, 116]]} | ${0} | ${"3yZe7d"}
|
||||
${'op'} | ${'bytes_to_b58'} | ${[[116, 101, 115, 116], 1]} | ${1} | ${"bytes_to_b58 accepts only single argument: array of numbers"}
|
||||
|
||||
${'op'} | ${'bytes_from_b58'} | ${["3yZe7d"]} | ${0} | ${[116, 101, 115, 116]}
|
||||
${'op'} | ${'bytes_from_b58'} | ${["3yZe7d", 1]} | ${1} | ${"bytes_from_b58 accepts only one string argument"}
|
||||
|
||||
${'op'} | ${'sha256_string'} | ${["hello, world!"]} | ${0} | ${"QmVQ8pg6L1tpoWYeq6dpoWqnzZoSLCh7E96fCFXKvfKD3u"}
|
||||
${'op'} | ${'sha256_string'} | ${["hello, world!", true]} | ${0} | ${"84V7ZxLW7qKsx1Qvbd63BdGaHxUc3TfT2MBPqAXM7Wyu"}
|
||||
${'op'} | ${'sha256_string'} | ${[]} | ${1} | ${"sha256_string accepts 1-3 arguments, found: 0"}
|
||||
|
||||
${'op'} | ${'concat_strings'} | ${[]} | ${0} | ${""}
|
||||
${'op'} | ${'concat_strings'} | ${["a", "b", "c"]} | ${0} | ${"abc"}
|
||||
|
||||
${'peer'} | ${'timeout'} | ${[200, []]} | ${0} | ${[]}}
|
||||
${'peer'} | ${'timeout'} | ${[200, ['test']]} | ${0} | ${['test']}}
|
||||
${'peer'} | ${'timeout'} | ${[]} | ${1} | ${'timeout accepts exactly two arguments: timeout duration in ms and a message string'}}
|
||||
${'peer'} | ${'timeout'} | ${[200, 'test', 1]} | ${1} | ${'timeout accepts exactly two arguments: timeout duration in ms and a message string'}}
|
||||
|
||||
${'debug'} | ${'stringify'} | ${[]} | ${0} | ${'"<empty argument list>"'}}
|
||||
${'debug'} | ${'stringify'} | ${[{a: 10, b: 20}]} | ${0} | ${a10b20}}
|
||||
${'debug'} | ${'stringify'} | ${[1, 2, 3, 4]} | ${0} | ${oneTwoThreeFour}}
|
||||
|
||||
${'math'} | ${'add'}" | ${[2, 2]} | ${0} | ${4}
|
||||
${'math'} | ${'add'}" | ${[2]} | ${1} | ${"Expected 2 argument(s). Got 1"}
|
||||
|
||||
${'math'} | ${'sub'}" | ${[2, 2]} | ${0} | ${0}
|
||||
${'math'} | ${'sub'}" | ${[2, 3]} | ${0} | ${-1}
|
||||
|
||||
${'math'} | ${'mul'}" | ${[2, 2]} | ${0} | ${4}
|
||||
${'math'} | ${'mul'}" | ${[2, 0]} | ${0} | ${0}
|
||||
${'math'} | ${'mul'}" | ${[2, -1]} | ${0} | ${-2}
|
||||
|
||||
${'math'} | ${'fmul'}" | ${[10, 0.66]} | ${0} | ${6}
|
||||
${'math'} | ${'fmul'}" | ${[0.5, 0.5]} | ${0} | ${0}
|
||||
${'math'} | ${'fmul'}" | ${[100.5, 0.5]} | ${0} | ${50}
|
||||
|
||||
${'math'} | ${'div'}" | ${[2, 2]} | ${0} | ${1}
|
||||
${'math'} | ${'div'}" | ${[2, 3]} | ${0} | ${0}
|
||||
${'math'} | ${'div'}" | ${[10, 5]} | ${0} | ${2}
|
||||
|
||||
${'math'} | ${'rem'}" | ${[10, 3]} | ${0} | ${1}
|
||||
|
||||
${'math'} | ${'pow'}" | ${[2, 2]} | ${0} | ${4}
|
||||
${'math'} | ${'pow'}" | ${[2, 0]} | ${0} | ${1}
|
||||
|
||||
${'math'} | ${'log'}" | ${[2, 2]} | ${0} | ${1}
|
||||
${'math'} | ${'log'}" | ${[2, 4]} | ${0} | ${2}
|
||||
|
||||
${'cmp'} | ${'gt'}" | ${[2, 4]} | ${0} | ${false}
|
||||
${'cmp'} | ${'gte'}" | ${[2, 4]} | ${0} | ${false}
|
||||
${'cmp'} | ${'gte'}" | ${[4, 2]} | ${0} | ${true}
|
||||
${'cmp'} | ${'gte'}" | ${[2, 2]} | ${0} | ${true}
|
||||
|
||||
${'cmp'} | ${'lt'}" | ${[2, 4]} | ${0} | ${true}
|
||||
${'cmp'} | ${'lte'}" | ${[2, 4]} | ${0} | ${true}
|
||||
${'cmp'} | ${'lte'}" | ${[4, 2]} | ${0} | ${false}
|
||||
${'cmp'} | ${'lte'}" | ${[2, 2]} | ${0} | ${true}
|
||||
|
||||
${'cmp'} | ${'cmp'}" | ${[2, 4]} | ${0} | ${-1}
|
||||
${'cmp'} | ${'cmp'}" | ${[2, -4]} | ${0} | ${1}
|
||||
${'cmp'} | ${'cmp'}" | ${[2, 2]} | ${0} | ${0}
|
||||
|
||||
${'array'} | ${'sum'}" | ${[[1, 2, 3]]} | ${0} | ${6}
|
||||
${'array'} | ${'dedup'}" | ${[["a", "a", "b", "c", "a", "b", "c"]]} | ${0} | ${["a", "b", "c"]}
|
||||
${'array'} | ${'intersect'}" | ${[["a", "b", "c"], ["c", "b", "d"]]} | ${0} | ${["b", "c"]}
|
||||
${'array'} | ${'diff'}" | ${[["a", "b", "c"], ["c", "b", "d"]]} | ${0} | ${["a"]}
|
||||
${'array'} | ${'sdiff'}" | ${[["a", "b", "c"], ["c", "b", "d"]]} | ${0} | ${["a", "d"]}
|
||||
|
||||
${'json'} | ${'obj'}" | ${["a", 10, "b", "string", "c", null]} | ${0} | ${{a: 10, b: "string", c: null}}
|
||||
${'json'} | ${'obj'}" | ${["a", 10, "b", "string", "c"]} | ${1} | ${"Expected even number of argument(s). Got 5"}
|
||||
${'json'} | ${'obj'}" | ${[]} | ${0} | ${{}}
|
||||
|
||||
${'json'} | ${'put'}" | ${[{}, "a", 10]} | ${0} | ${{a: 10}}
|
||||
${'json'} | ${'put'}" | ${[{b: 11}, "a", 10]} | ${0} | ${{a: 10, b: 11}}
|
||||
${'json'} | ${'put'}" | ${["a", "a", 11]} | ${1} | ${"Argument 0 expected to be of type object, Got string"}
|
||||
${'json'} | ${'put'}" | ${[{}, "a", 10, "b", 20]} | ${1} | ${"Expected 3 argument(s). Got 5"}
|
||||
${'json'} | ${'put'}" | ${[{}]} | ${1} | ${"Expected 3 argument(s). Got 1"}
|
||||
|
||||
${'json'} | ${'puts'}" | ${[{}, "a", 10]} | ${0} | ${{a: 10}}
|
||||
${'json'} | ${'puts'}" | ${[{b: 11}, "a", 10]} | ${0} | ${{a: 10, b: 11}}
|
||||
${'json'} | ${'puts'}" | ${[{}, "a", 10, "b", "string", "c", null]} | ${0} | ${{a: 10, b: "string", c: null}}
|
||||
${'json'} | ${'puts'}" | ${[{x: "text"}, "a", 10, "b", "string"]} | ${0} | ${{a: 10, b: "string", x: "text"}}
|
||||
${'json'} | ${'puts'}" | ${[{}]} | ${1} | ${"Expected more than 3 argument(s). Got 1"}
|
||||
${'json'} | ${'puts'}" | ${["a", "a", 11]} | ${1} | ${"Argument 0 expected to be of type object, Got string"}
|
||||
|
||||
${'json'} | ${'stringify'}" | ${[{a: 10, b: "string", c: null}]} | ${0} | ${"{\"a\":10,\"b\":\"string\",\"c\":null}"}
|
||||
${'json'} | ${'stringify'}" | ${[1]} | ${1} | ${"Argument 0 expected to be of type object, Got number"}
|
||||
${'json'} | ${'parse'}" | ${["{\"a\":10,\"b\":\"string\",\"c\":null}"]} | ${0} | ${{a: 10, b: "string", c: null}}
|
||||
${'json'} | ${'parse'}" | ${["incorrect"]} | ${1} | ${"Unexpected token i in JSON at position 0"}
|
||||
${'json'} | ${'parse'}" | ${[10]} | ${1} | ${"Argument 0 expected to be of type string, Got number"}
|
||||
|
||||
`.test(
|
||||
//
|
||||
'$fnName with $args expected retcode: $retCode and result: $result',
|
||||
async ({ serviceId, fnName, args, retCode, result }) => {
|
||||
// arrange
|
||||
const req: CallServiceData = {
|
||||
serviceId: serviceId,
|
||||
fnName: fnName,
|
||||
args: args,
|
||||
tetraplets: [],
|
||||
particleContext: {
|
||||
particleId: 'some',
|
||||
initPeerId: 'init peer id',
|
||||
timestamp: 595951200,
|
||||
ttl: 595961200,
|
||||
signature: 'sig',
|
||||
},
|
||||
};
|
||||
|
||||
// act
|
||||
const fn = builtInServices[req.serviceId][req.fnName];
|
||||
const res = await fn(req);
|
||||
|
||||
// assert
|
||||
expect(res).toMatchObject({
|
||||
retCode: retCode,
|
||||
result: result,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
it('should return correct error message for identiy service', async () => {
|
||||
// arrange
|
||||
const req: CallServiceData = {
|
||||
serviceId: 'peer',
|
||||
fnName: 'identify',
|
||||
args: [],
|
||||
tetraplets: [],
|
||||
particleContext: {
|
||||
particleId: 'some',
|
||||
initPeerId: 'init peer id',
|
||||
timestamp: 595951200,
|
||||
ttl: 595961200,
|
||||
signature: 'sig',
|
||||
},
|
||||
};
|
||||
|
||||
// act
|
||||
const fn = builtInServices[req.serviceId][req.fnName];
|
||||
const res = await fn(req);
|
||||
|
||||
// assert
|
||||
expect(res).toMatchObject({
|
||||
retCode: 0,
|
||||
result: {
|
||||
external_addresses: [],
|
||||
node_version: expect.stringContaining('js'),
|
||||
air_version: expect.stringContaining('js'),
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const key = '+cmeYlZKj+MfSa9dpHV+BmLPm6wq4inGlsPlQ1GvtPk=';
|
||||
|
||||
const context = (async () => {
|
||||
const keyBytes = toUint8Array(key);
|
||||
const kp = await KeyPair.fromEd25519SK(keyBytes);
|
||||
const res = {
|
||||
peerKeyPair: kp,
|
||||
peerId: kp.Libp2pPeerId.toB58String(),
|
||||
};
|
||||
return res;
|
||||
})();
|
||||
|
||||
const testData = [1, 2, 3, 4, 5, 6, 7, 9, 10];
|
||||
|
||||
// signature produced by KeyPair created from key above (`key` variable)
|
||||
const testDataSig = [
|
||||
224, 104, 245, 206, 140, 248, 27, 72, 68, 133, 111, 10, 164, 197, 242, 132, 107, 77, 224, 67, 99, 106, 76, 29, 144,
|
||||
121, 122, 169, 36, 173, 58, 80, 170, 102, 137, 253, 157, 247, 168, 87, 162, 223, 188, 214, 203, 220, 52, 246, 29,
|
||||
86, 77, 71, 224, 248, 16, 213, 254, 75, 78, 239, 243, 222, 241, 15,
|
||||
];
|
||||
|
||||
// signature produced by KeyPair created from some random KeyPair
|
||||
const testDataWrongSig = [
|
||||
116, 247, 189, 118, 236, 53, 147, 123, 219, 75, 176, 105, 101, 108, 233, 137, 97, 14, 146, 132, 252, 70, 51, 153,
|
||||
237, 167, 156, 150, 36, 90, 229, 108, 166, 231, 255, 137, 8, 246, 125, 0, 213, 150, 83, 196, 237, 221, 131, 159,
|
||||
157, 159, 25, 109, 95, 160, 181, 65, 254, 238, 47, 156, 240, 151, 58, 14,
|
||||
];
|
||||
|
||||
const makeTetraplet = (initPeerId: string, serviceId?: string, fnName?: string): CallParams<'data'> => {
|
||||
return {
|
||||
initPeerId: initPeerId,
|
||||
tetraplets: {
|
||||
data: [
|
||||
{
|
||||
function_name: fnName,
|
||||
service_id: serviceId,
|
||||
},
|
||||
],
|
||||
},
|
||||
} as any;
|
||||
};
|
||||
|
||||
describe('Sig service tests', () => {
|
||||
it('sig.sign should create the correct signature', async () => {
|
||||
const ctx = await context;
|
||||
const sig = new Sig(ctx.peerKeyPair);
|
||||
|
||||
const res = await sig.sign(testData, makeTetraplet(ctx.peerId));
|
||||
|
||||
expect(res.success).toBe(true);
|
||||
expect(res.signature).toStrictEqual(testDataSig);
|
||||
});
|
||||
|
||||
it('sig.verify should return true for the correct signature', async () => {
|
||||
const ctx = await context;
|
||||
const sig = new Sig(ctx.peerKeyPair);
|
||||
|
||||
const res = await sig.verify(testDataSig, testData);
|
||||
|
||||
expect(res).toBe(true);
|
||||
});
|
||||
|
||||
it('sig.verify should return false for the incorrect signature', async () => {
|
||||
const ctx = await context;
|
||||
const sig = new Sig(ctx.peerKeyPair);
|
||||
|
||||
const res = await sig.verify(testDataWrongSig, testData);
|
||||
|
||||
expect(res).toBe(false);
|
||||
});
|
||||
|
||||
it('sign-verify call chain should work', async () => {
|
||||
const ctx = await context;
|
||||
const sig = new Sig(ctx.peerKeyPair);
|
||||
|
||||
const signature = await sig.sign(testData, makeTetraplet(ctx.peerId));
|
||||
const res = await sig.verify(signature.signature as number[], testData);
|
||||
|
||||
expect(res).toBe(true);
|
||||
});
|
||||
|
||||
it('sig.sign with defaultSigGuard should work for correct callParams', async () => {
|
||||
const ctx = await context;
|
||||
const sig = new Sig(ctx.peerKeyPair);
|
||||
sig.securityGuard = defaultSigGuard(ctx.peerId);
|
||||
|
||||
const signature = await sig.sign(testData, makeTetraplet(ctx.peerId, 'registry', 'get_route_bytes'));
|
||||
|
||||
await expect(signature).toBeDefined();
|
||||
});
|
||||
|
||||
it('sig.sign with defaultSigGuard should not allow particles initiated from incorrect service', async () => {
|
||||
const ctx = await context;
|
||||
const sig = new Sig(ctx.peerKeyPair);
|
||||
sig.securityGuard = defaultSigGuard(ctx.peerId);
|
||||
|
||||
const res = await sig.sign(testData, makeTetraplet(ctx.peerId, 'other_service', 'other_fn'));
|
||||
|
||||
await expect(res.success).toBe(false);
|
||||
await expect(res.error).toBe('Security guard validation failed');
|
||||
});
|
||||
|
||||
it('sig.sign with defaultSigGuard should not allow particles initiated from other peers', async () => {
|
||||
const ctx = await context;
|
||||
const sig = new Sig(ctx.peerKeyPair);
|
||||
sig.securityGuard = defaultSigGuard(ctx.peerId);
|
||||
|
||||
const res = await sig.sign(
|
||||
testData,
|
||||
makeTetraplet((await KeyPair.randomEd25519()).getPeerId(), 'registry', 'get_key_bytes'),
|
||||
);
|
||||
|
||||
await expect(res.success).toBe(false);
|
||||
await expect(res.error).toBe('Security guard validation failed');
|
||||
});
|
||||
|
||||
it('changing securityGuard should work', async () => {
|
||||
const ctx = await context;
|
||||
const sig = new Sig(ctx.peerKeyPair);
|
||||
sig.securityGuard = allowServiceFn('test', 'test');
|
||||
|
||||
const successful1 = await sig.sign(testData, makeTetraplet(ctx.peerId, 'test', 'test'));
|
||||
const unSuccessful1 = await sig.sign(testData, makeTetraplet(ctx.peerId, 'wrong', 'wrong'));
|
||||
|
||||
sig.securityGuard = allowServiceFn('wrong', 'wrong');
|
||||
|
||||
const successful2 = await sig.sign(testData, makeTetraplet(ctx.peerId, 'wrong', 'wrong'));
|
||||
const unSuccessful2 = await sig.sign(testData, makeTetraplet(ctx.peerId, 'test', 'test'));
|
||||
|
||||
expect(successful1.success).toBe(true);
|
||||
expect(successful2.success).toBe(true);
|
||||
expect(unSuccessful1.success).toBe(false);
|
||||
expect(unSuccessful2.success).toBe(false);
|
||||
});
|
||||
});
|
@ -1,21 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist/",
|
||||
"baseUrl": ".",
|
||||
"downlevelIteration": true,
|
||||
"sourceMap": true,
|
||||
"inlineSources": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"resolveJsonModule": true,
|
||||
"target": "ES5",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"declaration": true,
|
||||
"esModuleInterop": true,
|
||||
"declarationMap": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"exclude": ["node_modules", "dist", "bundle"],
|
||||
"include": ["src/**/*"]
|
||||
}
|
21
packages/core/keypair/.gitignore
vendored
21
packages/core/keypair/.gitignore
vendored
@ -1,21 +0,0 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
bundle/
|
||||
|
||||
dist
|
||||
esm
|
||||
types
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
.idea
|
@ -1,12 +0,0 @@
|
||||
.idea
|
||||
.gitignore
|
||||
node_modules
|
||||
types
|
||||
|
||||
src/
|
||||
|
||||
tsconfig.json
|
||||
webpack.config.js
|
||||
|
||||
bundle
|
||||
pkg
|
@ -1,8 +0,0 @@
|
||||
module.exports = {
|
||||
semi: true,
|
||||
trailingComma: "all",
|
||||
singleQuote: true,
|
||||
printWidth: 120,
|
||||
tabWidth: 4,
|
||||
useTabs: false
|
||||
};
|
@ -1,13 +0,0 @@
|
||||
## Contribute Code
|
||||
|
||||
You are welcome to contribute to Fluence.
|
||||
|
||||
Things you need to know:
|
||||
|
||||
1. You need to **agree to the Contributors License Agreement**. This is a common practice in all major Open Source projects. At the current moment we are unable to accept contributions made on behalf of a company. Only individual contributions will be accepted.
|
||||
2. **Not all proposed contributions can be accepted**. Some features may e.g. just fit a third-party add-on better. The contribution must fit the overall direction of Fluence and really improve it. The more effort you invest, the better you should clarify in advance whether the contribution fits: the best way would be to just open an issue to discuss the contribution you plan to make.
|
||||
|
||||
### Contributor License Agreement
|
||||
|
||||
When you contribute, you have to be aware that your contribution is covered by **Apache License 2.0**, but might relicensed under few other software licenses mentioned in the **Contributor License Agreement**.
|
||||
In particular you need to agree to the [Contributor License Agreement](https://gist.github.com/fluencelabs-org/3f4cbb3cc14c1c0fb9ad99d8f7316ed7). If you agree to its content, you simply have to click on the link posted by the CLA assistant as a comment to the pull request. Click it to check the CLA, then accept it on the following screen if you agree to it. CLA assistant will save this decision for upcoming contributions and will notify you if there is any change to the CLA in the meantime.
|
@ -1,11 +0,0 @@
|
||||
# FluenceJS Keypair
|
||||
|
||||
This package is a part of FluenceJS, the official implementation of the Fluence Peer in typescript. See the [FluenceJS repo](https://github.com/fluencelabs/fluence-js) for more info
|
||||
|
||||
## Contributing
|
||||
|
||||
While the project is still in the early stages of development, you are welcome to track progress and contribute. As the project is undergoing rapid changes, interested contributors should contact the team before embarking on larger pieces of work. All contributors should consult with and agree to our [basic contributing rules](CONTRIBUTING.md).
|
||||
|
||||
## License
|
||||
|
||||
[Apache 2.0](LICENSE)
|
@ -1,25 +0,0 @@
|
||||
{
|
||||
"name": "@fluencelabs/keypair",
|
||||
"version": "0.2.0",
|
||||
"description": "Keypair implementation for Fluence JS Peer",
|
||||
"main": "./dist/index.js",
|
||||
"typings": "./dist/index.d.ts",
|
||||
"engines": {
|
||||
"node": ">=10",
|
||||
"pnpm": ">=3"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc"
|
||||
},
|
||||
"repository": "https://github.com/fluencelabs/fluence-js",
|
||||
"author": "Fluence Labs",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"peer-id": "0.16.0",
|
||||
"libp2p-crypto": "0.21.2",
|
||||
"js-base64": "3.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "4.6.4"
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist/",
|
||||
"lib": ["ES2015"],
|
||||
"target": "ES5",
|
||||
"sourceMap": true,
|
||||
"declaration": true,
|
||||
"module": "commonjs",
|
||||
},
|
||||
"exclude": ["node_modules", "dist"],
|
||||
"include": ["src/**/*"]
|
||||
}
|
69
packages/core/package.json
Normal file
69
packages/core/package.json
Normal file
@ -0,0 +1,69 @@
|
||||
{
|
||||
"name": "@fluencelabs/js-peer",
|
||||
"version": "0.1.0",
|
||||
"description": "TypeScript implementation of Fluence Peer",
|
||||
"main": "./dist/index.js",
|
||||
"typings": "./dist/index.d.ts",
|
||||
"engines": {
|
||||
"node": ">=10",
|
||||
"pnpm": ">=3"
|
||||
},
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"compile-aqua": "aqua -i ./aqua/ -o ./src/internal/_aqua",
|
||||
"test:smoke": "node ./dist/js-peer/__test__/integration/smokeTest.js",
|
||||
"test": "NODE_OPTIONS=--experimental-vm-modules pnpm jest",
|
||||
"test:unit": "NODE_OPTIONS=--experimental-vm-modules pnpm jest --testPathPattern=src/__test__/unit",
|
||||
"test:integration": "NODE_OPTIONS=--experimental-vm-modules pnpm jest --testPathPattern=src/__test__/integration"
|
||||
},
|
||||
"repository": "https://github.com/fluencelabs/fluence-js",
|
||||
"author": "Fluence Labs",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@fluencelabs/avm": "0.31.10",
|
||||
"@fluencelabs/marine-js": "0.3.44",
|
||||
"multiformats": "11.0.1",
|
||||
"async": "3.2.4",
|
||||
"bs58": "5.0.0",
|
||||
"buffer": "6.0.3",
|
||||
"loglevel": "1.8.1",
|
||||
"@libp2p/peer-id": "2.0.1",
|
||||
"platform": "1.3.6",
|
||||
"rxjs": "7.5.5",
|
||||
"ts-pattern": "3.3.3",
|
||||
"uuid": "8.3.2",
|
||||
"threads": "1.7.0",
|
||||
"@libp2p/crypto": "1.0.8",
|
||||
"@libp2p/peer-id-factory": "2.0.1",
|
||||
"@libp2p/interface-peer-id": "2.0.1",
|
||||
"@libp2p/interface-keys": "1.0.7",
|
||||
"js-base64": "3.7.2",
|
||||
"browser-or-node": "2.0.0",
|
||||
"it-length-prefixed": "8.0.4",
|
||||
"it-pipe": "2.0.5",
|
||||
"it-map": "2.0.0",
|
||||
"uint8arrays": "4.0.3",
|
||||
"@chainsafe/libp2p-noise": "11.0.0",
|
||||
"libp2p": "0.42.2",
|
||||
"@libp2p/interfaces": "3.3.1",
|
||||
"@libp2p/interface-connection": "3.0.8",
|
||||
"@libp2p/mplex": "7.1.1",
|
||||
"@libp2p/websockets": "5.0.3",
|
||||
"@multiformats/multiaddr": "11.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "16.11.59",
|
||||
"@fluencelabs/aqua": "0.7.7-362",
|
||||
"@fluencelabs/aqua-api": "0.9.1-373",
|
||||
"@fluencelabs/aqua-lib": "0.6.0",
|
||||
"@fluencelabs/fluence-network-environment": "1.0.13",
|
||||
"@multiformats/multiaddr": "11.3.0",
|
||||
"@types/bs58": "4.0.1",
|
||||
"@types/platform": "1.3.4",
|
||||
"@types/uuid": "8.3.2",
|
||||
"@types/jest": "29.4.0",
|
||||
"jest": "29.4.1",
|
||||
"ts-jest": "29.0.5"
|
||||
}
|
||||
}
|
@ -13,23 +13,25 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { FluenceConnection, ParticleHandler, PeerIdB58 } from '@fluencelabs/interfaces';
|
||||
// @ts-ignore
|
||||
import Websockets from 'libp2p-websockets';
|
||||
// @ts-ignore
|
||||
import Mplex from 'libp2p-mplex';
|
||||
import Lib2p2Peer from 'libp2p';
|
||||
import { decode, encode } from 'it-length-prefixed';
|
||||
import { FluenceConnection, ParticleHandler, PeerIdB58 } from '../interfaces/index.js';
|
||||
import { pipe } from 'it-pipe';
|
||||
import * as log from 'loglevel';
|
||||
import { Noise } from '@chainsafe/libp2p-noise';
|
||||
import PeerId from 'peer-id';
|
||||
import type { MultiaddrInput } from 'multiaddr';
|
||||
import { Connection } from 'libp2p-interfaces/src/topology';
|
||||
import { Multiaddr } from 'multiaddr';
|
||||
// @ts-ignore
|
||||
import { all as allow_all } from 'libp2p-websockets/src/filters';
|
||||
import Buffer from './Buffer';
|
||||
import { encode, decode } from 'it-length-prefixed';
|
||||
import type { PeerId } from '@libp2p/interface-peer-id';
|
||||
import { createLibp2p, Libp2p } from 'libp2p';
|
||||
|
||||
import { noise } from '@chainsafe/libp2p-noise';
|
||||
import { mplex } from '@libp2p/mplex';
|
||||
import { webSockets } from '@libp2p/websockets';
|
||||
import { all } from '@libp2p/websockets/filters';
|
||||
import { multiaddr } from '@multiformats/multiaddr';
|
||||
import type { MultiaddrInput, Multiaddr } from '@multiformats/multiaddr';
|
||||
import type { Connection } from '@libp2p/interface-connection';
|
||||
|
||||
import map from 'it-map';
|
||||
import { fromString } from 'uint8arrays/from-string';
|
||||
import { toString } from 'uint8arrays/to-string';
|
||||
|
||||
import log from 'loglevel';
|
||||
|
||||
export const PROTOCOL_NAME = '/fluence/particle/2.0.0';
|
||||
|
||||
@ -53,47 +55,43 @@ export interface FluenceConnectionOptions {
|
||||
dialTimeoutMs?: number;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Implementation for JS peers which connects to Fluence through relay node
|
||||
*/
|
||||
export class RelayConnection extends FluenceConnection {
|
||||
constructor(
|
||||
public peerId: PeerIdB58,
|
||||
private _lib2p2Peer: Lib2p2Peer,
|
||||
private _lib2p2Peer: Libp2p,
|
||||
private _relayAddress: Multiaddr,
|
||||
public readonly relayPeerId: PeerIdB58,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
private _connection?: Connection;
|
||||
|
||||
static async createConnection(options: FluenceConnectionOptions): Promise<RelayConnection> {
|
||||
const transportKey = Websockets.prototype[Symbol.toStringTag];
|
||||
const lib2p2Peer = await Lib2p2Peer.create({
|
||||
const lib2p2Peer = await createLibp2p({
|
||||
peerId: options.peerId,
|
||||
modules: {
|
||||
transport: [Websockets],
|
||||
streamMuxer: [Mplex],
|
||||
connEncryption: [new Noise()],
|
||||
},
|
||||
config: {
|
||||
transport: {
|
||||
[transportKey]: {
|
||||
filter: allow_all,
|
||||
},
|
||||
},
|
||||
},
|
||||
dialer: {
|
||||
dialTimeout: options?.dialTimeoutMs,
|
||||
},
|
||||
transports: [
|
||||
webSockets({
|
||||
filter: all,
|
||||
}),
|
||||
],
|
||||
streamMuxers: [mplex()],
|
||||
connectionEncryption: [noise()],
|
||||
});
|
||||
const relayMultiaddr = new Multiaddr(options.relayAddress);
|
||||
|
||||
|
||||
const relayMultiaddr = multiaddr(options.relayAddress);
|
||||
const relayPeerId = relayMultiaddr.getPeerId();
|
||||
if (relayPeerId === null) {
|
||||
throw new Error('Specified multiaddr is invalid or missing peer id: ' + options.relayAddress);
|
||||
}
|
||||
|
||||
return new RelayConnection(
|
||||
// force new line
|
||||
options.peerId.toB58String(),
|
||||
options.peerId.toString(),
|
||||
lib2p2Peer,
|
||||
relayMultiaddr,
|
||||
relayPeerId,
|
||||
@ -113,18 +111,22 @@ export class RelayConnection extends FluenceConnection {
|
||||
)} instead.`,
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
TODO:: find out why this doesn't work and a new connection has to be established each time
|
||||
if (this._connection.streams.length !== 1) {
|
||||
throw new Error('Incorrect number of streams in FluenceConnection');
|
||||
}
|
||||
|
||||
const sink = this._connection.streams[0].sink;
|
||||
*/
|
||||
const conn = await this._lib2p2Peer.dialProtocol(this._relayAddress, PROTOCOL_NAME);
|
||||
const sink = conn.stream.sink;
|
||||
|
||||
const stream = await this._lib2p2Peer.dialProtocol(this._relayAddress, PROTOCOL_NAME);
|
||||
const sink = stream.sink;
|
||||
|
||||
pipe(
|
||||
// force new line
|
||||
[Buffer.from(particle, 'utf8')],
|
||||
[fromString(particle)],
|
||||
// @ts-ignore
|
||||
encode(),
|
||||
sink,
|
||||
);
|
||||
@ -138,7 +140,9 @@ export class RelayConnection extends FluenceConnection {
|
||||
stream.source,
|
||||
// @ts-ignore
|
||||
decode(),
|
||||
async (source: AsyncIterable<string>) => {
|
||||
// @ts-ignore
|
||||
(source) => map(source, (buf) => toString(buf.subarray())),
|
||||
async (source) => {
|
||||
try {
|
||||
for await (const msg of source) {
|
||||
try {
|
||||
@ -153,7 +157,10 @@ export class RelayConnection extends FluenceConnection {
|
||||
},
|
||||
);
|
||||
});
|
||||
log.debug(`dialing to the node with client's address: ` + this._lib2p2Peer.peerId.toB58String());
|
||||
|
||||
|
||||
log.debug(`dialing to the node with client's address: ` + this._lib2p2Peer.peerId.toString());
|
||||
|
||||
try {
|
||||
this._connection = await this._lib2p2Peer.dial(this._relayAddress);
|
||||
} catch (e: any) {
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { JSONArray, JSONObject, LogLevel } from '@fluencelabs/marine-js';
|
||||
import type { JSONArray, JSONObject, LogLevel } from '@fluencelabs/marine-js/dist/types';
|
||||
import type { RunParameters, CallResultsArray, InterpreterResult } from '@fluencelabs/avm';
|
||||
import type { WorkerImplementation } from 'threads/dist/types/master';
|
||||
export type PeerIdB58 = string;
|
@ -15,26 +15,26 @@
|
||||
*/
|
||||
import 'buffer';
|
||||
|
||||
import { RelayConnection } from '@fluencelabs/connection';
|
||||
import { FluenceConnection, IAvmRunner, IMarine } from '@fluencelabs/interfaces';
|
||||
import { KeyPair } from '@fluencelabs/keypair';
|
||||
import type { MultiaddrInput } from 'multiaddr';
|
||||
import { CallServiceData, CallServiceResult, GenericCallServiceHandler, ResultCodes } from './commonTypes';
|
||||
import { PeerIdB58 } from './commonTypes';
|
||||
import { Particle, ParticleExecutionStage, ParticleQueueItem } from './Particle';
|
||||
import { throwIfNotSupported, dataToString, jsonify, isString, ServiceError } from './utils';
|
||||
import { RelayConnection } from '../connection/index.js';
|
||||
import { FluenceConnection, IAvmRunner, IMarine } from '../interfaces/index.js';
|
||||
import { KeyPair } from '../keypair/index.js';
|
||||
import type { MultiaddrInput } from '@multiformats/multiaddr';
|
||||
import { CallServiceData, CallServiceResult, GenericCallServiceHandler, ResultCodes } from './commonTypes.js';
|
||||
import { PeerIdB58 } from './commonTypes.js';
|
||||
import { Particle, ParticleExecutionStage, ParticleQueueItem } from './Particle.js';
|
||||
import { throwIfNotSupported, dataToString, jsonify, isString, ServiceError } from './utils.js';
|
||||
import { concatMap, filter, pipe, Subject, tap } from 'rxjs';
|
||||
import log from 'loglevel';
|
||||
import { builtInServices } from './builtins/common';
|
||||
import { defaultSigGuard, Sig } from './builtins/Sig';
|
||||
import { registerSig } from './_aqua/services';
|
||||
import { registerSrv } from './_aqua/single-module-srv';
|
||||
import { builtInServices } from './builtins/common.js';
|
||||
import { defaultSigGuard, Sig } from './builtins/Sig.js';
|
||||
import { registerSig } from './_aqua/services.js';
|
||||
import { registerSrv } from './_aqua/single-module-srv.js';
|
||||
import { Buffer } from 'buffer';
|
||||
|
||||
import { JSONValue } from '@fluencelabs/avm';
|
||||
import { NodeUtils, Srv } from './builtins/SingleModuleSrv';
|
||||
import { registerNodeUtils } from './_aqua/node-utils';
|
||||
import { LogLevel } from '@fluencelabs/marine-js';
|
||||
import { NodeUtils, Srv } from './builtins/SingleModuleSrv.js';
|
||||
import { registerNodeUtils } from './_aqua/node-utils.js';
|
||||
import { LogLevel } from '@fluencelabs/marine-js/dist/types';
|
||||
|
||||
/**
|
||||
* Node of the Fluence network specified as a pair of node's multiaddr and it's peer id
|
||||
@ -210,7 +210,7 @@ export class FluencePeer {
|
||||
if (this.connection === null) {
|
||||
return {
|
||||
isInitialized: true,
|
||||
peerId: this._keyPair.Libp2pPeerId.toB58String(),
|
||||
peerId: this._keyPair.getPeerId(),
|
||||
isConnected: false,
|
||||
relayPeerId: null,
|
||||
};
|
||||
@ -219,7 +219,7 @@ export class FluencePeer {
|
||||
if (this.connection.relayPeerId === null) {
|
||||
return {
|
||||
isInitialized: true,
|
||||
peerId: this._keyPair.Libp2pPeerId.toB58String(),
|
||||
peerId: this._keyPair.getPeerId(),
|
||||
isConnected: true,
|
||||
isDirect: true,
|
||||
relayPeerId: null,
|
||||
@ -228,7 +228,7 @@ export class FluencePeer {
|
||||
|
||||
return {
|
||||
isInitialized: true,
|
||||
peerId: this._keyPair.Libp2pPeerId.toB58String(),
|
||||
peerId: this._keyPair.getPeerId(),
|
||||
isConnected: true,
|
||||
relayPeerId: this.connection.relayPeerId,
|
||||
};
|
||||
@ -422,7 +422,7 @@ export class FluencePeer {
|
||||
async init(config: PeerConfig & Required<Pick<PeerConfig, 'KeyPair'>>) {
|
||||
this._keyPair = config.KeyPair;
|
||||
|
||||
const peerId = this._keyPair.Libp2pPeerId.toB58String();
|
||||
const peerId = this._keyPair.getPeerId();
|
||||
|
||||
if (config?.debug?.printParticleId) {
|
||||
this._printParticleId = true;
|
||||
@ -797,7 +797,7 @@ async function configToConnection(
|
||||
}
|
||||
|
||||
const res = await RelayConnection.createConnection({
|
||||
peerId: keyPair.Libp2pPeerId,
|
||||
peerId: keyPair.getLibp2pPeerId(),
|
||||
relayAddress: connectToMultiAddr,
|
||||
dialTimeoutMs: dialTimeoutMs,
|
||||
});
|
@ -13,12 +13,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { fromUint8Array, toUint8Array } from 'js-base64';
|
||||
import { CallResultsArray, LogLevel } from '@fluencelabs/avm';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { fromByteArray, toByteArray } from 'base64-js';
|
||||
import log from 'loglevel';
|
||||
import { ParticleContext } from './commonTypes';
|
||||
import { dataToString, jsonify } from './utils';
|
||||
import { ParticleContext } from './commonTypes.js';
|
||||
import { dataToString, jsonify } from './utils.js';
|
||||
import { Buffer } from 'buffer';
|
||||
|
||||
export class Particle {
|
||||
@ -45,7 +46,7 @@ export class Particle {
|
||||
json.id,
|
||||
json.timestamp,
|
||||
json.script,
|
||||
toByteArray(json.data),
|
||||
toUint8Array(json.data),
|
||||
json.ttl,
|
||||
json.init_peer_id,
|
||||
);
|
||||
@ -91,7 +92,7 @@ export class Particle {
|
||||
script: this.script,
|
||||
// TODO: copy signature from a particle after signatures will be implemented on nodes
|
||||
signature: [],
|
||||
data: this.data && fromByteArray(this.data),
|
||||
data: this.data && fromUint8Array(this.data),
|
||||
});
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { handleTimeout } from '../../utils';
|
||||
import { registerHandlersHelper, withPeer } from '../util';
|
||||
import { handleTimeout } from '../../utils.js';
|
||||
import { registerHandlersHelper, withPeer } from '../util.js';
|
||||
|
||||
describe('Avm spec', () => {
|
||||
it('Simple call', async () => {
|
@ -1,7 +1,7 @@
|
||||
import { Particle } from '../../Particle';
|
||||
import { doNothing } from '../../utils';
|
||||
import { FluencePeer } from '../../FluencePeer';
|
||||
import { mkTestPeer } from '../util';
|
||||
import { Particle } from '../../Particle.js';
|
||||
import { doNothing } from '../../utils.js';
|
||||
import { FluencePeer } from '../../FluencePeer.js';
|
||||
import { mkTestPeer } from '../util.js';
|
||||
|
||||
let peer: FluencePeer;
|
||||
|
@ -1,19 +1,21 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { compileAqua, withPeer } from '../util';
|
||||
import * as fs from 'fs';
|
||||
import * as url from 'url';
|
||||
import * as path from 'path';
|
||||
import { compileAqua, withPeer } from '../util.js';
|
||||
|
||||
let aqua: any;
|
||||
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
|
||||
|
||||
describe('Marine js tests', () => {
|
||||
beforeAll(async () => {
|
||||
const { services, functions } = await compileAqua(path.join(__dirname, './marine-js.aqua'));
|
||||
const { services, functions } = await compileAqua(path.join(__dirname, '../data/marine-js.aqua'));
|
||||
aqua = functions;
|
||||
});
|
||||
|
||||
it('should call marine service correctly', async () => {
|
||||
await withPeer(async (peer) => {
|
||||
// arrange
|
||||
const wasm = await fs.promises.readFile(__dirname + '/greeting.wasm');
|
||||
const wasm = await fs.promises.readFile(path.join(__dirname, '../data/greeting.wasm'));
|
||||
await peer.registerMarineService(wasm, 'greeting');
|
||||
|
||||
// act
|
||||
@ -37,7 +39,7 @@ describe('Marine js tests', () => {
|
||||
marineLogLevel: 'debug',
|
||||
},
|
||||
});
|
||||
const wasm = await fs.promises.readFile(__dirname + '/greeting-record.wasm');
|
||||
const wasm = await fs.promises.readFile(path.join(__dirname, '../data/greeting-record.wasm'));
|
||||
await peer.registerMarineService(wasm, 'greeting');
|
||||
|
||||
// act
|
@ -1,9 +1,7 @@
|
||||
import { Multiaddr } from 'multiaddr';
|
||||
|
||||
import { nodes } from '../connection';
|
||||
import { checkConnection, doNothing, handleTimeout } from '../../utils';
|
||||
import { registerHandlersHelper, mkTestPeer, withPeer, withConnectedPeer } from '../util';
|
||||
import { FluencePeer } from '../../FluencePeer';
|
||||
import { nodes } from '../connection.js';
|
||||
import { checkConnection, doNothing, handleTimeout } from '../../utils.js';
|
||||
import { registerHandlersHelper, mkTestPeer, withPeer, withConnectedPeer } from '../util.js';
|
||||
import { FluencePeer } from '../../FluencePeer.js';
|
||||
|
||||
describe('Typescript usage suite', () => {
|
||||
it('should perform test for FluencePeer class correctly', () => {
|
@ -1,9 +1,12 @@
|
||||
import path from 'path';
|
||||
import { KeyPair } from '@fluencelabs/keypair';
|
||||
import { allowServiceFn } from '../../builtins/securityGuard';
|
||||
import { Sig } from '../../builtins/Sig';
|
||||
import { compileAqua, withPeer } from '../util';
|
||||
import { registerServiceImpl } from '../../compilerSupport/registerService';
|
||||
import * as path from 'path';
|
||||
import * as url from 'url';
|
||||
import { KeyPair } from '../../../keypair/index.js';
|
||||
import { allowServiceFn } from '../../builtins/securityGuard.js';
|
||||
import { Sig } from '../../builtins/Sig.js';
|
||||
import { compileAqua, withPeer } from '../util.js';
|
||||
import { registerServiceImpl } from '../../compilerSupport/registerService.js';
|
||||
|
||||
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
|
||||
|
||||
let aqua: any;
|
||||
let sigDef: any;
|
||||
@ -11,7 +14,7 @@ let dataProviderDef: any;
|
||||
|
||||
describe('Sig service test suite', () => {
|
||||
beforeAll(async () => {
|
||||
const { services, functions } = await compileAqua(path.join(__dirname, './sigService.aqua'));
|
||||
const { services, functions } = await compileAqua(path.join(__dirname, '../data/sigService.aqua'));
|
||||
aqua = functions;
|
||||
sigDef = services.Sig;
|
||||
dataProviderDef = services.DataProvider;
|
67
packages/core/src/js-peer/__test__/integration/smokeTest.ts
Normal file
67
packages/core/src/js-peer/__test__/integration/smokeTest.ts
Normal file
@ -0,0 +1,67 @@
|
||||
import { handleTimeout } from '../../utils.js';
|
||||
import { nodes } from '../connection.js';
|
||||
import { mkTestPeer, registerHandlersHelper } from '../util.js';
|
||||
|
||||
const smokeTest = async () => {
|
||||
// arrange
|
||||
const peer = mkTestPeer();
|
||||
await peer.start({
|
||||
connectTo: nodes[0],
|
||||
});
|
||||
|
||||
const result = await new Promise<string[]>((resolve, reject) => {
|
||||
const script = `
|
||||
(xor
|
||||
(seq
|
||||
(call %init_peer_id% ("load" "relay") [] init_relay)
|
||||
(seq
|
||||
(call init_relay ("op" "identity") ["hello world!"] result)
|
||||
(call %init_peer_id% ("callback" "callback") [result])
|
||||
)
|
||||
)
|
||||
(seq
|
||||
(call init_relay ("op" "identity") [])
|
||||
(call %init_peer_id% ("callback" "error") [%last_error%])
|
||||
)
|
||||
)`;
|
||||
const particle = peer.internals.createNewParticle(script);
|
||||
|
||||
if (particle instanceof Error) {
|
||||
return reject(particle.message);
|
||||
}
|
||||
|
||||
registerHandlersHelper(peer, particle, {
|
||||
load: {
|
||||
relay: () => {
|
||||
return peer.getStatus().relayPeerId;
|
||||
},
|
||||
},
|
||||
callback: {
|
||||
callback: (args: any) => {
|
||||
const [val] = args;
|
||||
resolve(val);
|
||||
},
|
||||
error: (args: any) => {
|
||||
const [error] = args;
|
||||
reject(error);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
peer.internals.initiateParticle(particle, handleTimeout(reject));
|
||||
});
|
||||
|
||||
await peer.stop();
|
||||
|
||||
if (result[0] !== 'hello world!') {
|
||||
throw new Error('Expecting "hello wrold!" got ' + result[0]);
|
||||
}
|
||||
};
|
||||
|
||||
smokeTest()
|
||||
.then(() => {
|
||||
console.log('Test passed');
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('Test failed: ', err);
|
||||
});
|
@ -1,18 +1,20 @@
|
||||
import path from 'path';
|
||||
import { compileAqua, withPeer } from '../util';
|
||||
import * as path from 'path';
|
||||
import * as url from 'url';
|
||||
import { compileAqua, withPeer } from '../util.js';
|
||||
|
||||
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
|
||||
let aqua: any;
|
||||
|
||||
describe('Srv service test suite', () => {
|
||||
beforeAll(async () => {
|
||||
const { services, functions } = await compileAqua(path.join(__dirname, './srv.aqua'));
|
||||
const { services, functions } = await compileAqua(path.join(__dirname, '../data/srv.aqua'));
|
||||
aqua = functions;
|
||||
});
|
||||
|
||||
it('Use custom srv service, success path', async () => {
|
||||
await withPeer(async (peer) => {
|
||||
// arrange
|
||||
const wasm = path.join(__dirname, './greeting.wasm');
|
||||
const wasm = path.join(__dirname, '../data/greeting.wasm');
|
||||
|
||||
// act
|
||||
const res = await aqua.happy_path(peer, { file_path: wasm });
|
||||
@ -25,7 +27,7 @@ describe('Srv service test suite', () => {
|
||||
it('List deployed services', async () => {
|
||||
await withPeer(async (peer) => {
|
||||
// arrange
|
||||
const wasm = path.join(__dirname, './greeting.wasm');
|
||||
const wasm = path.join(__dirname, '../data/greeting.wasm');
|
||||
|
||||
// act
|
||||
const res = await aqua.list_services(peer, { file_path: wasm });
|
||||
@ -38,7 +40,7 @@ describe('Srv service test suite', () => {
|
||||
it('Correct error for removed services', async () => {
|
||||
await withPeer(async (peer) => {
|
||||
// arrange
|
||||
const wasm = path.join(__dirname, './greeting.wasm');
|
||||
const wasm = path.join(__dirname, '../data/greeting.wasm');
|
||||
|
||||
// act
|
||||
const res = await aqua.service_removed(peer, { file_path: wasm });
|
@ -1,4 +1,4 @@
|
||||
import { mkTestPeer } from '../util';
|
||||
import { mkTestPeer } from '../util.js';
|
||||
|
||||
const peer = mkTestPeer();
|
||||
|
304
packages/core/src/js-peer/__test__/unit/builtInHandler.spec.ts
Normal file
304
packages/core/src/js-peer/__test__/unit/builtInHandler.spec.ts
Normal file
@ -0,0 +1,304 @@
|
||||
import { toUint8Array } from 'js-base64';
|
||||
import { CallParams, CallServiceData } from '../../commonTypes.js';
|
||||
import { builtInServices } from '../../builtins/common.js';
|
||||
import { KeyPair } from '../../../keypair/index.js';
|
||||
import { Sig, defaultSigGuard } from '../../builtins/Sig.js';
|
||||
import { allowServiceFn } from '../../builtins/securityGuard.js';
|
||||
|
||||
const a10b20 = `{
|
||||
"a": 10,
|
||||
"b": 20
|
||||
}`;
|
||||
|
||||
const oneTwoThreeFour = `[
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4
|
||||
]`;
|
||||
|
||||
describe('Tests for default handler', () => {
|
||||
test.each`
|
||||
serviceId | fnName | args | retCode | result
|
||||
${'op'} | ${'identity'} | ${[]} | ${0} | ${{}}
|
||||
${'op'} | ${'identity'} | ${[1]} | ${0} | ${1}
|
||||
${'op'} | ${'identity'} | ${[1, 2]} | ${1} | ${'identity accepts up to 1 arguments, received 2 arguments'}
|
||||
${'op'} | ${'noop'} | ${[1, 2]} | ${0} | ${{}}
|
||||
${'op'} | ${'array'} | ${[1, 2, 3]} | ${0} | ${[1, 2, 3]}
|
||||
${'op'} | ${'array_length'} | ${[[1, 2, 3]]} | ${0} | ${3}
|
||||
${'op'} | ${'array_length'} | ${[]} | ${1} | ${'array_length accepts exactly one argument, found: 0'}
|
||||
${'op'} | ${'concat'} | ${[[1, 2], [3, 4], [5, 6]]} | ${0} | ${[1, 2, 3, 4, 5, 6]}
|
||||
${'op'} | ${'concat'} | ${[[1, 2]]} | ${0} | ${[1, 2]}
|
||||
${'op'} | ${'concat'} | ${[]} | ${0} | ${[]}
|
||||
${'op'} | ${'concat'} | ${[1, [1, 2], 1]} | ${1} | ${"All arguments of 'concat' must be arrays: arguments 0, 2 are not"}
|
||||
${'op'} | ${'string_to_b58'} | ${['test']} | ${0} | ${'3yZe7d'}
|
||||
${'op'} | ${'string_to_b58'} | ${['test', 1]} | ${1} | ${'string_to_b58 accepts only one string argument'}
|
||||
${'op'} | ${'string_from_b58'} | ${['3yZe7d']} | ${0} | ${'test'}
|
||||
${'op'} | ${'string_from_b58'} | ${['3yZe7d', 1]} | ${1} | ${'string_from_b58 accepts only one string argument'}
|
||||
${'op'} | ${'bytes_to_b58'} | ${[[116, 101, 115, 116]]} | ${0} | ${'3yZe7d'}
|
||||
${'op'} | ${'bytes_to_b58'} | ${[[116, 101, 115, 116], 1]} | ${1} | ${'bytes_to_b58 accepts only single argument: array of numbers'}
|
||||
${'op'} | ${'bytes_from_b58'} | ${['3yZe7d']} | ${0} | ${[116, 101, 115, 116]}
|
||||
${'op'} | ${'bytes_from_b58'} | ${['3yZe7d', 1]} | ${1} | ${'bytes_from_b58 accepts only one string argument'}
|
||||
${'op'} | ${'sha256_string'} | ${['hello, world!']} | ${0} | ${'QmVQ8pg6L1tpoWYeq6dpoWqnzZoSLCh7E96fCFXKvfKD3u'}
|
||||
${'op'} | ${'sha256_string'} | ${['hello, world!', true]} | ${0} | ${'84V7ZxLW7qKsx1Qvbd63BdGaHxUc3TfT2MBPqAXM7Wyu'}
|
||||
${'op'} | ${'sha256_string'} | ${[]} | ${1} | ${'sha256_string accepts 1-3 arguments, found: 0'}
|
||||
${'op'} | ${'concat_strings'} | ${[]} | ${0} | ${''}
|
||||
${'op'} | ${'concat_strings'} | ${['a', 'b', 'c']} | ${0} | ${'abc'}
|
||||
${'peer'} | ${'timeout'} | ${[200, []]} | ${0} | ${[]}
|
||||
${'peer'} | ${'timeout'} | ${[200, ['test']]} | ${0} | ${['test']}
|
||||
${'peer'} | ${'timeout'} | ${[]} | ${1} | ${'timeout accepts exactly two arguments: timeout duration in ms and a message string'}
|
||||
${'peer'} | ${'timeout'} | ${[200, 'test', 1]} | ${1} | ${'timeout accepts exactly two arguments: timeout duration in ms and a message string'}
|
||||
${'debug'} | ${'stringify'} | ${[]} | ${0} | ${'"<empty argument list>"'}
|
||||
${'debug'} | ${'stringify'} | ${[{ a: 10, b: 20 }]} | ${0} | ${a10b20}
|
||||
${'debug'} | ${'stringify'} | ${[1, 2, 3, 4]} | ${0} | ${oneTwoThreeFour}
|
||||
${'math'} | ${'add'} | ${[2, 2]} | ${0} | ${4}
|
||||
${'math'} | ${'add'} | ${[2]} | ${1} | ${'Expected 2 argument(s). Got 1'}
|
||||
${'math'} | ${'sub'} | ${[2, 2]} | ${0} | ${0}
|
||||
${'math'} | ${'sub'} | ${[2, 3]} | ${0} | ${-1}
|
||||
${'math'} | ${'mul'} | ${[2, 2]} | ${0} | ${4}
|
||||
${'math'} | ${'mul'} | ${[2, 0]} | ${0} | ${0}
|
||||
${'math'} | ${'mul'} | ${[2, -1]} | ${0} | ${-2}
|
||||
${'math'} | ${'fmul'} | ${[10, 0.66]} | ${0} | ${6}
|
||||
${'math'} | ${'fmul'} | ${[0.5, 0.5]} | ${0} | ${0}
|
||||
${'math'} | ${'fmul'} | ${[100.5, 0.5]} | ${0} | ${50}
|
||||
${'math'} | ${'div'} | ${[2, 2]} | ${0} | ${1}
|
||||
${'math'} | ${'div'} | ${[2, 3]} | ${0} | ${0}
|
||||
${'math'} | ${'div'} | ${[10, 5]} | ${0} | ${2}
|
||||
${'math'} | ${'rem'} | ${[10, 3]} | ${0} | ${1}
|
||||
${'math'} | ${'pow'} | ${[2, 2]} | ${0} | ${4}
|
||||
${'math'} | ${'pow'} | ${[2, 0]} | ${0} | ${1}
|
||||
${'math'} | ${'log'} | ${[2, 2]} | ${0} | ${1}
|
||||
${'math'} | ${'log'} | ${[2, 4]} | ${0} | ${2}
|
||||
${'cmp'} | ${'gt'} | ${[2, 4]} | ${0} | ${false}
|
||||
${'cmp'} | ${'gte'} | ${[2, 4]} | ${0} | ${false}
|
||||
${'cmp'} | ${'gte'} | ${[4, 2]} | ${0} | ${true}
|
||||
${'cmp'} | ${'gte'} | ${[2, 2]} | ${0} | ${true}
|
||||
${'cmp'} | ${'lt'} | ${[2, 4]} | ${0} | ${true}
|
||||
${'cmp'} | ${'lte'} | ${[2, 4]} | ${0} | ${true}
|
||||
${'cmp'} | ${'lte'} | ${[4, 2]} | ${0} | ${false}
|
||||
${'cmp'} | ${'lte'} | ${[2, 2]} | ${0} | ${true}
|
||||
${'cmp'} | ${'cmp'} | ${[2, 4]} | ${0} | ${-1}
|
||||
${'cmp'} | ${'cmp'} | ${[2, -4]} | ${0} | ${1}
|
||||
${'cmp'} | ${'cmp'} | ${[2, 2]} | ${0} | ${0}
|
||||
${'array'} | ${'sum'} | ${[[1, 2, 3]]} | ${0} | ${6}
|
||||
${'array'} | ${'dedup'} | ${[['a', 'a', 'b', 'c', 'a', 'b', 'c']]} | ${0} | ${['a', 'b', 'c']}
|
||||
${'array'} | ${'intersect'} | ${[['a', 'b', 'c'], ['c', 'b', 'd']]} | ${0} | ${['b', 'c']}
|
||||
${'array'} | ${'diff'} | ${[['a', 'b', 'c'], ['c', 'b', 'd']]} | ${0} | ${['a']}
|
||||
${'array'} | ${'sdiff'} | ${[['a', 'b', 'c'], ['c', 'b', 'd']]} | ${0} | ${['a', 'd']}
|
||||
${'json'} | ${'obj'} | ${['a', 10, 'b', 'string', 'c', null]} | ${0} | ${{ a: 10, b: 'string', c: null }}
|
||||
${'json'} | ${'obj'} | ${['a', 10, 'b', 'string', 'c']} | ${1} | ${'Expected even number of argument(s). Got 5'}
|
||||
${'json'} | ${'obj'} | ${[]} | ${0} | ${{}}
|
||||
${'json'} | ${'put'} | ${[{}, 'a', 10]} | ${0} | ${{ a: 10 }}
|
||||
${'json'} | ${'put'} | ${[{ b: 11 }, 'a', 10]} | ${0} | ${{ a: 10, b: 11 }}
|
||||
${'json'} | ${'put'} | ${['a', 'a', 11]} | ${1} | ${'Argument 0 expected to be of type object, Got string'}
|
||||
${'json'} | ${'put'} | ${[{}, 'a', 10, 'b', 20]} | ${1} | ${'Expected 3 argument(s). Got 5'}
|
||||
${'json'} | ${'put'} | ${[{}]} | ${1} | ${'Expected 3 argument(s). Got 1'}
|
||||
${'json'} | ${'puts'} | ${[{}, 'a', 10]} | ${0} | ${{ a: 10 }}
|
||||
${'json'} | ${'puts'} | ${[{ b: 11 }, 'a', 10]} | ${0} | ${{ a: 10, b: 11 }}
|
||||
${'json'} | ${'puts'} | ${[{}, 'a', 10, 'b', 'string', 'c', null]} | ${0} | ${{ a: 10, b: 'string', c: null }}
|
||||
${'json'} | ${'puts'} | ${[{ x: 'text' }, 'a', 10, 'b', 'string']} | ${0} | ${{ a: 10, b: 'string', x: 'text' }}
|
||||
${'json'} | ${'puts'} | ${[{}]} | ${1} | ${'Expected more than 3 argument(s). Got 1'}
|
||||
${'json'} | ${'puts'} | ${['a', 'a', 11]} | ${1} | ${'Argument 0 expected to be of type object, Got string'}
|
||||
${'json'} | ${'stringify'} | ${[{ a: 10, b: 'string', c: null }]} | ${0} | ${'{"a":10,"b":"string","c":null}'}
|
||||
${'json'} | ${'stringify'} | ${[1]} | ${1} | ${'Argument 0 expected to be of type object, Got number'}
|
||||
${'json'} | ${'parse'} | ${['{"a":10,"b":"string","c":null}']} | ${0} | ${{ a: 10, b: 'string', c: null }}
|
||||
${'json'} | ${'parse'} | ${['incorrect']} | ${1} | ${'Unexpected token i in JSON at position 0'}
|
||||
${'json'} | ${'parse'} | ${[10]} | ${1} | ${'Argument 0 expected to be of type string, Got number'}
|
||||
`(
|
||||
//
|
||||
'$fnName with $args expected retcode: $retCode and result: $result',
|
||||
async ({ serviceId, fnName, args, retCode, result }) => {
|
||||
// arrange
|
||||
const req: CallServiceData = {
|
||||
serviceId: serviceId,
|
||||
fnName: fnName,
|
||||
args: args,
|
||||
tetraplets: [],
|
||||
particleContext: {
|
||||
particleId: 'some',
|
||||
initPeerId: 'init peer id',
|
||||
timestamp: 595951200,
|
||||
ttl: 595961200,
|
||||
signature: 'sig',
|
||||
},
|
||||
};
|
||||
|
||||
// act
|
||||
const fn = builtInServices[req.serviceId][req.fnName];
|
||||
const res = await fn(req);
|
||||
|
||||
// assert
|
||||
expect(res).toMatchObject({
|
||||
retCode: retCode,
|
||||
result: result,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
it('should return correct error message for identiy service', async () => {
|
||||
// arrange
|
||||
const req: CallServiceData = {
|
||||
serviceId: 'peer',
|
||||
fnName: 'identify',
|
||||
args: [],
|
||||
tetraplets: [],
|
||||
particleContext: {
|
||||
particleId: 'some',
|
||||
initPeerId: 'init peer id',
|
||||
timestamp: 595951200,
|
||||
ttl: 595961200,
|
||||
signature: 'sig',
|
||||
},
|
||||
};
|
||||
|
||||
// act
|
||||
const fn = builtInServices[req.serviceId][req.fnName];
|
||||
const res = await fn(req);
|
||||
|
||||
// assert
|
||||
expect(res).toMatchObject({
|
||||
retCode: 0,
|
||||
result: {
|
||||
external_addresses: [],
|
||||
node_version: expect.stringContaining('js'),
|
||||
air_version: expect.stringContaining('js'),
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const key = '+cmeYlZKj+MfSa9dpHV+BmLPm6wq4inGlsPlQ1GvtPk=';
|
||||
|
||||
const context = (async () => {
|
||||
const keyBytes = toUint8Array(key);
|
||||
const kp = await KeyPair.fromEd25519SK(keyBytes);
|
||||
const res = {
|
||||
peerKeyPair: kp,
|
||||
peerId: kp.getPeerId(),
|
||||
};
|
||||
return res;
|
||||
})();
|
||||
|
||||
const testData = [1, 2, 3, 4, 5, 6, 7, 9, 10];
|
||||
|
||||
// signature produced by KeyPair created from key above (`key` variable)
|
||||
const testDataSig = [
|
||||
224, 104, 245, 206, 140, 248, 27, 72, 68, 133, 111, 10, 164, 197, 242, 132, 107, 77, 224, 67, 99, 106, 76, 29, 144,
|
||||
121, 122, 169, 36, 173, 58, 80, 170, 102, 137, 253, 157, 247, 168, 87, 162, 223, 188, 214, 203, 220, 52, 246, 29,
|
||||
86, 77, 71, 224, 248, 16, 213, 254, 75, 78, 239, 243, 222, 241, 15,
|
||||
];
|
||||
|
||||
// signature produced by KeyPair created from some random KeyPair
|
||||
const testDataWrongSig = [
|
||||
116, 247, 189, 118, 236, 53, 147, 123, 219, 75, 176, 105, 101, 108, 233, 137, 97, 14, 146, 132, 252, 70, 51, 153,
|
||||
237, 167, 156, 150, 36, 90, 229, 108, 166, 231, 255, 137, 8, 246, 125, 0, 213, 150, 83, 196, 237, 221, 131, 159,
|
||||
157, 159, 25, 109, 95, 160, 181, 65, 254, 238, 47, 156, 240, 151, 58, 14,
|
||||
];
|
||||
|
||||
const makeTetraplet = (initPeerId: string, serviceId?: string, fnName?: string): CallParams<'data'> => {
|
||||
return {
|
||||
initPeerId: initPeerId,
|
||||
tetraplets: {
|
||||
data: [
|
||||
{
|
||||
function_name: fnName,
|
||||
service_id: serviceId,
|
||||
},
|
||||
],
|
||||
},
|
||||
} as any;
|
||||
};
|
||||
|
||||
describe('Sig service tests', () => {
|
||||
it('sig.sign should create the correct signature', async () => {
|
||||
const ctx = await context;
|
||||
const sig = new Sig(ctx.peerKeyPair);
|
||||
|
||||
const res = await sig.sign(testData, makeTetraplet(ctx.peerId));
|
||||
|
||||
expect(res.success).toBe(true);
|
||||
expect(res.signature).toStrictEqual(testDataSig);
|
||||
});
|
||||
|
||||
it('sig.verify should return true for the correct signature', async () => {
|
||||
const ctx = await context;
|
||||
const sig = new Sig(ctx.peerKeyPair);
|
||||
|
||||
const res = await sig.verify(testDataSig, testData);
|
||||
|
||||
expect(res).toBe(true);
|
||||
});
|
||||
|
||||
it('sig.verify should return false for the incorrect signature', async () => {
|
||||
const ctx = await context;
|
||||
const sig = new Sig(ctx.peerKeyPair);
|
||||
|
||||
const res = await sig.verify(testDataWrongSig, testData);
|
||||
|
||||
expect(res).toBe(false);
|
||||
});
|
||||
|
||||
it('sign-verify call chain should work', async () => {
|
||||
const ctx = await context;
|
||||
const sig = new Sig(ctx.peerKeyPair);
|
||||
|
||||
const signature = await sig.sign(testData, makeTetraplet(ctx.peerId));
|
||||
const res = await sig.verify(signature.signature as number[], testData);
|
||||
|
||||
expect(res).toBe(true);
|
||||
});
|
||||
|
||||
it('sig.sign with defaultSigGuard should work for correct callParams', async () => {
|
||||
const ctx = await context;
|
||||
const sig = new Sig(ctx.peerKeyPair);
|
||||
sig.securityGuard = defaultSigGuard(ctx.peerId);
|
||||
|
||||
const signature = await sig.sign(testData, makeTetraplet(ctx.peerId, 'registry', 'get_route_bytes'));
|
||||
|
||||
await expect(signature).toBeDefined();
|
||||
});
|
||||
|
||||
it('sig.sign with defaultSigGuard should not allow particles initiated from incorrect service', async () => {
|
||||
const ctx = await context;
|
||||
const sig = new Sig(ctx.peerKeyPair);
|
||||
sig.securityGuard = defaultSigGuard(ctx.peerId);
|
||||
|
||||
const res = await sig.sign(testData, makeTetraplet(ctx.peerId, 'other_service', 'other_fn'));
|
||||
|
||||
await expect(res.success).toBe(false);
|
||||
await expect(res.error).toBe('Security guard validation failed');
|
||||
});
|
||||
|
||||
it('sig.sign with defaultSigGuard should not allow particles initiated from other peers', async () => {
|
||||
const ctx = await context;
|
||||
const sig = new Sig(ctx.peerKeyPair);
|
||||
sig.securityGuard = defaultSigGuard(ctx.peerId);
|
||||
|
||||
const res = await sig.sign(
|
||||
testData,
|
||||
makeTetraplet((await KeyPair.randomEd25519()).getPeerId(), 'registry', 'get_key_bytes'),
|
||||
);
|
||||
|
||||
await expect(res.success).toBe(false);
|
||||
await expect(res.error).toBe('Security guard validation failed');
|
||||
});
|
||||
|
||||
it('changing securityGuard should work', async () => {
|
||||
const ctx = await context;
|
||||
const sig = new Sig(ctx.peerKeyPair);
|
||||
sig.securityGuard = allowServiceFn('test', 'test');
|
||||
|
||||
const successful1 = await sig.sign(testData, makeTetraplet(ctx.peerId, 'test', 'test'));
|
||||
const unSuccessful1 = await sig.sign(testData, makeTetraplet(ctx.peerId, 'wrong', 'wrong'));
|
||||
|
||||
sig.securityGuard = allowServiceFn('wrong', 'wrong');
|
||||
|
||||
const successful2 = await sig.sign(testData, makeTetraplet(ctx.peerId, 'wrong', 'wrong'));
|
||||
const unSuccessful2 = await sig.sign(testData, makeTetraplet(ctx.peerId, 'test', 'test'));
|
||||
|
||||
expect(successful1.success).toBe(true);
|
||||
expect(successful2.success).toBe(true);
|
||||
expect(unSuccessful1.success).toBe(false);
|
||||
expect(unSuccessful2.success).toBe(false);
|
||||
});
|
||||
});
|
@ -1,5 +1,4 @@
|
||||
import each from 'jest-each';
|
||||
import { aqua2ts, ts2aqua } from '../../../compilerSupport/conversions';
|
||||
import { aqua2ts, ts2aqua } from '../../../compilerSupport/conversions.js';
|
||||
|
||||
const i32 = { tag: 'scalar', name: 'i32' } as const;
|
||||
|
||||
@ -152,24 +151,24 @@ const nestedStructs = [
|
||||
];
|
||||
|
||||
describe('Conversion from aqua to typescript', () => {
|
||||
each`
|
||||
aqua | ts | type
|
||||
${1} | ${1} | ${i32}
|
||||
${[]} | ${null} | ${opt_i32}
|
||||
${[1]} | ${1} | ${opt_i32}
|
||||
${[1, 2, 3]} | ${[1, 2, 3]} | ${array_i32}
|
||||
${[]} | ${[]} | ${array_i32}
|
||||
${[[1]]} | ${[1]} | ${array_opt_i32}
|
||||
${[[]]} | ${[null]} | ${array_opt_i32}
|
||||
${[[1], [2]]} | ${[1, 2]} | ${array_opt_i32}
|
||||
${[[], [2]]} | ${[null, 2]} | ${array_opt_i32}
|
||||
${structs[0].aqua} | ${structs[0].ts} | ${labeledProduct}
|
||||
${structs[1].aqua} | ${structs[1].ts} | ${labeledProduct}
|
||||
${structs[0].aqua} | ${structs[0].ts} | ${struct}
|
||||
${structs[1].aqua} | ${structs[1].ts} | ${struct}
|
||||
${nestedStructs[0].aqua} | ${nestedStructs[0].ts} | ${nestedLabeledProductType}
|
||||
${nestedStructs[1].aqua} | ${nestedStructs[1].ts} | ${nestedLabeledProductType}
|
||||
`.test(
|
||||
test.each`
|
||||
aqua | ts | type
|
||||
${1} | ${1} | ${i32}
|
||||
${[]} | ${null} | ${opt_i32}
|
||||
${[1]} | ${1} | ${opt_i32}
|
||||
${[1, 2, 3]} | ${[1, 2, 3]} | ${array_i32}
|
||||
${[]} | ${[]} | ${array_i32}
|
||||
${[[1]]} | ${[1]} | ${array_opt_i32}
|
||||
${[[]]} | ${[null]} | ${array_opt_i32}
|
||||
${[[1], [2]]} | ${[1, 2]} | ${array_opt_i32}
|
||||
${[[], [2]]} | ${[null, 2]} | ${array_opt_i32}
|
||||
${structs[0].aqua} | ${structs[0].ts} | ${labeledProduct}
|
||||
${structs[1].aqua} | ${structs[1].ts} | ${labeledProduct}
|
||||
${structs[0].aqua} | ${structs[0].ts} | ${struct}
|
||||
${structs[1].aqua} | ${structs[1].ts} | ${struct}
|
||||
${nestedStructs[0].aqua} | ${nestedStructs[0].ts} | ${nestedLabeledProductType}
|
||||
${nestedStructs[1].aqua} | ${nestedStructs[1].ts} | ${nestedLabeledProductType}
|
||||
`(
|
||||
//
|
||||
'aqua: $aqua. ts: $ts. type: $type',
|
||||
async ({ aqua, ts, type }) => {
|
@ -1,17 +1,18 @@
|
||||
import api from '@fluencelabs/aqua-api/aqua-api';
|
||||
import { InlinedWorkerLoader } from '@fluencelabs/marine.deps-loader.node';
|
||||
import * as api from '@fluencelabs/aqua-api/aqua-api.js';
|
||||
|
||||
import { promises as fs } from 'fs';
|
||||
import { FluencePeer, PeerConfig } from '../FluencePeer';
|
||||
import { Particle } from '../Particle';
|
||||
import { avmModuleLoader, controlModuleLoader, MakeServiceCall } from '../utils';
|
||||
import { ServiceDef } from '../compilerSupport/interface';
|
||||
import { callFunctionImpl } from '../compilerSupport/callFunction';
|
||||
import { FluencePeer, PeerConfig } from '../FluencePeer.js';
|
||||
import { Particle } from '../Particle.js';
|
||||
import { MakeServiceCall } from '../utils.js';
|
||||
import { avmModuleLoader, controlModuleLoader } from '../utilsForNode.js';
|
||||
import { ServiceDef } from '../compilerSupport/interface.js';
|
||||
import { callFunctionImpl } from '../compilerSupport/callFunction.js';
|
||||
|
||||
import { marineLogFunction } from '../utils';
|
||||
import { MarineBackgroundRunner } from '@fluencelabs/marine.background-runner';
|
||||
import { MarineBasedAvmRunner } from '../avm';
|
||||
import { nodes } from './connection';
|
||||
import { marineLogFunction } from '../utils.js';
|
||||
import { MarineBackgroundRunner } from '../../marine/worker/index.js';
|
||||
import { MarineBasedAvmRunner } from '../avm.js';
|
||||
import { nodes } from './connection.js';
|
||||
import { WorkerLoaderFromFs } from '../../marine/deps-loader/node.js';
|
||||
|
||||
export const registerHandlersHelper = (
|
||||
peer: FluencePeer,
|
||||
@ -51,7 +52,7 @@ export const compileAqua = async (aquaFile: string): Promise<CompiledFile> => {
|
||||
};
|
||||
|
||||
export const mkTestPeer = () => {
|
||||
const workerLoader = new InlinedWorkerLoader();
|
||||
const workerLoader = new WorkerLoaderFromFs('../../marine/worker-script');
|
||||
|
||||
const marine = new MarineBackgroundRunner(workerLoader, controlModuleLoader, marineLogFunction);
|
||||
const avm = new MarineBasedAvmRunner(marine, avmModuleLoader, undefined);
|
@ -6,9 +6,9 @@
|
||||
* Aqua version: 0.7.7-362
|
||||
*
|
||||
*/
|
||||
import { CallParams } from '../commonTypes';
|
||||
import { registerServiceImpl } from '../compilerSupport/registerService';
|
||||
import { FluencePeer } from '../FluencePeer';
|
||||
import { CallParams } from '../commonTypes.js';
|
||||
import { registerServiceImpl } from '../compilerSupport/registerService.js';
|
||||
import { FluencePeer } from '../FluencePeer.js';
|
||||
|
||||
// Services
|
||||
|
@ -6,9 +6,9 @@
|
||||
* Aqua version: 0.7.7-362
|
||||
*
|
||||
*/
|
||||
import { CallParams } from '../commonTypes';
|
||||
import { registerServiceImpl } from '../compilerSupport/registerService';
|
||||
import { FluencePeer } from '../FluencePeer';
|
||||
import { CallParams } from '../commonTypes.js';
|
||||
import { registerServiceImpl } from '../compilerSupport/registerService.js';
|
||||
import { FluencePeer } from '../FluencePeer.js';
|
||||
|
||||
// Services
|
||||
|
@ -6,9 +6,9 @@
|
||||
* Aqua version: 0.7.7-362
|
||||
*
|
||||
*/
|
||||
import { CallParams } from '../commonTypes';
|
||||
import { registerServiceImpl } from '../compilerSupport/registerService';
|
||||
import { FluencePeer } from '../FluencePeer';
|
||||
import { CallParams } from '../commonTypes.js';
|
||||
import { registerServiceImpl } from '../compilerSupport/registerService.js';
|
||||
import { FluencePeer } from '../FluencePeer.js';
|
||||
|
||||
// Services
|
||||
|
@ -1,7 +1,7 @@
|
||||
import type { CallResultsArray, InterpreterResult, RunParameters } from '@fluencelabs/avm';
|
||||
import { deserializeAvmResult, serializeAvmArgs } from '@fluencelabs/avm';
|
||||
import type { LogLevel } from '@fluencelabs/marine-js';
|
||||
import type { IMarine, IAvmRunner, IWasmLoader } from '@fluencelabs/interfaces';
|
||||
import type { LogLevel } from '@fluencelabs/marine-js/dist/types';
|
||||
import type { IMarine, IAvmRunner, IWasmLoader } from '../interfaces/index.js';
|
||||
|
||||
export class MarineBasedAvmRunner implements IAvmRunner {
|
||||
constructor(private marine: IMarine, private avmWasmLoader: IWasmLoader, private logLevel: LogLevel | undefined) {}
|
@ -1,7 +1,7 @@
|
||||
import { CallParams, PeerIdB58 } from '../commonTypes';
|
||||
import { KeyPair } from '@fluencelabs/keypair';
|
||||
import { SigDef } from '../_aqua/services';
|
||||
import { allowOnlyParticleOriginatedAt, allowServiceFn, and, or, SecurityGuard } from './securityGuard';
|
||||
import { CallParams, PeerIdB58 } from '../commonTypes.js';
|
||||
import { KeyPair } from '../../keypair/index.js';
|
||||
import { SigDef } from '../_aqua/services.js';
|
||||
import { allowOnlyParticleOriginatedAt, allowServiceFn, and, or, SecurityGuard } from './securityGuard.js';
|
||||
|
||||
export const defaultSigGuard = (peerId: PeerIdB58) => {
|
||||
return and<'data'>(
|
@ -1,12 +1,13 @@
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { SrvDef } from '../_aqua/single-module-srv';
|
||||
import { NodeUtilsDef } from '../_aqua/node-utils';
|
||||
import { FluencePeer } from '../FluencePeer';
|
||||
import { CallParams } from '../commonTypes';
|
||||
import { allowOnlyParticleOriginatedAt, SecurityGuard } from './securityGuard';
|
||||
import { SrvDef } from '../_aqua/single-module-srv.js';
|
||||
import { NodeUtilsDef } from '../_aqua/node-utils.js';
|
||||
import { FluencePeer } from '../FluencePeer.js';
|
||||
import { CallParams } from '../commonTypes.js';
|
||||
import { Buffer } from 'buffer';
|
||||
import { allowOnlyParticleOriginatedAt, SecurityGuard } from './securityGuard.js';
|
||||
|
||||
export const defaultGuard = (peer: FluencePeer) => {
|
||||
return allowOnlyParticleOriginatedAt<any>(peer.getStatus().peerId!);
|
||||
export const defaultGuard = (peer: () => FluencePeer) => {
|
||||
return allowOnlyParticleOriginatedAt<any>(peer().getStatus().peerId!);
|
||||
};
|
||||
|
||||
export class Srv implements SrvDef {
|
||||
@ -14,7 +15,7 @@ export class Srv implements SrvDef {
|
||||
|
||||
constructor(private peer: FluencePeer) {}
|
||||
|
||||
securityGuard_create: SecurityGuard<'wasm_b64_content'> = defaultGuard(this.peer);
|
||||
securityGuard_create: SecurityGuard<'wasm_b64_content'> = defaultGuard(() => this.peer);
|
||||
|
||||
async create(wasm_b64_content: string, callParams: CallParams<'wasm_b64_content'>) {
|
||||
if (!this.securityGuard_create(callParams)) {
|
||||
@ -48,7 +49,7 @@ export class Srv implements SrvDef {
|
||||
}
|
||||
}
|
||||
|
||||
securityGuard_remove: SecurityGuard<'service_id'> = defaultGuard(this.peer);
|
||||
securityGuard_remove: SecurityGuard<'service_id'> = defaultGuard(() => this.peer);
|
||||
|
||||
remove(service_id: string, callParams: CallParams<'service_id'>) {
|
||||
if (!this.securityGuard_remove(callParams)) {
|
||||
@ -83,7 +84,7 @@ export class Srv implements SrvDef {
|
||||
export class NodeUtils implements NodeUtilsDef {
|
||||
constructor(private peer: FluencePeer) {}
|
||||
|
||||
securityGuard_readFile: SecurityGuard<'path'> = defaultGuard(this.peer);
|
||||
securityGuard_readFile: SecurityGuard<'path'> = defaultGuard(() => this.peer);
|
||||
|
||||
async read_file(path: string, callParams: CallParams<'path'>) {
|
||||
// TODO: split node-only and universal services into different client packages
|
@ -14,14 +14,18 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { encode, decode } from 'bs58';
|
||||
import * as bs58 from 'bs58';
|
||||
|
||||
import { sha256 } from 'multiformats/hashes/sha2';
|
||||
import { CallServiceResult } from '@fluencelabs/avm';
|
||||
|
||||
import { CallServiceData, GenericCallServiceHandler, ResultCodes } from '../commonTypes';
|
||||
import { jsonify } from '../utils';
|
||||
import { GenericCallServiceHandler, ResultCodes } from '../commonTypes.js';
|
||||
import { jsonify } from '../utils.js';
|
||||
import { Buffer } from 'buffer';
|
||||
|
||||
//@ts-ignore
|
||||
const { encode, decode } = bs58.default;
|
||||
|
||||
const success = (result: any): CallServiceResult => {
|
||||
return {
|
||||
result: result,
|
@ -1,5 +1,5 @@
|
||||
import { SecurityTetraplet } from '@fluencelabs/avm';
|
||||
import { CallParams, PeerIdB58 } from '../commonTypes';
|
||||
import { CallParams, PeerIdB58 } from '../commonTypes.js';
|
||||
|
||||
type ArgName = string | null;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { ArrowWithoutCallbacks, FnConfig, FunctionCallDef, NonArrowType } from './interface';
|
||||
import { FluencePeer } from '../FluencePeer';
|
||||
import { ArrowWithoutCallbacks, FnConfig, FunctionCallDef, NonArrowType } from './interface.js';
|
||||
import { FluencePeer } from '../FluencePeer.js';
|
||||
|
||||
import {
|
||||
injectRelayService,
|
||||
@ -9,7 +9,7 @@ import {
|
||||
ServiceDescription,
|
||||
userHandlerService,
|
||||
injectValueService,
|
||||
} from './services';
|
||||
} from './services.js';
|
||||
|
||||
/**
|
||||
* Convenience function which does all the internal work of creating particles
|
@ -1,7 +1,7 @@
|
||||
import { jsonify } from '../utils';
|
||||
import { jsonify } from '../utils.js';
|
||||
import { match } from 'ts-pattern';
|
||||
import { ArrowType, ArrowWithoutCallbacks, NonArrowType } from './interface';
|
||||
import { CallServiceData } from '../commonTypes';
|
||||
import { ArrowType, ArrowWithoutCallbacks, NonArrowType } from './interface.js';
|
||||
import { CallServiceData } from '../commonTypes.js';
|
||||
|
||||
/**
|
||||
* Convert value from its representation in aqua language to representation in typescript
|
||||
@ -21,6 +21,7 @@ export const aqua2ts = (value: any, type: NonArrowType): any => {
|
||||
return aqua2ts(value[0], opt.type);
|
||||
}
|
||||
})
|
||||
// @ts-ignore
|
||||
.with({ tag: 'scalar' }, { tag: 'bottomType' }, { tag: 'topType' }, () => {
|
||||
return value;
|
||||
})
|
@ -1,6 +1,6 @@
|
||||
import type { FluencePeer } from '../FluencePeer';
|
||||
import { ServiceDef } from './interface';
|
||||
import { registerGlobalService, userHandlerService } from './services';
|
||||
import type { FluencePeer } from '../FluencePeer.js';
|
||||
import { ServiceDef } from './interface.js';
|
||||
import { registerGlobalService, userHandlerService } from './services.js';
|
||||
|
||||
export const registerServiceImpl = (
|
||||
peer: FluencePeer,
|
@ -1,12 +1,12 @@
|
||||
import { SecurityTetraplet } from '@fluencelabs/avm';
|
||||
import { match } from 'ts-pattern';
|
||||
|
||||
import { Particle } from '../Particle';
|
||||
import { CallParams, CallServiceData, GenericCallServiceHandler, ResultCodes } from '../commonTypes';
|
||||
import { FluencePeer } from '../FluencePeer';
|
||||
import { Particle } from '../Particle.js';
|
||||
import { CallParams, CallServiceData, GenericCallServiceHandler, ResultCodes } from '../commonTypes.js';
|
||||
import { FluencePeer } from '../FluencePeer.js';
|
||||
|
||||
import { aquaArgs2Ts, responseServiceValue2ts, returnType2Aqua, ts2aqua } from './conversions';
|
||||
import { ArrowWithoutCallbacks, FunctionCallConstants, FunctionCallDef, NonArrowType } from './interface';
|
||||
import { aquaArgs2Ts, responseServiceValue2ts, returnType2Aqua, ts2aqua } from './conversions.js';
|
||||
import { ArrowWithoutCallbacks, FunctionCallConstants, FunctionCallDef, NonArrowType } from './interface.js';
|
||||
|
||||
export interface ServiceDescription {
|
||||
serviceId: string;
|
@ -1,15 +1,15 @@
|
||||
import { FluenceConnection, ParticleHandler } from '@fluencelabs/interfaces';
|
||||
import { InlinedWorkerLoader } from '@fluencelabs/marine.deps-loader.node';
|
||||
|
||||
import { keyPairFromBase64Sk } from '@fluencelabs/keypair';
|
||||
|
||||
import { PeerIdB58 } from './commonTypes';
|
||||
import { FluencePeer } from './FluencePeer';
|
||||
import log from 'loglevel';
|
||||
import { MarineBackgroundRunner } from '@fluencelabs/marine.background-runner';
|
||||
import { avmModuleLoader, controlModuleLoader, marineLogFunction } from './utils';
|
||||
import { FluenceConnection, ParticleHandler } from '../interfaces/index.js';
|
||||
import { keyPairFromBase64Sk } from '../keypair/index.js';
|
||||
import { PeerIdB58 } from './commonTypes.js';
|
||||
import { FluencePeer } from './FluencePeer.js';
|
||||
import { MarineBackgroundRunner } from '../marine/worker/index.js';
|
||||
import { avmModuleLoader, controlModuleLoader } from './utilsForNode';
|
||||
import { marineLogFunction } from './utils';
|
||||
import { MarineBasedAvmRunner } from './avm';
|
||||
|
||||
import log from 'loglevel';
|
||||
import { WorkerLoaderFromFs } from '../marine/deps-loader/node.js';
|
||||
|
||||
interface EphemeralConfig {
|
||||
peers: Array<{
|
||||
peerId: PeerIdB58;
|
||||
@ -126,7 +126,7 @@ export class EphemeralNetwork {
|
||||
log.debug('Starting ephemeral network up...');
|
||||
const allPeerIds = this.config.peers.map((x) => x.peerId);
|
||||
// shared worker for all the peers
|
||||
const workerLoader = new InlinedWorkerLoader();
|
||||
const workerLoader = new WorkerLoaderFromFs('../../marine/worker-script');
|
||||
|
||||
const promises = this.config.peers.map(async (x) => {
|
||||
const logLevel = undefined;
|
@ -15,14 +15,13 @@
|
||||
*/
|
||||
|
||||
import log from 'loglevel';
|
||||
import platform from 'platform';
|
||||
import * as platform from 'platform';
|
||||
|
||||
import { Buffer } from 'buffer';
|
||||
import { CallServiceData, CallServiceResult, CallServiceResultType, ResultCodes } from './commonTypes';
|
||||
import { FluencePeer } from './FluencePeer';
|
||||
import { ParticleExecutionStage } from './Particle';
|
||||
import { LogFunction } from '@fluencelabs/marine-js';
|
||||
import { WasmNpmLoader } from '@fluencelabs/marine.deps-loader.node';
|
||||
import { CallServiceData, CallServiceResult, CallServiceResultType, ResultCodes } from './commonTypes.js';
|
||||
import { FluencePeer } from './FluencePeer.js';
|
||||
import { ParticleExecutionStage } from './Particle.js';
|
||||
import { LogFunction } from '@fluencelabs/marine-js/dist/types';
|
||||
|
||||
export const MakeServiceCall =
|
||||
(fn: (args: any[]) => CallServiceResultType) =>
|
||||
@ -202,6 +201,3 @@ export const marineLogFunction: LogFunction = (message) => {
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
export const controlModuleLoader = new WasmNpmLoader('@fluencelabs/marine-js', 'marine-js.wasm');
|
||||
export const avmModuleLoader = new WasmNpmLoader('@fluencelabs/avm', 'avm.wasm');
|
5
packages/core/src/js-peer/utilsForNode.ts
Normal file
5
packages/core/src/js-peer/utilsForNode.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { WorkerLoaderFromFs, WasmLoaderFromFs, WasmLoaderFromNpm } from '../marine/deps-loader/node.js';
|
||||
|
||||
// TODO!: after moving to ESM loaders stopped working. Should be fixed in scope of DXJ-194
|
||||
export const controlModuleLoader = new WasmLoaderFromNpm('@fluencelabs/marine-js', 'marine-js.wasm');
|
||||
export const avmModuleLoader = new WasmLoaderFromNpm('@fluencelabs/avm', 'avm.wasm');
|
95
packages/core/src/keypair/__test__/KeyPair.spec.ts
Normal file
95
packages/core/src/keypair/__test__/KeyPair.spec.ts
Normal file
@ -0,0 +1,95 @@
|
||||
import { toUint8Array } from 'js-base64';
|
||||
import * as bs58 from 'bs58';
|
||||
import { KeyPair } from '../index.js';
|
||||
|
||||
// @ts-ignore
|
||||
const { decode } = bs58.default;
|
||||
|
||||
const key = '+cmeYlZKj+MfSa9dpHV+BmLPm6wq4inGlsPlQ1GvtPk=';
|
||||
const keyBytes = toUint8Array(key);
|
||||
|
||||
const testData = Uint8Array.from([1, 2, 3, 4, 5, 6, 7, 9, 10]);
|
||||
|
||||
const testDataSig = Uint8Array.from([
|
||||
224, 104, 245, 206, 140, 248, 27, 72, 68, 133, 111, 10, 164, 197, 242, 132, 107, 77, 224, 67, 99, 106, 76, 29, 144,
|
||||
121, 122, 169, 36, 173, 58, 80, 170, 102, 137, 253, 157, 247, 168, 87, 162, 223, 188, 214, 203, 220, 52, 246, 29,
|
||||
86, 77, 71, 224, 248, 16, 213, 254, 75, 78, 239, 243, 222, 241, 15,
|
||||
]);
|
||||
|
||||
// signature produced by KeyPair created from some random KeyPair
|
||||
|
||||
describe('KeyPair tests', () => {
|
||||
it('generate keypair from seed', async function () {
|
||||
// arrange
|
||||
const random = await KeyPair.randomEd25519();
|
||||
const privateKey = random.toEd25519PrivateKey();
|
||||
|
||||
// act
|
||||
const keyPair = await KeyPair.fromEd25519SK(privateKey);
|
||||
const privateKey2 = keyPair.toEd25519PrivateKey();
|
||||
|
||||
// assert
|
||||
expect(privateKey).toStrictEqual(privateKey2);
|
||||
});
|
||||
|
||||
it('create keypair from ed25519 private key', async function () {
|
||||
// arrange
|
||||
// TODO: remove decoder which relies on "z"
|
||||
const rustSK = 'jDaxLJzYtzgwTMrELJCAqavtmx85ktQNfB2rLcK7MhH';
|
||||
const sk = decode(rustSK);
|
||||
|
||||
// act
|
||||
const keyPair = await KeyPair.fromEd25519SK(sk);
|
||||
|
||||
// assert
|
||||
const expectedPeerId = '12D3KooWH1W3VznVZ87JH4FwABK4mkntcspTVWJDta6c2xg9Pzbp';
|
||||
expect(keyPair.getPeerId()).toStrictEqual(expectedPeerId);
|
||||
});
|
||||
|
||||
it('create keypair from a seed phrase', async function () {
|
||||
// arrange
|
||||
const seedArray = new Uint8Array(32).fill(1);
|
||||
|
||||
// act
|
||||
const keyPair = await KeyPair.fromEd25519SK(seedArray);
|
||||
|
||||
// assert
|
||||
const expectedPeerId = '12D3KooWK99VoVxNE7XzyBwXEzW7xhK7Gpv85r9F3V3fyKSUKPH5';
|
||||
expect(keyPair.getPeerId()).toStrictEqual(expectedPeerId);
|
||||
});
|
||||
|
||||
it('sign', async function () {
|
||||
// arrange
|
||||
const keyPair = await KeyPair.fromEd25519SK(keyBytes);
|
||||
|
||||
// act
|
||||
const res = await keyPair.signBytes(testData);
|
||||
|
||||
// assert
|
||||
expect(res).toStrictEqual(testDataSig);
|
||||
});
|
||||
|
||||
it('verify', async function () {
|
||||
// arrange
|
||||
const keyPair = await KeyPair.fromEd25519SK(keyBytes);
|
||||
|
||||
// act
|
||||
const res = await keyPair.verify(testData, testDataSig);
|
||||
|
||||
// assert
|
||||
expect(res).toBe(true);
|
||||
});
|
||||
|
||||
it('sign-verify', async function () {
|
||||
// arrange
|
||||
const keyPair = await KeyPair.fromEd25519SK(keyBytes);
|
||||
|
||||
// act
|
||||
const data = new Uint8Array(32).fill(1);
|
||||
const sig = await keyPair.signBytes(data);
|
||||
const res = await keyPair.verify(data, sig);
|
||||
|
||||
// assert
|
||||
expect(res).toBe(true);
|
||||
});
|
||||
});
|
@ -14,30 +14,31 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as PeerId from 'peer-id';
|
||||
import { keys } from 'libp2p-crypto';
|
||||
import type { PeerId } from '@libp2p/interface-peer-id';
|
||||
import { generateKeyPairFromSeed, generateKeyPair } from '@libp2p/crypto/keys';
|
||||
import { createFromPrivKey } from '@libp2p/peer-id-factory';
|
||||
import type { PrivateKey } from '@libp2p/interface-keys';
|
||||
import { toUint8Array } from 'js-base64';
|
||||
|
||||
export class KeyPair {
|
||||
/**
|
||||
* Key pair in libp2p format. Used for backward compatibility with the current FluencePeer implementation
|
||||
*/
|
||||
public Libp2pPeerId: PeerId;
|
||||
|
||||
constructor(libp2pPeerId: PeerId) {
|
||||
this.Libp2pPeerId = libp2pPeerId;
|
||||
getLibp2pPeerId() {
|
||||
return this.libp2pPeerId;
|
||||
}
|
||||
|
||||
constructor(private key: PrivateKey, private libp2pPeerId: PeerId) {}
|
||||
|
||||
/**
|
||||
* Generates new KeyPair from ed25519 private key represented as a 32 byte array
|
||||
* @param key - Any sequence of 32 bytes
|
||||
* @param seed - Any sequence of 32 bytes
|
||||
* @returns - Promise with the created KeyPair
|
||||
*/
|
||||
static async fromEd25519SK(arr: Uint8Array): Promise<KeyPair> {
|
||||
// generateKeyPairFromSeed takes seed and copies it to private key as is
|
||||
const privateKey = await keys.generateKeyPairFromSeed('Ed25519', arr, 256);
|
||||
const lib2p2Pid = await PeerId.createFromPrivKey(privateKey.bytes);
|
||||
return new KeyPair(lib2p2Pid);
|
||||
static async fromEd25519SK(seed: Uint8Array): Promise<KeyPair> {
|
||||
const key = await generateKeyPairFromSeed('Ed25519', seed, 256);
|
||||
const lib2p2Pid = await createFromPrivKey(key);
|
||||
return new KeyPair(key, lib2p2Pid);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -45,27 +46,28 @@ export class KeyPair {
|
||||
* @returns - Promise with the created KeyPair
|
||||
*/
|
||||
static async randomEd25519(): Promise<KeyPair> {
|
||||
const lib2p2Pid = await PeerId.create({ keyType: 'Ed25519' });
|
||||
return new KeyPair(lib2p2Pid);
|
||||
const key = await generateKeyPair('Ed25519');
|
||||
const lib2p2Pid = await createFromPrivKey(key);
|
||||
return new KeyPair(key, lib2p2Pid);
|
||||
}
|
||||
|
||||
getPeerId(): string {
|
||||
return this.Libp2pPeerId.toB58String();
|
||||
return this.libp2pPeerId.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns 32 byte private key
|
||||
*/
|
||||
toEd25519PrivateKey(): Uint8Array {
|
||||
return this.Libp2pPeerId.privKey.marshal().subarray(0, 32);
|
||||
return this.key.marshal().subarray(0, 32);
|
||||
}
|
||||
|
||||
signBytes(data: Uint8Array): Promise<Uint8Array> {
|
||||
return this.Libp2pPeerId.privKey.sign(data);
|
||||
return this.key.sign(data);
|
||||
}
|
||||
|
||||
verify(data: Uint8Array, signature: Uint8Array): Promise<boolean> {
|
||||
return this.Libp2pPeerId.privKey.public.verify(data, signature);
|
||||
return this.key.public.verify(data, signature);
|
||||
}
|
||||
}
|
||||
|
23
packages/core/src/marine/deps-loader/common.ts
Normal file
23
packages/core/src/marine/deps-loader/common.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { BlobWorker } from 'threads';
|
||||
import { fromBase64, toUint8Array } from 'js-base64';
|
||||
import type { WorkerImplementation } from 'threads/dist/types/master';
|
||||
import { LazyLoader } from '../../interfaces/index.js';
|
||||
import { Buffer } from 'buffer';
|
||||
|
||||
export class InlinedWorkerLoader extends LazyLoader<WorkerImplementation> {
|
||||
constructor(b64script: string) {
|
||||
super(() => {
|
||||
const script = fromBase64(b64script);
|
||||
return BlobWorker.fromText(script);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class InlinedWasmLoader extends LazyLoader<Buffer> {
|
||||
constructor(b64wasm: string) {
|
||||
super(() => {
|
||||
const wasm = toUint8Array(b64wasm);
|
||||
return Buffer.from(wasm);
|
||||
});
|
||||
}
|
||||
}
|
68
packages/core/src/marine/deps-loader/node.ts
Normal file
68
packages/core/src/marine/deps-loader/node.ts
Normal file
@ -0,0 +1,68 @@
|
||||
import { createRequire } from 'module';
|
||||
import { LazyLoader } from '../../interfaces/index.js';
|
||||
|
||||
import type { WorkerImplementation } from 'threads/dist/types/master';
|
||||
import { Worker } from 'threads';
|
||||
import { Buffer } from 'buffer';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
const bufferToSharedArrayBuffer = (buffer: Buffer): SharedArrayBuffer => {
|
||||
const sab = new SharedArrayBuffer(buffer.length);
|
||||
const tmp = new Uint8Array(sab);
|
||||
tmp.set(buffer, 0);
|
||||
return sab;
|
||||
};
|
||||
|
||||
/**
|
||||
* Load wasm file from npm package. Only works in nodejs environment.
|
||||
* The function returns SharedArrayBuffer compatible with FluenceAppService methods.
|
||||
* @param source - object specifying the source of the file. Consist two fields: package name and file path.
|
||||
* @returns SharedArrayBuffer with the wasm file
|
||||
*/
|
||||
export const loadWasmFromNpmPackage = async (source: { package: string; file: string }): Promise<SharedArrayBuffer> => {
|
||||
const packagePath = require.resolve(source.package);
|
||||
const filePath = path.join(path.dirname(packagePath), source.file);
|
||||
return loadWasmFromFileSystem(filePath);
|
||||
};
|
||||
|
||||
/**
|
||||
* Load wasm file from the file system. Only works in nodejs environment.
|
||||
* The functions returns SharedArrayBuffer compatible with FluenceAppService methods.
|
||||
* @param filePath - path to the wasm file
|
||||
* @returns SharedArrayBuffer with the wasm fileWorker
|
||||
*/
|
||||
export const loadWasmFromFileSystem = async (filePath: string): Promise<SharedArrayBuffer> => {
|
||||
const buffer = await fs.promises.readFile(filePath);
|
||||
return bufferToSharedArrayBuffer(buffer);
|
||||
};
|
||||
|
||||
export class WasmLoaderFromFs extends LazyLoader<SharedArrayBuffer> {
|
||||
constructor(filePath: string) {
|
||||
super(() => loadWasmFromFileSystem(filePath));
|
||||
}
|
||||
}
|
||||
|
||||
export class WasmLoaderFromNpm extends LazyLoader<SharedArrayBuffer> {
|
||||
constructor(pkg: string, file: string) {
|
||||
super(() => loadWasmFromNpmPackage({ package: pkg, file: file }));
|
||||
}
|
||||
}
|
||||
|
||||
export class WorkerLoaderFromFs extends LazyLoader<WorkerImplementation> {
|
||||
constructor(scriptPath: string) {
|
||||
super(() => new Worker(scriptPath));
|
||||
}
|
||||
}
|
||||
|
||||
export class WorkerLoaderFromNpm extends LazyLoader<WorkerImplementation> {
|
||||
constructor(pkg: string, file: string) {
|
||||
super(() => {
|
||||
const packagePath = require.resolve(pkg);
|
||||
const scriptPath = path.join(path.dirname(packagePath), file);
|
||||
return new Worker(scriptPath);
|
||||
});
|
||||
}
|
||||
}
|
40
packages/core/src/marine/deps-loader/web.ts
Normal file
40
packages/core/src/marine/deps-loader/web.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { Buffer } from 'buffer';
|
||||
import { LazyLoader } from '../../interfaces/index.js';
|
||||
|
||||
const bufferToSharedArrayBuffer = (buffer: Buffer): SharedArrayBuffer => {
|
||||
const sab = new SharedArrayBuffer(buffer.length);
|
||||
const tmp = new Uint8Array(sab);
|
||||
tmp.set(buffer, 0);
|
||||
return sab;
|
||||
};
|
||||
|
||||
/**
|
||||
* Load wasm file from the server. Only works in browsers.
|
||||
* The function will try load file into SharedArrayBuffer if the site is cross-origin isolated.
|
||||
* Otherwise the return value fallbacks to Buffer which is less performant but is still compatible with FluenceAppService methods.
|
||||
* We strongly recommend to set-up cross-origin headers. For more details see: See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer#security_requirements
|
||||
* Filename is relative to current origin.
|
||||
* @param filePath - path to the wasm file relative to current origin
|
||||
* @returns Either SharedArrayBuffer or Buffer with the wasm file
|
||||
*/
|
||||
export const loadWasmFromServer = async (filePath: string): Promise<SharedArrayBuffer | Buffer> => {
|
||||
const fullUrl = window.location.origin + '/' + filePath;
|
||||
const res = await fetch(fullUrl);
|
||||
const ab = await res.arrayBuffer();
|
||||
new Uint8Array(ab);
|
||||
const buffer = Buffer.from(ab);
|
||||
|
||||
// only convert to shared buffers if necessary CORS headers have been set:
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer#security_requirements
|
||||
if (crossOriginIsolated) {
|
||||
return bufferToSharedArrayBuffer(buffer);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
};
|
||||
|
||||
export class WebLoaderFromUrl extends LazyLoader<SharedArrayBuffer | Buffer> {
|
||||
constructor(filePath: string) {
|
||||
super(() => loadWasmFromServer(filePath));
|
||||
}
|
||||
}
|
83
packages/core/src/marine/worker-script/index.ts
Normal file
83
packages/core/src/marine/worker-script/index.ts
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2022 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 { MarineService } from '@fluencelabs/marine-js/dist/MarineService';
|
||||
import type { Env, MarineServiceConfig } from '@fluencelabs/marine-js/dist/config';
|
||||
import type { JSONArray, JSONObject, LogMessage } from '@fluencelabs/marine-js/dist/types';
|
||||
import { Observable, Subject } from 'threads/observable';
|
||||
import { expose } from 'threads/worker';
|
||||
|
||||
let marineServices = new Map<string, MarineService>();
|
||||
let controlModule: WebAssembly.Module | undefined;
|
||||
|
||||
const onLogMessage = new Subject<LogMessage>();
|
||||
|
||||
const asArray = (buf: SharedArrayBuffer | Buffer) => {
|
||||
return new Uint8Array(buf);
|
||||
};
|
||||
|
||||
const toExpose = {
|
||||
init: async (controlModuleWasm: SharedArrayBuffer | Buffer): Promise<void> => {
|
||||
controlModule = await WebAssembly.compile(asArray(controlModuleWasm));
|
||||
},
|
||||
|
||||
createService: async (
|
||||
wasm: SharedArrayBuffer | Buffer,
|
||||
serviceId: string,
|
||||
marineConfig?: MarineServiceConfig,
|
||||
envs?: Env,
|
||||
): Promise<void> => {
|
||||
if (!controlModule) {
|
||||
throw new Error('MarineJS is not initialized. To initialize call `init` function');
|
||||
}
|
||||
|
||||
const service = await WebAssembly.compile(asArray(wasm));
|
||||
const srv = new MarineService(
|
||||
controlModule,
|
||||
service,
|
||||
serviceId,
|
||||
onLogMessage.next.bind(onLogMessage),
|
||||
marineConfig,
|
||||
envs,
|
||||
);
|
||||
await srv.init();
|
||||
marineServices.set(serviceId, srv);
|
||||
},
|
||||
|
||||
terminate: () => {
|
||||
marineServices.forEach((val, key) => {
|
||||
val.terminate();
|
||||
});
|
||||
onLogMessage.complete();
|
||||
},
|
||||
|
||||
callService: (serviceId: string, functionName: string, args: JSONArray | JSONObject, callParams: any): unknown => {
|
||||
const srv = marineServices.get(serviceId);
|
||||
if (!srv) {
|
||||
throw new Error(`service with id=${serviceId} not found`);
|
||||
}
|
||||
|
||||
return srv.call(functionName, args, callParams);
|
||||
},
|
||||
|
||||
onLogMessage(): Observable<LogMessage> {
|
||||
return Observable.from(onLogMessage);
|
||||
},
|
||||
};
|
||||
|
||||
export type MarineBackgroundInterface = typeof toExpose;
|
||||
|
||||
expose(toExpose);
|
77
packages/core/src/marine/worker/index.ts
Normal file
77
packages/core/src/marine/worker/index.ts
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2022 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 type { JSONArray, JSONObject, LogLevel } from '@fluencelabs/marine-js/dist/types';
|
||||
import { LogFunction, logLevelToEnv } from '@fluencelabs/marine-js/dist/types';
|
||||
import type { IMarine, IWorkerLoader, IWasmLoader } from '../../interfaces/index.js';
|
||||
import type { MarineBackgroundInterface } from '../worker-script/index.js';
|
||||
import { spawn, Thread } from 'threads';
|
||||
import type { ModuleThread } from 'threads';
|
||||
|
||||
export class MarineBackgroundRunner implements IMarine {
|
||||
private workerThread?: ModuleThread<MarineBackgroundInterface>;
|
||||
|
||||
constructor(
|
||||
private workerLoader: IWorkerLoader,
|
||||
private controlModuleLoader: IWasmLoader,
|
||||
private logFunction: LogFunction,
|
||||
) {}
|
||||
|
||||
async start(): Promise<void> {
|
||||
if (this.workerThread) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.workerLoader.start();
|
||||
await this.controlModuleLoader.start();
|
||||
const worker = this.workerLoader.getValue();
|
||||
const wasm = this.controlModuleLoader.getValue();
|
||||
this.workerThread = await spawn<MarineBackgroundInterface>(worker, { timeout: 99999999 });
|
||||
this.workerThread.onLogMessage().subscribe(this.logFunction);
|
||||
await this.workerThread.init(wasm);
|
||||
}
|
||||
|
||||
createService(serviceModule: SharedArrayBuffer | Buffer, serviceId: string, logLevel?: LogLevel): Promise<void> {
|
||||
if (!this.workerThread) {
|
||||
throw 'Worker is not initialized';
|
||||
}
|
||||
|
||||
const env = logLevel ? logLevelToEnv(logLevel) : {};
|
||||
return this.workerThread.createService(serviceModule, serviceId, undefined, env);
|
||||
}
|
||||
|
||||
callService(
|
||||
serviceId: string,
|
||||
functionName: string,
|
||||
args: JSONArray | JSONObject,
|
||||
callParams: any,
|
||||
): Promise<unknown> {
|
||||
if (!this.workerThread) {
|
||||
throw 'Worker is not initialized';
|
||||
}
|
||||
|
||||
return this.workerThread.callService(serviceId, functionName, args, callParams);
|
||||
}
|
||||
|
||||
async stop(): Promise<void> {
|
||||
if (!this.workerThread) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.workerThread.terminate();
|
||||
await Thread.terminate(this.workerThread);
|
||||
}
|
||||
}
|
9
packages/core/tsconfig.json
Normal file
9
packages/core/tsconfig.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"moduleResolution": "node"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
Reference in New Issue
Block a user