diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..c1322dc7b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = false \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 965947d80..42344f6d1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,8 +38,10 @@ "devDependencies": { "@fluencelabs/aqua": "^0.7.0-285", "@fluencelabs/aqua-lib": "^0.4.3", + "@types/bs58": "^4.0.1", "@types/jest": "^26.0.22", "@types/platform": "^1.3.4", + "@types/uuid": "^8.3.4", "jest": "^26.6.3", "js-base64": "^3.7.2", "ts-jest": "^26.5.4", @@ -2369,6 +2371,15 @@ "@babel/types": "^7.3.0" } }, + "node_modules/@types/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-yfAgiWgVLjFCmRv8zAcOIHywYATEwiTVccTLnRp6UxTNavT55M9d/uhK3T03St/+8/z/wW+CRjGKUNmEqoHHCA==", + "dev": true, + "dependencies": { + "base-x": "^3.0.6" + } + }, "node_modules/@types/graceful-fs": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", @@ -2450,6 +2461,12 @@ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" }, + "node_modules/@types/uuid": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", + "dev": true + }, "node_modules/@types/yargs": { "version": "15.0.14", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.14.tgz", @@ -14239,6 +14256,15 @@ "@babel/types": "^7.3.0" } }, + "@types/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-yfAgiWgVLjFCmRv8zAcOIHywYATEwiTVccTLnRp6UxTNavT55M9d/uhK3T03St/+8/z/wW+CRjGKUNmEqoHHCA==", + "dev": true, + "requires": { + "base-x": "^3.0.6" + } + }, "@types/graceful-fs": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", @@ -14320,6 +14346,12 @@ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" }, + "@types/uuid": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", + "dev": true + }, "@types/yargs": { "version": "15.0.14", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.14.tgz", diff --git a/package.json b/package.json index 955071b5e..60ba56e97 100644 --- a/package.json +++ b/package.json @@ -48,8 +48,10 @@ "devDependencies": { "@fluencelabs/aqua": "^0.7.0-285", "@fluencelabs/aqua-lib": "^0.4.3", + "@types/bs58": "^4.0.1", "@types/jest": "^26.0.22", "@types/platform": "^1.3.4", + "@types/uuid": "^8.3.4", "jest": "^26.6.3", "js-base64": "^3.7.2", "ts-jest": "^26.5.4", diff --git a/src/__test__/integration/avm.spec.ts b/src/__test__/integration/avm.spec.ts index 55d504be6..bf414f5ab 100644 --- a/src/__test__/integration/avm.spec.ts +++ b/src/__test__/integration/avm.spec.ts @@ -1,34 +1,33 @@ -import { FluencePeer, setLogLevel } from '../../index'; -import { Particle } from '../../internal/Particle'; +import { FluencePeer } from '../../index'; import { handleTimeout } from '../../internal/utils'; import { registerHandlersHelper } from '../util'; let peer: FluencePeer; describe('Avm spec', () => { - afterEach(async () => { - if (peer) { - await peer.stop(); - } + beforeEach(async () => { + peer = new FluencePeer(); + await peer.start(); }); - beforeEach(() => { - peer = new FluencePeer(); + afterEach(async () => { + await peer.stop(); }); it('Simple call', async () => { - // arrange - await peer.start(); - - // act - const promise = new Promise((resolve, reject) => { + const res = await new Promise((resolve, reject) => { const script = ` (call %init_peer_id% ("print" "print") ["1"]) `; - const particle = Particle.createNew(script); + const particle = peer.internals.createNewParticle(script); + + if (particle instanceof Error) { + return reject(particle.message); + } + registerHandlersHelper(peer, particle, { print: { - print: async (args) => { + print: (args: Array>) => { const [res] = args; resolve(res); }, @@ -38,20 +37,12 @@ describe('Avm spec', () => { peer.internals.initiateParticle(particle, handleTimeout(reject)); }); - // assert - const res = await promise; expect(res).toBe('1'); - - await peer.stop(); }); it('Par call', async () => { - // arrange - await peer.start(); - - // act - const promise = new Promise((resolve, reject) => { - let res = []; + const res = await new Promise((resolve, reject) => { + const res: any[] = []; const script = ` (seq (par @@ -61,10 +52,15 @@ describe('Avm spec', () => { (call %init_peer_id% ("print" "print") ["2"]) ) `; - const particle = Particle.createNew(script); + const particle = peer.internals.createNewParticle(script); + + if (particle instanceof Error) { + return reject(particle.message); + } + registerHandlersHelper(peer, particle, { print: { - print: (args) => { + print: (args: any) => { res.push(args[0]); if (res.length == 2) { resolve(res); @@ -76,19 +72,11 @@ describe('Avm spec', () => { peer.internals.initiateParticle(particle, handleTimeout(reject)); }); - // assert - const res = await promise; expect(res).toStrictEqual(['1', '2']); - - await peer.stop(); }); it('Timeout in par call: race', async () => { - // arrange - await peer.start(); - - // act - const promise = new Promise((resolve, reject) => { + const res = await new Promise((resolve, reject) => { const script = ` (seq (call %init_peer_id% ("op" "identity") ["slow_result"] arg) @@ -101,10 +89,15 @@ describe('Avm spec', () => { ) ) `; - const particle = Particle.createNew(script); + const particle = peer.internals.createNewParticle(script); + + if (particle instanceof Error) { + return reject(particle.message); + } + registerHandlersHelper(peer, particle, { return: { - return: (args) => { + return: (args: any) => { resolve(args[0]); }, }, @@ -113,19 +106,11 @@ describe('Avm spec', () => { peer.internals.initiateParticle(particle, handleTimeout(reject)); }); - // assert - const res = await promise; expect(res).toBe('fast_result'); - - await peer.stop(); }); it('Timeout in par call: wait', async () => { - // arrange - await peer.start(); - - // act - const promise = new Promise((resolve, reject) => { + const res = await new Promise((resolve, reject) => { const script = ` (seq (call %init_peer_id% ("op" "identity") ["timeout_msg"] arg) @@ -146,10 +131,15 @@ describe('Avm spec', () => { ) ) `; - const particle = Particle.createNew(script); + const particle = peer.internals.createNewParticle(script); + + if (particle instanceof Error) { + return reject(particle.message); + } + registerHandlersHelper(peer, particle, { return: { - return: (args) => { + return: (args: any) => { resolve(args[0]); }, }, @@ -158,10 +148,6 @@ describe('Avm spec', () => { peer.internals.initiateParticle(particle, handleTimeout(reject)); }); - // assert - const res = await promise; expect(res).toBe('failed_with_timeout'); - - await peer.stop(); }); }); diff --git a/src/__test__/integration/peer.spec.ts b/src/__test__/integration/peer.spec.ts index 388253219..d1f45dc2c 100644 --- a/src/__test__/integration/peer.spec.ts +++ b/src/__test__/integration/peer.spec.ts @@ -1,8 +1,8 @@ import { Multiaddr } from 'multiaddr'; + import { nodes } from '../connection'; -import { Fluence, FluencePeer, setLogLevel } from '../../index'; +import { FluencePeer } from '../../index'; import { checkConnection, doNothing, handleTimeout } from '../../internal/utils'; -import { Particle } from '../../internal/Particle'; import { registerHandlersHelper } from '../util'; let peer: FluencePeer; @@ -20,10 +20,9 @@ describe('Typescript usage suite', () => { it('should perform test for FluencePeer class correctly', () => { // arrange - const peer: any = new FluencePeer(); - const number: any = 1; - const object: any = { str: 'Hello!' }; - const undefinedVal: any = undefined; + const number = 1; + const object = { str: 'Hello!' }; + const undefinedVal = undefined; // act const isPeerPeer = FluencePeer.isInstance(peer); @@ -40,13 +39,8 @@ describe('Typescript usage suite', () => { describe('Should expose correct peer status', () => { it('Should expose correct status for uninitialized peer', () => { - // arrange - const nonStartedPeer = new FluencePeer(); - - // act - const status = nonStartedPeer.getStatus(); + const status = peer.getStatus(); - // assert expect(status.isConnected).toBe(false); expect(status.isInitialized).toBe(false); expect(status.peerId).toBe(null); @@ -65,8 +59,6 @@ describe('Typescript usage suite', () => { expect(status.isInitialized).toBe(true); expect(status.peerId).not.toBe(null); expect(status.relayPeerId).toBe(null); - - await peer.stop(); }); it('Should expose correct status for connected peer', async () => { @@ -81,8 +73,6 @@ describe('Typescript usage suite', () => { expect(status.isInitialized).toBe(true); expect(status.peerId).not.toBe(null); expect(status.relayPeerId).not.toBe(null); - - await peer.stop(); }); }); @@ -90,8 +80,7 @@ describe('Typescript usage suite', () => { // arrange await peer.start({ connectTo: nodes[0] }); - // act - const promise = new Promise((resolve, reject) => { + const result = await new Promise((resolve, reject) => { const script = ` (xor (seq @@ -106,19 +95,24 @@ describe('Typescript usage suite', () => { (call %init_peer_id% ("callback" "error") [%last_error%]) ) )`; - const particle = Particle.createNew(script); + const particle = peer.internals.createNewParticle(script); + + if (particle instanceof Error) { + return reject(particle.message); + } + registerHandlersHelper(peer, particle, { load: { - relay: (args) => { + relay: () => { return peer.getStatus().relayPeerId; }, }, callback: { - callback: (args) => { + callback: (args: any) => { const [val] = args; resolve(val); }, - error: (args) => { + error: (args: any) => { const [error] = args; reject(error); }, @@ -128,15 +122,13 @@ describe('Typescript usage suite', () => { peer.internals.initiateParticle(particle, handleTimeout(reject)); }); - // assert - const result = await promise; expect(result).toBe('hello world!'); }); it('check connection should work', async function () { await peer.start({ connectTo: nodes[0] }); - let isConnected = await checkConnection(peer); + const isConnected = await checkConnection(peer); expect(isConnected).toEqual(true); }); @@ -144,20 +136,18 @@ describe('Typescript usage suite', () => { it('check connection should work with ttl', async function () { await peer.start({ connectTo: nodes[0] }); - let isConnected = await checkConnection(peer, 10000); + const isConnected = await checkConnection(peer, 10000); expect(isConnected).toEqual(true); }); it('two clients should work inside the same time browser', async () => { - // arrange const peer1 = new FluencePeer(); await peer1.start({ connectTo: nodes[0] }); const peer2 = new FluencePeer(); await peer2.start({ connectTo: nodes[0] }); - // act - const resMakingPromise = new Promise((resolve) => { + const res = new Promise((resolve) => { peer2.internals.regHandler.common('test', 'test', (req) => { resolve(req.args[0]); return { @@ -173,12 +163,15 @@ describe('Typescript usage suite', () => { (call "${peer2.getStatus().peerId}" ("test" "test") ["test"]) ) `; - const particle = Particle.createNew(script); - await peer1.internals.initiateParticle(particle, doNothing); + const particle = peer1.internals.createNewParticle(script); + + if (particle instanceof Error) { + throw particle; + } - // assert - const res = await resMakingPromise; - expect(res).toEqual('test'); + peer1.internals.initiateParticle(particle, doNothing); + + expect(await res).toEqual('test'); await peer1.stop(); await peer2.stop(); @@ -186,130 +179,74 @@ describe('Typescript usage suite', () => { describe('should make connection to network', () => { it('address as string', async () => { - // arrange - const addr = nodes[0]; - - // act - await peer.start({ connectTo: addr }); + await peer.start({ connectTo: nodes[0].multiaddr }); const isConnected = await checkConnection(peer); - // assert expect(isConnected).toBeTruthy(); }); it('address as multiaddr', async () => { - // arrange - const addr = new Multiaddr(nodes[0].multiaddr); - - // act - await peer.start({ connectTo: addr }); + await peer.start({ connectTo: new Multiaddr(nodes[0].multiaddr) }); const isConnected = await checkConnection(peer); - // assert expect(isConnected).toBeTruthy(); }); it('address as node', async () => { - // arrange - const addr = nodes[0]; - - // act - await peer.start({ connectTo: addr }); - const isConnected = await checkConnection(peer); - - // assert - expect(isConnected).toBeTruthy(); - }); - - it('peerid as peer id', async () => { - // arrange - const addr = nodes[0]; - - // act - await peer.start({ connectTo: addr }); - const isConnected = await checkConnection(peer); - - // assert - expect(isConnected).toBeTruthy(); - }); - - it('peerid as seed', async () => { - // arrange - const addr = nodes[0]; - - // act - await peer.start({ connectTo: addr }); + await peer.start({ connectTo: nodes[0] }); const isConnected = await checkConnection(peer); - // assert expect(isConnected).toBeTruthy(); }); it('With connection options: dialTimeout', async () => { - // arrange - const addr = nodes[0]; - - // act - await peer.start({ connectTo: addr, dialTimeoutMs: 100000 }); + await peer.start({ connectTo: nodes[0], dialTimeoutMs: 100000 }); const isConnected = await checkConnection(peer); - // assert expect(isConnected).toBeTruthy(); }); it('With connection options: skipCheckConnection', async () => { - // arrange - const addr = nodes[0]; - - // act - await peer.start({ connectTo: addr, skipCheckConnection: true }); + await peer.start({ connectTo: nodes[0], skipCheckConnection: true }); const isConnected = await checkConnection(peer); - // assert expect(isConnected).toBeTruthy(); }); it('With connection options: checkConnectionTTL', async () => { - // arrange - const addr = nodes[0]; - - // act - await peer.start({ connectTo: addr, checkConnectionTimeoutMs: 1000 }); + await peer.start({ connectTo: nodes[0], checkConnectionTimeoutMs: 1000 }); const isConnected = await checkConnection(peer); - // assert expect(isConnected).toBeTruthy(); }); it('With connection options: defaultTTL', async () => { - // arrange - const addr = nodes[0]; - - // act - await peer.start({ connectTo: addr, defaultTtlMs: 1 }); + await peer.start({ connectTo: nodes[0], defaultTtlMs: 1 }); const isConnected = await checkConnection(peer); - // assert expect(isConnected).toBeFalsy(); }); }); it('Should successfully call identity on local peer', async function () { - // arrange await peer.start(); - // act - const promise = new Promise((resolve, reject) => { + const res = await new Promise((resolve, reject) => { const script = ` (seq (call %init_peer_id% ("op" "identity") ["test"] res) (call %init_peer_id% ("callback" "callback") [res]) ) `; - const particle = Particle.createNew(script); + const particle = peer.internals.createNewParticle(script); + + if (particle instanceof Error) { + return reject(particle.message); + } + registerHandlersHelper(peer, particle, { callback: { - callback: async (args) => { + callback: async (args: any) => { const [res] = args; resolve(res); }, @@ -319,21 +256,14 @@ describe('Typescript usage suite', () => { peer.internals.initiateParticle(particle, handleTimeout(reject)); }); - // assert - const res = await promise; expect(res).toBe('test'); }); it('Should throw correct message when calling non existing local service', async function () { - // arrange await peer.start({ connectTo: nodes[0] }); - // act const res = callIncorrectService(peer); - // console.log(await res); - - // assert await expect(res).rejects.toMatchObject({ message: expect.stringContaining( `No handler has been registered for serviceId='incorrect' fnName='incorrect' args='[]'\"'`, @@ -343,11 +273,9 @@ describe('Typescript usage suite', () => { }); it('Should not crash if undefined is passed as a variable', async () => { - // arrange; await peer.start(); - // act - const promise = new Promise((resolve, reject) => { + const res = await new Promise((resolve, reject) => { const script = ` (seq (call %init_peer_id% ("load" "arg") [] arg) @@ -356,20 +284,22 @@ describe('Typescript usage suite', () => { (call %init_peer_id% ("callback" "callback") [res]) ) )`; - const particle = Particle.createNew(script); + const particle = peer.internals.createNewParticle(script); + + if (particle instanceof Error) { + return reject(particle.message); + } registerHandlersHelper(peer, particle, { load: { - arg: (args) => { - return undefined; - }, + arg: () => undefined, }, callback: { - callback: (args) => { + callback: (args: any) => { const [val] = args; resolve(val); }, - error: (args) => { + error: (args: any) => { const [error] = args; reject(error); }, @@ -379,32 +309,32 @@ describe('Typescript usage suite', () => { peer.internals.initiateParticle(particle, handleTimeout(reject)); }); - // assert - const res = await promise; expect(res).toBe(null); }); it('Should not crash if an error ocurred in user-defined handler', async () => { - // arrange; await peer.start(); - // act - const promise = new Promise((resolve, reject) => { + const promise = new Promise((_resolve, reject) => { const script = ` (xor (call %init_peer_id% ("load" "arg") [] arg) (call %init_peer_id% ("callback" "error") [%last_error%]) )`; - const particle = Particle.createNew(script); + const particle = peer.internals.createNewParticle(script); + + if (particle instanceof Error) { + return reject(particle.message); + } registerHandlersHelper(peer, particle, { load: { - arg: (args) => { - throw 'my super custom error message'; + arg: () => { + throw new Error('my super custom error message'); }, }, callback: { - error: (args) => { + error: (args: any) => { const [error] = args; reject(error); }, @@ -414,44 +344,36 @@ describe('Typescript usage suite', () => { peer.internals.initiateParticle(particle, handleTimeout(reject)); }); - // assert await expect(promise).rejects.toMatchObject({ message: expect.stringContaining('my super custom error message'), }); }); - it('Should throw error if particle is initiated on a stopped peer', async () => { - // arrange; - const stoppedPeer = new FluencePeer(); - - // act - const action = () => { - const script = `(null)`; - const particle = Particle.createNew(script); - - stoppedPeer.internals.initiateParticle(particle, doNothing); - }; + it('Should return error if particle is created on a stopped peer', async () => { + await peer.stop(); + const particle = peer.internals.createNewParticle(`(null)`); - // assert - await expect(action).toThrow('Cannot initiate new particle: peer is not initialized'); + expect(particle instanceof Error).toBe(true); }); it.skip('Should throw correct error when the client tries to send a particle not to the relay', async () => { - // arrange; await peer.start({ connectTo: nodes[0] }); - // act const promise = new Promise((resolve, reject) => { const script = ` (xor (call "incorrect_peer_id" ("any" "service") []) (call %init_peer_id% ("callback" "error") [%last_error%]) )`; - const particle = Particle.createNew(script); + const particle = peer.internals.createNewParticle(script); + + if (particle instanceof Error) { + return reject(particle.message); + } registerHandlersHelper(peer, particle, { callback: { - error: (args) => { + error: (args: any) => { const [error] = args; reject(error); }, @@ -461,7 +383,6 @@ describe('Typescript usage suite', () => { peer.internals.initiateParticle(particle, doNothing); }); - // assert await expect(promise).rejects.toMatch( 'Particle is expected to be sent to only the single peer (relay which client is connected to)', ); @@ -469,20 +390,24 @@ describe('Typescript usage suite', () => { }); async function callIncorrectService(peer: FluencePeer): Promise { - const promise = new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const script = ` (xor (call %init_peer_id% ("incorrect" "incorrect") [] res) (call %init_peer_id% ("callback" "error") [%last_error%]) )`; - const particle = Particle.createNew(script); + const particle = peer.internals.createNewParticle(script); + + if (particle instanceof Error) { + return reject(particle.message); + } registerHandlersHelper(peer, particle, { callback: { - callback: (args) => { + callback: (args: any) => { resolve(args); }, - error: (args) => { + error: (args: any) => { const [error] = args; reject(error); }, @@ -491,6 +416,4 @@ async function callIncorrectService(peer: FluencePeer): Promise { peer.internals.initiateParticle(particle, handleTimeout(reject)); }); - - return promise; } diff --git a/src/__test__/integration/sigService.spec.ts b/src/__test__/integration/sigService.spec.ts index b02aee9cb..0914d6220 100644 --- a/src/__test__/integration/sigService.spec.ts +++ b/src/__test__/integration/sigService.spec.ts @@ -34,7 +34,7 @@ describe('Sig service test suite', () => { const result = await callSig(peer, 'CustomSig'); expect(result.success).toBe(true); - const isSigCorrect = await customSig.verify(result.signature, data); + const isSigCorrect = await customSig.verify(result.signature as number[], data); expect(isSigCorrect).toBe(true); }); @@ -67,7 +67,7 @@ describe('Sig service test suite', () => { }); const callAsSigRes = await callSig(peer, 'sig'); - const callAsPeerIdRes = await callSig(peer, peer.getStatus().peerId); + const callAsPeerIdRes = await callSig(peer, peer.getStatus().peerId as string); expect(callAsSigRes.success).toBe(false); expect(callAsPeerIdRes.success).toBe(false); @@ -75,12 +75,12 @@ describe('Sig service test suite', () => { sig.securityGuard = () => true; const callAsSigResAfterGuardChange = await callSig(peer, 'sig'); - const callAsPeerIdResAfterGuardChange = await callSig(peer, peer.getStatus().peerId); + const callAsPeerIdResAfterGuardChange = await callSig(peer, peer.getStatus().peerId as string); expect(callAsSigResAfterGuardChange.success).toBe(true); expect(callAsPeerIdResAfterGuardChange.success).toBe(true); - const isValid = await sig.verify(callAsSigResAfterGuardChange.signature, data); + const isValid = await sig.verify(callAsSigResAfterGuardChange.signature as number[], data); expect(isValid).toBe(true); }); diff --git a/src/__test__/unit/builtInHandler.spec.ts b/src/__test__/unit/builtInHandler.spec.ts index 07a95818c..727259c04 100644 --- a/src/__test__/unit/builtInHandler.spec.ts +++ b/src/__test__/unit/builtInHandler.spec.ts @@ -246,7 +246,7 @@ describe('Sig service tests', () => { const sig = new Sig(ctx.peerKeyPair); const signature = await sig.sign(testData, makeTetraplet(ctx.peerId)); - const res = await sig.verify(signature.signature, testData); + const res = await sig.verify(signature.signature as number[], testData); expect(res).toBe(true); }); diff --git a/src/__test__/unit/compiler/v2.spec.ts b/src/__test__/unit/compiler/v2.spec.ts index 92155cb77..8fed09562 100644 --- a/src/__test__/unit/compiler/v2.spec.ts +++ b/src/__test__/unit/compiler/v2.spec.ts @@ -1,4 +1,5 @@ import each from 'jest-each'; + import { Fluence, FluencePeer } from '../../..'; import { forTests } from '../../../internal/compilerSupport/v2'; @@ -19,7 +20,7 @@ describe('Compiler support tests', () => { `.test( // 'raw rawArgs: $rawArgs, numArgs: $numArgs. expected args: $expectedArgs, config: $expectedConfig, default peer?: $isExpectedPeerDefault', - async ({ rawArgs, numArgs, expectedArgs, expectedConfig, isExpectedPeerDefault }) => { + ({ rawArgs, numArgs, expectedArgs, expectedConfig, isExpectedPeerDefault }) => { // arrange const testFn = forTests.extractFunctionArgs; diff --git a/src/__test__/util.ts b/src/__test__/util.ts index d7e3e895d..1c22e6b96 100644 --- a/src/__test__/util.ts +++ b/src/__test__/util.ts @@ -2,12 +2,14 @@ import { FluencePeer } from '../index'; import { Particle } from '../internal/Particle'; import { MakeServiceCall } from '../internal/utils'; -export const registerHandlersHelper = (peer: FluencePeer, particle: Particle, handlers) => { - for (let serviceId in handlers) { - for (let fnName in handlers[serviceId]) { - // of type [args] => result - const h = handlers[serviceId][fnName]; - peer.internals.regHandler.forParticle(particle.id, serviceId, fnName, MakeServiceCall(h)); - } - } +export const registerHandlersHelper = ( + peer: FluencePeer, + particle: Particle, + handlers: Record>, +) => { + Object.entries(handlers).forEach(([serviceId, service]) => { + Object.entries(service).forEach(([fnName, fn]) => { + peer.internals.regHandler.forParticle(particle.id, serviceId, fnName, MakeServiceCall(fn)); + }); + }); }; diff --git a/src/index.ts b/src/index.ts index aea16bd37..9d1e7eecc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -15,6 +15,7 @@ */ import log, { LogLevelDesc } from 'loglevel'; + import { FluencePeer, PeerConfig } from './internal/FluencePeer'; export { PeerStatus } from './internal/FluencePeer'; diff --git a/src/internal/FluenceConnection.ts b/src/internal/FluenceConnection.ts index f5f82493d..b86f89c3d 100644 --- a/src/internal/FluenceConnection.ts +++ b/src/internal/FluenceConnection.ts @@ -13,10 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +// @ts-ignore import Websockets from 'libp2p-websockets'; +// @ts-ignore import Mplex from 'libp2p-mplex'; import Lib2p2Peer from 'libp2p'; +// @ts-ignore import { decode, encode } from 'it-length-prefixed'; import pipe from 'it-pipe'; import * as log from 'loglevel'; @@ -24,6 +26,7 @@ import { Particle } from './Particle'; import { NOISE } from '@chainsafe/libp2p-noise'; import PeerId from 'peer-id'; import { Multiaddr } from 'multiaddr'; +// @ts-ignore import { all as allow_all } from 'libp2p-websockets/src/filters'; import { Connection } from 'libp2p-interfaces/src/topology'; import Buffer from './Buffer'; @@ -56,13 +59,13 @@ export interface FluenceConnectionOptions { } export class FluenceConnection { - constructor() {} + constructor(private _lib2p2Peer: Lib2p2Peer, private _relayAddress: Multiaddr) {} - static async createConnection(options: FluenceConnectionOptions): Promise { - const res = new FluenceConnection(); + private _connection?: Connection; + static async createConnection(options: FluenceConnectionOptions): Promise { const transportKey = Websockets.prototype[Symbol.toStringTag]; - res._lib2p2Peer = await Lib2p2Peer.create({ + const lib2p2Peer = await Lib2p2Peer.create({ peerId: options.peerId, modules: { transport: [Websockets], @@ -81,7 +84,7 @@ export class FluenceConnection { }, }); - res._lib2p2Peer.handle([PROTOCOL_NAME], async ({ connection, stream }) => { + lib2p2Peer.handle([PROTOCOL_NAME], async ({ connection, stream }) => { pipe(stream.source, decode(), async (source: AsyncIterable) => { try { for await (const msg of source) { @@ -98,22 +101,22 @@ export class FluenceConnection { }); }); - res._relayAddress = options.relayAddress; + const relayAddress = options.relayAddress; - return res; + return new FluenceConnection(lib2p2Peer, relayAddress); } async disconnect() { await this._lib2p2Peer.stop(); } - public async sendParticle(particle: Particle): Promise { + async sendParticle(particle: Particle): Promise { particle.logTo('debug', 'sending particle:'); /* 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 'Incorrect number of streams in FluenceConnection'; + throw new Error('Incorrect number of streams in FluenceConnection'); } const sink = this._connection.streams[0].sink; @@ -130,25 +133,20 @@ export class FluenceConnection { ); } - public async connect() { + async connect() { await this._lib2p2Peer.start(); log.debug(`dialing to the node with client's address: ` + this._lib2p2Peer.peerId.toB58String()); try { this._connection = await this._lib2p2Peer.dial(this._relayAddress); - } catch (e1) { - const e = e1 as any; - if (e.name === 'AggregateError' && e._errors.length === 1) { + } catch (e: any) { + if (e.name === 'AggregateError' && e._errors?.length === 1) { const error = e._errors[0]; - throw `Error dialing node ${this._relayAddress}:\n${error.code}\n${error.message}`; + throw new Error(`Error dialing node ${this._relayAddress}:\n${error.code}\n${error.message}`); } else { throw e; } } } - - private _lib2p2Peer: Lib2p2Peer; - private _connection: Connection; - private _relayAddress: Multiaddr; } diff --git a/src/internal/FluencePeer.ts b/src/internal/FluencePeer.ts index 82eb47c4f..770ff6a15 100644 --- a/src/internal/FluencePeer.ts +++ b/src/internal/FluencePeer.ts @@ -149,62 +149,68 @@ export interface PeerConfig { /** * Information about Fluence Peer connection */ -export interface PeerStatus { - /** - * Is the peer initialized or not - */ - isInitialized: Boolean; - - /** - * Is the peer connected to network or not - */ - isConnected: Boolean; - - /** - * The Peer's identification in the Fluence network - */ - peerId: PeerIdB58 | null; - - /** - * The relays's peer id to which the peer is connected to - */ - relayPeerId: PeerIdB58 | null; -} +export type PeerStatus = + | { + isInitialized: false; + peerId: null; + isConnected: false; + relayPeerId: null; + } + | { + isInitialized: true; + peerId: PeerIdB58; + isConnected: false; + relayPeerId: null; + } + | { + isInitialized: true; + peerId: PeerIdB58; + isConnected: true; + relayPeerId: PeerIdB58; + }; /** * This class implements the Fluence protocol for javascript-based environments. * It provides all the necessary features to communicate with Fluence network */ export class FluencePeer { - /** - * Creates a new Fluence Peer instance. - */ - constructor() {} - /** * Checks whether the object is instance of FluencePeer class * @param obj - object to check if it is FluencePeer * @returns true if the object is FluencePeer false otherwise */ - static isInstance(obj: FluencePeer): boolean { - if (obj && obj._isFluenceAwesome) { - return true; - } else { - return false; - } + static isInstance(obj: unknown): obj is FluencePeer { + return obj instanceof FluencePeer; } /** * Get the peer's status */ getStatus(): PeerStatus { - const hasKeyPair = this._keyPair !== undefined; + // TODO:: use explicit mechanism for peer's state + if (this._keyPair === undefined) { + return { + isInitialized: false, + peerId: null, + isConnected: false, + relayPeerId: null, + }; + } + + if (this._connection === undefined || this._relayPeerId === null) { + return { + isInitialized: true, + peerId: this._keyPair.Libp2pPeerId.toB58String(), + isConnected: false, + relayPeerId: null, + }; + } + return { - // TODO:: use explicit mechanism for peer's state - isInitialized: hasKeyPair, - isConnected: this._connection !== undefined, - peerId: this._keyPair?.Libp2pPeerId?.toB58String() || null, - relayPeerId: this._relayPeerId || null, + isInitialized: true, + peerId: this._keyPair.Libp2pPeerId.toB58String(), + isConnected: true, + relayPeerId: this._relayPeerId, }; } @@ -216,20 +222,16 @@ export class FluencePeer { async start(config?: PeerConfig): Promise { throwIfNotSupported(); - if (config?.KeyPair) { - this._keyPair = config!.KeyPair; - } else { - this._keyPair = await KeyPair.randomEd25519(); - } + const keyPair = config?.KeyPair ?? (await KeyPair.randomEd25519()); + this._keyPair = keyPair; + + const peerId = keyPair.Libp2pPeerId.toB58String(); if (config?.debug?.printParticleId) { this._printParticleId = true; } - this._defaultTTL = - config?.defaultTtlMs !== undefined // don't miss value 0 (zero) - ? config?.defaultTtlMs - : DEFAULT_TTL; + this._defaultTTL = config?.defaultTtlMs ?? DEFAULT_TTL; if (config?.debug?.marineLogLevel) { this._marineLogLevel = config.debug.marineLogLevel; @@ -246,7 +248,7 @@ export class FluencePeer { if (config?.connectTo) { let connectToMultiAddr: Multiaddr; - let fromNode = (config.connectTo as any).multiaddr; + const fromNode = (config.connectTo as any).multiaddr; if (fromNode) { connectToMultiAddr = new Multiaddr(fromNode); } else { @@ -274,14 +276,17 @@ export class FluencePeer { this._classServices = { sig: new Sig(this._keyPair), }; - this._classServices.sig.securityGuard = defaultSigGuard(this.getStatus().peerId); + this._classServices.sig.securityGuard = defaultSigGuard(peerId); registerSig(this, this._classServices.sig); - registerSig(this, this.getStatus().peerId, this._classServices.sig); + registerSig(this, peerId, this._classServices.sig); this._startParticleProcessing(); } getServices() { + if (this._classServices === undefined) { + throw new Error(`Can't get services: peer is not initialized`); + } return { ...this._classServices, }; @@ -297,6 +302,9 @@ export class FluencePeer { * @param serviceId - the service id by which the service can be accessed in aqua */ async registerMarineService(wasm: SharedArrayBuffer | Buffer, serviceId: string): Promise { + if (!this._fluenceAppService) { + throw new Error("Can't register marine service: peer is not initialized"); + } if (this._containsService(serviceId)) { throw new Error(`Service with '${serviceId}' id already exists`); } @@ -327,6 +335,7 @@ export class FluencePeer { await this._fluenceAppService?.terminate(); this._avmRunner = undefined; this._fluenceAppService = undefined; + this._classServices = undefined; this._particleSpecificHandlers.clear(); this._commonHandlers.clear(); @@ -340,13 +349,23 @@ export class FluencePeer { */ get internals() { return { + createNewParticle: (script: string, ttl: number = this._defaultTTL) => { + const status = this.getStatus(); + + if (!status.isInitialized) { + return new Error("Can't create new particle: peer is not initialized"); + } + + return Particle.createNew(script, ttl, status.peerId); + }, /** * Initiates a new particle execution starting from local peer * @param particle - particle to start execution of */ initiateParticle: (particle: Particle, onStageChange: (stage: ParticleExecutionStage) => void): void => { - if (!this.getStatus().isInitialized) { - throw 'Cannot initiate new particle: peer is not initialized'; + const status = this.getStatus(); + if (!status.isInitialized) { + throw new Error('Cannot initiate new particle: peer is not initialized'); } if (this._printParticleId) { @@ -354,7 +373,7 @@ export class FluencePeer { } if (particle.initPeerId === undefined) { - particle.initPeerId = this.getStatus().peerId; + particle.initPeerId = status.peerId; } if (particle.ttl === undefined) { @@ -405,21 +424,14 @@ export class FluencePeer { // private - /** - * Used in `isInstance` to check if an object is of type FluencePeer. That's a hack to work around corner cases in JS type system - */ - private _isFluenceAwesome = true; - // TODO:: make public when full connection\disconnection cycle is implemented properly - private async _connect(): Promise { - return this._connection?.connect(); + private _connect(): Promise { + return this._connection?.connect() || Promise.resolve(); } // TODO:: make public when full connection\disconnection cycle is implemented properly - private async _disconnect(): Promise { - if (this._connection) { - return this._connection.disconnect(); - } + private _disconnect(): Promise { + return this._connection?.disconnect() || Promise.resolve(); } // Queues for incoming and outgoing particles @@ -434,7 +446,7 @@ export class FluencePeer { private _particleSpecificHandlers = new Map>(); private _commonHandlers = new Map(); - private _classServices: { + private _classServices?: { sig: Sig; }; @@ -444,17 +456,17 @@ export class FluencePeer { // Internal peer state - private _printParticleId: boolean = false; - private _defaultTTL: number; + private _printParticleId = false; + private _defaultTTL: number = DEFAULT_TTL; private _relayPeerId: PeerIdB58 | null = null; - private _keyPair: KeyPair; - private _connection: FluenceConnection; + private _keyPair: KeyPair | undefined; + private _connection?: FluenceConnection; /** * @deprecated. AVM run through marine-js infrastructure. This field is needed for backward compatibility with the previous API */ - private _avmRunner: AvmRunner; - private _fluenceAppService: FluenceAppService; + private _avmRunner?: AvmRunner; + private _fluenceAppService?: FluenceAppService; private _timeouts: Array = []; private _particleQueues = new Map>(); @@ -484,7 +496,7 @@ export class FluencePeer { particlesQueue.next(item); }); - this._outgoingParticles.subscribe(async (item) => { + this._outgoingParticles.subscribe((item) => { // Do not send particle after the peer has been stopped if (!this.getStatus().isInitialized) { return; @@ -495,8 +507,14 @@ export class FluencePeer { item.onStageChange({ stage: 'sendingError' }); return; } - await this._connection.sendParticle(item.particle); - item.onStageChange({ stage: 'sent' }); + this._connection.sendParticle(item.particle).then( + () => { + item.onStageChange({ stage: 'sent' }); + }, + (e) => { + log.error(e); + }, + ); }); } @@ -513,17 +531,17 @@ export class FluencePeer { } private _createParticlesProcessingQueue() { - let particlesQueue = new Subject(); + const particlesQueue = new Subject(); let prevData: Uint8Array = Buffer.from([]); particlesQueue .pipe( - // force new line filterExpiredParticles(this._expireParticle.bind(this)), concatMap(async (item) => { - // Is `.stop()` was called we need to stop particle processing immediately - if (!this.getStatus().isInitialized) { + const status = this.getStatus(); + if (!status.isInitialized || this._avmRunner === undefined) { + // If `.stop()` was called return null to stop particle processing immediately return null; } @@ -532,12 +550,7 @@ export class FluencePeer { // MUST happen sequentially (in a critical section). // Otherwise the race between runner might occur corrupting the prevData - const result = await runAvmRunner( - this.getStatus().peerId, - this._avmRunner, - item.particle, - prevData, - ); + const result = await runAvmRunner(status.peerId, this._avmRunner, item.particle, prevData); const newData = Buffer.from(result.data); prevData = newData; @@ -548,9 +561,9 @@ export class FluencePeer { }; }), ) - .subscribe(async (item) => { - // Is `.stop()` was called we need to stop particle processing immediately - if (!this.getStatus().isInitialized) { + .subscribe((item) => { + // If `.stop()` was called then item will be null and we need to stop particle processing immediately + if (item === null || !this.getStatus().isInitialized) { return; } @@ -574,7 +587,7 @@ export class FluencePeer { // execute call requests if needed // and put particle with the results back to queue if (item.result.callRequests.length > 0) { - for (let [key, cr] of item.result.callRequests) { + for (const [key, cr] of item.result.callRequests) { const req = { fnName: cr.functionName, args: cr.arguments, @@ -617,7 +630,7 @@ export class FluencePeer { log.debug('executing call service handler', jsonify(req)); const particleId = req.particleContext.particleId; - if (this._marineServices.has(req.serviceId)) { + if (this._fluenceAppService && this._marineServices.has(req.serviceId)) { const args = JSON.stringify(req.args); const rawResult = await this._fluenceAppService.callService(req.serviceId, req.fnName, args, undefined); @@ -631,7 +644,9 @@ export class FluencePeer { } if (result.result === undefined) { - throw 'Call to marine-js returned no error and empty result. Original request: ' + jsonify(req); + throw new Error( + `Call to marine-js returned no error and empty result. Original request: ${jsonify(req)}`, + ); } return { @@ -639,13 +654,13 @@ export class FluencePeer { result: result.result, }; } catch (e) { - throw 'Call to marine-js. Result parsing error: ' + e + ', original text: ' + rawResult; + throw new Error(`Call to marine-js. Result parsing error: ${e}, original text: ${rawResult}`); } } const key = serviceFnKey(req.serviceId, req.fnName); const psh = this._particleSpecificHandlers.get(particleId); - let handler: GenericCallServiceHandler; + let handler: GenericCallServiceHandler | undefined; // we should prioritize handler for this particle if there is one // if particle-specific handlers exist for this particle try getting handler there @@ -682,9 +697,9 @@ export class FluencePeer { private _stopParticleProcessing() { // do not hang if the peer has been stopped while some of the timeouts are still being executed - for (let item of this._timeouts) { - clearTimeout(item); - } + this._timeouts.forEach((timeout) => { + clearTimeout(timeout); + }); this._particleQueues.clear(); } } @@ -698,12 +713,11 @@ function serviceFnKey(serviceId: string, fnName: string) { } function registerDefaultServices(peer: FluencePeer) { - for (let serviceId in builtInServices) { - for (let fnName in builtInServices[serviceId]) { - const h = builtInServices[serviceId][fnName]; - peer.internals.regHandler.common(serviceId, fnName, h); - } - } + Object.entries(builtInServices).forEach(([serviceId, service]) => { + Object.entries(service).forEach(([fnName, fn]) => { + peer.internals.regHandler.common(serviceId, fnName, fn); + }); + }); } async function runAvmRunner( @@ -727,8 +741,7 @@ async function runAvmRunner( particle.callResults, ); - const toLog: any = { ...interpreterResult }; - toLog.data = dataToString(toLog.data); + const toLog = { ...interpreterResult, data: dataToString(interpreterResult.data) }; if (isInterpretationSuccessful(interpreterResult)) { log.debug('Interpreter result: ', jsonify(toLog)); diff --git a/src/internal/Particle.ts b/src/internal/Particle.ts index 310676ea4..5a3af5633 100644 --- a/src/internal/Particle.ts +++ b/src/internal/Particle.ts @@ -23,36 +23,35 @@ import Buffer from './Buffer'; import { CallResultsArray, LogLevel } from '@fluencelabs/avm'; export class Particle { - id: string; - initPeerId: string; - timestamp: number; - ttl: number; - script: string; - signature: string; - data: Uint8Array; + // TODO: make it not optional (should be added to the constructor) + signature?: string; callResults: CallResultsArray = []; - static createNew(script: string, ttlMs?: number): Particle { - const res = new Particle(); - res.id = genUUID(); - res.script = script; - res.ttl = ttlMs; - res.data = Buffer.from([]); - res.timestamp = Date.now(); - - return res; + constructor( + public id: string, + public timestamp: number, + public script: string, + public data: Uint8Array, + public ttl: number, + public initPeerId: string, + ) {} + + static createNew(script: string, ttl: number, initPeerId: string): Particle { + return new Particle(genUUID(), Date.now(), script, Buffer.from([]), ttl, initPeerId); } static fromString(str: string): Particle { const json = JSON.parse(str); - const res = new Particle(); - res.id = json.id; - res.initPeerId = json.init_peer_id; - res.timestamp = json.timestamp; - res.ttl = json.ttl; - res.script = json.script; + const res = new Particle( + json.id, + json.timestamp, + json.script, + toByteArray(json.data), + json.ttl, + json.init_peer_id, + ); + res.signature = json.signature; - res.data = toByteArray(json.data); return res; } @@ -76,38 +75,30 @@ export class Particle { } clone(): Particle { - const res = new Particle(); - res.id = this.id; - res.initPeerId = this.initPeerId; - res.timestamp = this.timestamp; - res.ttl = this.ttl; - res.script = this.script; + const res = new Particle(this.id, this.timestamp, this.script, this.data, this.ttl, this.initPeerId); + res.signature = this.signature; - res.data = this.data; res.callResults = this.callResults; return res; } toString(): string { - const particle = this; - const payload = { + return JSON.stringify({ action: 'Particle', - id: particle.id, - init_peer_id: particle.initPeerId, - timestamp: particle.timestamp, - ttl: particle.ttl, - script: particle.script, + id: this.id, + init_peer_id: this.initPeerId, + timestamp: this.timestamp, + ttl: this.ttl, + script: this.script, // TODO: copy signature from a particle after signatures will be implemented on nodes signature: [], - data: fromByteArray(particle.data), - }; - - return JSON.stringify(payload); + data: this.data && fromByteArray(this.data), + }); } logTo(level: LogLevel, message: string) { let fn; - let data; + let data: string | undefined; switch (level) { case 'debug': fn = log.debug; @@ -117,8 +108,6 @@ export class Particle { fn = log.error; break; case 'info': - fn = log.info; - break; case 'trace': fn = log.info; break; @@ -139,7 +128,7 @@ export class Particle { script: this.script, signature: this.signature, callResults: this.callResults, - data: data, + data, }), ); } diff --git a/src/internal/builtins/common.ts b/src/internal/builtins/common.ts index 9883da13e..b56a77c03 100644 --- a/src/internal/builtins/common.ts +++ b/src/internal/builtins/common.ts @@ -16,10 +16,11 @@ import { encode, decode } from 'bs58'; import { sha256 } from 'multiformats/hashes/sha2'; -import { ResultCodes } from '../commonTypes'; +import { CallServiceResult } from '@fluencelabs/avm'; + +import { GenericCallServiceHandler, ResultCodes } from '../commonTypes'; import { jsonify } from '../utils'; import Buffer from '../Buffer'; -import { CallServiceResult } from '@fluencelabs/avm'; const success = (result: any): CallServiceResult => { return { @@ -39,29 +40,29 @@ const errorNotImpl = (methodName: string) => { return error(`The JS implementation of Peer does not support "${methodName}"`); }; -export const builtInServices = { +export const builtInServices: Record> = { peer: { - identify: (req) => { + identify: () => { return errorNotImpl('peer.identify'); }, - timestamp_ms: (req) => { + timestamp_ms: () => { return success(Date.now()); }, - timestamp_sec: (req) => { + timestamp_sec: () => { return success(Math.floor(Date.now() / 1000)); }, - is_connected: (req) => { + is_connected: () => { return errorNotImpl('peer.is_connected'); }, - connect: (req) => { + connect: () => { return errorNotImpl('peer.connect'); }, - get_contact: (req) => { + get_contact: () => { return errorNotImpl('peer.get_contact'); }, @@ -82,103 +83,103 @@ export const builtInServices = { }, kad: { - neighborhood: (req) => { + neighborhood: () => { return errorNotImpl('kad.neighborhood'); }, - merge: (req) => { + merge: () => { return errorNotImpl('kad.merge'); }, }, srv: { - list: (req) => { + list: () => { return errorNotImpl('srv.list'); }, - create: (req) => { + create: () => { return errorNotImpl('srv.create'); }, - get_interface: (req) => { + get_interface: () => { return errorNotImpl('srv.get_interface'); }, - resolve_alias: (req) => { + resolve_alias: () => { return errorNotImpl('srv.resolve_alias'); }, - add_alias: (req) => { + add_alias: () => { return errorNotImpl('srv.add_alias'); }, - remove: (req) => { + remove: () => { return errorNotImpl('srv.remove'); }, }, dist: { - add_module_from_vault: (req) => { + add_module_from_vault: () => { return errorNotImpl('dist.add_module_from_vault'); }, - add_module: (req) => { + add_module: () => { return errorNotImpl('dist.add_module'); }, - add_blueprint: (req) => { + add_blueprint: () => { return errorNotImpl('dist.add_blueprint'); }, - make_module_config: (req) => { + make_module_config: () => { return errorNotImpl('dist.make_module_config'); }, - load_module_config: (req) => { + load_module_config: () => { return errorNotImpl('dist.load_module_config'); }, - default_module_config: (req) => { + default_module_config: () => { return errorNotImpl('dist.default_module_config'); }, - make_blueprint: (req) => { + make_blueprint: () => { return errorNotImpl('dist.make_blueprint'); }, - load_blueprint: (req) => { + load_blueprint: () => { return errorNotImpl('dist.load_blueprint'); }, - list_modules: (req) => { + list_modules: () => { return errorNotImpl('dist.list_modules'); }, - get_module_interface: (req) => { + get_module_interface: () => { return errorNotImpl('dist.get_module_interface'); }, - list_blueprints: (req) => { + list_blueprints: () => { return errorNotImpl('dist.list_blueprints'); }, }, script: { - add: (req) => { + add: () => { return errorNotImpl('script.add'); }, - remove: (req) => { + remove: () => { return errorNotImpl('script.remove'); }, - list: (req) => { + list: () => { return errorNotImpl('script.list'); }, }, op: { - noop: (req) => { + noop: () => { return success({}); }, @@ -251,7 +252,7 @@ export const builtInServices = { sha256_string: async (req) => { if (req.args.length < 1 || req.args.length > 3) { - return error('sha256_string accepts 1-3 arguments, found: ' + req.args.length); + return error(`sha256_string accepts 1-3 arguments, found: ${req.args.length}`); } else { const [input, digestOnly, asBytes] = req.args; const inBuffer = Buffer.from(input); @@ -414,7 +415,7 @@ export const builtInServices = { return err; } const [xs] = req.args; - return success(xs.reduce((agg, cur) => agg + cur, 0)); + return success(xs.reduce((agg: any, cur: any) => agg + cur, 0)); }, dedup: (req) => { @@ -433,7 +434,7 @@ export const builtInServices = { return err; } const [xs, ys] = req.args; - const intersection = xs.filter((x) => ys.includes(x)); + const intersection = xs.filter((x: any) => ys.includes(x)); return success(intersection); }, @@ -443,7 +444,7 @@ export const builtInServices = { return err; } const [xs, ys] = req.args; - const diff = xs.filter((x) => !ys.includes(x)); + const diff = xs.filter((x: unknown) => !ys.includes(x)); return success(diff); }, @@ -455,15 +456,15 @@ export const builtInServices = { const [xs, ys] = req.args; const sdiff = [ // force new line - ...xs.filter((y) => !ys.includes(y)), - ...ys.filter((x) => !xs.includes(x)), + ...xs.filter((y: unknown) => !ys.includes(y)), + ...ys.filter((x: unknown) => !xs.includes(x)), ]; return success(sdiff); }, }, -}; +} as const; -const checkForArgumentsCount = (req, count: number) => { +const checkForArgumentsCount = (req: { args: Array }, count: number) => { if (req.args.length !== count) { return error(`Expected ${count} argument(s). Got ${req.args.length}`); } diff --git a/src/internal/commonTypes.ts b/src/internal/commonTypes.ts index 6630bfe21..0810d8500 100644 --- a/src/internal/commonTypes.ts +++ b/src/internal/commonTypes.ts @@ -49,12 +49,12 @@ export interface CallParams { /** * Particle's signature */ - signature: string; + signature?: string; /** * Security tetraplets */ - tetraplets: { [key in ArgName]: SecurityTetraplet[] }; + tetraplets: ArgName extends string ? Record : Record; } export enum ResultCodes { @@ -89,7 +89,7 @@ export interface ParticleContext { /** * Particle's signature */ - signature: string; + signature?: string; } /** @@ -123,7 +123,7 @@ export interface CallServiceData { } /** - * Type for all the possible objects that can be return to the AVM + * Type for all the possible objects that can be returned to the AVM */ export type CallServiceResultType = object | boolean | number | string | null; diff --git a/src/internal/compilerSupport/v2.ts b/src/internal/compilerSupport/v2.ts index 6150f93c3..26a8b27e3 100644 --- a/src/internal/compilerSupport/v2.ts +++ b/src/internal/compilerSupport/v2.ts @@ -215,7 +215,11 @@ export function callFunction(rawFnArgs: Array, def: FunctionCallDef, script } const promise = new Promise((resolve, reject) => { - const particle = Particle.createNew(script, config?.ttl); + const particle = peer.internals.createNewParticle(script, config?.ttl); + + if (particle instanceof Error) { + return reject(particle.message); + } for (let i = 0; i < def.argDefs.length; i++) { const argDef = def.argDefs[i]; diff --git a/src/internal/compilerSupport/v3impl/callFunction.ts b/src/internal/compilerSupport/v3impl/callFunction.ts index dcaf161ad..6b9e3f209 100644 --- a/src/internal/compilerSupport/v3impl/callFunction.ts +++ b/src/internal/compilerSupport/v3impl/callFunction.ts @@ -35,7 +35,11 @@ export function callFunction(rawFnArgs: Array, def: FunctionCallDef, script } const promise = new Promise((resolve, reject) => { - const particle = Particle.createNew(script, config?.ttl); + const particle = peer.internals.createNewParticle(script, config?.ttl); + + if (particle instanceof Error) { + return reject(particle.message); + } for (let i = 0; i < expectedNumberOfArguments; i++) { const [name, type] = argumentTypes[i]; diff --git a/src/internal/compilerSupport/v3impl/conversions.ts b/src/internal/compilerSupport/v3impl/conversions.ts index a6ef718ce..578d491b8 100644 --- a/src/internal/compilerSupport/v3impl/conversions.ts +++ b/src/internal/compilerSupport/v3impl/conversions.ts @@ -9,7 +9,7 @@ import { CallServiceData } from 'src/internal/commonTypes'; * @param type - definition of the aqua type * @returns value represented in typescript */ -export const aqua2ts = (value: any, type: NonArrowType) => { +export const aqua2ts = (value: any, type: NonArrowType): any => { const res = match(type) .with({ tag: 'nil' }, () => { return null; @@ -25,7 +25,7 @@ export const aqua2ts = (value: any, type: NonArrowType) => { return value; }) .with({ tag: 'array' }, (arr) => { - return value.map((y) => aqua2ts(y, arr.type)); + return value.map((y: any) => aqua2ts(y, arr.type)); }) .with({ tag: 'struct' }, (x) => { return Object.entries(x.fields).reduce((agg, [key, type]) => { @@ -90,7 +90,7 @@ export const aquaArgs2Ts = (req: CallServiceData, arrow: ArrowWithoutCallbacks) * @param type - definition of the aqua type * @returns value represented in aqua */ -export const ts2aqua = (value: any, type: NonArrowType) => { +export const ts2aqua = (value: any, type: NonArrowType): any => { const res = match(type) .with({ tag: 'nil' }, () => { return null; @@ -106,7 +106,7 @@ export const ts2aqua = (value: any, type: NonArrowType) => { return value; }) .with({ tag: 'array' }, (arr) => { - return value.map((y) => ts2aqua(y, arr.type)); + return value.map((y: any) => ts2aqua(y, arr.type)); }) .with({ tag: 'struct' }, (x) => { return Object.entries(x.fields).reduce((agg, [key, type]) => { diff --git a/src/internal/compilerSupport/v3impl/services.ts b/src/internal/compilerSupport/v3impl/services.ts index ed40b45b2..b8e5ff40e 100644 --- a/src/internal/compilerSupport/v3impl/services.ts +++ b/src/internal/compilerSupport/v3impl/services.ts @@ -1,14 +1,10 @@ import { SecurityTetraplet } from '@fluencelabs/avm'; -import { Particle } from 'src/internal/Particle'; import { match } from 'ts-pattern'; -import { - CallParams, - CallServiceData, - CallServiceResult, - GenericCallServiceHandler, - ResultCodes, -} from '../../commonTypes'; + +import { Particle } from '../../Particle'; +import { CallParams, CallServiceData, GenericCallServiceHandler, ResultCodes } from '../../commonTypes'; import { FluencePeer } from '../../FluencePeer'; + import { aquaArgs2Ts, responseServiceValue2ts, returnType2Aqua, ts2aqua } from './conversions'; import { ArrowWithoutCallbacks, FunctionCallConstants, FunctionCallDef, NonArrowType } from './interface'; @@ -25,7 +21,7 @@ export const injectRelayService = (def: FunctionCallDef, peer: FluencePeer) => { return { serviceId: def.names.getDataSrv, fnName: def.names.relay, - handler: (req) => { + handler: () => { return { retCode: ResultCodes.success, result: peer.getStatus().relayPeerId, @@ -41,7 +37,7 @@ export const injectValueService = (serviceId: string, fnName: string, valueType: return { serviceId: serviceId, fnName: fnName, - handler: (req) => { + handler: () => { return { retCode: ResultCodes.success, result: ts2aqua(value, valueType), @@ -57,7 +53,7 @@ export const responseService = (def: FunctionCallDef, resolveCallback: Function) return { serviceId: def.names.responseSrv, fnName: def.names.responseFnName, - handler: (req) => { + handler: (req: CallServiceData) => { const userFunctionReturn = responseServiceValue2ts(req, def.arrow); setTimeout(() => { @@ -79,7 +75,7 @@ export const errorHandlingService = (def: FunctionCallDef, rejectCallback: Funct return { serviceId: def.names.errorHandlingSrv, fnName: def.names.errorFnName, - handler: (req) => { + handler: (req: CallServiceData) => { const [err, _] = req.args; setTimeout(() => { rejectCallback(err); @@ -95,12 +91,16 @@ export const errorHandlingService = (def: FunctionCallDef, rejectCallback: Funct /** * Creates a service for user-defined service function handler */ -export const userHandlerService = (serviceId: string, arrowType: [string, ArrowWithoutCallbacks], userHandler) => { +export const userHandlerService = ( + serviceId: string, + arrowType: [string, ArrowWithoutCallbacks], + userHandler: (...args: Array) => Promise, +) => { const [fnName, type] = arrowType; return { serviceId, fnName, - handler: async (req) => { + handler: async (req: CallServiceData) => { const args = [...aquaArgs2Ts(req, type), extractCallParams(req, type)]; const rawResult = await userHandler.apply(null, args); const result = returnType2Aqua(rawResult, type); @@ -147,7 +147,7 @@ const extractCallParams = (req: CallServiceData, arrow: ArrowWithoutCallbacks): }) .exhaustive(); - let tetraplets: { [key in string]: SecurityTetraplet[] } = {}; + const tetraplets: Record = {}; for (let i = 0; i < req.args.length; i++) { if (names[i]) { tetraplets[names[i]] = req.tetraplets[i]; diff --git a/src/internal/utils.ts b/src/internal/utils.ts index e1b0dc5fd..f0683b464 100644 --- a/src/internal/utils.ts +++ b/src/internal/utils.ts @@ -15,28 +15,26 @@ */ import log from 'loglevel'; +import platform from 'platform'; + import { CallServiceData, CallServiceResult, CallServiceResultType, ResultCodes } from './commonTypes'; import { FluencePeer } from './FluencePeer'; -import { Particle, ParticleExecutionStage } from './Particle'; +import { ParticleExecutionStage } from './Particle'; import Buffer from './Buffer'; -import platform from 'platform'; -export const MakeServiceCall = (fn: (args: any[]) => CallServiceResultType) => { - return (req: CallServiceData): CallServiceResult => { - return { - retCode: ResultCodes.success, - result: fn(req.args), - }; - }; -}; +export const MakeServiceCall = + (fn: (args: any[]) => CallServiceResultType) => + (req: CallServiceData): CallServiceResult => ({ + retCode: ResultCodes.success, + result: fn(req.args), + }); -export const handleTimeout = (fn: Function) => (stage: ParticleExecutionStage) => { +export const handleTimeout = (fn: () => void) => (stage: ParticleExecutionStage) => { if (stage.stage === 'expired') { fn(); } }; - -export const doNothing = (stage: ParticleExecutionStage) => {}; +export const doNothing = (..._args: Array) => undefined; /** * Checks the network connection by sending a ping-like request to relay node @@ -67,7 +65,12 @@ export const checkConnection = async (peer: FluencePeer, ttl?: number): Promise< (call %init_peer_id% ("callback" "error") [%last_error%]) ) )`; - const particle = Particle.createNew(script, ttl); + const particle = peer.internals.createNewParticle(script, ttl); + + if (particle instanceof Error) { + return reject(particle.message); + } + peer.internals.regHandler.forParticle( particle.id, 'load', @@ -142,12 +145,12 @@ export function dataToString(data: Uint8Array) { } } -export function jsonify(obj) { +export function jsonify(obj: unknown) { return JSON.stringify(obj, null, 4); } export function throwIfNotSupported() { - if (platform.name === 'Node.js') { + if (platform.name === 'Node.js' && platform.version) { const version = platform.version.split('.').map(Number); const major = version[0]; if (major < 16) { diff --git a/tsconfig.json b/tsconfig.json index 466d9d752..cb7f120d4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,35 +1,22 @@ { - "compilerOptions": { - "typeRoots": [ - "./node_modules/@types", - "./node_modules/libp2p-ts/types", - ], - "outDir": "./dist/", - "baseUrl": ".", - "downlevelIteration": true, - "sourceMap": true, - "inlineSources": true, - "strictFunctionTypes": true, - "allowSyntheticDefaultImports": true, - "resolveJsonModule": true, - "pretty": true, - "target": "ES5", - "module": "commonjs", - "moduleResolution": "node", - "declaration": true, - "esModuleInterop": true, - "declarationMap": true, - "strict": true, - "noImplicitAny": false, - "alwaysStrict": true, - "noImplicitThis": true, - "strictNullChecks": false - }, - "exclude": [ - "node_modules", - "dist", - "bundle", - "src/__test__" - ], - "include": ["src/**/*"] + "compilerOptions": { + "typeRoots": ["./node_modules/@types", "./node_modules/libp2p-ts/types"], + "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/**/*"] }