mirror of
https://github.com/fluencelabs/fluence-js.git
synced 2025-04-25 09:52:12 +00:00
Xor (#24)
* Wrapping scripts with xor * Add function subscribeForErrors to handle errors
This commit is contained in:
parent
619c8a6d6a
commit
c65c0afbee
@ -10,7 +10,6 @@ import {
|
|||||||
} from '../../internal/builtins';
|
} from '../../internal/builtins';
|
||||||
import { ModuleConfig } from '../../internal/moduleConfig';
|
import { ModuleConfig } from '../../internal/moduleConfig';
|
||||||
import { checkConnection } from '../../api';
|
import { checkConnection } from '../../api';
|
||||||
import log from 'loglevel';
|
|
||||||
import { generatePeerId } from '../..';
|
import { generatePeerId } from '../..';
|
||||||
import { FluenceClientImpl } from '../../internal/FluenceClientImpl';
|
import { FluenceClientImpl } from '../../internal/FluenceClientImpl';
|
||||||
import { createConnectedClient, nodes } from '../connection';
|
import { createConnectedClient, nodes } from '../connection';
|
||||||
|
@ -2,9 +2,9 @@ import { encode } from 'bs58';
|
|||||||
import { generatePeerId, peerIdToSeed, seedToPeerId } from '../../internal/peerIdUtils';
|
import { generatePeerId, peerIdToSeed, seedToPeerId } from '../../internal/peerIdUtils';
|
||||||
import { FluenceClientImpl } from '../../internal/FluenceClientImpl';
|
import { FluenceClientImpl } from '../../internal/FluenceClientImpl';
|
||||||
import log from 'loglevel';
|
import log from 'loglevel';
|
||||||
import { createClient } from '../../api';
|
import { createClient, subscribeForErrors } from '../../api';
|
||||||
import Multiaddr from 'multiaddr';
|
import Multiaddr from 'multiaddr';
|
||||||
import { createConnectedClient, nodes } from '../connection';
|
import { createConnectedClient, createLocalClient, nodes } from '../connection';
|
||||||
|
|
||||||
describe('Typescript usage suite', () => {
|
describe('Typescript usage suite', () => {
|
||||||
it('should create private key from seed and back', async function () {
|
it('should create private key from seed and back', async function () {
|
||||||
@ -219,4 +219,46 @@ describe('Typescript usage suite', () => {
|
|||||||
let res = await resMakingPromise;
|
let res = await resMakingPromise;
|
||||||
expect(res).toEqual(['some a', 'some b', 'some c', 'some d']);
|
expect(res).toEqual(['some a', 'some b', 'some c', 'some d']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('xor handling should work with connected client', async function () {
|
||||||
|
// arrange
|
||||||
|
const client = await createConnectedClient(nodes[0].multiaddr);
|
||||||
|
log.setLevel('info');
|
||||||
|
|
||||||
|
// act
|
||||||
|
let script = `
|
||||||
|
(seq
|
||||||
|
(call relay ("op" "identity") [])
|
||||||
|
(call relay ("incorrect" "service") ["incorrect_arg"])
|
||||||
|
)
|
||||||
|
`;
|
||||||
|
const data = new Map();
|
||||||
|
data.set('relay', client.relayPeerId);
|
||||||
|
|
||||||
|
const promise = subscribeForErrors(client, 7000);
|
||||||
|
await client.sendScript(script, data);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
await expect(promise).rejects.toMatchObject({
|
||||||
|
error: expect.stringContaining("Service with id 'incorrect' not found"),
|
||||||
|
instruction: expect.stringContaining('incorrect'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('xor handling should work with local client', async function () {
|
||||||
|
// arrange
|
||||||
|
const client = await createLocalClient();
|
||||||
|
|
||||||
|
// act
|
||||||
|
let script = `(call %init_peer_id% ("incorrect" "service") ["incorrect_arg"])`;
|
||||||
|
|
||||||
|
const promise = subscribeForErrors(client, 7000);
|
||||||
|
await client.sendScript(script);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
await expect(promise).rejects.toMatchObject({
|
||||||
|
error: expect.stringContaining('There is no service: incorrect'),
|
||||||
|
instruction: expect.stringContaining('incorrect'),
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -45,27 +45,39 @@ describe('== AIR suite', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('call broken script', async function () {
|
it('call broken script', async function () {
|
||||||
|
// arrange
|
||||||
const client = await createLocalClient();
|
const client = await createLocalClient();
|
||||||
|
const script = `(incorrect)`;
|
||||||
|
|
||||||
const script = `(htyth)`;
|
// act
|
||||||
|
const promise = client.sendScript(script);
|
||||||
|
|
||||||
await expect(client.sendScript(script)).rejects.toContain("aqua script can't be parsed");
|
// assert
|
||||||
|
await expect(promise).rejects.toContain("aqua script can't be parsed");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('call script without ttl', async function () {
|
it('call script without ttl', async function () {
|
||||||
|
// arrange
|
||||||
const client = await createLocalClient();
|
const client = await createLocalClient();
|
||||||
|
|
||||||
const script = `(call %init_peer_id% ("" "") [""])`;
|
const script = `(call %init_peer_id% ("" "") [""])`;
|
||||||
|
|
||||||
await expect(client.sendScript(script, undefined, 1)).rejects.toContain('Particle expired');
|
// act
|
||||||
|
const promise = client.sendScript(script, undefined, 1);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
await expect(promise).rejects.toContain('Particle expired');
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip('call broken script by fetch', async function () {
|
it.skip('call broken script by fetch', async function () {
|
||||||
|
// arrange
|
||||||
const client = await createLocalClient();
|
const client = await createLocalClient();
|
||||||
|
const script = `(incorrect)`;
|
||||||
|
|
||||||
const script = `(htyth)`;
|
// act
|
||||||
|
const promise = client.fetch(script, ['result']);
|
||||||
|
|
||||||
await expect(client.fetch(script, ['result'])).rejects.toContain("aqua script can't be parsed");
|
// assert
|
||||||
|
await expect(promise).rejects.toContain("aqua script can't be parsed");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('check particle arguments', async function () {
|
it('check particle arguments', async function () {
|
||||||
|
22
src/api.ts
22
src/api.ts
@ -190,3 +190,25 @@ export const checkConnection = async (client: FluenceClient): Promise<boolean> =
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const subscribeForErrors = (client: FluenceClient, ttl: number): Promise<void> => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
registerServiceFunction(client, '__magic', 'handle_xor', (args, _) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
try {
|
||||||
|
reject(JSON.parse(args[0]));
|
||||||
|
} catch {
|
||||||
|
reject(args);
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
unregisterServiceFunction(client, '__magic', 'handle_xor');
|
||||||
|
return {};
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
unregisterServiceFunction(client, '__magic', 'handle_xor');
|
||||||
|
resolve();
|
||||||
|
}, ttl);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
@ -26,7 +26,7 @@ import { PeerIdB58 } from './commonTypes';
|
|||||||
export abstract class FluenceClientBase {
|
export abstract class FluenceClientBase {
|
||||||
readonly selfPeerIdFull: PeerId;
|
readonly selfPeerIdFull: PeerId;
|
||||||
|
|
||||||
get relayPeerId(): PeerIdB58 {
|
get relayPeerId(): PeerIdB58 | undefined {
|
||||||
return this.connection?.nodePeerId.toB58String();
|
return this.connection?.nodePeerId.toB58String();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ export abstract class FluenceClientBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async sendScript(script: string, data?: Map<string, any>, ttl?: number): Promise<string> {
|
async sendScript(script: string, data?: Map<string, any>, ttl?: number): Promise<string> {
|
||||||
const particle = await build(this.selfPeerIdFull, script, data, ttl);
|
const particle = await build(this.selfPeerIdFull, this.relayPeerId, script, data, ttl);
|
||||||
await this.processor.executeLocalParticle(particle);
|
await this.processor.executeLocalParticle(particle);
|
||||||
return particle.id;
|
return particle.id;
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ export class FluenceClientImpl extends FluenceClientBase implements FluenceClien
|
|||||||
data = this.addRelayToArgs(data);
|
data = this.addRelayToArgs(data);
|
||||||
const callBackId = genUUID();
|
const callBackId = genUUID();
|
||||||
script = wrapFetchCall(script, callBackId, resultArgNames);
|
script = wrapFetchCall(script, callBackId, resultArgNames);
|
||||||
const particle = await build(this.selfPeerIdFull, script, data, ttl, callBackId);
|
const particle = await build(this.selfPeerIdFull, this.relayPeerId, script, data, ttl, callBackId);
|
||||||
|
|
||||||
const prFetch = new Promise<T>(async (resolve, reject) => {
|
const prFetch = new Promise<T>(async (resolve, reject) => {
|
||||||
this.fetchParticles.set(callBackId, { resolve, reject });
|
this.fetchParticles.set(callBackId, { resolve, reject });
|
||||||
|
@ -77,23 +77,17 @@ export class ParticleProcessor {
|
|||||||
// TODO: destroy interpreter
|
// TODO: destroy interpreter
|
||||||
}
|
}
|
||||||
|
|
||||||
async executeLocalParticle(particle: ParticleDto) {
|
async executeLocalParticle(particle: ParticleDto): Promise<void> {
|
||||||
this.strategy?.onLocalParticleRecieved(particle);
|
this.strategy?.onLocalParticleRecieved(particle);
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
const resolveCallback = function () {
|
|
||||||
resolve();
|
|
||||||
};
|
|
||||||
const rejectCallback = function (err: any) {
|
|
||||||
reject(err);
|
|
||||||
};
|
|
||||||
// we check by callbacks that the script passed through the interpreter without errors
|
// we check by callbacks that the script passed through the interpreter without errors
|
||||||
this.handleParticle(particle, resolveCallback, rejectCallback);
|
this.handleParticle(particle, resolve, reject);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async executeExternalParticle(particle: ParticleDto) {
|
async executeExternalParticle(particle: ParticleDto): Promise<void> {
|
||||||
this.strategy?.onExternalParticleRecieved(particle);
|
this.strategy?.onExternalParticleRecieved(particle);
|
||||||
await this.handleExternalParticle(particle);
|
return await this.handleExternalParticle(particle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -231,7 +225,7 @@ export class ParticleProcessor {
|
|||||||
if (nextParticle) {
|
if (nextParticle) {
|
||||||
// update current particle
|
// update current particle
|
||||||
this.setCurrentParticleId(nextParticle.id);
|
this.setCurrentParticleId(nextParticle.id);
|
||||||
await this.handleParticle(nextParticle);
|
return await this.handleParticle(nextParticle);
|
||||||
} else {
|
} else {
|
||||||
// wait for a new call (do nothing) if there is no new particle in a queue
|
// wait for a new call (do nothing) if there is no new particle in a queue
|
||||||
this.setCurrentParticleId(undefined);
|
this.setCurrentParticleId(undefined);
|
||||||
@ -249,7 +243,7 @@ export class ParticleProcessor {
|
|||||||
if (error !== undefined) {
|
if (error !== undefined) {
|
||||||
log.error('error in external particle: ', error);
|
log.error('error in external particle: ', error);
|
||||||
} else {
|
} else {
|
||||||
await this.handleParticle(particle);
|
return await this.handleParticle(particle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ import { fromByteArray, toByteArray } from 'base64-js';
|
|||||||
import PeerId from 'peer-id';
|
import PeerId from 'peer-id';
|
||||||
import { encode } from 'bs58';
|
import { encode } from 'bs58';
|
||||||
import { injectDataIntoParticle } from './ParticleProcessor';
|
import { injectDataIntoParticle } from './ParticleProcessor';
|
||||||
|
import { PeerIdB58 } from './commonTypes';
|
||||||
|
|
||||||
const DEFAULT_TTL = 7000;
|
const DEFAULT_TTL = 7000;
|
||||||
|
|
||||||
@ -91,8 +92,28 @@ function wrapWithVariableInjectionScript(script: string, fields: string[]): stri
|
|||||||
return script;
|
return script;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function wrapWithXor(script: string): string {
|
||||||
|
return `
|
||||||
|
(xor
|
||||||
|
${script}
|
||||||
|
(seq
|
||||||
|
(call __magic_relay ("op" "identity") [])
|
||||||
|
(call %init_peer_id% ("__magic" "handle_xor") [%last_error%])
|
||||||
|
)
|
||||||
|
)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function wrapWithXorLocal(script: string): string {
|
||||||
|
return `
|
||||||
|
(xor
|
||||||
|
${script}
|
||||||
|
(call %init_peer_id% ("__magic" "handle_xor") [%last_error%])
|
||||||
|
)`;
|
||||||
|
}
|
||||||
|
|
||||||
export async function build(
|
export async function build(
|
||||||
peerId: PeerId,
|
peerId: PeerId,
|
||||||
|
relay: PeerIdB58 | undefined,
|
||||||
script: string,
|
script: string,
|
||||||
data?: Map<string, any>,
|
data?: Map<string, any>,
|
||||||
ttl?: number,
|
ttl?: number,
|
||||||
@ -109,8 +130,16 @@ export async function build(
|
|||||||
ttl = DEFAULT_TTL;
|
ttl = DEFAULT_TTL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (relay) {
|
||||||
|
data.set('__magic_relay', relay);
|
||||||
|
}
|
||||||
injectDataIntoParticle(id, data, ttl);
|
injectDataIntoParticle(id, data, ttl);
|
||||||
script = wrapWithVariableInjectionScript(script, Array.from(data.keys()));
|
script = wrapWithVariableInjectionScript(script, Array.from(data.keys()));
|
||||||
|
if (relay) {
|
||||||
|
script = wrapWithXor(script);
|
||||||
|
} else {
|
||||||
|
script = wrapWithXorLocal(script);
|
||||||
|
}
|
||||||
|
|
||||||
let particle: ParticleDto = {
|
let particle: ParticleDto = {
|
||||||
id: id,
|
id: id,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user