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';
|
||||
import { ModuleConfig } from '../../internal/moduleConfig';
|
||||
import { checkConnection } from '../../api';
|
||||
import log from 'loglevel';
|
||||
import { generatePeerId } from '../..';
|
||||
import { FluenceClientImpl } from '../../internal/FluenceClientImpl';
|
||||
import { createConnectedClient, nodes } from '../connection';
|
||||
|
@ -2,9 +2,9 @@ import { encode } from 'bs58';
|
||||
import { generatePeerId, peerIdToSeed, seedToPeerId } from '../../internal/peerIdUtils';
|
||||
import { FluenceClientImpl } from '../../internal/FluenceClientImpl';
|
||||
import log from 'loglevel';
|
||||
import { createClient } from '../../api';
|
||||
import { createClient, subscribeForErrors } from '../../api';
|
||||
import Multiaddr from 'multiaddr';
|
||||
import { createConnectedClient, nodes } from '../connection';
|
||||
import { createConnectedClient, createLocalClient, nodes } from '../connection';
|
||||
|
||||
describe('Typescript usage suite', () => {
|
||||
it('should create private key from seed and back', async function () {
|
||||
@ -219,4 +219,46 @@ describe('Typescript usage suite', () => {
|
||||
let res = await resMakingPromise;
|
||||
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 () {
|
||||
// arrange
|
||||
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 () {
|
||||
// arrange
|
||||
const client = await createLocalClient();
|
||||
|
||||
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 () {
|
||||
// arrange
|
||||
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 () {
|
||||
|
22
src/api.ts
22
src/api.ts
@ -190,3 +190,25 @@ export const checkConnection = async (client: FluenceClient): Promise<boolean> =
|
||||
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 {
|
||||
readonly selfPeerIdFull: PeerId;
|
||||
|
||||
get relayPeerId(): PeerIdB58 {
|
||||
get relayPeerId(): PeerIdB58 | undefined {
|
||||
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> {
|
||||
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);
|
||||
return particle.id;
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ export class FluenceClientImpl extends FluenceClientBase implements FluenceClien
|
||||
data = this.addRelayToArgs(data);
|
||||
const callBackId = genUUID();
|
||||
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) => {
|
||||
this.fetchParticles.set(callBackId, { resolve, reject });
|
||||
|
@ -77,23 +77,17 @@ export class ParticleProcessor {
|
||||
// TODO: destroy interpreter
|
||||
}
|
||||
|
||||
async executeLocalParticle(particle: ParticleDto) {
|
||||
async executeLocalParticle(particle: ParticleDto): Promise<void> {
|
||||
this.strategy?.onLocalParticleRecieved(particle);
|
||||
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
|
||||
this.handleParticle(particle, resolveCallback, rejectCallback);
|
||||
this.handleParticle(particle, resolve, reject);
|
||||
});
|
||||
}
|
||||
|
||||
async executeExternalParticle(particle: ParticleDto) {
|
||||
async executeExternalParticle(particle: ParticleDto): Promise<void> {
|
||||
this.strategy?.onExternalParticleRecieved(particle);
|
||||
await this.handleExternalParticle(particle);
|
||||
return await this.handleExternalParticle(particle);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -231,7 +225,7 @@ export class ParticleProcessor {
|
||||
if (nextParticle) {
|
||||
// update current particle
|
||||
this.setCurrentParticleId(nextParticle.id);
|
||||
await this.handleParticle(nextParticle);
|
||||
return await this.handleParticle(nextParticle);
|
||||
} else {
|
||||
// wait for a new call (do nothing) if there is no new particle in a queue
|
||||
this.setCurrentParticleId(undefined);
|
||||
@ -249,7 +243,7 @@ export class ParticleProcessor {
|
||||
if (error !== undefined) {
|
||||
log.error('error in external particle: ', error);
|
||||
} 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 { encode } from 'bs58';
|
||||
import { injectDataIntoParticle } from './ParticleProcessor';
|
||||
import { PeerIdB58 } from './commonTypes';
|
||||
|
||||
const DEFAULT_TTL = 7000;
|
||||
|
||||
@ -91,8 +92,28 @@ function wrapWithVariableInjectionScript(script: string, fields: string[]): stri
|
||||
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(
|
||||
peerId: PeerId,
|
||||
relay: PeerIdB58 | undefined,
|
||||
script: string,
|
||||
data?: Map<string, any>,
|
||||
ttl?: number,
|
||||
@ -109,8 +130,16 @@ export async function build(
|
||||
ttl = DEFAULT_TTL;
|
||||
}
|
||||
|
||||
if (relay) {
|
||||
data.set('__magic_relay', relay);
|
||||
}
|
||||
injectDataIntoParticle(id, data, ttl);
|
||||
script = wrapWithVariableInjectionScript(script, Array.from(data.keys()));
|
||||
if (relay) {
|
||||
script = wrapWithXor(script);
|
||||
} else {
|
||||
script = wrapWithXorLocal(script);
|
||||
}
|
||||
|
||||
let particle: ParticleDto = {
|
||||
id: id,
|
||||
|
Loading…
x
Reference in New Issue
Block a user