diff options
-rw-r--r-- | CHANGELOG.md | 2 | ||||
-rw-r--r-- | app/scripts/controllers/network.js | 5 | ||||
-rw-r--r-- | app/scripts/controllers/transactions.js | 40 | ||||
-rw-r--r-- | app/scripts/lib/util.js | 8 | ||||
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | test/unit/network-contoller-test.js | 36 | ||||
-rw-r--r-- | test/unit/tx-controller-test.js | 8 |
7 files changed, 68 insertions, 33 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index bf18bb361..eeeda9d68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Current Master +- Include stack traces in txMeta's to better understand the life cycle of transactions + ## 3.9.1 2017-7-19 - No longer automatically request 1 ropsten ether for the first account in a new vault. diff --git a/app/scripts/controllers/network.js b/app/scripts/controllers/network.js index c07f13b8d..0a3e5e26b 100644 --- a/app/scripts/controllers/network.js +++ b/app/scripts/controllers/network.js @@ -28,9 +28,9 @@ module.exports = class NetworkController extends EventEmitter { this._provider = provider } - initializeProvider (opts) { + initializeProvider (opts, providerContructor = MetaMaskProvider) { this.providerInit = opts - this._provider = MetaMaskProvider(opts) + this._provider = providerContructor(opts) this._proxy = new Proxy(this._provider, { get: (obj, name) => { if (name === 'on') return this._on.bind(this) @@ -38,6 +38,7 @@ module.exports = class NetworkController extends EventEmitter { }, set: (obj, name, value) => { this._provider[name] = value + return value }, }) this.provider.on('block', this._logBlock.bind(this)) diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js index 5f3d84ebe..5f2d75b47 100644 --- a/app/scripts/controllers/transactions.js +++ b/app/scripts/controllers/transactions.js @@ -1,10 +1,12 @@ const EventEmitter = require('events') const async = require('async') const extend = require('xtend') +const clone = require('clone') const ObservableStore = require('obs-store') const ethUtil = require('ethereumjs-util') const pify = require('pify') const TxProviderUtil = require('../lib/tx-utils') +const getStack = require('../lib/util').getStack const createId = require('../lib/random-id') const NonceTracker = require('../lib/nonce-tracker') @@ -117,9 +119,14 @@ module.exports = class TransactionController extends EventEmitter { // updateTx (txMeta) { + const txMetaForHistory = clone(txMeta) + txMetaForHistory.stack = getStack() var txId = txMeta.id var txList = this.getFullTxList() var index = txList.findIndex(txData => txData.id === txId) + if (!txMeta.history) txMeta.history = [] + txMeta.history.push(txMetaForHistory) + txList[index] = txMeta this._saveTxList(txList) this.emit('update') @@ -134,7 +141,7 @@ module.exports = class TransactionController extends EventEmitter { } addUnapprovedTransaction (txParams, done) { - let txMeta + let txMeta = {} async.waterfall([ // validate (cb) => this.txProviderUtils.validateTxParams(txParams, cb), @@ -146,6 +153,7 @@ module.exports = class TransactionController extends EventEmitter { status: 'unapproved', metamaskNetworkId: this.getNetwork(), txParams: txParams, + history: [], } cb() }, @@ -165,6 +173,7 @@ module.exports = class TransactionController extends EventEmitter { txParams.value = txParams.value || '0x0' if (!txParams.gasPrice) { this.query.gasPrice((err, gasPrice) => { + if (err) return cb(err) // set gasPrice txParams.gasPrice = gasPrice @@ -201,6 +210,7 @@ module.exports = class TransactionController extends EventEmitter { nonceLock.releaseLock() } catch (err) { this.setTxStatusFailed(txId, { + stack: err.stack || err.message, errCode: err.errCode || err, message: err.message || 'Transaction failed during approval', }) @@ -364,11 +374,11 @@ module.exports = class TransactionController extends EventEmitter { var txId = txMeta.id if (!txHash) { - const errReason = { + return this.setTxStatusFailed(txId, { + stack: 'checkForTxInBlock: custom tx-controller error message', errCode: 'No hash was provided', message: 'We had an error while submitting this transaction, please try again.', - } - return this.setTxStatusFailed(txId, errReason) + }) } block.transactions.forEach((tx) => { @@ -452,13 +462,14 @@ module.exports = class TransactionController extends EventEmitter { if (isKnownTx) return // encountered real error - transition to error state this.setTxStatusFailed(txMeta.id, { + stack: err.stack || err.message, errCode: err.errCode || err, message: err.message, }) })) } - async _resubmitTx (txMeta, cb) { + async _resubmitTx (txMeta) { const address = txMeta.txParams.from const balance = this.ethStore.getState().accounts[address].balance if (!('retryCount' in txMeta)) txMeta.retryCount = 0 @@ -466,18 +477,21 @@ module.exports = class TransactionController extends EventEmitter { // if the value of the transaction is greater then the balance, fail. if (!this.txProviderUtils.sufficientBalance(txMeta.txParams, balance)) { const message = 'Insufficient balance.' - this.setTxStatusFailed(txMeta.id, { message }) - cb() - return log.error(message) + this.setTxStatusFailed(txMeta.id, { + stack: '_resubmitTx: custom tx-controller error', + message, + }) + log.error(message) + return } // Only auto-submit already-signed txs: - if (!('rawTx' in txMeta)) return cb() + if (!('rawTx' in txMeta)) return // Increment a try counter. txMeta.retryCount++ const rawTx = txMeta.rawTx - return await this.txProviderUtils.publishTransaction(rawTx, cb) + return await this.txProviderUtils.publishTransaction(rawTx) } // checks the network for signed txs and @@ -501,11 +515,11 @@ module.exports = class TransactionController extends EventEmitter { // extra check in case there was an uncaught error during the // signature and submission process if (!txHash) { - const errReason = { + this.setTxStatusFailed(txId, { + stack: '_checkPendingTxs: custom tx-controller error message', errCode: 'No hash was provided', message: 'We had an error while submitting this transaction, please try again.', - } - this.setTxStatusFailed(txId, errReason) + }) return } // get latest transaction status diff --git a/app/scripts/lib/util.js b/app/scripts/lib/util.js new file mode 100644 index 000000000..bddd60ee8 --- /dev/null +++ b/app/scripts/lib/util.js @@ -0,0 +1,8 @@ +module.exports = { + getStack, +} + +function getStack () { + const stack = new Error('Stack trace generator - not an error').stack + return stack +} diff --git a/package.json b/package.json index 6d5227356..dcd25cda6 100644 --- a/package.json +++ b/package.json @@ -129,7 +129,7 @@ "valid-url": "^1.0.9", "vreme": "^3.0.2", "web3": "0.19.1", - "web3-provider-engine": "^13.2.8", + "web3-provider-engine": "^13.2.9", "web3-stream-provider": "^3.0.1", "xtend": "^4.0.1" }, diff --git a/test/unit/network-contoller-test.js b/test/unit/network-contoller-test.js index 0c7ee9d70..87c2ee7a3 100644 --- a/test/unit/network-contoller-test.js +++ b/test/unit/network-contoller-test.js @@ -3,6 +3,9 @@ const NetworkController = require('../../app/scripts/controllers/network') describe('# Network Controller', function () { let networkController + const networkControllerProviderInit = { + getAccounts: () => {}, + } beforeEach(function () { networkController = new NetworkController({ @@ -10,26 +13,13 @@ describe('# Network Controller', function () { type: 'rinkeby', }, }) - // stub out provider - networkController._provider = new Proxy({}, { - get: (obj, name) => { - return () => {} - }, - }) - networkController.providerInit = { - getAccounts: () => {}, - } - networkController.ethQuery = new Proxy({}, { - get: (obj, name) => { - return () => {} - }, - }) + networkController.initializeProvider(networkControllerProviderInit, dummyProviderConstructor) }) describe('network', function () { describe('#provider', function () { it('provider should be updatable without reassignment', function () { - networkController.initializeProvider(networkController.providerInit) + networkController.initializeProvider(networkControllerProviderInit, dummyProviderConstructor) const provider = networkController.provider networkController._provider = {test: true} assert.ok(provider.test) @@ -75,3 +65,19 @@ describe('# Network Controller', function () { }) }) }) + +function dummyProviderConstructor() { + return { + // provider + sendAsync: noop, + // block tracker + start: noop, + stop: noop, + on: noop, + addListener: noop, + once: noop, + removeAllListeners: noop, + } +} + +function noop() {}
\ No newline at end of file diff --git a/test/unit/tx-controller-test.js b/test/unit/tx-controller-test.js index 7b86cfe14..31908569a 100644 --- a/test/unit/tx-controller-test.js +++ b/test/unit/tx-controller-test.js @@ -343,13 +343,17 @@ describe('Transaction Controller', function () { // Adding the fake tx: txController.addTx(clone(txMeta)) - txController._resubmitTx(txMeta, function (err) { - assert.ifError(err, 'should not throw an error') + txController._resubmitTx(txMeta) + .then(() => { const updatedMeta = txController.getTx(txMeta.id) assert.notEqual(updatedMeta.status, txMeta.status, 'status changed.') assert.equal(updatedMeta.status, 'failed', 'tx set to failed.') done() }) + .catch((err) => { + assert.ifError(err, 'should not throw an error') + done() + }) }) }) }) |