Show TX reason when there is a failure (#1970)

* feat(@embark/transactions-logger): show reason of failure in tx log

* feat(@embark/ui): show tx failure reason in cockpit contract logs
This commit is contained in:
Jonathan Rainville 2019-10-13 22:00:24 -04:00 committed by Iuri Matias
parent 918a00c24c
commit 637e4f965d
3 changed files with 48 additions and 15 deletions

View File

@ -1,6 +1,6 @@
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import React from 'react'; import React from 'react';
import {Row, Col, Table, FormGroup, Label, Input, Form} from 'reactstrap'; import {Row, Col, Table, FormGroup, Label, Input, Form, UncontrolledTooltip} from 'reactstrap';
import DebugButton from './DebugButton'; import DebugButton from './DebugButton';
import "./ContractLog.scss"; import "./ContractLog.scss";
@ -149,7 +149,13 @@ class ContractLog extends React.Component {
<td>{log.events.join(', ')}</td> <td>{log.events.join(', ')}</td>
<td>{log.gasUsed}</td> <td>{log.gasUsed}</td>
<td>{log.blockNumber}</td> <td>{log.blockNumber}</td>
<td>{log.status}</td> <td id={'tx-status-' + index}>
{log.status}
{log.reason && <sup>?</sup>}
{log.reason && <UncontrolledTooltip placement="bottom" target={'tx-status-' + index}>
Reason of the failure: {log.reason}
</UncontrolledTooltip>}
</td>
<td>{log.transactionHash}</td> <td>{log.transactionHash}</td>
</tr> </tr>
); );

View File

@ -47,7 +47,8 @@
"embark-i18n": "^4.1.1", "embark-i18n": "^4.1.1",
"embark-utils": "^4.1.1", "embark-utils": "^4.1.1",
"ethereumjs-tx": "1.3.7", "ethereumjs-tx": "1.3.7",
"ethereumjs-util": "6.0.0" "ethereumjs-util": "6.0.0",
"web3": "1.2.1"
}, },
"devDependencies": { "devDependencies": {
"embark-solo": "^4.1.1", "embark-solo": "^4.1.1",

View File

@ -1,5 +1,6 @@
const async = require('async'); const async = require('async');
import {__} from 'embark-i18n'; import {__} from 'embark-i18n';
const Web3 = require('Web3');
const {blockchain: blockchainConstants} = require('embark-core/constants'); const {blockchain: blockchainConstants} = require('embark-core/constants');
import {dappPath, getAddressToContract, getTransactionParams, hexToNumber} from 'embark-utils'; import {dappPath, getAddressToContract, getTransactionParams, hexToNumber} from 'embark-utils';
@ -59,6 +60,16 @@ class TransactionLogger {
}); });
} }
get web3() {
return (async () => {
if (!this._web3) {
const provider = await this.events.request2("blockchain:client:provider", "ethereum");
this._web3 = new Web3(provider);
}
return this._web3;
})();
}
_getContractsList(callback) { _getContractsList(callback) {
this.events.request("contracts:list", (err, contractsList) => { this.events.request("contracts:list", (err, contractsList) => {
if (err) { if (err) {
@ -86,7 +97,7 @@ class TransactionLogger {
this.events.on('blockchain:proxy:response', this._onLogRequest.bind(this)); this.events.on('blockchain:proxy:response', this._onLogRequest.bind(this));
} }
_onLogRequest(args) { async _onLogRequest(args) {
const method = args.reqData.method; const method = args.reqData.method;
if (!this.contractsDeployed || !LISTENED_METHODS.includes(method)) { if (!this.contractsDeployed || !LISTENED_METHODS.includes(method)) {
return; return;
@ -161,20 +172,36 @@ class TransactionLogger {
} }
let {transactionHash, blockNumber, gasUsed, status} = args.respData.result; let {transactionHash, blockNumber, gasUsed, status} = args.respData.result;
let reason;
if (status !== '0x0' && status !== '0x1') {
status = !status ? '0x0' : '0x1';
}
if (status === '0x0') {
const web3 = await this.web3;
const tx = await web3.eth.getTransaction(transactionHash);
if (tx) {
const code = await web3.eth.call(tx, tx.blockNumber);
// Convert to Ascii and remove the useless bytes around the revert message
reason = web3.utils.hexToAscii('0x' + code.substring(138)).toString().replace(/[^\x20-\x7E]/g, '');
}
}
gasUsed = hexToNumber(gasUsed); gasUsed = hexToNumber(gasUsed);
blockNumber = hexToNumber(blockNumber); blockNumber = hexToNumber(blockNumber);
const log = Object.assign({}, args, {name, functionName, paramString, gasUsed, blockNumber}); const log = Object.assign({}, args, {name, functionName, paramString, gasUsed, blockNumber, reason, status, transactionHash});
this.events.emit('contracts:log', log); this.events.emit('contracts:log', log);
this.logger.info(`Blockchain>`.underline + ` ${name}.${functionName}(${paramString})`.bold + ` | ${transactionHash} | gas:${gasUsed} | blk:${blockNumber} | status:${status}`); this.logger.info(`Blockchain>`.underline + ` ${name}.${functionName}(${paramString})`.bold + ` | ${transactionHash} | gas:${gasUsed} | blk:${blockNumber} | status:${status}${reason ? ` | reason: "${reason}"` : ''}`);
this.events.emit('blockchain:tx', { this.events.emit('blockchain:tx', {
name: name, name,
functionName: functionName, functionName,
paramString: paramString, paramString,
transactionHash: transactionHash, transactionHash,
gasUsed: gasUsed, gasUsed,
blockNumber: blockNumber, blockNumber,
status: status status,
reason
}); });
} }
@ -184,8 +211,7 @@ class TransactionLogger {
'ws', 'ws',
apiRoute, apiRoute,
(ws, _req) => { (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', (log) => {
this.events.on('contracts:log', function(log) {
ws.send(JSON.stringify(log), () => { ws.send(JSON.stringify(log), () => {
}); });
}); });