From 48fc017a1bfa41ce1d71182e925a28fa397461ce Mon Sep 17 00:00:00 2001 From: Pavel Date: Fri, 10 Dec 2021 16:47:58 +0300 Subject: [PATCH] Add built-in service (Sig) which signs data and verifies signatures (#110) --- package-lock.json | 14 +- package.json | 4 +- src/__test__/unit/builtInHandler.spec.ts | 266 +++++++++++++++--- src/internal/FluencePeer.ts | 20 +- src/internal/KeyPair.ts | 8 + src/internal/builtInServices.ts | 175 ++++++++++++ src/internal/commonTypes.ts | 3 +- .../LegacyCallServiceHandler.ts | 2 +- src/internal/defaultServices.ts | 121 -------- 9 files changed, 443 insertions(+), 170 deletions(-) create mode 100644 src/internal/builtInServices.ts delete mode 100644 src/internal/defaultServices.ts diff --git a/package-lock.json b/package-lock.json index 7d99205c..7637e3a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,6 @@ "@chainsafe/libp2p-noise": "4.0.0", "@fluencelabs/avm": "^0.17.6", "async": "3.2.0", - "base64-js": "1.5.1", "bs58": "4.0.1", "cids": "0.8.1", "it-length-prefixed": "3.0.1", @@ -31,6 +30,7 @@ "devDependencies": { "@types/jest": "^26.0.22", "jest": "^26.6.3", + "js-base64": "^3.7.2", "ts-jest": "^26.5.4", "typedoc": "^0.21.9", "typescript": "^4.0.0" @@ -4668,6 +4668,12 @@ "node": ">= 10.14.2" } }, + "node_modules/js-base64": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.2.tgz", + "integrity": "sha512-NnRs6dsyqUXejqk/yv2aiXlAvOs56sLkX6nUdeaNezI5LFFLlsZjOThmwnrcwh5ZZRwZlCMnVAY3CvhIhoVEKQ==", + "dev": true + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -11936,6 +11942,12 @@ "supports-color": "^7.0.0" } }, + "js-base64": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.2.tgz", + "integrity": "sha512-NnRs6dsyqUXejqk/yv2aiXlAvOs56sLkX6nUdeaNezI5LFFLlsZjOThmwnrcwh5ZZRwZlCMnVAY3CvhIhoVEKQ==", + "dev": true + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", diff --git a/package.json b/package.json index b883ebe4..14f83a34 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,6 @@ "@chainsafe/libp2p-noise": "4.0.0", "@fluencelabs/avm": "^0.17.6", "async": "3.2.0", - "base64-js": "1.5.1", "bs58": "4.0.1", "cids": "0.8.1", "it-length-prefixed": "3.0.1", @@ -44,6 +43,7 @@ "jest": "^26.6.3", "ts-jest": "^26.5.4", "typedoc": "^0.21.9", - "typescript": "^4.0.0" + "typescript": "^4.0.0", + "js-base64": "^3.7.2" } } diff --git a/src/__test__/unit/builtInHandler.spec.ts b/src/__test__/unit/builtInHandler.spec.ts index 105fe723..a8fe5ce1 100644 --- a/src/__test__/unit/builtInHandler.spec.ts +++ b/src/__test__/unit/builtInHandler.spec.ts @@ -1,41 +1,75 @@ -import each from 'jest-each'; import { CallServiceData } from '../../internal/commonTypes'; -import { defaultServices } from '../../internal/defaultServices'; +import each from 'jest-each'; +import { BuiltInServiceContext, builtInServices } from '../../internal/builtInServices'; +import { KeyPair } from '../../internal/KeyPair'; +import { toUint8Array } from 'js-base64'; + +const key = '+cmeYlZKj+MfSa9dpHV+BmLPm6wq4inGlsPlQ1GvtPk='; + +const context = (async () => { + const keyBytes = toUint8Array(key); + const kp = await KeyPair.fromEd25519SK(keyBytes); + const res: BuiltInServiceContext = { + 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, +]; 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'} | ${'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"} - - ${'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'}} - + 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'} | ${'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"} + + ${'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'}} + + ${'sig'} | ${'verify'} | ${[testData, testDataSig]} | ${0} | ${true}} + ${'sig'} | ${'verify'} | ${[testData, testDataWrongSig]} | ${0} | ${false}} + ${'sig'} | ${'sign'} | ${[]} | ${1} | ${'sign accepts exactly one argument: data be signed in format of u8 array of bytes'}} + ${'sig'} | ${'verify'} | ${[testData]} | ${1} | ${'verify accepts exactly two arguments: data and signature, both in format of u8 array of bytes'}} `.test( // '$fnName with $args expected retcode: $retCode and result: $result', @@ -56,7 +90,7 @@ describe('Tests for default handler', () => { }; // act - const fn = defaultServices[req.serviceId][req.fnName]; + const fn = builtInServices(await context)[req.serviceId][req.fnName]; const res = await fn(req); // assert @@ -84,7 +118,7 @@ describe('Tests for default handler', () => { }; // act - const fn = defaultServices[req.serviceId][req.fnName]; + const fn = builtInServices(await context)[req.serviceId][req.fnName]; const res = await fn(req); // assert @@ -93,4 +127,166 @@ describe('Tests for default handler', () => { result: 'The JS implementation of Peer does not support identify', }); }); + + it('sig.sign should create the correct signature', async () => { + // arrange + const ctx = await context; + const req: CallServiceData = { + serviceId: 'sig', + fnName: 'sign', + args: [testData], + tetraplets: [ + [ + { + function_name: 'get_trust_bytes', + json_path: '', + peer_pk: '', + service_id: 'trust-graph', + }, + ], + ], + particleContext: { + particleId: 'some', + initPeerId: ctx.peerId, + timestamp: 595951200, + ttl: 595961200, + signature: 'sig', + }, + }; + + // act + const fn = builtInServices(ctx)[req.serviceId][req.fnName]; + const res = await fn(req); + + // assert + expect(res).toMatchObject({ + retCode: 0, + result: testDataSig, + }); + }); + + it('sign-verify call chain should work', async () => { + const ctx = await context; + const signReq: CallServiceData = { + serviceId: 'sig', + fnName: 'sign', + args: [testData], + tetraplets: [ + [ + { + function_name: 'get_trust_bytes', + json_path: '', + peer_pk: '', + service_id: 'trust-graph', + }, + ], + ], + particleContext: { + particleId: 'some', + initPeerId: ctx.peerId, + timestamp: 595951200, + ttl: 595961200, + signature: 'sig', + }, + }; + + const signFn = builtInServices(ctx)[signReq.serviceId][signReq.fnName]; + const signRes = await signFn(signReq); + + const verifyReq: CallServiceData = { + serviceId: 'sig', + fnName: 'verify', + args: [testData, signRes.result], + tetraplets: [], + particleContext: { + particleId: 'some', + initPeerId: ctx.peerId, + timestamp: 595951200, + ttl: 595961200, + signature: 'sig', + }, + }; + + const verifyFn = builtInServices(ctx)[verifyReq.serviceId][verifyReq.fnName]; + const verifyRes = await verifyFn(verifyReq); + + expect(verifyRes).toMatchObject({ + retCode: 0, + result: true, + }); + }); + + it('sig.sign should not allow data from incorrect services', async () => { + // arrange + const ctx = await context; + const req: CallServiceData = { + serviceId: 'sig', + fnName: 'sign', + args: [testData], + tetraplets: [ + [ + { + function_name: 'some-other-fn', + json_path: '', + peer_pk: '', + service_id: 'cool-service', + }, + ], + ], + particleContext: { + particleId: 'some', + initPeerId: ctx.peerId, + timestamp: 595951200, + ttl: 595961200, + signature: 'sig', + }, + }; + + // act + const fn = builtInServices(ctx)[req.serviceId][req.fnName]; + const res = await fn(req); + + // assert + expect(res).toMatchObject({ + retCode: 1, + result: expect.stringContaining("Only data from the following services is allowed to be signed:"), + }); + }); + + it('sig.sign should not allow particles initiated from other peers', async () => { + // arrange + const ctx = await context; + const req: CallServiceData = { + serviceId: 'sig', + fnName: 'sign', + args: [testData], + tetraplets: [ + [ + { + function_name: 'some-other-fn', + json_path: '', + peer_pk: '', + service_id: 'cool-service', + }, + ], + ], + particleContext: { + particleId: 'some', + initPeerId: (await KeyPair.randomEd25519()).Libp2pPeerId.toB58String(), + timestamp: 595951200, + ttl: 595961200, + signature: 'sig', + }, + }; + + // act + const fn = builtInServices(ctx)[req.serviceId][req.fnName]; + const res = await fn(req); + + // assert + expect(res).toMatchObject({ + retCode: 1, + result: 'sign is only allowed to be called on the same peer the particle was initiated from', + }); + }); }); diff --git a/src/internal/FluencePeer.ts b/src/internal/FluencePeer.ts index 401ec7d7..2516e351 100644 --- a/src/internal/FluencePeer.ts +++ b/src/internal/FluencePeer.ts @@ -33,7 +33,7 @@ import { createInterpreter, dataToString } from './utils'; import { filter, pipe, Subject, tap } from 'rxjs'; import { RequestFlow } from './compilerSupport/v1'; import log from 'loglevel'; -import { defaultServices } from './defaultServices'; +import { BuiltInServiceContext, builtInServices } from './builtInServices'; import { instanceOf } from 'ts-pattern'; /** @@ -210,7 +210,10 @@ export class FluencePeer { } this._legacyCallServiceHandler = new LegacyCallServiceHandler(); - registerDefaultServices(this); + registerDefaultServices(this, { + peerKeyPair: this._keyPair, + peerId: this.getStatus().peerId, + }); this._startParticleProcessing(); } @@ -459,7 +462,7 @@ export class FluencePeer { this._execSingleCallRequest(req) .catch( (err): CallServiceResult => ({ - retCode: ResultCodes.exceptionInHandler, + retCode: ResultCodes.error, result: `Handler failed. fnName="${req.fnName}" serviceId="${ req.serviceId }" error: ${err.toString()}`, @@ -529,7 +532,7 @@ export class FluencePeer { res = handler ? await handler(req) : { - retCode: ResultCodes.unknownError, + retCode: ResultCodes.error, result: `No handler has been registered for serviceId='${req.serviceId}' fnName='${req.fnName}' args='${req.args}'`, }; } @@ -576,10 +579,11 @@ function serviceFnKey(serviceId: string, fnName: string) { return `${serviceId}/${fnName}`; } -function registerDefaultServices(peer: FluencePeer) { - for (let serviceId in defaultServices) { - for (let fnName in defaultServices[serviceId]) { - const h = defaultServices[serviceId][fnName]; +function registerDefaultServices(peer: FluencePeer, context: BuiltInServiceContext) { + const ctx = builtInServices(context); + for (let serviceId in ctx) { + for (let fnName in ctx[serviceId]) { + const h = ctx[serviceId][fnName]; peer.internals.regHandler.common(serviceId, fnName, h); } } diff --git a/src/internal/KeyPair.ts b/src/internal/KeyPair.ts index 8fadd3f6..c97f5655 100644 --- a/src/internal/KeyPair.ts +++ b/src/internal/KeyPair.ts @@ -54,4 +54,12 @@ export class KeyPair { toEd25519PrivateKey(): Uint8Array { return this.Libp2pPeerId.privKey.marshal().subarray(0, 32); } + + signBytes(data: Uint8Array): Promise { + return this.Libp2pPeerId.privKey.sign(data); + } + + verify(data: Uint8Array, signature: Uint8Array): Promise { + return this.Libp2pPeerId.privKey.public.verify(data, signature); + } } diff --git a/src/internal/builtInServices.ts b/src/internal/builtInServices.ts new file mode 100644 index 00000000..3e34c557 --- /dev/null +++ b/src/internal/builtInServices.ts @@ -0,0 +1,175 @@ +/* + * Copyright 2021 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 { CallServiceResult } from '@fluencelabs/avm'; +import { encode, decode } from 'bs58'; +import { PeerIdB58 } from 'src'; +import { GenericCallServiceHandler, ResultCodes } from './commonTypes'; +import { KeyPair } from './KeyPair'; + +const success = (result: any): CallServiceResult => { + return { + result: result, + retCode: ResultCodes.success, + }; +}; + +const error = (error: string): CallServiceResult => { + return { + result: error, + retCode: ResultCodes.error, + }; +}; + +export interface BuiltInServiceContext { + peerKeyPair: KeyPair; + peerId: PeerIdB58; +} + +export function builtInServices(context: BuiltInServiceContext): { + [serviceId in string]: { [fnName in string]: GenericCallServiceHandler }; +} { + return { + op: { + noop: (req) => { + return success({}); + }, + + array: (req) => { + return success(req.args); + }, + + identity: (req) => { + if (req.args.length > 1) { + return error(`identity accepts up to 1 arguments, received ${req.args.length} arguments`); + } else { + return success(req.args.length === 0 ? {} : req.args[0]); + } + }, + + concat: (req) => { + const incorrectArgIndices = req.args // + .map((x, i) => [Array.isArray(x), i]) + .filter(([isArray, _]) => !isArray) + .map(([_, index]) => index); + + if (incorrectArgIndices.length > 0) { + const str = incorrectArgIndices.join(', '); + return error(`All arguments of 'concat' must be arrays: arguments ${str} are not`); + } else { + return success([].concat.apply([], req.args)); + } + }, + + string_to_b58: (req) => { + if (req.args.length !== 1) { + return error('string_to_b58 accepts only one string argument'); + } else { + return success(encode(new TextEncoder().encode(req.args[0]))); + } + }, + + string_from_b58: (req) => { + if (req.args.length !== 1) { + return error('string_from_b58 accepts only one string argument'); + } else { + return success(new TextDecoder().decode(decode(req.args[0]))); + } + }, + + bytes_to_b58: (req) => { + if (req.args.length !== 1 || !Array.isArray(req.args[0])) { + return error('bytes_to_b58 accepts only single argument: array of numbers'); + } else { + const argumentArray = req.args[0] as number[]; + return success(encode(new Uint8Array(argumentArray))); + } + }, + + bytes_from_b58: (req) => { + if (req.args.length !== 1) { + return error('bytes_from_b58 accepts only one string argument'); + } else { + return success(Array.from(decode(req.args[0]))); + } + }, + }, + + peer: { + timeout: (req) => { + if (req.args.length !== 2) { + return error('timeout accepts exactly two arguments: timeout duration in ms and a message string'); + } + const durationMs = req.args[0]; + const message = req.args[1]; + + return new Promise((resolve) => { + setTimeout(() => { + const res = success(message); + resolve(res); + }, durationMs); + }); + }, + + identify: (req) => { + return error('The JS implementation of Peer does not support identify'); + }, + }, + + sig: { + sign: async (req) => { + if (req.args.length !== 1) { + return error('sign accepts exactly one argument: data be signed in format of u8 array of bytes'); + } + + if (req.particleContext.initPeerId !== context.peerId) { + return error('sign is only allowed to be called on the same peer the particle was initiated from'); + } + + const t = req.tetraplets[0][0]; + const serviceFnPair = `${t.service_id}.${t.function_name}`; + + const allowedServices = [ + 'trust-graph.get_trust_bytes', + 'trust-graph.get_revocation_bytes', + 'registry.get_key_bytes', + 'registry.get_record_bytes', + ]; + + if (allowedServices.indexOf(serviceFnPair) === -1) { + return error( + 'Only data from the following services is allowed to be signed: ' + allowedServices.join(', '), + ); + } + + const [data] = req.args; + const signedData = await context.peerKeyPair.signBytes(Uint8Array.from(data)); + return success(Array.from(signedData)); + }, + + verify: async (req) => { + if (req.args.length !== 2) { + return error( + 'verify accepts exactly two arguments: data and signature, both in format of u8 array of bytes', + ); + } + const [data, signature] = req.args; + const result = await context.peerKeyPair.verify(Uint8Array.from(data), Uint8Array.from(signature)); + return success(result); + }, + }, + }; +} diff --git a/src/internal/commonTypes.ts b/src/internal/commonTypes.ts index 02d2e92d..6e1ddea4 100644 --- a/src/internal/commonTypes.ts +++ b/src/internal/commonTypes.ts @@ -59,8 +59,7 @@ export interface CallParams { export enum ResultCodes { success = 0, - unknownError = 1, - exceptionInHandler = 2, + error = 1, } /** diff --git a/src/internal/compilerSupport/LegacyCallServiceHandler.ts b/src/internal/compilerSupport/LegacyCallServiceHandler.ts index 34c484a3..73b1de01 100644 --- a/src/internal/compilerSupport/LegacyCallServiceHandler.ts +++ b/src/internal/compilerSupport/LegacyCallServiceHandler.ts @@ -37,7 +37,7 @@ export const callLegacyCallServiceHandler = ( if (res.retCode === undefined) { res = { - retCode: ResultCodes.unknownError, + retCode: ResultCodes.error, result: `The handler did not set any result. Make sure you are calling the right peer and the handler has been registered. Original request data was: serviceId='${req.serviceId}' fnName='${req.fnName}' args='${req.args}'`, }; } diff --git a/src/internal/defaultServices.ts b/src/internal/defaultServices.ts deleted file mode 100644 index b33c45a3..00000000 --- a/src/internal/defaultServices.ts +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2021 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 { CallServiceResult } from '@fluencelabs/avm'; -import { encode, decode } from 'bs58'; -import { GenericCallServiceHandler, ResultCodes } from './commonTypes'; - -const success = (result: any): CallServiceResult => { - return { - result: result, - retCode: ResultCodes.success, - }; -}; - -const error = (error: string): CallServiceResult => { - return { - result: error, - retCode: ResultCodes.unknownError, - }; -}; - -export const defaultServices: { [serviceId in string]: { [fnName in string]: GenericCallServiceHandler } } = { - op: { - noop: (req) => { - return success({}); - }, - - array: (req) => { - return success(req.args); - }, - - identity: (req) => { - if (req.args.length > 1) { - return error(`identity accepts up to 1 arguments, received ${req.args.length} arguments`); - } else { - return success(req.args.length === 0 ? {} : req.args[0]); - } - }, - - concat: (req) => { - const incorrectArgIndices = req.args // - .map((x, i) => [Array.isArray(x), i]) - .filter(([isArray, _]) => !isArray) - .map(([_, index]) => index); - - if (incorrectArgIndices.length > 0) { - const str = incorrectArgIndices.join(', '); - return error(`All arguments of 'concat' must be arrays: arguments ${str} are not`); - } else { - return success([].concat.apply([], req.args)); - } - }, - - string_to_b58: (req) => { - if (req.args.length !== 1) { - return error('string_to_b58 accepts only one string argument'); - } else { - return success(encode(new TextEncoder().encode(req.args[0]))); - } - }, - - string_from_b58: (req) => { - if (req.args.length !== 1) { - return error('string_from_b58 accepts only one string argument'); - } else { - return success(new TextDecoder().decode(decode(req.args[0]))); - } - }, - - bytes_to_b58: (req) => { - if (req.args.length !== 1 || !Array.isArray(req.args[0])) { - return error('bytes_to_b58 accepts only single argument: array of numbers'); - } else { - const argumentArray = req.args[0] as number[]; - return success(encode(new Uint8Array(argumentArray))); - } - }, - - bytes_from_b58: (req) => { - if (req.args.length !== 1) { - return error('bytes_from_b58 accepts only one string argument'); - } else { - return success(Array.from(decode(req.args[0]))); - } - }, - }, - - peer: { - timeout: (req) => { - if (req.args.length !== 2) { - return error('timeout accepts exactly two arguments: timeout duration in ms and a message string'); - } - const durationMs = req.args[0]; - const message = req.args[1]; - - return new Promise((resolve) => { - setTimeout(() => { - const res = success(message); - resolve(res); - }, durationMs); - }); - }, - - identify: (req) => { - return error('The JS implementation of Peer does not support identify'); - }, - }, -};