UNPKG

int-cli

Version:

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

453 lines (452 loc) 18.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const fs = require("fs-extra"); const path = require("path"); const error_code_1 = require("./error_code"); const bignumber_js_1 = require("bignumber.js"); const tmp_manager_1 = require("./lib/tmp_manager"); const storage_1 = require("./storage_json/storage"); const storage_2 = require("./storage_sqlite/storage"); const value_chain_1 = require("./value_chain"); const address_1 = require("./address"); const storage_3 = require("./storage"); const util_1 = require("util"); class ValueChainDebugSession { constructor(debuger) { this.debuger = debuger; } async init(options) { const chain = this.debuger.chain; const dumpSnapshotManager = new storage_3.StorageDumpSnapshotManager({ logger: chain.logger, path: options.storageDir }); this.m_dumpSnapshotManager = dumpSnapshotManager; const snapshotManager = new storage_3.StorageLogSnapshotManager({ path: chain.storageManager.path, headerStorage: chain.headerStorage, storageType: storage_1.JsonStorage, logger: chain.logger, dumpSnapshotManager }); const tmpManager = new tmp_manager_1.TmpManager({ root: options.storageDir, logger: chain.logger }); let err = tmpManager.init({ clean: true }); if (err) { chain.logger.error(`ValueChainDebugSession init tmpManager init failed `, error_code_1.stringifyErrorCode(err)); return err; } const storageManager = new storage_3.StorageManager({ tmpManager, path: options.storageDir, storageType: storage_1.JsonStorage, logger: chain.logger, snapshotManager }); this.m_storageManager = storageManager; err = await this.m_storageManager.init(); if (err) { chain.logger.error(`ValueChainDebugSession init storageManager init failed `, error_code_1.stringifyErrorCode(err)); return err; } const ghr = await chain.headerStorage.getHeader(0); if (ghr.err) { chain.logger.error(`ValueChainDebugSession init get genesis header failed `, error_code_1.stringifyErrorCode(ghr.err)); return ghr.err; } const genesisHash = ghr.header.hash; const gsr = await this.m_dumpSnapshotManager.getSnapshot(genesisHash); if (!gsr.err) { return error_code_1.ErrorCode.RESULT_OK; } else if (gsr.err !== error_code_1.ErrorCode.RESULT_NOT_FOUND) { chain.logger.error(`ValueChainDebugSession init get gensis dump snapshot err `, error_code_1.stringifyErrorCode(gsr.err)); return gsr.err; } const gsvr = await chain.storageManager.getSnapshotView(genesisHash); if (gsvr.err) { chain.logger.error(`ValueChainDebugSession init get gensis dump snapshot err `, error_code_1.stringifyErrorCode(gsvr.err)); return gsvr.err; } const srcStorage = gsvr.storage; let csr = await storageManager.createStorage('genesis'); if (csr.err) { chain.logger.error(`ValueChainDebugSession init create genesis memory storage failed `, error_code_1.stringifyErrorCode(csr.err)); return csr.err; } const dstStorage = csr.storage; const tjsr = await srcStorage.toJsonStorage(dstStorage); if (tjsr.err) { chain.logger.error(`ValueChainDebugSession init transfer genesis memory storage failed `, error_code_1.stringifyErrorCode(tjsr.err)); return tjsr.err; } csr = await this.m_storageManager.createSnapshot(dstStorage, genesisHash, true); if (csr.err) { chain.logger.error(`ValueChainDebugSession init create genesis memory dump failed `, error_code_1.stringifyErrorCode(csr.err)); return csr.err; } return error_code_1.ErrorCode.RESULT_OK; } async block(hash) { const chain = this.debuger.chain; const block = chain.blockStorage.get(hash); if (!block) { chain.logger.error(`block ${hash} not found`); return { err: error_code_1.ErrorCode.RESULT_NOT_FOUND }; } const csr = await this.m_storageManager.createStorage(hash, block.header.preBlockHash); if (csr.err) { chain.logger.error(`block ${hash} create pre block storage failed `, error_code_1.stringifyErrorCode(csr.err)); } const { err } = await this.debuger.debugBlock(csr.storage, block); csr.storage.remove(); return { err }; } async transaction(hash) { const chain = this.debuger.chain; const gtrr = await chain.getTransactionReceipt(hash); if (gtrr.err) { chain.logger.error(`transaction ${hash} get receipt failed `, error_code_1.stringifyErrorCode(gtrr.err)); return { err: gtrr.err }; } return this.block(gtrr.block.hash); } async view(from, method, params) { const chain = this.debuger.chain; let hr = await chain.headerStorage.getHeader(from); if (hr.err !== error_code_1.ErrorCode.RESULT_OK) { chain.logger.error(`view ${method} failed for load header ${from} failed for ${hr.err}`); return { err: hr.err }; } let header = hr.header; let svr = await this.m_storageManager.getSnapshotView(header.hash); if (svr.err !== error_code_1.ErrorCode.RESULT_OK) { chain.logger.error(`view ${method} failed for get snapshot ${header.hash} failed for ${svr.err}`); return { err: svr.err }; } const ret = await this.debuger.debugView(svr.storage, header, method, params); this.m_storageManager.releaseSnapshotView(header.hash); return ret; } } exports.ValueChainDebugSession = ValueChainDebugSession; class ValueIndependDebugSession { constructor(debuger) { this.debuger = debuger; this.m_fakeNonces = new Map(); } async init(options) { const storageOptions = Object.create(null); storageOptions.memory = options.memoryStorage; if (!(util_1.isNullOrUndefined(options.memoryStorage) || options.memoryStorage)) { const storageDir = options.storageDir; fs.ensureDirSync(storageDir); const storagePath = path.join(storageDir, `${Date.now()}`); storageOptions.path = storagePath; } const csr = await this.debuger.createStorage(storageOptions); if (csr.err) { return { err: csr.err }; } this.m_storage = csr.storage; this.m_storage.createLogger(); if (util_1.isArray(options.accounts)) { this.m_accounts = options.accounts.map((x) => Buffer.from(x)); } else { this.m_accounts = []; for (let i = 0; i < options.accounts; ++i) { this.m_accounts.push(address_1.createKeyPair()[1]); } } this.m_interval = options.interval; const chain = this.debuger.chain; let gh = chain.newBlockHeader(); gh.timestamp = Date.now() / 1000; let block = chain.newBlock(gh); let genesissOptions = {}; genesissOptions.candidates = []; genesissOptions.miners = []; genesissOptions.coinbase = address_1.addressFromSecretKey(this.m_accounts[options.coinbase]); if (options.preBalance) { genesissOptions.preBalances = []; this.m_accounts.forEach((value) => { genesissOptions.preBalances.push({ address: address_1.addressFromSecretKey(value), amount: options.preBalance }); }); } const err = await chain.onCreateGenesisBlock(block, csr.storage, genesissOptions); if (err) { chain.logger.error(`onCreateGenesisBlock failed for `, error_code_1.stringifyErrorCode(err)); return { err }; } block.header.updateHash(); const dber = await this.debuger.debugBlockEvent(this.m_storage, block.header, { preBlock: true }); if (dber.err) { return { err }; } this.m_curBlock = { header: block.header, transactions: [], receipts: [] }; this.m_curBlock.receipts.push(...dber.receipts); if (options.height > 0) { return await this.updateHeightTo(options.height, options.coinbase); } return { err: error_code_1.ErrorCode.RESULT_OK }; } get curHeader() { return this.m_curBlock.header; } get storage() { return this.m_storage; } async updateHeightTo(height, coinbase) { if (height <= this.m_curBlock.header.number) { this.debuger.chain.logger.error(`updateHeightTo ${height} failed for current height ${this.m_curBlock.header.number} is larger`); return { err: error_code_1.ErrorCode.RESULT_INVALID_PARAM }; } const offset = height - this.m_curBlock.header.number; let blocks = []; for (let i = 0; i < offset; ++i) { const nhr = await this._nextHeight(coinbase, []); if (nhr.err) { return { err: nhr.err }; } blocks.push(nhr.block); } return { err: error_code_1.ErrorCode.RESULT_OK, blocks }; } nextHeight(coinbase, transactions) { return this._nextHeight(coinbase, transactions); } async _nextHeight(coinbase, transactions) { let curHeader = this.m_curBlock.header; for (let tx of transactions) { const dtr = await this.debuger.debugTransaction(this.m_storage, curHeader, tx); if (dtr.err) { return { err: dtr.err }; } this.m_curBlock.transactions.push(tx); this.m_curBlock.receipts.push(dtr.receipt); } let dber = await this.debuger.debugBlockEvent(this.m_storage, curHeader, { postBlock: true }); if (dber.err) { return { err: dber.err }; } this.m_curBlock.receipts.push(...dber.receipts); let block = this.debuger.chain.newBlock(this.m_curBlock.header); for (const tx of this.m_curBlock.transactions) { block.content.addTransaction(tx); } block.content.setReceipts(this.m_curBlock.receipts); block.header.updateHash(); let header = this.debuger.chain.newBlockHeader(); header.timestamp = curHeader.timestamp + this.m_interval; header.coinbase = address_1.addressFromSecretKey(this.m_accounts[coinbase]); header.setPreBlock(block.header); this.m_curBlock = { header: header, transactions: [], receipts: [] }; dber = await this.debuger.debugBlockEvent(this.m_storage, curHeader, { preBlock: true }); if (dber.err) { return { err: dber.err }; } this.m_curBlock.receipts.push(...dber.receipts); return { err: error_code_1.ErrorCode.RESULT_OK, block }; } createTransaction(options) { const tx = new value_chain_1.ValueTransaction(); tx.limit = new bignumber_js_1.BigNumber(0); tx.price = new bignumber_js_1.BigNumber(0); tx.value = new bignumber_js_1.BigNumber(options.value); tx.method = options.method; tx.input = options.input; tx.limit = options.limit; tx.price = options.price; let pk; if (Buffer.isBuffer(options.caller)) { pk = options.caller; } else { pk = this.m_accounts[options.caller]; } tx.nonce = util_1.isNullOrUndefined(options.nonce) ? 0 : options.nonce; tx.sign(pk); return tx; } async transaction(options) { let pk; if (Buffer.isBuffer(options.caller)) { pk = options.caller; } else { pk = this.m_accounts[options.caller]; } let addr = address_1.addressFromSecretKey(pk); const nonce = this.m_fakeNonces.has(addr) ? this.m_fakeNonces.get(addr) : 0; this.m_fakeNonces.set(addr, nonce + 1); const txop = Object.create(options); txop.nonce = nonce; const tx = this.createTransaction(txop); const dtr = await this.debuger.debugTransaction(this.m_storage, this.m_curBlock.header, tx); if (dtr.err) { return { err: dtr.err }; } this.m_curBlock.transactions.push(tx); this.m_curBlock.receipts.push(dtr.receipt); return dtr; } wage() { return this.debuger.debugMinerWageEvent(this.m_storage, this.m_curBlock.header); } view(options) { return this.debuger.debugView(this.m_storage, this.m_curBlock.header, options.method, options.params); } getAccount(index) { return address_1.addressFromSecretKey(this.m_accounts[index]); } } exports.ValueIndependDebugSession = ValueIndependDebugSession; class ChainDebuger { constructor(chain, logger) { this.chain = chain; this.logger = logger; } async createStorage(options) { const inMemory = (util_1.isNullOrUndefined(options.memory) || options.memory); let storage; if (inMemory) { storage = new storage_1.JsonStorage({ filePath: '', logger: this.logger }); } else { storage = new storage_2.SqliteStorage({ filePath: options.path, logger: this.logger }); } const err = await storage.init(); if (err) { this.chain.logger.error(`init storage failed `, error_code_1.stringifyErrorCode(err)); return { err }; } storage.createLogger(); return { err: error_code_1.ErrorCode.RESULT_OK, storage }; } async debugTransaction(storage, header, tx) { const block = this.chain.newBlock(header); const nber = await this.chain.newBlockExecutor({ block, storage }); if (nber.err) { return { err: nber.err }; } const etr = await nber.executor.executeTransaction(tx, { ignoreNoce: true }); if (etr.err) { return { err: etr.err }; } await nber.executor.finalize(); return { err: error_code_1.ErrorCode.RESULT_OK, receipt: etr.receipt }; } async debugBlockEvent(storage, header, options) { const block = this.chain.newBlock(header); const nber = await this.chain.newBlockExecutor({ block, storage }); if (nber.err) { return { err: nber.err }; } let result; do { if (options.listener) { const ebr = await nber.executor.executeBlockEvent(options.listener); if (ebr.err) { result = { err: ebr.err }; break; } else { result = { err: error_code_1.ErrorCode.RESULT_OK, receipts: [ebr.receipt] }; break; } } else { let receipts = []; if (options.preBlock) { const ebr = await nber.executor.executePreBlockEvent(); if (ebr.err) { result = { err: ebr.err }; break; } receipts.push(...ebr.receipts); } if (options.postBlock) { const ebr = await nber.executor.executePostBlockEvent(); if (ebr.err) { result = { err: ebr.err }; break; } receipts.push(...ebr.receipts); } result = { err: error_code_1.ErrorCode.RESULT_OK, receipts }; } } while (false); await nber.executor.finalize(); return result; } async debugView(storage, header, method, params) { const nver = await this.chain.newViewExecutor(header, storage, method, params); if (nver.err) { return { err: nver.err }; } return nver.executor.execute(); } async debugBlock(storage, block) { const nber = await this.chain.newBlockExecutor({ block, storage }); if (nber.err) { return { err: nber.err }; } const err = await nber.executor.execute(); await nber.executor.finalize(); return { err }; } } class ValueChainDebuger extends ChainDebuger { async debugMinerWageEvent(storage, header) { const block = this.chain.newBlock(header); const nber = await this.chain.newBlockExecutor({ block, storage }); if (nber.err) { return { err: nber.err }; } const err = await nber.executor.executeMinerWageEvent(); await nber.executor.finalize(); return { err }; } createIndependSession() { return new ValueIndependDebugSession(this); } async createChainSession(storageDir) { let err = await this.chain.initComponents(); if (err) { return { err }; } const session = new ValueChainDebugSession(this); err = await session.init({ storageDir }); if (err) { return { err }; } return { err: error_code_1.ErrorCode.RESULT_OK, session }; } } exports.ValueChainDebuger = ValueChainDebuger; async function createValueDebuger(chainCreator, dataDir) { const ccir = await chainCreator.createChainInstance(dataDir, { readonly: true, initComponents: false }); if (ccir.err) { chainCreator.logger.error(`create chain instance from ${dataDir} failed `, error_code_1.stringifyErrorCode(ccir.err)); return { err: ccir.err }; } return { err: error_code_1.ErrorCode.RESULT_OK, debuger: new ValueChainDebuger(ccir.chain, chainCreator.logger) }; } exports.createValueDebuger = createValueDebuger;