UNPKG

int-cli

Version:

INT is the new generation of bottom-up created system of IoT and blockchain

400 lines (399 loc) 15.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = require("path"); const fs = require("fs-extra"); const childProcess = require("child_process"); const assert = require('assert'); const util_1 = require("util"); const error_code_1 = require("../error_code"); const reader_1 = require("../lib/reader"); const writer_1 = require("../lib/writer"); const storage_1 = require("../storage_sqlite/storage"); const executor_routine_1 = require("./executor_routine"); var RoutineType; (function (RoutineType) { RoutineType[RoutineType["execute"] = 0] = "execute"; RoutineType[RoutineType["verify"] = 1] = "verify"; })(RoutineType || (RoutineType = {})); class BlockExecutorWorkerRoutine { constructor() { } static async encodeParams(params) { const writer = new writer_1.BufferWriter(); let err; if (params.type === RoutineType.execute) { err = params.block.encode(writer); } else if (params.type === RoutineType.verify) { err = params.block.encode(writer); } else { assert(false, `invalid routine type`); return { err: error_code_1.ErrorCode.RESULT_INVALID_PARAM }; } if (err) { return { err }; } const blockPath = params.chain.tmpManager.getPath(`${params.name}.block`); try { fs.writeFileSync(blockPath, writer.render()); } catch (e) { params.chain.logger.error(`write block to ${blockPath} failed `, e); return { err: error_code_1.ErrorCode.RESULT_EXCEPTION }; } const epr = await params.chain.executorParamCreator.interprocessEncode(params.externParams); if (epr.err) { return { err: epr.err }; } try { const message = { type: params.type, name: params.name, dataDir: params.chain.dataDir, blockPath, storagePath: params.storage.filePath, externParams: epr.encoded }; return { err: error_code_1.ErrorCode.RESULT_OK, message }; } catch (e) { return { err: error_code_1.ErrorCode.RESULT_INVALID_PARAM }; } } static async decodeParams(creator, message) { let ccr = await creator.createChainInstance(message.dataDir, { readonly: true }); if (ccr.err) { return { err: ccr.err }; } const chain = ccr.chain; let block = chain.newBlock(); let blockRaw; let err; try { blockRaw = fs.readFileSync(message.blockPath); } catch (e) { chain.logger.error(`read block from ${message.blockPath} failed `, e); return { err: error_code_1.ErrorCode.RESULT_INVALID_PARAM }; } if (message.type === RoutineType.execute) { err = block.decode(new reader_1.BufferReader(blockRaw)); } else if (message.type === RoutineType.verify) { err = block.decode(new reader_1.BufferReader(blockRaw)); } else { assert(false, `invalid routine type`); return { err: error_code_1.ErrorCode.RESULT_INVALID_PARAM }; } if (err) { chain.logger.error(`decode block from params failed `, err); return { err }; } const storage = new storage_1.SqliteStorage({ filePath: message.storagePath, logger: chain.logger }); err = await storage.init(); if (err) { chain.logger.error(`init storage ${message.storagePath} failed `, err); return { err }; } const dpr = await chain.executorParamCreator.interprocessDecode(message.externParams); if (dpr.err) { return { err: dpr.err }; } return { err: error_code_1.ErrorCode.RESULT_OK, params: { type: message.type, chain, storage, block, name: message.name, externParams: dpr.params } }; } static encodeResult(result) { const message = Object.create(null); message.name = result.name; message.err = result.err; message.type = result.type; if (result.type === RoutineType.execute) { if (result.block) { const writer = new writer_1.BufferWriter(); let err = result.block.encode(writer); if (err) { return { err }; } const blockPath = result.chain.tmpManager.getPath(`${result.name}.block`); try { fs.writeFileSync(blockPath, writer.render()); } catch (e) { result.chain.logger.error(`write block to ${blockPath} failed `, e); return { err: error_code_1.ErrorCode.RESULT_EXCEPTION }; } message.blockPath = blockPath; } } else if (result.type === RoutineType.verify) { if (!util_1.isNullOrUndefined(result.valid)) { message.valid = result.valid; } } else { assert(false, `invalid result type`); return { err: error_code_1.ErrorCode.RESULT_INVALID_PARAM }; } if (result.storage) { const writer = new writer_1.BufferWriter(); if (result.storage.storageLogger) { let err = result.storage.storageLogger.encode(writer); if (err) { return { err }; } const redoPath = result.chain.tmpManager.getPath(`${result.name}.redo`); try { fs.writeFileSync(redoPath, writer.render()); } catch (e) { result.chain.logger.error(`write redo log to ${redoPath} failed `, e); return { err: error_code_1.ErrorCode.RESULT_EXCEPTION }; } message.redoPath = redoPath; } } return { err: error_code_1.ErrorCode.RESULT_OK, message }; } static decodeResult(params, message) { let result = Object.create(null); result.name = message.name; result.chain = params.chain; result.type = message.type; assert(result.name === params.name, `routine params' name is ${params.name} while result name is ${result.name}`); if (result.name !== params.name) { params.chain.logger.error(`routine result name mismatch`); return { err: error_code_1.ErrorCode.RESULT_INVALID_PARAM }; } result.err = message.err; if (message.type === RoutineType.execute) { if (message.blockPath) { let blockRaw; try { blockRaw = fs.readFileSync(message.blockPath); } catch (e) { params.chain.logger.error(`read block from ${message.blockPath} failed `, e); return { err: error_code_1.ErrorCode.RESULT_INVALID_PARAM }; } let reader = new reader_1.BufferReader(blockRaw); let block = params.chain.newBlock(); let err = block.decode(reader); if (err) { params.chain.logger.error(`decode block from ${message.blockPath} failed `, err); return { err }; } result.block = block; params.chain.logger.debug(`about to remove tmp block `, message.blockPath); fs.unlinkSync(message.blockPath); } } else if (message.type === RoutineType.verify) { if (!util_1.isNullOrUndefined(message.valid)) { result.valid = message.valid; } } else { assert(false, `invalid routine type`); return { err: error_code_1.ErrorCode.RESULT_INVALID_PARAM }; } if (message.redoPath) { let redoRaw; try { redoRaw = fs.readFileSync(message.redoPath); } catch (e) { params.chain.logger.error(`read redo log from ${message.redoPath} failed `, e); return { err: error_code_1.ErrorCode.RESULT_INVALID_PARAM }; } let reader = new reader_1.BufferReader(redoRaw); params.storage.createLogger(); let err = params.storage.storageLogger.decode(reader); if (err) { params.chain.logger.error(`decode redo log from ${message.redoPath} failed `, err); return { err }; } params.chain.logger.debug(`about to remove tmp redo log `, message.redoPath); fs.unlinkSync(message.redoPath); result.storage = params.storage; } return { err: error_code_1.ErrorCode.RESULT_OK, result }; } async run(params) { let result = Object.create(null); result.name = params.name; result.chain = params.chain; result.type = params.type; do { params.storage.createLogger(); const nber = await params.chain.newBlockExecutor(params); if (nber.err) { result.err = nber.err; break; } if (params.type === RoutineType.execute) { let err = await nber.executor.execute(); result.err = err; if (!result.err) { result.block = params.block; result.storage = params.storage; } } else if (params.type === RoutineType.verify) { let vr = await nber.executor.verify(); result.err = vr.err; if (!result.err) { result.valid = vr.valid; result.block = params.block; result.storage = params.storage; } } else { assert(false, `invalid routine type`); result.err = error_code_1.ErrorCode.RESULT_INVALID_PARAM; } } while (false); await params.storage.uninit(); return result; } } exports.BlockExecutorWorkerRoutine = BlockExecutorWorkerRoutine; class InterprocessRoutineManager { constructor(chain) { this.m_chain = chain; } create(options) { const routine = new InterprocessRoutine({ name: options.name, chain: this.m_chain, block: options.block, storage: options.storage }); return { err: error_code_1.ErrorCode.RESULT_OK, routine }; } } exports.InterprocessRoutineManager = InterprocessRoutineManager; class InterprocessRoutine extends executor_routine_1.BlockExecutorRoutine { constructor(options) { super({ name: options.name, logger: options.chain.logger, block: options.block, storage: options.storage }); this.m_state = executor_routine_1.BlockExecutorRoutineState.init; this.m_cancelSet = false; this.m_chain = options.chain; } async _executeOrVerify(type) { if (this.m_state !== executor_routine_1.BlockExecutorRoutineState.init) { return { err: error_code_1.ErrorCode.RESULT_INVALID_STATE }; } this.m_state = executor_routine_1.BlockExecutorRoutineState.running; this.m_worker = new WorkerProxy(this.m_logger); const epr = await this.m_chain.prepareExternParams(this.m_block, this.m_storage); if (epr.err) { return { err: epr.err }; } const result = await this.m_worker.run({ type, name: this.m_name, chain: this.m_chain, block: this.m_block, storage: this.m_storage, externParams: epr.params }); if (this.m_cancelSet) { return { err: error_code_1.ErrorCode.RESULT_CANCELED }; } if (result.block) { this.m_block = result.block; } if (result.storage) { this.m_storage = result.storage; } return { err: error_code_1.ErrorCode.RESULT_OK, result: { err: result.err, valid: result.valid } }; } async execute() { return this._executeOrVerify(RoutineType.execute); } async verify() { return this._executeOrVerify(RoutineType.verify); } cancel() { if (this.m_state === executor_routine_1.BlockExecutorRoutineState.finished) { return; } else if (this.m_state === executor_routine_1.BlockExecutorRoutineState.init) { this.m_state = executor_routine_1.BlockExecutorRoutineState.finished; return; } this.m_cancelSet = true; this.m_worker.cancel(); } } class WorkerProxy { constructor(logger) { this.m_logger = logger; } async run(params) { await params.storage.uninit(); const epr = await BlockExecutorWorkerRoutine.encodeParams(params); if (epr.err) { return { err: error_code_1.ErrorCode.RESULT_INVALID_PARAM, type: params.type, chain: params.chain, name: params.name }; } const workerPath = path.join(__dirname, '../../routine/executor_routine.js'); if (this.m_logger.level === 'debug') { let command = JSON.stringify(epr.message).replace(/\\\\/g, '/').replace(/\"/g, '\\"'); this.m_logger.debug('run command in worker routine: ', command); } this.m_childProcess = childProcess.fork(workerPath); if (!this.m_childProcess.send(epr.message)) { return { err: error_code_1.ErrorCode.RESULT_EXCEPTION, type: params.type, chain: params.chain, name: params.name }; } const result = await new Promise((resolve) => { const errListener = () => { this.m_logger.debug(`routine process error`); resolve({ err: error_code_1.ErrorCode.RESULT_EXCEPTION, type: params.type, chain: params.chain, name: params.name }); }; this.m_childProcess.on('error', errListener); this.m_childProcess.on('message', (message) => { this.m_childProcess.removeListener('error', errListener); if (this.m_logger.level === 'debug') { const rawResult = JSON.stringify(message).replace(/\\\\/g, '/').replace(/\"/g, '\\"'); this.m_logger.debug('result of worker routine: ', rawResult); } const dr = BlockExecutorWorkerRoutine.decodeResult(params, message); if (dr.err) { resolve({ err: dr.err, type: params.type, name: params.name, chain: params.chain }); } else { resolve(dr.result); } }); }); return result; } cancel() { if (!this.m_childProcess || this.m_childProcess.killed) { return; } this.m_logger.debug(`executor canceled, will kill routine process`); this.m_childProcess.kill(); } }