* Wrapping scripts with xor

* Add function subscribeForErrors to handle errors
This commit is contained in:
Pavel 2021-02-25 18:36:10 +03:00 committed by GitHub
parent 619c8a6d6a
commit c65c0afbee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 122 additions and 24 deletions

View File

@ -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';

View File

@ -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'),
});
});
}); });

View File

@ -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 () {

View File

@ -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);
});
};

View File

@ -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;
} }

View File

@ -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 });

View File

@ -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);
} }
} }

View File

@ -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,