diff --git a/packages/embark-console-listener/README.md b/packages/embark-console-listener/README.md index 05c1bff43..41536cc72 100644 --- a/packages/embark-console-listener/README.md +++ b/packages/embark-console-listener/README.md @@ -1,6 +1,6 @@ # `embark-console-listener` -> Console listener component for Embark +> Listens to Embark logs and stores them so that they can be printed in Cockpit Visit [embark.status.im](https://embark.status.im/) to get started with [Embark](https://github.com/embark-framework/embark). diff --git a/packages/embark-console-listener/package.json b/packages/embark-console-listener/package.json index 029a25db9..6b4eb59c0 100644 --- a/packages/embark-console-listener/package.json +++ b/packages/embark-console-listener/package.json @@ -47,12 +47,7 @@ }, "dependencies": { "@babel/runtime-corejs2": "7.3.1", - "async": "2.6.1", - "embark-core": "^4.1.0-beta.5", - "embark-i18n": "^4.1.0-beta.3", - "embark-utils": "^4.1.0-beta.5", - "ethereumjs-tx": "1.3.7", - "ethereumjs-util": "6.0.0" + "embark-process-logs-api": "^4.1.0-beta.5" }, "devDependencies": { "@babel/cli": "7.2.3", diff --git a/packages/embark-console-listener/src/index.js b/packages/embark-console-listener/src/index.js index 6e1213709..ba9e34367 100644 --- a/packages/embark-console-listener/src/index.js +++ b/packages/embark-console-listener/src/index.js @@ -1,229 +1,15 @@ -const async = require('async'); -import {__} from 'embark-i18n'; - -const {blockchain: blockchainConstants} = require('embark-core/constants'); -import {dappPath, getAddressToContract, getTransactionParams, hexToNumber} from 'embark-utils'; const ProcessLogsApi = require('embark-process-logs-api'); const EMBARK_PROCESS_NAME = 'embark'; -const Transaction = require('ethereumjs-tx'); -const ethUtil = require('ethereumjs-util'); - -const LISTENED_METHODS = [ - blockchainConstants.transactionMethods.eth_call, - blockchainConstants.transactionMethods.eth_getTransactionReceipt, - blockchainConstants.transactionMethods.eth_sendTransaction, - blockchainConstants.transactionMethods.eth_sendRawTransaction -]; - class ConsoleListener { constructor(embark, _options) { - this.embark = embark; - this.logger = embark.logger; this.events = embark.events; - this.fs = embark.fs; - this.addressToContract = []; - this.contractsConfig = embark.config.contractsConfig; - this.contractsDeployed = false; - this.outputDone = false; - this.logFile = dappPath(".embark", "contractLogs.json"); - this.processLogsApi = new ProcessLogsApi({embark: this.embark, processName: EMBARK_PROCESS_NAME, silent: false}); - this.transactions = {}; + this.processLogsApi = new ProcessLogsApi({embark: embark, processName: EMBARK_PROCESS_NAME, silent: false}); - this._listenForLogRequests(); - this._registerAPI(); - - this.events.on("contracts:log", this._saveLog.bind(this)); this.events.on("log", (logLevel, message) => { this.processLogsApi.logHandler.handleLog({logLevel, message}, true); }); - this.events.on('outputDone', () => { - this.outputDone = true; - }); - this.events.on("contractsDeployed", () => { - this.contractsDeployed = true; - - this._getContractsList((contractsList) => { - this.addressToContract = getAddressToContract(contractsList, this.addressToContract); - }); - }); - - this.writeLogFile = async.cargo((tasks, callback) => { - const data = this._readLogs(); - - tasks.forEach(task => { - data[new Date().getTime()] = task; - }); - - this.fs.writeJson(this.logFile, data, err => { - if (err) { - console.error(err); - } - callback(); - }); - }); - } - - _getContractsList(callback) { - this.events.request("contracts:list", (err, contractsList) => { - if (err) { - this.logger.error(__("no contracts found")); - return callback(); - } - callback(contractsList); - }); - } - - _listenForLogRequests() { - this.events.on('deploy:contract:receipt', receipt => { - this.events.emit('contracts:log', { - name: receipt.className, - functionName: 'constructor', - paramString: '', - address: receipt.contractAddress, - status: receipt.status ? '0x1' : '0x0', - gasUsed: receipt.gasUsed, - blockNumber: receipt.blockNumber, - transactionHash: receipt.transactionHash - }); - }); - - this.events.on('blockchain:proxy:response', this._onLogRequest.bind(this)); - } - - _onLogRequest(args) { - const method = args.reqData.method; - if (!this.contractsDeployed || !LISTENED_METHODS.includes(method)) { - return; - } - - if (method === blockchainConstants.transactionMethods.eth_sendTransaction) { - // We just gather data and wait for the receipt - this.transactions[args.respData.result] = { - address: args.reqData.params[0].to, - data: args.reqData.params[0].data, - txHash: args.respData.result - }; - return; - } else if (method === blockchainConstants.transactionMethods.eth_sendRawTransaction) { - const rawData = Buffer.from(ethUtil.stripHexPrefix(args.reqData.params[0]), 'hex'); - const tx = new Transaction(rawData, 'hex'); - this.transactions[args.respData.result] = { - address: '0x' + tx.to.toString('hex'), - data: '0x' + tx.data.toString('hex') - }; - return; - } - - let dataObject; - if (method === blockchainConstants.transactionMethods.eth_getTransactionReceipt) { - dataObject = args.respData.result; - if (!dataObject) { - return; - } - - if (this.transactions[args.respData.result.transactionHash]) { - // This is the normal case. If we don't get here, it's because we missed a TX - dataObject = Object.assign(dataObject, this.transactions[args.respData.result.transactionHash]); - delete this.transactions[args.respData.result.transactionHash]; // No longer needed - } - } else { - dataObject = args.reqData.params[0]; - } - const {to: address, data} = dataObject; - if (!address) { - // It's a deployment - return; - } - const contract = this.addressToContract[address]; - - if (!contract) { - this.logger.info(`Contract log for unknown contract: ${JSON.stringify(args)}`); - return this._getContractsList((contractsList) => { - this.addressToContract = getAddressToContract(contractsList, this.addressToContract); - }); - } - - const {name, silent} = contract; - if (silent && !this.outputDone) { - return; - } - - let functionName, paramString; - if (!data) { - // We missed the TX - functionName = 'unknown'; - paramString = 'unknown'; - } else { - const txParams = getTransactionParams(contract, data); - functionName = txParams.functionName; - paramString = txParams.paramString; - } - - if (method === blockchainConstants.transactionMethods.eth_call) { - const log = Object.assign({}, args, {name, functionName, paramString}); - log.status = '0x1'; - return this.events.emit('contracts:log', log); - } - - let {transactionHash, blockNumber, gasUsed, status} = args.respData.result; - gasUsed = hexToNumber(gasUsed); - blockNumber = hexToNumber(blockNumber); - const log = Object.assign({}, args, {name, functionName, paramString, gasUsed, blockNumber}); - - this.events.emit('contracts:log', log); - this.logger.info(`Blockchain>`.underline + ` ${name}.${functionName}(${paramString})`.bold + ` | ${transactionHash} | gas:${gasUsed} | blk:${blockNumber} | status:${status}`); - this.events.emit('blockchain:tx', { - name: name, - functionName: functionName, - paramString: paramString, - transactionHash: transactionHash, - gasUsed: gasUsed, - blockNumber: blockNumber, - status: status - }); - } - - _registerAPI() { - const apiRoute = '/embark-api/contracts/logs'; - this.embark.registerAPICall( - 'ws', - apiRoute, - (ws, _req) => { - // FIXME this will be broken probably in the cokcpit because we don't send the same data as before - this.events.on('contracts:log', function(log) { - ws.send(JSON.stringify(log), () => { - }); - }); - } - ); - - this.embark.registerAPICall( - 'get', - apiRoute, - (req, res) => { - res.send(JSON.stringify(this._getLogs())); - } - ); - } - - _getLogs() { - const data = this._readLogs(); - return Object.values(data).reverse(); - } - - _saveLog(log) { - this.writeLogFile.push(log); - } - - _readLogs() { - this.fs.ensureFileSync(this.logFile); - try { - return JSON.parse(this.fs.readFileSync(this.logFile)); - } catch (_error) { - return {}; - } } } diff --git a/packages/embark-transaction-logger/.npmrc b/packages/embark-transaction-logger/.npmrc new file mode 100644 index 000000000..e031d3432 --- /dev/null +++ b/packages/embark-transaction-logger/.npmrc @@ -0,0 +1,4 @@ +engine-strict = true +package-lock = false +save-exact = true +scripts-prepend-node-path = true diff --git a/packages/embark-transaction-logger/CHANGELOG.md b/packages/embark-transaction-logger/CHANGELOG.md new file mode 100644 index 000000000..9e8a451e9 --- /dev/null +++ b/packages/embark-transaction-logger/CHANGELOG.md @@ -0,0 +1,40 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [4.1.0-beta.5](https://github.com/embark-framework/embark/compare/v4.1.0-beta.4...v4.1.0-beta.5) (2019-07-10) + +**Note:** Version bump only for package embark-console-listener + + + + + +# [4.1.0-beta.4](https://github.com/embark-framework/embark/compare/v4.1.0-beta.3...v4.1.0-beta.4) (2019-06-27) + +**Note:** Version bump only for package embark-console-listener + + + + + +# [4.1.0-beta.3](https://github.com/embark-framework/embark/compare/v4.1.0-beta.2...v4.1.0-beta.3) (2019-06-07) + +**Note:** Version bump only for package embark-console-listener + + + + + +# [4.1.0-beta.2](https://github.com/embark-framework/embark/compare/v4.1.0-beta.1...v4.1.0-beta.2) (2019-05-22) + +**Note:** Version bump only for package embark-console-listener + + + + + +# [4.1.0-beta.1](https://github.com/embark-framework/embark/compare/v4.1.0-beta.0...v4.1.0-beta.1) (2019-05-15) + +**Note:** Version bump only for package embark-console-listener diff --git a/packages/embark-transaction-logger/README.md b/packages/embark-transaction-logger/README.md new file mode 100644 index 000000000..863e4fb49 --- /dev/null +++ b/packages/embark-transaction-logger/README.md @@ -0,0 +1,6 @@ +# `embark-transaction-logger` + +> Listens and logs transactions in a human readble way + +Visit [embark.status.im](https://embark.status.im/) to get started with +[Embark](https://github.com/embark-framework/embark). diff --git a/packages/embark-transaction-logger/package.json b/packages/embark-transaction-logger/package.json new file mode 100644 index 000000000..5764fa066 --- /dev/null +++ b/packages/embark-transaction-logger/package.json @@ -0,0 +1,73 @@ +{ + "name": "embark-transaction-logger", + "version": "4.1.0-beta.5", + "author": "Iuri Matias ", + "contributors": [], + "description": "Transaction listener and logger for Embark", + "homepage": "https://github.com/embark-framework/embark/tree/master/packages/embark-transaction-logger#readme", + "bugs": "https://github.com/embark-framework/embark/issues", + "keywords": [ + "blockchain", + "dapps", + "ethereum", + "ipfs", + "serverless", + "solc", + "solidity" + ], + "files": [ + "dist" + ], + "license": "MIT", + "repository": { + "directory": "packages/embark-transaction-logger", + "type": "git", + "url": "https://github.com/embark-framework/embark.git" + }, + "main": "./dist/index.js", + "scripts": { + "build": "cross-env BABEL_ENV=node babel src --extensions \".js\" --out-dir dist --root-mode upward --source-maps", + "ci": "npm run qa", + "clean": "npm run reset", + "lint": "npm-run-all lint:*", + "lint:js": "eslint src/", + "// lint:ts": "tslint -c tslint.json \"src/**/*.ts\"", + "package": "npm pack", + "// qa": "npm-run-all lint typecheck build package", + "qa": "npm-run-all lint build package", + "reset": "npx rimraf dist embark-*.tgz package", + "start": "npm run watch", + "// typecheck": "tsc", + "watch": "run-p watch:*", + "watch:build": "npm run build -- --verbose --watch", + "// watch:typecheck": "npm run typecheck -- --preserveWatchOutput --watch" + }, + "eslintConfig": { + "extends": "../../.eslintrc.json" + }, + "dependencies": { + "@babel/runtime-corejs2": "7.3.1", + "async": "2.6.1", + "embark-core": "^4.1.0-beta.5", + "embark-i18n": "^4.1.0-beta.3", + "embark-utils": "^4.1.0-beta.5", + "ethereumjs-tx": "1.3.7", + "ethereumjs-util": "6.0.0" + }, + "devDependencies": { + "@babel/cli": "7.2.3", + "@babel/core": "7.2.2", + "cross-env": "5.2.0", + "eslint": "5.7.0", + "npm-run-all": "4.1.5", + "rimraf": "2.6.3", + "source-map-support": "0.5.9", + "tslint": "5.16.0", + "typescript": "3.4.5" + }, + "engines": { + "node": ">=8.12.0 <12.0.0", + "npm": ">=6.4.1", + "yarn": ">=1.12.3" + } +} diff --git a/packages/embark-transaction-logger/src/index.js b/packages/embark-transaction-logger/src/index.js new file mode 100644 index 000000000..ae56d2d97 --- /dev/null +++ b/packages/embark-transaction-logger/src/index.js @@ -0,0 +1,220 @@ +const async = require('async'); +import {__} from 'embark-i18n'; + +const {blockchain: blockchainConstants} = require('embark-core/constants'); +import {dappPath, getAddressToContract, getTransactionParams, hexToNumber} from 'embark-utils'; + + +const Transaction = require('ethereumjs-tx'); +const ethUtil = require('ethereumjs-util'); + +const LISTENED_METHODS = [ + blockchainConstants.transactionMethods.eth_call, + blockchainConstants.transactionMethods.eth_getTransactionReceipt, + blockchainConstants.transactionMethods.eth_sendTransaction, + blockchainConstants.transactionMethods.eth_sendRawTransaction +]; + +class TransactionLogger { + constructor(embark, _options) { + this.embark = embark; + this.logger = embark.logger; + this.events = embark.events; + this.fs = embark.fs; + this.addressToContract = []; + this.contractsConfig = embark.config.contractsConfig; + this.contractsDeployed = false; + this.outputDone = false; + this.logFile = dappPath(".embark", "contractLogs.json"); + this.transactions = {}; + + this._listenForLogRequests(); + this._registerAPI(); + + this.events.on("contracts:log", this._saveLog.bind(this)); + this.events.on('outputDone', () => { + this.outputDone = true; + }); + this.events.on("contractsDeployed", () => { + this.contractsDeployed = true; + + this._getContractsList((contractsList) => { + this.addressToContract = getAddressToContract(contractsList, this.addressToContract); + }); + }); + + this.writeLogFile = async.cargo((tasks, callback) => { + const data = this._readLogs(); + + tasks.forEach(task => { + data[new Date().getTime()] = task; + }); + + this.fs.writeJson(this.logFile, data, err => { + if (err) { + console.error(err); + } + callback(); + }); + }); + } + + _getContractsList(callback) { + this.events.request("contracts:list", (err, contractsList) => { + if (err) { + this.logger.error(__("no contracts found")); + return callback(); + } + callback(contractsList); + }); + } + + _listenForLogRequests() { + this.events.on('deploy:contract:receipt', receipt => { + this.events.emit('contracts:log', { + name: receipt.className, + functionName: 'constructor', + paramString: '', + address: receipt.contractAddress, + status: receipt.status ? '0x1' : '0x0', + gasUsed: receipt.gasUsed, + blockNumber: receipt.blockNumber, + transactionHash: receipt.transactionHash + }); + }); + + this.events.on('blockchain:proxy:response', this._onLogRequest.bind(this)); + } + + _onLogRequest(args) { + const method = args.reqData.method; + if (!this.contractsDeployed || !LISTENED_METHODS.includes(method)) { + return; + } + + if (method === blockchainConstants.transactionMethods.eth_sendTransaction) { + // We just gather data and wait for the receipt + this.transactions[args.respData.result] = { + address: args.reqData.params[0].to, + data: args.reqData.params[0].data, + txHash: args.respData.result + }; + return; + } else if (method === blockchainConstants.transactionMethods.eth_sendRawTransaction) { + const rawData = Buffer.from(ethUtil.stripHexPrefix(args.reqData.params[0]), 'hex'); + const tx = new Transaction(rawData, 'hex'); + this.transactions[args.respData.result] = { + address: '0x' + tx.to.toString('hex'), + data: '0x' + tx.data.toString('hex') + }; + return; + } + + let dataObject; + if (method === blockchainConstants.transactionMethods.eth_getTransactionReceipt) { + dataObject = args.respData.result; + if (this.transactions[args.respData.result.transactionHash]) { + // This is the normal case. If we don't get here, it's because we missed a TX + dataObject = Object.assign(dataObject, this.transactions[args.respData.result.transactionHash]); + delete this.transactions[args.respData.result.transactionHash]; // No longer needed + } + } else { + dataObject = args.reqData.params[0]; + } + const {to: address, data} = dataObject; + if (!address) { + // It's a deployment + return; + } + const contract = this.addressToContract[address]; + + if (!contract) { + this.logger.info(`Contract log for unknown contract: ${JSON.stringify(args)}`); + return this._getContractsList((contractsList) => { + this.addressToContract = getAddressToContract(contractsList, this.addressToContract); + }); + } + + const {name, silent} = contract; + if (silent && !this.outputDone) { + return; + } + + let functionName, paramString; + if (!data) { + // We missed the TX + functionName = 'unknown'; + paramString = 'unknown'; + } else { + const txParams = getTransactionParams(contract, data); + functionName = txParams.functionName; + paramString = txParams.paramString; + } + + if (method === blockchainConstants.transactionMethods.eth_call) { + const log = Object.assign({}, args, {name, functionName, paramString}); + log.status = '0x1'; + return this.events.emit('contracts:log', log); + } + + let {transactionHash, blockNumber, gasUsed, status} = args.respData.result; + gasUsed = hexToNumber(gasUsed); + blockNumber = hexToNumber(blockNumber); + const log = Object.assign({}, args, {name, functionName, paramString, gasUsed, blockNumber}); + + this.events.emit('contracts:log', log); + this.logger.info(`Blockchain>`.underline + ` ${name}.${functionName}(${paramString})`.bold + ` | ${transactionHash} | gas:${gasUsed} | blk:${blockNumber} | status:${status}`); + this.events.emit('blockchain:tx', { + name: name, + functionName: functionName, + paramString: paramString, + transactionHash: transactionHash, + gasUsed: gasUsed, + blockNumber: blockNumber, + status: status + }); + } + + _registerAPI() { + const apiRoute = '/embark-api/contracts/logs'; + this.embark.registerAPICall( + 'ws', + apiRoute, + (ws, _req) => { + // FIXME this will be broken probably in the cokcpit because we don't send the same data as before + this.events.on('contracts:log', function(log) { + ws.send(JSON.stringify(log), () => { + }); + }); + } + ); + + this.embark.registerAPICall( + 'get', + apiRoute, + (req, res) => { + res.send(JSON.stringify(this._getLogs())); + } + ); + } + + _getLogs() { + const data = this._readLogs(); + return Object.values(data).reverse(); + } + + _saveLog(log) { + this.writeLogFile.push(log); + } + + _readLogs() { + this.fs.ensureFileSync(this.logFile); + try { + return JSON.parse(this.fs.readFileSync(this.logFile)); + } catch (_error) { + return {}; + } + } +} + +module.exports = TransactionLogger; diff --git a/packages/embark-transaction-logger/tsconfig.json b/packages/embark-transaction-logger/tsconfig.json new file mode 100644 index 000000000..52d43eaaa --- /dev/null +++ b/packages/embark-transaction-logger/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src/**/*"] +} diff --git a/packages/embark-transaction-logger/tslint.json b/packages/embark-transaction-logger/tslint.json new file mode 100644 index 000000000..0946f2096 --- /dev/null +++ b/packages/embark-transaction-logger/tslint.json @@ -0,0 +1,3 @@ +{ + "extends": "../../tslint.json" +} diff --git a/packages/embark/package.json b/packages/embark/package.json index bbe0642d6..67f977fc3 100644 --- a/packages/embark/package.json +++ b/packages/embark/package.json @@ -127,6 +127,7 @@ "embark-storage": "^4.1.0-beta.5", "embark-swarm": "^4.1.0-beta.5", "embark-test-runner": "^4.1.0-beta.5", + "embark-transaction-logger": "^4.1.0-beta.5", "embark-transaction-tracker": "^4.1.0-beta.3", "embark-ui": "^4.1.0-beta.5", "embark-utils": "^4.1.0-beta.5", diff --git a/packages/embark/src/lib/core/engine.js b/packages/embark/src/lib/core/engine.js index bd9809fd1..9fd349cbe 100644 --- a/packages/embark/src/lib/core/engine.js +++ b/packages/embark/src/lib/core/engine.js @@ -213,6 +213,7 @@ class Engine { this.registerModulePackage('embark-accounts-manager'); this.registerModulePackage('embark-specialconfigs', {plugins: this.plugins}); this.registerModulePackage('embark-console-listener'); + this.registerModulePackage('embark-transaction-logger'); } storageComponent() {