UNPKG

@activeledger/activeprotocol

Version:

Underlying protocol which handles consensus and the smart contract virtual machine of Activeledger

439 lines 16.2 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.VirtualMachine = void 0; const events = __importStar(require("events")); const activeoptions_1 = require("@activeledger/activeoptions"); const activequery_1 = require("@activeledger/activequery"); const activelogger_1 = require("@activeledger/activelogger"); const activecrypto_1 = require("@activeledger/activecrypto"); const vm2_1 = require("@activeledger/vm2"); const timers_1 = require("timers"); const fs = __importStar(require("fs")); const events_1 = require("events"); const readline_1 = require("readline"); class VirtualMachine extends events.EventEmitter { constructor(selfHost, secured, db, dbev) { super(); this.selfHost = selfHost; this.secured = secured; this.db = db; this.dbev = dbev; this.scriptFinishedExec = false; this.emitter = new events_1.EventEmitter(); this.listenForVolatile(); this.listenForFetch(); } initialiseVirtualMachine(extraBuiltins, extraExternals, extraMocks) { let toolkitAvailable = true; try { require.resolve("@activeledger/activetoolkits"); } catch (error) { toolkitAvailable = false; } let external = ["@activeledger/activecontracts"]; let builtin = ["buffer"]; let mock = {}; if (toolkitAvailable) { external.push("@activeledger/activeutilities", "@activeledger/activetoolkits"); builtin.push("http", "https", "url", "zlib"); } if (extraExternals) { external = [...external, ...extraExternals]; } if (extraBuiltins) { builtin = [...builtin, ...extraBuiltins]; } if (extraMocks) { extraMocks.forEach((libPackage) => { mock[libPackage] = MockBuiltinSecurity; }); } this.virtual = new vm2_1.NodeVM({ sandbox: { logger: activelogger_1.ActiveLogger, crypto: activecrypto_1.ActiveCrypto, secured: this.secured, self: this.selfHost, }, require: { context: "sandbox", builtin, external, mock, }, wasm: false, }); const script = new vm2_1.VMScript(fs.readFileSync(__dirname + "/vmscript.js", "utf-8")); this.virtualInstance = this.virtual.run(script); } getActivityStreamsFromVM(umid) { let activities = this.virtualInstance.getActivityStreams(umid); let streams = Object.keys(activities); let i = streams.length; let contractData; contractData = this.virtualInstance.getContractData(umid); let exported = []; if (contractData) { exported.push(contractData); } while (i--) { if (activities[streams[i]].updated) { exported.push(activities[streams[i]].export2Ledger(this.contractReferences[umid].key)); } } return exported; } destroy(umid) { this.virtualInstance.destroy(umid); if (this.contractReferences && this.contractReferences[umid]) { delete this.contractReferences[umid]; } } getInternodeCommsFromVM(umid) { return this.virtualInstance.getInternodeComms(umid); } clearingInternodeCommsFromVM(umid) { return this.virtualInstance.clearInternodeComms(umid); } getReturnContractData(umid) { return this.virtualInstance.returnContractData(umid); } getThrowsFromVM(umid) { return this.virtualInstance.throwFrom(umid); } getInputs(umid) { return this.contractReferences[umid].inputs; } initialise(payload, contractName) { return new Promise(async (resolve, reject) => { if (!this.contractReferences) { this.contractReferences = {}; } this.contractReferences[payload.umid] = { contractName, contractLocation: payload.contractLocation, inputs: payload.inputs, tx: payload.transaction, key: payload.key, }; this.event = new activequery_1.EventEngine(this.dbev, payload.transaction.$contract); try { this.virtualInstance.initialiseContract(payload, this.event, this.emitter); if (payload.transaction.$namespace === "default") { this.virtualInstance.setSysConfig(payload.umid, JSON.stringify(activeoptions_1.ActiveOptions.fetch(false))); } this.maxTimeout = new Date(); this.maxTimeout.setMilliseconds(activeoptions_1.ActiveOptions.get("contractMaxTimeout", 20) * 60 * 1000); resolve(); } catch (e) { if (e instanceof Error) { reject(await this.catchException(e, payload.umid)); } } }); } setPhase(phase) { if (this.event) { this.event.setPhase(phase); } } read(umid, readMethod) { return new Promise(async (resolve, reject) => { this.scriptFinishedExec = false; this.setPhase("read"); this.checkTimeout(readMethod, () => { reject("VM Error : Read phase timeout"); }, umid); try { resolve(await this.virtualInstance.runRead(umid, readMethod)); } catch (error) { activelogger_1.ActiveLogger.debug(error, `VM Contract Read - Error`); if (error instanceof Error) { reject(await this.catchException(error, umid)); } else { reject(error); } } finally { this.scriptFinishedExec = true; } }); } verify(sigless, umid) { return new Promise(async (resolve, reject) => { this.scriptFinishedExec = false; this.setPhase("verify"); this.checkTimeout("verify", () => { reject("VM Error : Verify phase timeout"); }, umid); try { await this.virtualInstance.runVerify(umid, sigless); resolve(true); } catch (error) { activelogger_1.ActiveLogger.debug(error, `VM Contract Verify - Error`); if (error instanceof Error) { reject(await this.catchException(error, umid)); } else { reject(error); } } finally { this.scriptFinishedExec = true; } }); } vote(nodes, umid) { return new Promise(async (resolve, reject) => { this.incMarshel(nodes, umid); this.scriptFinishedExec = false; this.setPhase("vote"); this.checkTimeout("vote", () => { reject("VM Error : Vote phase timeout"); }, umid); try { resolve(await this.virtualInstance.runVote(umid)); } catch (error) { activelogger_1.ActiveLogger.debug(error, `VM Contract Vote - Error`); if (error instanceof Error) { reject(await this.catchException(error, umid)); } else { reject(error); } } finally { this.scriptFinishedExec = true; } }); } commit(nodes, possibleTerritoriality = false, umid) { return new Promise(async (resolve, reject) => { this.incMarshel(nodes, umid); this.scriptFinishedExec = false; this.setPhase("commit"); this.checkTimeout("commit", () => { reject("VM Error : Commit phase timeout"); }, umid); try { await this.virtualInstance.runCommit(umid, possibleTerritoriality); resolve(true); } catch (error) { activelogger_1.ActiveLogger.debug(error, `VM Contract Commit - Error`); if (error instanceof Error) { reject(await this.catchException(error, umid)); } else { reject(error); } } finally { this.scriptFinishedExec = true; } }); } reconcile(nodes, umid) { return new Promise(async (resolve, reject) => { try { this.incMarshel(nodes, umid); this.scriptFinishedExec = false; this.setPhase("reconcile"); this.checkTimeout("reconcile", () => { reject("VM Error : Reconcile phase timeout"); }, umid); await this.virtualInstance.reconcile(umid); resolve(true); } catch (error) { activelogger_1.ActiveLogger.debug(error, `VM Contract Reconcile - Error`); if (error instanceof Error) { reject(await this.catchException(error, umid)); } else { reject(error); } } finally { this.scriptFinishedExec = true; } }); } postProcess(territoriality, who, umid) { return new Promise(async (resolve, reject) => { this.scriptFinishedExec = false; this.setPhase("post"); this.checkTimeout("post", () => { reject("VM Error : Post phase timeout"); }, umid); try { const postProcess = this.virtualInstance.postProcess(umid, territoriality, who); if (this.contractReferences[umid].tx.$namespace == "default") { if (this.virtualInstance.reloadSysConfig(umid)) { activelogger_1.ActiveLogger.info("Reloading Configuration Request"); this.emit("reload"); } } resolve(postProcess); } catch (error) { if (error instanceof Error) { reject(await this.catchException(error, umid)); } else { reject(error); } } finally { this.scriptFinishedExec = true; } }); } incMarshel(nodes, umid) { let keys = Object.keys(nodes); let i = keys.length; if (i) { let comms = {}; let sendComms = false; while (i--) { if (nodes[keys[i]].incomms) { comms[keys[i]] = nodes[keys[i]].incomms; if (!sendComms) sendComms = true; } } if (sendComms) { return this.virtualInstance.setInternodeComms(umid, comms, this.contractReferences[umid].key); } } } listenForVolatile() { this.emitter.on("getVolatile", async (umid, streamId) => { try { const volatile = await this.db.get(`${streamId}:volatile`); this.emitter.emit(`volatileFetched-${umid}${streamId}`, null, volatile); } catch (error) { this.emitter.emit(`volatileFetched-${umid}${streamId}`, error); } }); } listenForFetch() { this.emitter.on("getStreamData", async (umid, streamId) => { try { const data = await this.db.get(streamId); this.emitter.emit(`getStreamDataFetched-${umid}${streamId}`, null, data); } catch (error) { this.emitter.emit(`getStreamDataFetched-${umid}${streamId}`, error); } }); } checkTimeout(type, timedout, umid) { (0, timers_1.setTimeout)(() => { if (!this.scriptFinishedExec) { !this.hasBeenExtended(umid) ? timedout() : this.checkTimeout(type, timedout, umid); } }, activeoptions_1.ActiveOptions.get("contractCheckTimeout", 10000)); } hasBeenExtended(umid) { let timeoutRequestTime = this.virtualInstance.getTimeout(umid); if (timeoutRequestTime) { if (this.maxTimeout > timeoutRequestTime && timeoutRequestTime > new Date()) { return true; } } return false; } async catchException(e, umid) { var _a; if (e.stack && umid && this.contractReferences && ((_a = this.contractReferences[umid]) === null || _a === void 0 ? void 0 : _a.contractName)) { const contract = this.contractReferences[umid].contractName; const contractErrorLine = e.stack.match(new RegExp(`^.*${contract}.*$`, "m")); if (contractErrorLine && contractErrorLine.length) { const contractLastCallLine = contractErrorLine[0].trim(); const lastIndexFolder = contractLastCallLine.indexOf(contract); let contractErrorInfo = contractLastCallLine.substring(lastIndexFolder, contractLastCallLine.length); let line; if (activeoptions_1.ActiveOptions.get("debug", false)) { line = (await this.readNthLine(this.contractReferences[umid].contractLocation, parseInt(contractErrorInfo.substring(contractErrorInfo.indexOf(":") + 1, contractErrorInfo.lastIndexOf(":"))))).trim(); } return { error: e.message, at: contractErrorInfo, line, }; } else { let msg = e.stack .split("\n", 2)[1] .trim() .replace(/.*\(|\)/gi, ""); msg = contract + ":" + msg.substr(msg.indexOf(".js") + 4); return { error: e.message, at: msg, }; } } else { return e.message; } } async readNthLine(file, nthLine) { const rl = (0, readline_1.createInterface)({ input: fs.createReadStream(file), }); let lineNumber = 0; for await (const line of rl) { lineNumber++; if (lineNumber === nthLine) { rl.close(); return line; } } rl.close(); return ""; } } exports.VirtualMachine = VirtualMachine; class MockBuiltinSecurity { } //# sourceMappingURL=vm.js.map