1
0
mirror of https://github.com/fluencelabs/aquavm synced 2025-03-16 04:50:49 +00:00

Marine-js stage 2: move avm-related helpers to appropriate package ()

This commit is contained in:
Pavel 2022-04-18 15:38:19 +03:00 committed by GitHub
parent 06d275ea16
commit c2bfad7f79
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 311 additions and 60 deletions

@ -2,8 +2,3 @@ dist
node_modules
wasm
*.tgz
# this file is auto-generated
src/wasm.js
src/importObject.ts
src/avm.wasm

@ -8,20 +8,10 @@
"name": "@fluencelabs/avm",
"version": "0.0.0",
"license": "Apache 2.0",
"bin": {
"copy-avm": "dist/copyAvm.js"
},
"devDependencies": {
"@types/node": "^14.0.0",
"typescript": "^4.0.0"
}
},
"node_modules/@types/node": {
"version": "14.18.9",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.9.tgz",
"integrity": "sha512-j11XSuRuAlft6vLDEX4RvhqC0KxNxx6QIyMXNb0vHHSNPXTPeiy3algESWmOOIzEtiEL0qiowPU3ewW9hHVa7Q==",
"dev": true
},
"node_modules/typescript": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz",
@ -37,12 +27,6 @@
}
},
"dependencies": {
"@types/node": {
"version": "14.18.9",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.9.tgz",
"integrity": "sha512-j11XSuRuAlft6vLDEX4RvhqC0KxNxx6QIyMXNb0vHHSNPXTPeiy3algESWmOOIzEtiEL0qiowPU3ewW9hHVa7Q==",
"dev": true
},
"typescript": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz",

@ -2,23 +2,19 @@
"name": "@fluencelabs/avm",
"description": "Aquamarine VM",
"version": "0.0.0",
"main": "./dist/avm.wasm",
"main": "./dist/index.js",
"repository": "https://github.com/fluencelabs/air",
"author": "Fluence Labs",
"license": "Apache 2.0",
"files": [
"dist/*"
],
"bin": {
"copy-avm": "./dist/copyAvm.js"
},
"scripts": {
"build": "tsc && ./build_wasm.sh"
},
"private": false,
"dependencies": {},
"devDependencies": {
"typescript": "^4.0.0",
"@types/node": "^14.0.0"
"typescript": "^4.0.0"
}
}

@ -0,0 +1,169 @@
/*
* Copyright 2022 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 { CallResultsArray, InterpreterResult, CallRequest } from './types';
const decoder = new TextDecoder();
const encoder = new TextEncoder();
/**
* Serializes AVM arguments in JSON string which can be passed into marine-js
* @param initPeerId - peer ID which initialized particle
* @param currentPeerId - peer ID which is currently executing the particle
* @param air - particle's air script as string
* @param prevData - particle's prev data as raw byte array
* @param data - particle's data as raw byte array
* @param callResults - array of tuples [callResultKey, callResult]
* @returns AVM call arguments as serialized JSON string
*/
export function serializeAvmArgs(
initPeerId: string,
currentPeerId: string,
air: string,
prevData: Uint8Array,
data: Uint8Array,
callResults: CallResultsArray,
): string {
const callResultsToPass: any = {};
for (let [key, callResult] of callResults) {
callResultsToPass[key] = {
ret_code: callResult.retCode,
result: callResult.result,
};
}
const paramsToPass = {
init_peer_id: initPeerId,
current_peer_id: currentPeerId,
};
const encoded = encoder.encode(JSON.stringify(callResultsToPass));
const avmArg = JSON.stringify([
// force new line
air,
Array.from(prevData),
Array.from(data),
paramsToPass,
Array.from(encoded),
]);
return avmArg;
}
/**
* Deserializes raw result of AVM call obtained from marine-js into structured form
* @param rawResult - string containing raw result of AVM call
* @returns structured InterpreterResult
*/
export function deserializeAvmResult(rawResult: string): InterpreterResult {
let result: any;
try {
result = JSON.parse(rawResult);
} catch (ex) {
throw 'call_module result parsing error: ' + ex + ', original text: ' + rawResult;
}
if (result.error !== '') {
throw 'call_module returned error: ' + result.error;
}
result = result.result;
const callRequestsStr = decoder.decode(new Uint8Array(result.call_requests));
let parsedCallRequests;
try {
if (callRequestsStr.length === 0) {
parsedCallRequests = {};
} else {
parsedCallRequests = JSON.parse(callRequestsStr);
}
} catch (e) {
throw "Couldn't parse call requests: " + e + '. Original string is: ' + callRequestsStr;
}
let resultCallRequests: Array<[key: number, callRequest: CallRequest]> = [];
for (const key in parsedCallRequests) {
const callRequest = parsedCallRequests[key];
let arguments_;
let tetraplets;
try {
arguments_ = JSON.parse(callRequest.arguments);
} catch (e) {
throw "Couldn't parse arguments: " + e + '. Original string is: ' + arguments_;
}
try {
tetraplets = JSON.parse(callRequest.tetraplets);
} catch (e) {
throw "Couldn't parse tetraplets: " + e + '. Original string is: ' + tetraplets;
}
resultCallRequests.push([
key as any,
{
serviceId: callRequest.service_id,
functionName: callRequest.function_name,
arguments: arguments_,
tetraplets: tetraplets,
},
]);
}
return {
retCode: result.ret_code,
errorMessage: result.error_message,
data: result.data,
nextPeerPks: result.next_peer_pks,
callRequests: resultCallRequests,
};
}
type CallToAvm = ((args: string) => Promise<string>) | ((args: string) => string);
/**
* Utility function which serializes AVM args and passed them into AVM returning interpreter result.
* Call to AVM is delegated to a function which must be provided by user.
* It might be either synchronous or asynchronous (returning a promise)
* @param fn - delegated call to AVM
* @param initPeerId - peer ID which initialized particle
* @param currentPeerId - peer ID which is currently executing the particle
* @param air - particle's air script as string
* @param prevData - particle's prev data as raw byte array
* @param data - particle's data as raw byte array
* @param callResults - array of tuples [callResultKey, callResult]
* @returns structured InterpreterResult
*/
export async function callAvm(
fn: CallToAvm,
initPeerId: string,
currentPeerId: string,
air: string,
prevData: Uint8Array,
data: Uint8Array,
callResults: CallResultsArray,
): Promise<InterpreterResult> {
try {
const avmArg = serializeAvmArgs(initPeerId, currentPeerId, air, prevData, data, callResults);
const rawResult = await fn(avmArg);
return deserializeAvmResult(rawResult);
} catch (e) {
return {
retCode: -1,
errorMessage: 'marine-js call failed, ' + e,
} as any;
}
}

@ -1,33 +0,0 @@
#! /usr/bin/env node
import fs from 'fs';
import path from 'path';
const firstArgument = process.argv[2];
if (!firstArgument) {
console.log(`Expected exactly 1 argument, got 0. Usage: ${path.basename(process.argv[1])} <destination directory>`);
process.exit(1);
}
let destPath = firstArgument;
if (!path.isAbsolute(destPath)) {
destPath = path.join(process.cwd(), destPath);
}
const wasmName = 'avm.wasm';
const packageName = '@fluencelabs/avm';
const modulePath = require.resolve(packageName);
const source = path.join(path.dirname(modulePath), wasmName);
const dest = path.join(destPath, wasmName);
console.log('ensure directory exists: ', destPath);
fs.mkdirSync(destPath, { recursive: true });
console.log('copying AVM wasm');
console.log('from: ', source);
console.log('to: ', dest);
fs.copyFileSync(source, dest);
console.log('done!');

18
avm/client/src/index.ts Normal file

@ -0,0 +1,18 @@
/*
* Copyright 2022 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.
*/
export * from './types';
export * from './avmHelpers';

122
avm/client/src/types.ts Normal file

@ -0,0 +1,122 @@
/*
* Copyright 2022 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.
*/
export type LogLevel = 'info' | 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'off';
/**
* Represents an executed host function result.
*/
export interface CallServiceResult {
/**
* A error code service or builtin returned, where 0 represents success.
*/
retCode: number;
/**
* Serialized return value from the service.
*/
result: string;
}
/**
* Contains arguments of a call instruction and all other necessary information required for calling a service.
*/
export interface CallRequest {
/**
* Id of a service that should be called.
*/
serviceId: string;
/**
* Name of a function from service identified by service_id that should be called.
*/
functionName: string;
/**
* Arguments that should be passed to the service.
*/
arguments: any[];
/**
* Security tetraplets that should be passed to the service.
*/
tetraplets: SecurityTetraplet[][];
}
export type CallRequestsArray = Array<[key: number, callRequest: CallRequest]>;
export type CallResultsArray = Array<[key: number, callServiceResult: CallServiceResult]>;
/**
* Describes a result returned at the end of the interpreter execution_step.
*/
export interface InterpreterResult {
/**
* A return code, where 0 means success.
*/
retCode: number;
/**
* Contains error message if ret_code != 0
*/
errorMessage: string;
/**
* Contains script data that should be preserved in an executor of this interpreter regardless of ret_code value.
*/
data: Uint8Array;
/**
* Public keys of peers that should receive data.
*/
nextPeerPks: Array<string>;
/**
* Collected parameters of all met call instructions that could be executed on a current peer.
*/
callRequests: CallRequestsArray;
}
/**
* ResolvedTriplet represents peer network location with all variables, literals and etc resolved into final string.
* This structure contains a subset of values that SecurityTetraplet consists of.
*/
export interface ResolvedTriplet {
/**
* Id of a peer where corresponding value was set.
*/
peer_pk: string;
/**
* Id of a service that set corresponding value.
*/
service_id: string;
/**
* Name of a function that returned corresponding value.
*/
function_name: string;
}
/**
* Describes an origin that set corresponding value.
*/
export interface SecurityTetraplet extends ResolvedTriplet {
/**
* Value was produced by applying this `json_path` to the output from `call_service`.
*/
json_path: string;
}