/**
* This file is part of savant-ide.
* Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
*
* savant-ide is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* savant-ide is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* savant-ide. If not, see .
*/
import { execFile } from "child_process";
import fs from "fs";
import { promisify } from "util";
import { Paths } from "../constants";
import { parseExecutionError, parseCheckerError } from "../util/error";
const execAsync = promisify(execFile);
const readAsync = promisify(fs.readFile);
const unlinkAsync = promisify(fs.unlink);
interface BaseOpt {
code: string;
stdlib: string;
}
interface RunOpt extends BaseOpt {
init: string;
blockchain: string;
state?: string;
message?: string;
output: string;
gaslimit: string;
}
/**
* runner
*
* Asynchronously runs scilla-runner.
*
* @param {RunOpt} opts
* @returns {Promise<{ stdout: string, stderr: string }>}
*/
export const runner = async (opts: RunOpt) => {
// mandatory
const { code, stdlib, init, blockchain, output, gaslimit, ...optional } =
opts;
try {
const params = [
"-i",
code,
"-libdir",
stdlib,
"-o",
output,
"-init",
init,
"-iblockchain",
blockchain,
"-gaslimit",
parseInt(gaslimit, 10).toString(),
];
if (optional.state) {
params.push("-istate", optional.state);
}
if (optional.message) {
params.push("-imessage", optional.message);
}
const { stderr } = await execAsync(Paths.RUNNER, params);
if (stderr) {
throw new Error(stderr);
}
const result = await getOutput(opts.output);
return result;
} catch (err) {
const executionError = parseExecutionError(err.stderr);
if (executionError) {
throw executionError;
}
throw err;
} finally {
await cleanUp(opts);
}
};
/**
* checker
*
* Asynchronously invokes `scilla-checker`, returning JSON ABI or a ScillaError with the
* parsed error output from the binary.
*
* @param {CheckOpt} opts
* @returns {Promise}
*/
export const checker = async (opts: BaseOpt) => {
try {
const { stdout } = await execAsync(Paths.CHECKER, [
"-libdir",
opts.stdlib,
"-contractinfo",
"-cf",
"-gaslimit",
"80000",
"-jsonerrors",
opts.code,
]);
return stdout;
} catch (err) {
throw parseCheckerError(err.stderr);
} finally {
await cleanUp(opts);
}
};
/**
* cleanUp
*
* @param {RunOpt} files
* @returns {Promise}
*/
const cleanUp = async (files: Partial) => {
const paths = Object.keys(files)
.filter((file) => {
return file !== "stdlib" && file !== "gaslimit";
})
.map((file: string) => {
return unlinkAsync(files[file as keyof RunOpt] as string);
});
return Promise.all(paths);
};
/**
* getOutput
*
* @param {string} path
* @returns {Promise}
*/
const getOutput = async (path: string) => {
const buf = await readAsync(path);
return JSON.parse(buf.toString());
};