aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Serrano <kevin.serrano@consensys.net>2017-10-10 02:54:16 +0800
committerKevin Serrano <kevin.serrano@consensys.net>2017-10-10 02:54:16 +0800
commit5ae5ee9a2080017d56f7096a8fe5bef54368603a (patch)
tree41059e0891f120754ab08f8629fb3093db4dbe5c
parente557d7f7563d6e9ac9a9a98f012a324ce3c0881a (diff)
parent4a4338c1f4669f621fc39d34b06a77f247c7ce65 (diff)
downloadtangerine-wallet-browser-5ae5ee9a2080017d56f7096a8fe5bef54368603a.tar
tangerine-wallet-browser-5ae5ee9a2080017d56f7096a8fe5bef54368603a.tar.gz
tangerine-wallet-browser-5ae5ee9a2080017d56f7096a8fe5bef54368603a.tar.bz2
tangerine-wallet-browser-5ae5ee9a2080017d56f7096a8fe5bef54368603a.tar.lz
tangerine-wallet-browser-5ae5ee9a2080017d56f7096a8fe5bef54368603a.tar.xz
tangerine-wallet-browser-5ae5ee9a2080017d56f7096a8fe5bef54368603a.tar.zst
tangerine-wallet-browser-5ae5ee9a2080017d56f7096a8fe5bef54368603a.zip
Merge branch 'master' into precision-fix
-rw-r--r--CHANGELOG.md2
-rw-r--r--app/scripts/controllers/transactions.js1
-rw-r--r--app/scripts/lib/nodeify.js12
-rw-r--r--app/scripts/lib/pending-tx-tracker.js21
-rw-r--r--app/scripts/lib/tx-state-manager.js8
-rw-r--r--test/unit/nodeify-test.js7
-rw-r--r--test/unit/pending-tx-test.js54
7 files changed, 97 insertions, 8 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8e9fb2530..c037508e1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,8 @@
## Current Master
+- Fix bug where some transactions would be shown as pending forever, even after successfully mined.
+
## 3.10.9 2017-10-5
- Only rebrodcast transactions for a day not a days worth of blocks
diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js
index a0f983deb..ef659a300 100644
--- a/app/scripts/controllers/transactions.js
+++ b/app/scripts/controllers/transactions.js
@@ -62,6 +62,7 @@ module.exports = class TransactionController extends EventEmitter {
retryTimePeriod: 86400000, // Retry 3500 blocks, or about 1 day.
publishTransaction: (rawTx) => this.query.sendRawTransaction(rawTx),
getPendingTransactions: this.txStateManager.getPendingTransactions.bind(this.txStateManager),
+ getCompletedTransactions: this.txStateManager.getConfirmedTransactions.bind(this.txStateManager),
})
this.txStateManager.store.subscribe(() => this.emit('update:badge'))
diff --git a/app/scripts/lib/nodeify.js b/app/scripts/lib/nodeify.js
index 832d6c6d3..d24e92206 100644
--- a/app/scripts/lib/nodeify.js
+++ b/app/scripts/lib/nodeify.js
@@ -1,10 +1,18 @@
const promiseToCallback = require('promise-to-callback')
+const noop = function(){}
module.exports = function nodeify (fn, context) {
return function(){
const args = [].slice.call(arguments)
- const callback = args.pop()
- if (typeof callback !== 'function') throw new Error('callback is not a function')
+ const lastArg = args[args.length - 1]
+ const lastArgIsCallback = typeof lastArg === 'function'
+ let callback
+ if (lastArgIsCallback) {
+ callback = lastArg
+ args.pop()
+ } else {
+ callback = noop
+ }
promiseToCallback(fn.apply(context, args))(callback)
}
}
diff --git a/app/scripts/lib/pending-tx-tracker.js b/app/scripts/lib/pending-tx-tracker.js
index 8a626e222..df504c126 100644
--- a/app/scripts/lib/pending-tx-tracker.js
+++ b/app/scripts/lib/pending-tx-tracker.js
@@ -25,7 +25,9 @@ module.exports = class PendingTransactionTracker extends EventEmitter {
// default is one day
this.retryTimePeriod = config.retryTimePeriod || 86400000
this.getPendingTransactions = config.getPendingTransactions
+ this.getCompletedTransactions = config.getCompletedTransactions
this.publishTransaction = config.publishTransaction
+ this._checkPendingTxs()
}
// checks if a signed tx is in a block and
@@ -120,6 +122,7 @@ module.exports = class PendingTransactionTracker extends EventEmitter {
async _checkPendingTx (txMeta) {
const txHash = txMeta.hash
const txId = txMeta.id
+
// extra check in case there was an uncaught error during the
// signature and submission process
if (!txHash) {
@@ -128,6 +131,15 @@ module.exports = class PendingTransactionTracker extends EventEmitter {
this.emit('tx:failed', txId, noTxHashErr)
return
}
+
+ // If another tx with the same nonce is mined, set as failed.
+ const taken = await this._checkIfNonceIsTaken(txMeta)
+ if (taken) {
+ const nonceTakenErr = new Error('Another transaction with this nonce has been mined.')
+ nonceTakenErr.name = 'NonceTakenErr'
+ return this.emit('tx:failed', txId, nonceTakenErr)
+ }
+
// get latest transaction status
let txParams
try {
@@ -159,4 +171,13 @@ module.exports = class PendingTransactionTracker extends EventEmitter {
}
nonceGlobalLock.releaseLock()
}
+
+ async _checkIfNonceIsTaken (txMeta) {
+ const completed = this.getCompletedTransactions()
+ const sameNonce = completed.filter((otherMeta) => {
+ return otherMeta.txParams.nonce === txMeta.txParams.nonce
+ })
+ return sameNonce.length > 0
+ }
+
}
diff --git a/app/scripts/lib/tx-state-manager.js b/app/scripts/lib/tx-state-manager.js
index cf8117864..2250403f6 100644
--- a/app/scripts/lib/tx-state-manager.js
+++ b/app/scripts/lib/tx-state-manager.js
@@ -46,6 +46,12 @@ module.exports = class TransactionStateManger extends EventEmitter {
return this.getFilteredTxList(opts)
}
+ getConfirmedTransactions (address) {
+ const opts = { status: 'confirmed' }
+ if (address) opts.from = address
+ return this.getFilteredTxList(opts)
+ }
+
addTx (txMeta) {
this.once(`${txMeta.id}:signed`, function (txId) {
this.removeAllListeners(`${txMeta.id}:rejected`)
@@ -242,4 +248,4 @@ module.exports = class TransactionStateManger extends EventEmitter {
_saveTxList (transactions) {
this.store.updateState({ transactions })
}
-} \ No newline at end of file
+}
diff --git a/test/unit/nodeify-test.js b/test/unit/nodeify-test.js
index 537dae605..c7b127889 100644
--- a/test/unit/nodeify-test.js
+++ b/test/unit/nodeify-test.js
@@ -18,14 +18,13 @@ describe('nodeify', function () {
})
})
- it('should throw if the last argument is not a function', function (done) {
+ it('should allow the last argument to not be a function', function (done) {
const nodified = nodeify(obj.promiseFunc, obj)
try {
nodified('baz')
- done(new Error('should have thrown if the last argument is not a function'))
- } catch (err) {
- assert.equal(err.message, 'callback is not a function')
done()
+ } catch (err) {
+ done(new Error('should not have thrown if the last argument is not a function'))
}
})
})
diff --git a/test/unit/pending-tx-test.js b/test/unit/pending-tx-test.js
index 6b62bb5b1..32421a44f 100644
--- a/test/unit/pending-tx-test.js
+++ b/test/unit/pending-tx-test.js
@@ -5,6 +5,8 @@ const ObservableStore = require('obs-store')
const clone = require('clone')
const { createStubedProvider } = require('../stub/provider')
const PendingTransactionTracker = require('../../app/scripts/lib/pending-tx-tracker')
+const MockTxGen = require('../lib/mock-tx-gen')
+const sinon = require('sinon')
const noop = () => true
const currentNetworkId = 42
const otherNetworkId = 36
@@ -46,10 +48,60 @@ describe('PendingTransactionTracker', function () {
}
},
getPendingTransactions: () => {return []},
+ getCompletedTransactions: () => {return []},
publishTransaction: () => {},
})
})
+ describe('_checkPendingTx state management', function () {
+ let stub
+
+ afterEach(function () {
+ if (stub) {
+ stub.restore()
+ }
+ })
+
+ it('should become failed if another tx with the same nonce succeeds', async function () {
+
+ // SETUP
+ const txGen = new MockTxGen()
+
+ txGen.generate({
+ id: '456',
+ value: '0x01',
+ hash: '0xbad',
+ status: 'confirmed',
+ nonce: '0x01',
+ }, { count: 1 })
+
+ const pending = txGen.generate({
+ id: '123',
+ value: '0x02',
+ hash: '0xfad',
+ status: 'submitted',
+ nonce: '0x01',
+ }, { count: 1 })[0]
+
+ stub = sinon.stub(pendingTxTracker, 'getCompletedTransactions')
+ .returns(txGen.txs)
+
+ // THE EXPECTATION
+ const spy = sinon.spy()
+ pendingTxTracker.on('tx:failed', (txId, err) => {
+ assert.equal(txId, pending.id, 'should fail the pending tx')
+ assert.equal(err.name, 'NonceTakenErr', 'should emit a nonce taken error.')
+ spy(txId, err)
+ })
+
+ // THE METHOD
+ await pendingTxTracker._checkPendingTx(pending)
+
+ // THE ASSERTION
+ assert.ok(spy.calledWith(pending.id), 'tx failed should be emitted')
+ })
+ })
+
describe('#checkForTxInBlock', function () {
it('should return if no pending transactions', function () {
// throw a type error if it trys to do anything on the block
@@ -239,4 +291,4 @@ describe('PendingTransactionTracker', function () {
})
})
})
-}) \ No newline at end of file
+})