Skip to content
This repository has been archived by the owner on Jun 29, 2021. It is now read-only.

Commit

Permalink
Merge pull request #63 from Zilliqa/patch/newjslib
Browse files Browse the repository at this point in the history
Patch Kaya RPC to work with latest zilliqa-js
  • Loading branch information
edison0xyz authored Dec 6, 2018
2 parents 763c190 + 0e8e9fd commit c6a73c0
Show file tree
Hide file tree
Showing 18 changed files with 2,860 additions and 2,360 deletions.
4 changes: 4 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,9 @@
"no-lonely-if": 0,
"prefer-destructuring": 0
},
"env": {
"es6" : true,
"node" : true
},
"extends": ["airbnb-base"]
}
11 changes: 6 additions & 5 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ if (process.env.NODE_ENV === 'test') {
* Account creation/loading based on presets given
* @dev : Only create wallets if the user does not supply any load file
*/
if(!options.load) {
if (!options.load) {
if (options.fixtures) {
utils.logVerbose(logLabel, `Bootstrapping from account fixture files: ${options.fixtures}`);
const accountsPath = options.fixtures;
Expand All @@ -114,7 +114,6 @@ if(!options.load) {

wallet.printWallet();


// cross region settings with Env
if (process.env.NODE_ENV === 'dev') {
expressjs.use(cors());
Expand Down Expand Up @@ -199,10 +198,12 @@ const handler = async (req, res) => {
res.status(200).send(makeResponse(body.id, body.jsonrpc, data, false));
break;
case 'CreateTransaction':
console.log(body.params);
try {
const txnId = await logic.processCreateTxn(body.params, options);
data = txnId;
} catch (err) {
console.log(err);
data = err.message;
res.status(200).send(makeResponse(body.id, body.jsonrpc, data, true));
break;
Expand Down Expand Up @@ -248,15 +249,15 @@ process.on('SIGINT', function () {
// If `save` is enabled, store files under the saved/ directory
if (options.save) {
console.log(`Save mode enabled. Extracting data now..`);

const dir = config.savedFilesDir;
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}

// Saved files will be prefixed with the timestamp when the user decides to end the session
const timestamp = utils.getDateTimeString();

const outputData = `${dir}${timestamp}`;
const targetFilePath = `${outputData}_data.json`;
utils.consolePrint(`Files will be saved at ${targetFilePath}`);
Expand All @@ -283,7 +284,7 @@ process.on('SIGINT', function () {
// remove files from the db_path
rimraf.sync(`${options.dataPath}*`);
console.log(`Files from ${options.dataPath} removed. Shutting down now.`);
process.exit(0);
process.exit(0);
})

module.exports = {
Expand Down
34 changes: 34 additions & 0 deletions components/CustomErrors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
class InterpreterError extends Error {
constructor(message) {
super(message);
this.name = "InterpreterError";
}
}

class BalanceError extends Error {
constructor(message) {
super(message);
this.name = "BalanceError";
}
}

class MultiContractError extends Error {
constructor(message) {
super(message);
this.name = "MulticontractError";
}
}

class InsufficientGasError extends Error {
constructor(message) {
super(message);
this.name = "InsufficientGasError";
}
}

module.exports = {
InterpreterError: InterpreterError,
BalanceError : BalanceError,
MultiContractError : MultiContractError,
InsufficientGasError : InsufficientGasError
}
114 changes: 77 additions & 37 deletions components/scilla/scilla.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@
const fs = require("fs");
const { promisify } = require("util");
const rp = require("request-promise");
const { exec } = require("child_process");
const { execFile } = require("child_process");
const { paramsCleanup, codeCleanup, logVerbose } = require("../../utilities");
const { InterpreterError } = require('../CustomErrors');
const config = require("../../config");
const logLabel = "Scilla";

const execAsync = promisify(exec);
const execFileAsync = promisify(execFile);


const makeBlockchainJson = (val, blockchainPath) => {
const blockchainData = [
Expand All @@ -47,15 +49,18 @@ const initializeContractState = amt => {
];
return initState;
};
/**
* Runs the remote interpreter (currently hosted by zilliqa)
* @async
* @method runRemoteInterpreterAsync
* @param {Object} data object containing the code, state, init, message and blockchain filepath
* @returns: Output message received from the remote scilla interpreter
*/

/*
Runs the remote interpreter (currently hosted by zilliqa)
[DEFAULT] - configurable through the config file
@params: data object containing the code, state, init, message and blockchain filepath
@returns: Output message received from the remote scilla interpreter
*/
const runRemoteInterpreterAsync = async data => {
logVerbose(logLabel, "Running Remote Interpreter");
console.log('throwing interpreter error');
throw new InterpreterError('Remote interpreter is currently unavailable');

const reqData = {
code: fs.readFileSync(data.code, "utf-8"),
Expand All @@ -77,16 +82,19 @@ const runRemoteInterpreterAsync = async data => {
body: reqData,
};

logVerbose(logLabel, 'Attempting to run remote interpreter now');
let response;
try {
response = await rp(options);
} catch (err) {
console.log(`Interpreter failed to process code. Error message received:`);
console.log(`${err.message}`);
console.log("Possible fix: Have your code passed type checking?");
throw new Error("KayaRPC-specific: Interpreter error");
throw new InterpreterError('Remote interpreter failed to run')
}


// FIXME: Change error mechanism once the Scilla versioning is completed
// https://github.com/Zilliqa/scilla/issues/291
if (!response.message.gas_remaining) {
console.log(
"WARNING: You are using an outdated scilla interpreter. Please upgrade to the latest version"
Expand All @@ -97,17 +105,28 @@ const runRemoteInterpreterAsync = async data => {
return response.message;
};

const runLocalInterpreterAsync = async (command, outputPath) => {
/**
* Executes the local interpreter
* @async
* @method runLocalInterpreterAsync
* @param { Object } cmdOptions: Command options required to run the scilla interpreter
* @param { String } outputPath : File path to the output file
* @returns { Object } - response object
*/

const runLocalInterpreterAsync = async (cmdOptions, outputPath) => {
logVerbose(logLabel, "Running local scilla interpreter");

const SCILLA_BIN_PATH = config.constants.smart_contract.SCILLA_BINARY;
// Run Scilla Interpreter
if (!fs.existsSync(config.scilla.runnerPath)) {
if (!fs.existsSync(SCILLA_BIN_PATH)) {
logVerbose(logLabel, "Scilla runner not found. Hint: Have you compiled the scilla binaries?");
throw new Error("Kaya RPC Runtime Error: Scilla-runner not found");
throw new InterpreterError("Kaya RPC Runtime Error: Scilla-runner not found");
}

const result = await execAsync(command);
const result = await execFileAsync(SCILLA_BIN_PATH, cmdOptions);
if (result.stderr !== "") {
throw new Error(`Interpreter error: ${result.stderr}`);
throw new InterpreterError(`Interpreter error: ${result.stderr}`);
}

logVerbose(logLabel, "Scilla execution completed");
Expand All @@ -117,25 +136,43 @@ const runLocalInterpreterAsync = async (command, outputPath) => {
};

module.exports = {
executeScillaRun: async (payload, address, dir, currentBnum, gasLimit) => {

/**
* Takes arguments from `logic.js` and runs the scilla interpreter
*
* @method executeScillaRun
* @async
* @param { Object } payload - payload object from the message
* @param { String } contractAddr - Contract address, only applicable if it is a deployment call
* @param { String } senderAddress - message sender address
* @param { String } directory of the data files
* @param { String } current block number
* @param { String } gasLimit - gasLimit specified by the caller
* @returns { Object } consisting of `gasRemaining and nextAddress`
*/
executeScillaRun: async (payload, contractAddr, senderAddr, dir, currentBnum) => {
// Get the blocknumber into a json file
const blockchainPath = `${dir}/blockchain.json`;
const blockchainPath = `${dir}blockchain.json`;
makeBlockchainJson(currentBnum, blockchainPath);

let isCodeDeployment = payload.code && payload.to === "0".repeat(40);
const contractAddr = isCodeDeployment ? address : payload.to;
let isCodeDeployment = payload.code && payload.toAddr === "0".repeat(40);
contractAddr = (isCodeDeployment) ? contractAddr : payload.toAddr;

const initPath = `${dir}${contractAddr}_init.json`;
const codePath = `${dir}${contractAddr}_code.scilla`;
const outputPath = `${dir}/${contractAddr}_out.json`;
const outputPath = `${dir}${contractAddr}_out.json`;
const statePath = `${dir}${contractAddr}_state.json`;
let cmd;

const standardOpt = ['-libdir', config.constants.smart_contract.SCILLA_LIB, '-gaslimit', payload.gasLimit];
const initOpt = ['-init', initPath];
const outputOpt = ['-o', outputPath];
const codeOpt = ['-i', codePath];
const blockchainOpt = ['-iblockchain', blockchainPath];

const cmdOpt = [].concat.apply([], [standardOpt, initOpt, outputOpt, codeOpt, blockchainOpt])

if (isCodeDeployment) {
logVerbose(logLabel, "Code Deployment");
// initialized with standard message template
isCodeDeployment = true;
cmd = `${config.scilla.runnerPath} -iblockchain ${blockchainPath} -o ${outputPath} -init ${initPath} -i ${codePath} -gaslimit ${gasLimit} -libdir ${
config.scilla.localLibDir}`;

// get init data from payload
const initParams = JSON.stringify(payload.data);
Expand All @@ -147,7 +184,7 @@ module.exports = {
fs.writeFileSync(codePath, cleanedCode);
} else {
// Invoke transition
logVerbose(logLabel, `Calling transition within contract ${payload.to}`);
logVerbose(logLabel, `Calling transition within contract ${payload.toAddr}`);

logVerbose(logLabel, `Code Path: ${codePath}`);
logVerbose(logLabel, `Init Path: ${initPath}`);
Expand All @@ -156,15 +193,18 @@ module.exports = {
throw new Error("Address does not exist");
}

// get message from payload information
const msgPath = `${dir}${payload.to}_message.json`;

const incomingMessage = JSON.stringify(payload.data);
const cleanedMsg = paramsCleanup(incomingMessage);
fs.writeFileSync(msgPath, cleanedMsg);

// Invoke contract requires additional message and state paths
cmd = `${cmd} -imessage ${msgPath} -istate ${statePath}`;
// Create message object from payload
const msgPath = `${dir}${payload.toAddr}_message.json`;
msgObj = JSON.parse(payload.data);
msgObj._amount = payload.amount;
msgObj._sender = `0x${senderAddr}`;
fs.writeFileSync(msgPath, JSON.stringify(msgObj));

// Append additional options for transition calls
cmdOpt.push('-imessage');
cmdOpt.push(msgPath);
cmdOpt.push('-istate');
cmdOpt.push(statePath);
}

if (!fs.existsSync(codePath) || !fs.existsSync(initPath)) {
Expand All @@ -176,7 +216,7 @@ module.exports = {

if (!config.scilla.remote) {
// local scilla interpreter
retMsg = await runLocalInterpreterAsync(cmd, outputPath);
retMsg = await runLocalInterpreterAsync(cmdOpt, outputPath);
} else {
const apiReqParams = {
output: outputPath,
Expand Down Expand Up @@ -209,7 +249,7 @@ module.exports = {
logVerbose(logLabel, `Next address: ${retMsg.message._recipient}`);
responseData.nextAddress = retMsg.message._recipient;
}
// Contract deployment runs do not have returned message
// Contract deployment do not have the next address
responseData.nextAddress = "0".repeat(40);

return responseData;
Expand Down
Loading

0 comments on commit c6a73c0

Please sign in to comment.