diff options
Diffstat (limited to 'app/scripts')
-rw-r--r-- | app/scripts/controllers/transactions.js | 7 | ||||
-rw-r--r-- | app/scripts/lib/nonce-tracker.js | 95 |
2 files changed, 76 insertions, 26 deletions
diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js index 1bcee60ab..6f49c9633 100644 --- a/app/scripts/controllers/transactions.js +++ b/app/scripts/controllers/transactions.js @@ -33,6 +33,13 @@ module.exports = class TransactionController extends EventEmitter { err: undefined, }) }, + getConfirmedTransactions: (address) => { + return this.getFilteredTxList({ + from: address, + status: 'confirmed', + err: undefined, + }) + }, }) this.query = new EthQuery(this.provider) this.txProviderUtil = new TxProviderUtil(this.provider) diff --git a/app/scripts/lib/nonce-tracker.js b/app/scripts/lib/nonce-tracker.js index 8328e81ec..30c59fa46 100644 --- a/app/scripts/lib/nonce-tracker.js +++ b/app/scripts/lib/nonce-tracker.js @@ -1,13 +1,14 @@ -const EthQuery = require('eth-query') +const EthQuery = require('ethjs-query') const assert = require('assert') const Mutex = require('await-semaphore').Mutex class NonceTracker { - constructor ({ provider, getPendingTransactions }) { + constructor ({ provider, getPendingTransactions, getConfirmedTransactions }) { this.provider = provider this.ethQuery = new EthQuery(provider) this.getPendingTransactions = getPendingTransactions + this.getConfirmedTransactions = getConfirmedTransactions this.lockMap = {} } @@ -25,21 +26,14 @@ class NonceTracker { await this._globalMutexFree() // await lock free, then take lock const releaseLock = await this._takeMutex(address) - // calculate next nonce - // we need to make sure our base count - // and pending count are from the same block - const currentBlock = await this._getCurrentBlock() - const pendingTransactions = this.getPendingTransactions(address) - const pendingCount = pendingTransactions.length - assert(Number.isInteger(pendingCount), `nonce-tracker - pendingCount is not an integer - got: (${typeof pendingCount}) "${pendingCount}"`) - const baseCountHex = await this._getTxCount(address, currentBlock) - const baseCount = parseInt(baseCountHex, 16) - assert(Number.isInteger(baseCount), `nonce-tracker - baseCount is not an integer - got: (${typeof baseCount}) "${baseCount}"`) - const nextNonce = baseCount + pendingCount + // evaluate multiple nextNonce strategies + const nonceDetails = {} + const localNonceResult = await this._getlocalNextNonce(address) + nonceDetails.local = localNonceResult.details + const networkNonceResult = await this._getNetworkNextNonce(address) + nonceDetails.network = networkNonceResult.details + const nextNonce = Math.max(networkNonceResult.nonce, localNonceResult.nonce) assert(Number.isInteger(nextNonce), `nonce-tracker - nextNonce is not an integer - got: (${typeof nextNonce}) "${nextNonce}"`) - // collect the numbers used to calculate the nonce for debugging - const blockNumber = currentBlock.number - const nonceDetails = { blockNumber, baseCount, baseCountHex, pendingCount } // return nonce and release cb return { nextNonce, nonceDetails, releaseLock } } @@ -53,15 +47,6 @@ class NonceTracker { }) } - async _getTxCount (address, currentBlock) { - const blockNumber = currentBlock.number - return new Promise((resolve, reject) => { - this.ethQuery.getTransactionCount(address, blockNumber, (err, result) => { - err ? reject(err) : resolve(result) - }) - }) - } - async _globalMutexFree () { const globalMutex = this._lookupMutex('global') const release = await globalMutex.acquire() @@ -83,12 +68,70 @@ class NonceTracker { return mutex } + async _getNetworkNextNonce (address) { + // calculate next nonce + // we need to make sure our base count + // and pending count are from the same block + const currentBlock = await this._getCurrentBlock() + const blockNumber = currentBlock.blockNumber + const baseCountHex = await this.ethQuery.getTransactionCount(address, blockNumber) + const baseCount = parseInt(baseCountHex, 16) + assert(Number.isInteger(baseCount), `nonce-tracker - baseCount is not an integer - got: (${typeof baseCount}) "${baseCount}"`) + const nonceDetails = { blockNumber, baseCountHex, baseCount } + return { name: 'network', nonce: baseCount, details: nonceDetails } + } + + async _getlocalNextNonce (address) { + let nextNonce + // check our local tx history for the highest nonce (if any) + const confirmedTransactions = this.getConfirmedTransactions(address) + const pendingTransactions = this.getPendingTransactions(address) + const transactions = confirmedTransactions.concat(pendingTransactions) + const highestConfirmedNonce = this._getHighestNonce(confirmedTransactions) + const highestPendingNonce = this._getHighestNonce(pendingTransactions) + const highestNonce = this._getHighestNonce(transactions) + + const haveHighestNonce = Number.isInteger(highestNonce) + if (haveHighestNonce) { + // next nonce is the nonce after our last + nextNonce = highestNonce + 1 + } else { + // no local tx history so next must be first (zero) + nextNonce = 0 + } + const nonceDetails = { highestNonce, haveHighestNonce, highestConfirmedNonce, highestPendingNonce } + return { name: 'local', nonce: nextNonce, details: nonceDetails } + } + + _getPendingTransactionCount (address) { + const pendingTransactions = this.getPendingTransactions(address) + return this._reduceTxListToUniqueNonces(pendingTransactions).length + } + + _reduceTxListToUniqueNonces (txList) { + const reducedTxList = txList.reduce((reducedList, txMeta, index) => { + if (!index) return [txMeta] + const nonceMatches = txList.filter((txData) => { + return txMeta.txParams.nonce === txData.txParams.nonce + }) + if (nonceMatches.length > 1) return reducedList + reducedList.push(txMeta) + return reducedList + }, []) + return reducedTxList + } + + _getHighestNonce (txList) { + const nonces = txList.map((txMeta) => parseInt(txMeta.txParams.nonce, 16)) + const highestNonce = Math.max.apply(null, nonces) + return highestNonce + } + // this is a hotfix for the fact that the blockTracker will // change when the network changes _getBlockTracker () { return this.provider._blockTracker } - } module.exports = NonceTracker |