From bf8a62eb39aac454c1f23e549078ce5f4b8a2d2a Mon Sep 17 00:00:00 2001 From: frankiebee Date: Fri, 18 Aug 2017 13:53:18 -0700 Subject: add test for using localNonce --- test/unit/nonce-tracker-test.js | 46 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/test/unit/nonce-tracker-test.js b/test/unit/nonce-tracker-test.js index 36025a360..2171d859d 100644 --- a/test/unit/nonce-tracker-test.js +++ b/test/unit/nonce-tracker-test.js @@ -2,24 +2,53 @@ const assert = require('assert') const NonceTracker = require('../../app/scripts/lib/nonce-tracker') describe('Nonce Tracker', function () { - let nonceTracker, provider, getPendingTransactions, pendingTxs - + let nonceTracker, provider + let getPendingTransactions, pendingTxs + let getConfirmedTransactions, confirmedTxs + let providerResultStub = {} beforeEach(function () { pendingTxs = [{ 'status': 'submitted', + 'txParams': { + 'from': '0x7d3517b0d011698406d6e0aed8453f0be2697926', + 'gas': '0x30d40', + 'value': '0x0', + 'nonce': '0x3', + }, + }] + confirmedTxs = [{ + 'status': 'confirmed', 'txParams': { 'from': '0x7d3517b0d011698406d6e0aed8453f0be2697926', 'gas': '0x30d40', 'value': '0x0', 'nonce': '0x0', }, + }, { + 'status': 'confirmed', + 'txParams': { + 'from': '0x7d3517b0d011698406d6e0aed8453f0be2697926', + 'gas': '0x30d40', + 'value': '0x0', + 'nonce': '0x1', + }, + }, { + 'status': 'confirmed', + 'txParams': { + 'from': '0x7d3517b0d011698406d6e0aed8453f0be2697926', + 'gas': '0x30d40', + 'value': '0x0', + 'nonce': '0x2', + }, }] getPendingTransactions = () => pendingTxs + getConfirmedTransactions = () => confirmedTxs + providerResultStub.result = '0x3' provider = { - sendAsync: (_, cb) => { cb(undefined, {result: '0x0'}) }, + sendAsync: (_, cb) => { cb(undefined, providerResultStub) }, _blockTracker: { getCurrentBlock: () => '0x11b568', }, @@ -27,6 +56,7 @@ describe('Nonce Tracker', function () { nonceTracker = new NonceTracker({ provider, getPendingTransactions, + getConfirmedTransactions, }) }) @@ -34,7 +64,15 @@ describe('Nonce Tracker', function () { it('should work', async function () { this.timeout(15000) const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') - assert.equal(nonceLock.nextNonce, '1', 'nonce should be 1') + assert.equal(nonceLock.nextNonce, '4', 'nonce should be 4') + await nonceLock.releaseLock() + }) + + it('should use localNonce if network returns a nonce lower then a confirmed tx in state', async function () { + this.timeout(15000) + providerResultStub.result = '0x1' + const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') + assert.equal(nonceLock.nextNonce, '4', 'nonce should be 4') await nonceLock.releaseLock() }) }) -- cgit v1.2.3 From a5a32f3d5742972586893741847ea4516afa19ac Mon Sep 17 00:00:00 2001 From: frankiebee Date: Fri, 18 Aug 2017 13:54:16 -0700 Subject: use "localNonce" when the network returns a nonce that is lower then a known confirmed tx --- app/scripts/controllers/transactions.js | 7 +++++++ app/scripts/lib/nonce-tracker.js | 23 ++++++++++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js index 58c468e22..f21cb6e00 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..3a26c374c 100644 --- a/app/scripts/lib/nonce-tracker.js +++ b/app/scripts/lib/nonce-tracker.js @@ -4,10 +4,11 @@ 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 = {} } @@ -28,6 +29,14 @@ class NonceTracker { // calculate next nonce // we need to make sure our base count // and pending count are from the same block + const localNonceHex = this._getLocalNonce(address) + let localNonce = parseInt(localNonceHex, 16) + try { + assert(Number.isInteger(localNonce), `nonce-tracker - localNonce is not an integer - got: (${typeof localNonce}) "${localNonce}"`) + } catch (e) { + // throw out localNonce if not a number + localNonce = 0 + } const currentBlock = await this._getCurrentBlock() const pendingTransactions = this.getPendingTransactions(address) const pendingCount = pendingTransactions.length @@ -35,11 +44,11 @@ class NonceTracker { 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 + const nextNonce = Math.max(baseCount, localNonce + 1) + pendingCount 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 } + const nonceDetails = { blockNumber, baseCount, baseCountHex, pendingCount, localNonceHex, localNonce } // return nonce and release cb return { nextNonce, nonceDetails, releaseLock } } @@ -83,6 +92,14 @@ class NonceTracker { return mutex } + _getLocalNonce (address) { + const confirmedTransactions = this.getConfirmedTransactions(address) + const localNonces = confirmedTransactions.map((txMeta) => txMeta.txParams.nonce) + return localNonces.reduce((nonce, highestNonce) => { + return parseInt(nonce, 16) > parseInt(highestNonce, 16) ? nonce : highestNonce + }, '0x0') + } + // this is a hotfix for the fact that the blockTracker will // change when the network changes _getBlockTracker () { -- cgit v1.2.3 From f8eca95ca513c6d8af8e599ce143ae0e78b77e26 Mon Sep 17 00:00:00 2001 From: frankiebee Date: Fri, 18 Aug 2017 15:01:05 -0700 Subject: include pendingTxs in localNonce --- app/scripts/lib/nonce-tracker.js | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/app/scripts/lib/nonce-tracker.js b/app/scripts/lib/nonce-tracker.js index 3a26c374c..a4ee5c38f 100644 --- a/app/scripts/lib/nonce-tracker.js +++ b/app/scripts/lib/nonce-tracker.js @@ -29,14 +29,10 @@ class NonceTracker { // calculate next nonce // we need to make sure our base count // and pending count are from the same block - const localNonceHex = this._getLocalNonce(address) + const localNonceHex = this._getHighestLocalNonce(address) let localNonce = parseInt(localNonceHex, 16) - try { - assert(Number.isInteger(localNonce), `nonce-tracker - localNonce is not an integer - got: (${typeof localNonce}) "${localNonce}"`) - } catch (e) { - // throw out localNonce if not a number - localNonce = 0 - } + // throw out localNonce if not a number + if (!Number.isInteger(localNonce)) localNonce = 0 const currentBlock = await this._getCurrentBlock() const pendingTransactions = this.getPendingTransactions(address) const pendingCount = pendingTransactions.length @@ -44,7 +40,8 @@ class NonceTracker { 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 = Math.max(baseCount, localNonce + 1) + pendingCount + if (localNonce) ++localNonce + const nextNonce = Math.max(baseCount + pendingCount, localNonce) 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 @@ -92,9 +89,11 @@ class NonceTracker { return mutex } - _getLocalNonce (address) { + _getHighestLocalNonce (address) { const confirmedTransactions = this.getConfirmedTransactions(address) - const localNonces = confirmedTransactions.map((txMeta) => txMeta.txParams.nonce) + const pendingTransactions = this.getPendingTransactions(address) + const transactions = confirmedTransactions.concat(pendingTransactions) + const localNonces = transactions.map((txMeta) => txMeta.txParams.nonce) return localNonces.reduce((nonce, highestNonce) => { return parseInt(nonce, 16) > parseInt(highestNonce, 16) ? nonce : highestNonce }, '0x0') -- cgit v1.2.3 From 37f86e874f8c3c3d5f6d78cd8abeb0c835f20ae2 Mon Sep 17 00:00:00 2001 From: frankiebee Date: Fri, 18 Aug 2017 15:44:32 -0700 Subject: fix 0x0 nonce calc. --- app/scripts/lib/nonce-tracker.js | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/app/scripts/lib/nonce-tracker.js b/app/scripts/lib/nonce-tracker.js index a4ee5c38f..3063209ee 100644 --- a/app/scripts/lib/nonce-tracker.js +++ b/app/scripts/lib/nonce-tracker.js @@ -29,10 +29,8 @@ class NonceTracker { // calculate next nonce // we need to make sure our base count // and pending count are from the same block - const localNonceHex = this._getHighestLocalNonce(address) - let localNonce = parseInt(localNonceHex, 16) + const localNextNonce = this._getLocalNextNonce(address) // throw out localNonce if not a number - if (!Number.isInteger(localNonce)) localNonce = 0 const currentBlock = await this._getCurrentBlock() const pendingTransactions = this.getPendingTransactions(address) const pendingCount = pendingTransactions.length @@ -40,12 +38,11 @@ class NonceTracker { 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}"`) - if (localNonce) ++localNonce - const nextNonce = Math.max(baseCount + pendingCount, localNonce) + const nextNonce = Math.max(baseCount + pendingCount, localNextNonce) 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, localNonceHex, localNonce } + const nonceDetails = { blockNumber, baseCount, baseCountHex, pendingCount, localNextNonce } // return nonce and release cb return { nextNonce, nonceDetails, releaseLock } } @@ -89,14 +86,27 @@ class NonceTracker { return mutex } - _getHighestLocalNonce (address) { + // _getNetworkNonce (address) { + + // } + + _getLocalNextNonce (address) { const confirmedTransactions = this.getConfirmedTransactions(address) const pendingTransactions = this.getPendingTransactions(address) const transactions = confirmedTransactions.concat(pendingTransactions) const localNonces = transactions.map((txMeta) => txMeta.txParams.nonce) - return localNonces.reduce((nonce, highestNonce) => { + const localNonceHex = localNonces.reduce((nonce, highestNonce) => { return parseInt(nonce, 16) > parseInt(highestNonce, 16) ? nonce : highestNonce }, '0x0') + let localNonce = parseInt(localNonceHex, 16) + if ( + // the local nonce is not 0 + localNonce || + // or their are pending or confirmed transactions + pendingTransactions.length || + confirmedTransactions.length + ) ++localNonce + return localNonce } // this is a hotfix for the fact that the blockTracker will -- cgit v1.2.3 From 1ffb40648066189cd9e3abffc94299bbeecb6334 Mon Sep 17 00:00:00 2001 From: frankiebee Date: Fri, 18 Aug 2017 16:05:21 -0700 Subject: break out network nonce calc. --- app/scripts/lib/nonce-tracker.js | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/app/scripts/lib/nonce-tracker.js b/app/scripts/lib/nonce-tracker.js index 3063209ee..01d8e5f19 100644 --- a/app/scripts/lib/nonce-tracker.js +++ b/app/scripts/lib/nonce-tracker.js @@ -26,23 +26,13 @@ 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 localNextNonce = this._getLocalNextNonce(address) - // throw out localNonce if not a number - 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 = Math.max(baseCount + pendingCount, localNextNonce) + const nonceDetails = await this._getNetworkNonceAndDetails(address) + const networkNonce = nonceDetails.networkNonce + const nextNonce = Math.max(networkNonce, localNextNonce) 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, localNextNonce } + nonceDetails.localNextNonce = localNextNonce // return nonce and release cb return { nextNonce, nonceDetails, releaseLock } } @@ -86,9 +76,21 @@ class NonceTracker { return mutex } - // _getNetworkNonce (address) { - - // } + async _getNetworkNonceAndDetails (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 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 networkNonce = baseCount + pendingCount + return {networkNonce, blockNumber, baseCountHex, baseCount, pendingCount} + } _getLocalNextNonce (address) { const confirmedTransactions = this.getConfirmedTransactions(address) @@ -99,6 +101,8 @@ class NonceTracker { return parseInt(nonce, 16) > parseInt(highestNonce, 16) ? nonce : highestNonce }, '0x0') let localNonce = parseInt(localNonceHex, 16) + // throw out localNonce if not a number + if (!Number.isInteger(localNonce)) localNonce = 0 if ( // the local nonce is not 0 localNonce || -- cgit v1.2.3 From c76194d7c315f9fb8e536328f97a2ac2dc411097 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 21 Aug 2017 11:35:18 -0700 Subject: Move mock txs to tx mocking class --- test/lib/mock-tx-gen.js | 40 ++++++++++++++++++++++++++++++++++++++ test/unit/nonce-tracker-test.js | 43 ++++++++--------------------------------- 2 files changed, 48 insertions(+), 35 deletions(-) create mode 100644 test/lib/mock-tx-gen.js diff --git a/test/lib/mock-tx-gen.js b/test/lib/mock-tx-gen.js new file mode 100644 index 000000000..7aea09c59 --- /dev/null +++ b/test/lib/mock-tx-gen.js @@ -0,0 +1,40 @@ +const extend = require('xtend') +const BN = require('ethereumjs-util').BN +const template = { + 'status': 'submitted', + 'txParams': { + 'from': '0x7d3517b0d011698406d6e0aed8453f0be2697926', + 'gas': '0x30d40', + 'value': '0x0', + 'nonce': '0x3', + }, +} + +class TxGenerator { + + constructor () { + this.txs = [] + } + + generate (tx = {}, opts = {}) { + let { count, fromNonce } = opts + let nonce = fromNonce || this.txs.length + let txs = [] + for (let i = 0; i < count; i++) { + txs.push(extend(template, { + txParams: { + nonce: hexify(nonce++), + } + }, tx)) + } + this.txs = this.txs.concat(txs) + return txs + } + +} + +function hexify (number) { + return '0x' + (new BN(number)).toString(16) +} + +module.exports = TxGenerator diff --git a/test/unit/nonce-tracker-test.js b/test/unit/nonce-tracker-test.js index 2171d859d..225cfbae5 100644 --- a/test/unit/nonce-tracker-test.js +++ b/test/unit/nonce-tracker-test.js @@ -1,5 +1,6 @@ const assert = require('assert') const NonceTracker = require('../../app/scripts/lib/nonce-tracker') +const MockTxGen = require('../lib/mock-tx-gen') describe('Nonce Tracker', function () { let nonceTracker, provider @@ -8,41 +9,9 @@ describe('Nonce Tracker', function () { let providerResultStub = {} beforeEach(function () { - pendingTxs = [{ - 'status': 'submitted', - 'txParams': { - 'from': '0x7d3517b0d011698406d6e0aed8453f0be2697926', - 'gas': '0x30d40', - 'value': '0x0', - 'nonce': '0x3', - }, - }] - confirmedTxs = [{ - 'status': 'confirmed', - 'txParams': { - 'from': '0x7d3517b0d011698406d6e0aed8453f0be2697926', - 'gas': '0x30d40', - 'value': '0x0', - 'nonce': '0x0', - }, - }, { - 'status': 'confirmed', - 'txParams': { - 'from': '0x7d3517b0d011698406d6e0aed8453f0be2697926', - 'gas': '0x30d40', - 'value': '0x0', - 'nonce': '0x1', - }, - }, { - 'status': 'confirmed', - 'txParams': { - 'from': '0x7d3517b0d011698406d6e0aed8453f0be2697926', - 'gas': '0x30d40', - 'value': '0x0', - 'nonce': '0x2', - }, - }] - + const txGen = new MockTxGen() + confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 3 }) + pendingTxs = txGen.generate({ status: 'pending' }, { count: 1 }) getPendingTransactions = () => pendingTxs getConfirmedTransactions = () => confirmedTxs @@ -68,6 +37,10 @@ describe('Nonce Tracker', function () { await nonceLock.releaseLock() }) + it('should return 0 if there are no previous transactions', async function () { + + }) + it('should use localNonce if network returns a nonce lower then a confirmed tx in state', async function () { this.timeout(15000) providerResultStub.result = '0x1' -- cgit v1.2.3 From f13c637b23135dcf9ba1b4fbd82da1422e8ea326 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 21 Aug 2017 11:37:39 -0700 Subject: Confine mock strategy to describe block --- test/unit/nonce-tracker-test.js | 74 ++++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/test/unit/nonce-tracker-test.js b/test/unit/nonce-tracker-test.js index 225cfbae5..5b8318f59 100644 --- a/test/unit/nonce-tracker-test.js +++ b/test/unit/nonce-tracker-test.js @@ -8,45 +8,49 @@ describe('Nonce Tracker', function () { let getConfirmedTransactions, confirmedTxs let providerResultStub = {} - beforeEach(function () { - const txGen = new MockTxGen() - confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 3 }) - pendingTxs = txGen.generate({ status: 'pending' }, { count: 1 }) - - getPendingTransactions = () => pendingTxs - getConfirmedTransactions = () => confirmedTxs - providerResultStub.result = '0x3' - provider = { - sendAsync: (_, cb) => { cb(undefined, providerResultStub) }, - _blockTracker: { - getCurrentBlock: () => '0x11b568', - }, - } - nonceTracker = new NonceTracker({ - provider, - getPendingTransactions, - getConfirmedTransactions, - }) - }) - describe('#getNonceLock', function () { - it('should work', async function () { - this.timeout(15000) - const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') - assert.equal(nonceLock.nextNonce, '4', 'nonce should be 4') - await nonceLock.releaseLock() - }) - it('should return 0 if there are no previous transactions', async function () { + describe('with 3 confirmed and 1 pending', function () { + beforeEach(function () { + const txGen = new MockTxGen() + confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 3 }) + pendingTxs = txGen.generate({ status: 'pending' }, { count: 1 }) - }) + getPendingTransactions = () => pendingTxs + getConfirmedTransactions = () => confirmedTxs + providerResultStub.result = '0x3' + provider = { + sendAsync: (_, cb) => { cb(undefined, providerResultStub) }, + _blockTracker: { + getCurrentBlock: () => '0x11b568', + }, + } + nonceTracker = new NonceTracker({ + provider, + getPendingTransactions, + getConfirmedTransactions, + }) + }) + + it('should work', async function () { + this.timeout(15000) + const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') + assert.equal(nonceLock.nextNonce, '4', 'nonce should be 4') + await nonceLock.releaseLock() + }) + + it('should return 0 if there are no previous transactions', async function () { + + }) + + it('should use localNonce if network returns a nonce lower then a confirmed tx in state', async function () { + this.timeout(15000) + providerResultStub.result = '0x1' + const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') + assert.equal(nonceLock.nextNonce, '4', 'nonce should be 4') + await nonceLock.releaseLock() + }) - it('should use localNonce if network returns a nonce lower then a confirmed tx in state', async function () { - this.timeout(15000) - providerResultStub.result = '0x1' - const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') - assert.equal(nonceLock.nextNonce, '4', 'nonce should be 4') - await nonceLock.releaseLock() }) }) }) -- cgit v1.2.3 From 306249e89e84db3d3eab9b454b8ef9daad4ac035 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 21 Aug 2017 11:39:22 -0700 Subject: Add test for no previous txs --- test/unit/nonce-tracker-test.js | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/test/unit/nonce-tracker-test.js b/test/unit/nonce-tracker-test.js index 5b8318f59..617d9b56c 100644 --- a/test/unit/nonce-tracker-test.js +++ b/test/unit/nonce-tracker-test.js @@ -39,10 +39,6 @@ describe('Nonce Tracker', function () { await nonceLock.releaseLock() }) - it('should return 0 if there are no previous transactions', async function () { - - }) - it('should use localNonce if network returns a nonce lower then a confirmed tx in state', async function () { this.timeout(15000) providerResultStub.result = '0x1' @@ -50,7 +46,32 @@ describe('Nonce Tracker', function () { assert.equal(nonceLock.nextNonce, '4', 'nonce should be 4') await nonceLock.releaseLock() }) + }) + + describe('with no previous txs', function () { + beforeEach(function () { + getPendingTransactions = () => [] + getConfirmedTransactions = () => [] + providerResultStub.result = '0x0' + provider = { + sendAsync: (_, cb) => { cb(undefined, providerResultStub) }, + _blockTracker: { + getCurrentBlock: () => '0x11b568', + }, + } + nonceTracker = new NonceTracker({ + provider, + getPendingTransactions, + getConfirmedTransactions, + }) + }) + it('should return 0', async function () { + this.timeout(15000) + const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') + assert.equal(nonceLock.nextNonce, '0', 'nonce should be 0') + await nonceLock.releaseLock() + }) }) }) }) -- cgit v1.2.3 From 440101f2b561a92e81545140f0232b94af143831 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 21 Aug 2017 11:52:41 -0700 Subject: Add new assertions and de-duplicate nonce tracker generation --- test/unit/nonce-tracker-test.js | 102 ++++++++++++++++++++++++++++------------ 1 file changed, 73 insertions(+), 29 deletions(-) diff --git a/test/unit/nonce-tracker-test.js b/test/unit/nonce-tracker-test.js index 617d9b56c..3ed0eda9b 100644 --- a/test/unit/nonce-tracker-test.js +++ b/test/unit/nonce-tracker-test.js @@ -15,21 +15,7 @@ describe('Nonce Tracker', function () { const txGen = new MockTxGen() confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 3 }) pendingTxs = txGen.generate({ status: 'pending' }, { count: 1 }) - - getPendingTransactions = () => pendingTxs - getConfirmedTransactions = () => confirmedTxs - providerResultStub.result = '0x3' - provider = { - sendAsync: (_, cb) => { cb(undefined, providerResultStub) }, - _blockTracker: { - getCurrentBlock: () => '0x11b568', - }, - } - nonceTracker = new NonceTracker({ - provider, - getPendingTransactions, - getConfirmedTransactions, - }) + nonceTracker = generateNonceTrackerWith(pendingTxs, confirmedTxs) }) it('should work', async function () { @@ -50,20 +36,7 @@ describe('Nonce Tracker', function () { describe('with no previous txs', function () { beforeEach(function () { - getPendingTransactions = () => [] - getConfirmedTransactions = () => [] - providerResultStub.result = '0x0' - provider = { - sendAsync: (_, cb) => { cb(undefined, providerResultStub) }, - _blockTracker: { - getCurrentBlock: () => '0x11b568', - }, - } - nonceTracker = new NonceTracker({ - provider, - getPendingTransactions, - getConfirmedTransactions, - }) + nonceTracker = generateNonceTrackerWith([], []) }) it('should return 0', async function () { @@ -73,5 +46,76 @@ describe('Nonce Tracker', function () { await nonceLock.releaseLock() }) }) + + describe('with multiple previous txs with same nonce', function () { + beforeEach(function () { + const txGen = new MockTxGen() + confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 1 }) + pendingTxs = txGen.generate({ + status: 'pending', + txParams: { nonce: '0x01' }, + }, { count: 5 }) + + nonceTracker = generateNonceTrackerWith(pendingTxs, confirmedTxs) + }) + + it('should return nonce after those', async function () { + this.timeout(15000) + const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') + console.dir(nonceLock.nextNonce) + assert.equal(nonceLock.nextNonce, '2', 'nonce should be 2') + await nonceLock.releaseLock() + }) + }) + + describe('when local confirmed count is higher than network nonce', function () { + beforeEach(function () { + const txGen = new MockTxGen() + confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 2 }) + nonceTracker = generateNonceTrackerWith([], confirmedTxs) + }) + + it('should return nonce after those', async function () { + this.timeout(15000) + const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') + console.dir(nonceLock.nextNonce) + assert.equal(nonceLock.nextNonce, '2', 'nonce should be 2') + await nonceLock.releaseLock() + }) + }) + + describe('when local pending count is higher than other metrics', function () { + beforeEach(function () { + const txGen = new MockTxGen() + pendingTxs = txGen.generate({ status: 'pending' }, { count: 2 }) + nonceTracker = generateNonceTrackerWith(pendingTxs, []) + }) + + it('should return nonce after those', async function () { + this.timeout(15000) + const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') + console.dir(nonceLock.nextNonce) + assert.equal(nonceLock.nextNonce, '2', 'nonce should be 2') + await nonceLock.releaseLock() + }) + }) }) }) + +function generateNonceTrackerWith(pending, confirmed) { + const getPendingTransactions = () => pending + const getConfirmedTransactions = () => confirmed + providerResultStub.result = '0x0' + const provider = { + sendAsync: (_, cb) => { cb(undefined, providerResultStub) }, + _blockTracker: { + getCurrentBlock: () => '0x11b568', + }, + } + return new NonceTracker({ + provider, + getPendingTransactions, + getConfirmedTransactions, + }) +} + -- cgit v1.2.3 From 0f36e0e6da4027ea4b41510e9a81939d71b11586 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 21 Aug 2017 11:57:42 -0700 Subject: Fix test --- test/unit/nonce-tracker-test.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/unit/nonce-tracker-test.js b/test/unit/nonce-tracker-test.js index 3ed0eda9b..e8fa73b55 100644 --- a/test/unit/nonce-tracker-test.js +++ b/test/unit/nonce-tracker-test.js @@ -1,12 +1,12 @@ const assert = require('assert') const NonceTracker = require('../../app/scripts/lib/nonce-tracker') const MockTxGen = require('../lib/mock-tx-gen') +let providerResultStub = {} describe('Nonce Tracker', function () { let nonceTracker, provider let getPendingTransactions, pendingTxs let getConfirmedTransactions, confirmedTxs - let providerResultStub = {} describe('#getNonceLock', function () { @@ -15,7 +15,8 @@ describe('Nonce Tracker', function () { const txGen = new MockTxGen() confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 3 }) pendingTxs = txGen.generate({ status: 'pending' }, { count: 1 }) - nonceTracker = generateNonceTrackerWith(pendingTxs, confirmedTxs) + console.dir(txGen.txs) + nonceTracker = generateNonceTrackerWith(pendingTxs, confirmedTxs, '0x1') }) it('should work', async function () { @@ -27,7 +28,6 @@ describe('Nonce Tracker', function () { it('should use localNonce if network returns a nonce lower then a confirmed tx in state', async function () { this.timeout(15000) - providerResultStub.result = '0x1' const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') assert.equal(nonceLock.nextNonce, '4', 'nonce should be 4') await nonceLock.releaseLock() @@ -102,10 +102,10 @@ describe('Nonce Tracker', function () { }) }) -function generateNonceTrackerWith(pending, confirmed) { +function generateNonceTrackerWith(pending, confirmed, providerStub = '0x0') { const getPendingTransactions = () => pending const getConfirmedTransactions = () => confirmed - providerResultStub.result = '0x0' + providerResultStub.result = providerStub const provider = { sendAsync: (_, cb) => { cb(undefined, providerResultStub) }, _blockTracker: { -- cgit v1.2.3 From bb24f07b1759eb46fa4e4f733c348f1b87521eb6 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 21 Aug 2017 11:59:51 -0700 Subject: When network nonce is highest, it should be used. --- test/unit/nonce-tracker-test.js | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/test/unit/nonce-tracker-test.js b/test/unit/nonce-tracker-test.js index e8fa73b55..78fdee209 100644 --- a/test/unit/nonce-tracker-test.js +++ b/test/unit/nonce-tracker-test.js @@ -15,7 +15,6 @@ describe('Nonce Tracker', function () { const txGen = new MockTxGen() confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 3 }) pendingTxs = txGen.generate({ status: 'pending' }, { count: 1 }) - console.dir(txGen.txs) nonceTracker = generateNonceTrackerWith(pendingTxs, confirmedTxs, '0x1') }) @@ -62,7 +61,6 @@ describe('Nonce Tracker', function () { it('should return nonce after those', async function () { this.timeout(15000) const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') - console.dir(nonceLock.nextNonce) assert.equal(nonceLock.nextNonce, '2', 'nonce should be 2') await nonceLock.releaseLock() }) @@ -78,7 +76,6 @@ describe('Nonce Tracker', function () { it('should return nonce after those', async function () { this.timeout(15000) const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') - console.dir(nonceLock.nextNonce) assert.equal(nonceLock.nextNonce, '2', 'nonce should be 2') await nonceLock.releaseLock() }) @@ -94,11 +91,25 @@ describe('Nonce Tracker', function () { it('should return nonce after those', async function () { this.timeout(15000) const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') - console.dir(nonceLock.nextNonce) assert.equal(nonceLock.nextNonce, '2', 'nonce should be 2') await nonceLock.releaseLock() }) }) + + describe('when provider nonce is higher than other metrics', function () { + beforeEach(function () { + const txGen = new MockTxGen() + pendingTxs = txGen.generate({ status: 'pending' }, { count: 2 }) + nonceTracker = generateNonceTrackerWith(pendingTxs, [], '0x05') + }) + + it('should return nonce after those', async function () { + this.timeout(15000) + const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') + assert.equal(nonceLock.nextNonce, '6', 'nonce should be 6') + await nonceLock.releaseLock() + }) + }) }) }) -- cgit v1.2.3 From e43db262d8aa3fb725bc8e05d2ecbf38de32688a Mon Sep 17 00:00:00 2001 From: frankiebee Date: Mon, 21 Aug 2017 16:44:29 -0700 Subject: fix test --- test/unit/nonce-tracker-test.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/unit/nonce-tracker-test.js b/test/unit/nonce-tracker-test.js index 78fdee209..9a819b146 100644 --- a/test/unit/nonce-tracker-test.js +++ b/test/unit/nonce-tracker-test.js @@ -14,7 +14,7 @@ describe('Nonce Tracker', function () { beforeEach(function () { const txGen = new MockTxGen() confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 3 }) - pendingTxs = txGen.generate({ status: 'pending' }, { count: 1 }) + pendingTxs = txGen.generate({ status: 'submitted' }, { count: 1 }) nonceTracker = generateNonceTrackerWith(pendingTxs, confirmedTxs, '0x1') }) @@ -51,7 +51,7 @@ describe('Nonce Tracker', function () { const txGen = new MockTxGen() confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 1 }) pendingTxs = txGen.generate({ - status: 'pending', + status: 'submitted', txParams: { nonce: '0x01' }, }, { count: 5 }) @@ -61,7 +61,7 @@ describe('Nonce Tracker', function () { it('should return nonce after those', async function () { this.timeout(15000) const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') - assert.equal(nonceLock.nextNonce, '2', 'nonce should be 2') + assert.equal(nonceLock.nextNonce, '2', `nonce should be 2 got ${nonceLock.nextNonce}`) await nonceLock.releaseLock() }) }) @@ -76,7 +76,7 @@ describe('Nonce Tracker', function () { it('should return nonce after those', async function () { this.timeout(15000) const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') - assert.equal(nonceLock.nextNonce, '2', 'nonce should be 2') + assert.equal(nonceLock.nextNonce, '2', `nonce should be 2 got ${nonceLock.nextNonce}`) await nonceLock.releaseLock() }) }) @@ -84,14 +84,14 @@ describe('Nonce Tracker', function () { describe('when local pending count is higher than other metrics', function () { beforeEach(function () { const txGen = new MockTxGen() - pendingTxs = txGen.generate({ status: 'pending' }, { count: 2 }) + pendingTxs = txGen.generate({ status: 'submitted' }, { count: 2 }) nonceTracker = generateNonceTrackerWith(pendingTxs, []) }) it('should return nonce after those', async function () { this.timeout(15000) const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') - assert.equal(nonceLock.nextNonce, '2', 'nonce should be 2') + assert.equal(nonceLock.nextNonce, '2', `nonce should be 2 got ${nonceLock.nextNonce}`) await nonceLock.releaseLock() }) }) @@ -99,21 +99,21 @@ describe('Nonce Tracker', function () { describe('when provider nonce is higher than other metrics', function () { beforeEach(function () { const txGen = new MockTxGen() - pendingTxs = txGen.generate({ status: 'pending' }, { count: 2 }) + pendingTxs = txGen.generate({ status: 'submitted' }, { count: 2 }) nonceTracker = generateNonceTrackerWith(pendingTxs, [], '0x05') }) it('should return nonce after those', async function () { this.timeout(15000) const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') - assert.equal(nonceLock.nextNonce, '6', 'nonce should be 6') + assert.equal(nonceLock.nextNonce, '5', `nonce should be 5 got ${nonceLock.nextNonce}`) await nonceLock.releaseLock() }) }) }) }) -function generateNonceTrackerWith(pending, confirmed, providerStub = '0x0') { +function generateNonceTrackerWith (pending, confirmed, providerStub = '0x0') { const getPendingTransactions = () => pending const getConfirmedTransactions = () => confirmed providerResultStub.result = providerStub -- cgit v1.2.3 From 5c74f316a8adbcbcca642f9ba0734bd1fc181bc1 Mon Sep 17 00:00:00 2001 From: frankiebee Date: Mon, 21 Aug 2017 16:45:10 -0700 Subject: nonce-tracker - pass tests --- app/scripts/lib/nonce-tracker.js | 49 +++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/app/scripts/lib/nonce-tracker.js b/app/scripts/lib/nonce-tracker.js index 01d8e5f19..92a9cf4f1 100644 --- a/app/scripts/lib/nonce-tracker.js +++ b/app/scripts/lib/nonce-tracker.js @@ -37,6 +37,25 @@ class NonceTracker { return { nextNonce, nonceDetails, releaseLock } } + 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 + } + async _getCurrentBlock () { const blockTracker = this._getBlockTracker() const currentBlock = blockTracker.getCurrentBlock() @@ -82,22 +101,31 @@ class NonceTracker { // and pending count are from the same block const currentBlock = await this._getCurrentBlock() const blockNumber = currentBlock.blockNumber - const pendingTransactions = this.getPendingTransactions(address) - const pendingCount = pendingTransactions.length + const pendingNonce = this._getLocalPendingNonce(address) + const pendingCount = this.getPendingTransactionCount(address) 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 networkNonce = baseCount + pendingCount - return {networkNonce, blockNumber, baseCountHex, baseCount, pendingCount} + let networkNonce = pendingNonce > baseCount ? baseCount + pendingCount : baseCount + return {networkNonce, blockNumber, baseCountHex, baseCount, pendingCount, pendingNonce} + } + + _getLocalPendingNonce (address) { + const pendingTransactions = this.reduceTxListToUniqueNonces(this.getPendingTransactions(address)) + const localNonces = pendingTransactions.map((txMeta) => txMeta.txParams.nonce) + const localNonceHex = localNonces.reduce((highestNonce, nonce) => { + return parseInt(nonce, 16) > parseInt(highestNonce, 16) ? nonce : highestNonce + }, '0x0') + return parseInt(localNonceHex, 16) } _getLocalNextNonce (address) { - const confirmedTransactions = this.getConfirmedTransactions(address) - const pendingTransactions = this.getPendingTransactions(address) - const transactions = confirmedTransactions.concat(pendingTransactions) + const confirmedTransactions = this.reduceTxListToUniqueNonces(this.getConfirmedTransactions(address)) + const pendingTransactions = this.reduceTxListToUniqueNonces(this.getPendingTransactions(address)) + const transactions = this.reduceTxListToUniqueNonces(confirmedTransactions.concat(pendingTransactions)) const localNonces = transactions.map((txMeta) => txMeta.txParams.nonce) - const localNonceHex = localNonces.reduce((nonce, highestNonce) => { + const localNonceHex = localNonces.reduce((highestNonce, nonce) => { return parseInt(nonce, 16) > parseInt(highestNonce, 16) ? nonce : highestNonce }, '0x0') let localNonce = parseInt(localNonceHex, 16) @@ -107,9 +135,9 @@ class NonceTracker { // the local nonce is not 0 localNonce || // or their are pending or confirmed transactions - pendingTransactions.length || + this.getPendingTransactionCount(address) || confirmedTransactions.length - ) ++localNonce + ) ++localNonce return localNonce } @@ -118,7 +146,6 @@ class NonceTracker { _getBlockTracker () { return this.provider._blockTracker } - } module.exports = NonceTracker -- cgit v1.2.3 From 7d34b22d78713aa1a12dbdab0559ccaf4c1be93c Mon Sep 17 00:00:00 2001 From: frankiebee Date: Mon, 21 Aug 2017 17:04:47 -0700 Subject: clean up code --- app/scripts/lib/nonce-tracker.js | 82 +++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 39 deletions(-) diff --git a/app/scripts/lib/nonce-tracker.js b/app/scripts/lib/nonce-tracker.js index 92a9cf4f1..c20459650 100644 --- a/app/scripts/lib/nonce-tracker.js +++ b/app/scripts/lib/nonce-tracker.js @@ -37,25 +37,6 @@ class NonceTracker { return { nextNonce, nonceDetails, releaseLock } } - 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 - } - async _getCurrentBlock () { const blockTracker = this._getBlockTracker() const currentBlock = blockTracker.getCurrentBlock() @@ -102,45 +83,68 @@ class NonceTracker { const currentBlock = await this._getCurrentBlock() const blockNumber = currentBlock.blockNumber const pendingNonce = this._getLocalPendingNonce(address) - const pendingCount = this.getPendingTransactionCount(address) + const pendingCount = this._getPendingTransactionCount(address) 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}"`) - let networkNonce = pendingNonce > baseCount ? baseCount + pendingCount : baseCount - return {networkNonce, blockNumber, baseCountHex, baseCount, pendingCount, pendingNonce} - } + // if the nonce provided by the network is higher then a pending tx + // toss out the pending txCount + const networkNonce = pendingNonce > baseCount ? baseCount + pendingCount : baseCount - _getLocalPendingNonce (address) { - const pendingTransactions = this.reduceTxListToUniqueNonces(this.getPendingTransactions(address)) - const localNonces = pendingTransactions.map((txMeta) => txMeta.txParams.nonce) - const localNonceHex = localNonces.reduce((highestNonce, nonce) => { - return parseInt(nonce, 16) > parseInt(highestNonce, 16) ? nonce : highestNonce - }, '0x0') - return parseInt(localNonceHex, 16) + return {networkNonce, blockNumber, baseCountHex, baseCount, pendingCount, pendingNonce} } _getLocalNextNonce (address) { - const confirmedTransactions = this.reduceTxListToUniqueNonces(this.getConfirmedTransactions(address)) - const pendingTransactions = this.reduceTxListToUniqueNonces(this.getPendingTransactions(address)) - const transactions = this.reduceTxListToUniqueNonces(confirmedTransactions.concat(pendingTransactions)) - const localNonces = transactions.map((txMeta) => txMeta.txParams.nonce) - const localNonceHex = localNonces.reduce((highestNonce, nonce) => { - return parseInt(nonce, 16) > parseInt(highestNonce, 16) ? nonce : highestNonce - }, '0x0') - let localNonce = parseInt(localNonceHex, 16) + const confirmedTransactions = this._reduceTxListToUniqueNonces(this.getConfirmedTransactions(address)) + const pendingTransactions = this._reduceTxListToUniqueNonces(this.getPendingTransactions(address)) + const transactions = this._reduceTxListToUniqueNonces(confirmedTransactions.concat(pendingTransactions)) + let localNonce = this._getHighestNonce(transactions) // throw out localNonce if not a number if (!Number.isInteger(localNonce)) localNonce = 0 if ( // the local nonce is not 0 localNonce || // or their are pending or confirmed transactions - this.getPendingTransactionCount(address) || + this._getPendingTransactionCount(address) || confirmedTransactions.length ) ++localNonce return localNonce } + _getLocalPendingNonce (address) { + const pendingTransactions = this._reduceTxListToUniqueNonces(this.getPendingTransactions(address)) + const localNonce = this._getHighestNonce(pendingTransactions) + return localNonce + } + + _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) => txMeta.txParams.nonce) + const nonceHex = nonces.reduce((highestNonce, nonce) => { + return parseInt(nonce, 16) > parseInt(highestNonce, 16) ? nonce : highestNonce + }, '0x0') + return parseInt(nonceHex, 16) + } + // this is a hotfix for the fact that the blockTracker will // change when the network changes _getBlockTracker () { -- cgit v1.2.3 From 38ba31bbe01785761c03f5d7d781939104d34f37 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 21 Aug 2017 17:29:31 -0700 Subject: Refer to pending nonces, not just their count --- test/unit/nonce-tracker-test.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/unit/nonce-tracker-test.js b/test/unit/nonce-tracker-test.js index 9a819b146..11f99751c 100644 --- a/test/unit/nonce-tracker-test.js +++ b/test/unit/nonce-tracker-test.js @@ -110,6 +110,21 @@ describe('Nonce Tracker', function () { await nonceLock.releaseLock() }) }) + + describe('when there are some pending nonces below the remote one and some over.', function () { + beforeEach(function () { + const txGen = new MockTxGen() + pendingTxs = txGen.generate({ status: 'submitted' }, { count: 5 }) + nonceTracker = generateNonceTrackerWith(pendingTxs, [], '0x03') + }) + + it('should return nonce after those', async function () { + this.timeout(15000) + const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') + assert.equal(nonceLock.nextNonce, '5', `nonce should be 5 got ${nonceLock.nextNonce}`) + await nonceLock.releaseLock() + }) + }) }) }) -- cgit v1.2.3 From 604c91f7b2158b09acfcf0e56ea77d0fad0d9dec Mon Sep 17 00:00:00 2001 From: frankiebee Date: Mon, 21 Aug 2017 18:04:05 -0700 Subject: nonce-tracker - pass tests --- app/scripts/lib/nonce-tracker.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/app/scripts/lib/nonce-tracker.js b/app/scripts/lib/nonce-tracker.js index c20459650..ee36e3d5d 100644 --- a/app/scripts/lib/nonce-tracker.js +++ b/app/scripts/lib/nonce-tracker.js @@ -30,9 +30,11 @@ class NonceTracker { const nonceDetails = await this._getNetworkNonceAndDetails(address) const networkNonce = nonceDetails.networkNonce const nextNonce = Math.max(networkNonce, localNextNonce) + const currentPendingNonce = this._getLocalPendingNonce(address) 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 nonceDetails.localNextNonce = localNextNonce + nonceDetails.currentPendingNonce = currentPendingNonce // return nonce and release cb return { nextNonce, nonceDetails, releaseLock } } @@ -82,17 +84,14 @@ class NonceTracker { // and pending count are from the same block const currentBlock = await this._getCurrentBlock() const blockNumber = currentBlock.blockNumber - const pendingNonce = this._getLocalPendingNonce(address) - const pendingCount = this._getPendingTransactionCount(address) - 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}"`) // if the nonce provided by the network is higher then a pending tx // toss out the pending txCount - const networkNonce = pendingNonce > baseCount ? baseCount + pendingCount : baseCount + const networkNonce = baseCount - return {networkNonce, blockNumber, baseCountHex, baseCount, pendingCount, pendingNonce} + return {networkNonce, blockNumber, baseCountHex, baseCount} } _getLocalNextNonce (address) { -- cgit v1.2.3 From 98bc9b6656a7fd712085df76252030ddadc653dd Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 22 Aug 2017 13:52:15 -0700 Subject: nonce-tracker - make nonce strategy api and naming more symmetical --- app/scripts/lib/nonce-tracker.js | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/app/scripts/lib/nonce-tracker.js b/app/scripts/lib/nonce-tracker.js index ee36e3d5d..0a87f040d 100644 --- a/app/scripts/lib/nonce-tracker.js +++ b/app/scripts/lib/nonce-tracker.js @@ -26,14 +26,16 @@ class NonceTracker { await this._globalMutexFree() // await lock free, then take lock const releaseLock = await this._takeMutex(address) - const localNextNonce = this._getLocalNextNonce(address) - const nonceDetails = await this._getNetworkNonceAndDetails(address) - const networkNonce = nonceDetails.networkNonce - const nextNonce = Math.max(networkNonce, localNextNonce) - const currentPendingNonce = this._getLocalPendingNonce(address) + // 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 - nonceDetails.localNextNonce = localNextNonce + const currentPendingNonce = this._getLocalPendingNonce(address) nonceDetails.currentPendingNonce = currentPendingNonce // return nonce and release cb return { nextNonce, nonceDetails, releaseLock } @@ -78,7 +80,7 @@ class NonceTracker { return mutex } - async _getNetworkNonceAndDetails (address) { + async _getNetworkNextNonce (address) { // calculate next nonce // we need to make sure our base count // and pending count are from the same block @@ -87,28 +89,29 @@ class NonceTracker { 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}"`) - // if the nonce provided by the network is higher then a pending tx - // toss out the pending txCount - const networkNonce = baseCount - - return {networkNonce, blockNumber, baseCountHex, baseCount} + const nonceDetails = { blockNumber, baseCountHex, baseCount } + return { name: 'network', nonce: baseCount, details: nonceDetails } } - _getLocalNextNonce (address) { + async _getlocalNextNonce (address) { const confirmedTransactions = this._reduceTxListToUniqueNonces(this.getConfirmedTransactions(address)) const pendingTransactions = this._reduceTxListToUniqueNonces(this.getPendingTransactions(address)) const transactions = this._reduceTxListToUniqueNonces(confirmedTransactions.concat(pendingTransactions)) - let localNonce = this._getHighestNonce(transactions) + const highestNonce = this._getHighestNonce(transactions) + let localNonce = highestNonce // throw out localNonce if not a number - if (!Number.isInteger(localNonce)) localNonce = 0 + if (!Number.isInteger(highestNonce)) localNonce = 0 + const pendingCount = this._getPendingTransactionCount(address) + const confirmedCount = confirmedTransactions.length if ( // the local nonce is not 0 localNonce || // or their are pending or confirmed transactions - this._getPendingTransactionCount(address) || - confirmedTransactions.length + pendingCount || + confirmedCount ) ++localNonce - return localNonce + const nonceDetails = { highestNonce, localNonce, pendingCount, confirmedCount } + return { name: 'local', nonce: localNonce, details: nonceDetails } } _getLocalPendingNonce (address) { @@ -122,7 +125,6 @@ class NonceTracker { return this._reduceTxListToUniqueNonces(pendingTransactions).length } - _reduceTxListToUniqueNonces (txList) { const reducedTxList = txList.reduce((reducedList, txMeta, index) => { if (!index) return [txMeta] -- cgit v1.2.3 From b191649ef5ee952c636b224658181fdf2fd73b97 Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 22 Aug 2017 13:58:26 -0700 Subject: nonce-tracker - getHighestNonce doesnt need uniqued input --- app/scripts/lib/nonce-tracker.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/scripts/lib/nonce-tracker.js b/app/scripts/lib/nonce-tracker.js index 0a87f040d..d44f9c858 100644 --- a/app/scripts/lib/nonce-tracker.js +++ b/app/scripts/lib/nonce-tracker.js @@ -94,9 +94,9 @@ class NonceTracker { } async _getlocalNextNonce (address) { - const confirmedTransactions = this._reduceTxListToUniqueNonces(this.getConfirmedTransactions(address)) - const pendingTransactions = this._reduceTxListToUniqueNonces(this.getPendingTransactions(address)) - const transactions = this._reduceTxListToUniqueNonces(confirmedTransactions.concat(pendingTransactions)) + const confirmedTransactions = this.getConfirmedTransactions(address) + const pendingTransactions = this.getPendingTransactions(address) + const transactions = confirmedTransactions.concat(pendingTransactions) const highestNonce = this._getHighestNonce(transactions) let localNonce = highestNonce // throw out localNonce if not a number @@ -115,7 +115,7 @@ class NonceTracker { } _getLocalPendingNonce (address) { - const pendingTransactions = this._reduceTxListToUniqueNonces(this.getPendingTransactions(address)) + const pendingTransactions = this.getPendingTransactions(address) const localNonce = this._getHighestNonce(pendingTransactions) return localNonce } -- cgit v1.2.3 From e43da3e4aa703cf49543df7726b8cbeea987943d Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 22 Aug 2017 14:04:31 -0700 Subject: nonce-tracker - simplify getHighestNonce --- app/scripts/lib/nonce-tracker.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/scripts/lib/nonce-tracker.js b/app/scripts/lib/nonce-tracker.js index d44f9c858..dab6ace1f 100644 --- a/app/scripts/lib/nonce-tracker.js +++ b/app/scripts/lib/nonce-tracker.js @@ -139,11 +139,9 @@ class NonceTracker { } _getHighestNonce (txList) { - const nonces = txList.map((txMeta) => txMeta.txParams.nonce) - const nonceHex = nonces.reduce((highestNonce, nonce) => { - return parseInt(nonce, 16) > parseInt(highestNonce, 16) ? nonce : highestNonce - }, '0x0') - return parseInt(nonceHex, 16) + 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 -- cgit v1.2.3 From a7e3dc83272d795a48ee7e10c2588a18affbe72e Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 22 Aug 2017 14:15:56 -0700 Subject: nonce-tracker - simplify _getlocalNextNonce --- app/scripts/lib/nonce-tracker.js | 49 ++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/app/scripts/lib/nonce-tracker.js b/app/scripts/lib/nonce-tracker.js index dab6ace1f..0fb245f7b 100644 --- a/app/scripts/lib/nonce-tracker.js +++ b/app/scripts/lib/nonce-tracker.js @@ -94,30 +94,39 @@ class NonceTracker { } async _getlocalNextNonce (address) { - const confirmedTransactions = this.getConfirmedTransactions(address) - const pendingTransactions = this.getPendingTransactions(address) - const transactions = confirmedTransactions.concat(pendingTransactions) - const highestNonce = this._getHighestNonce(transactions) - let localNonce = highestNonce - // throw out localNonce if not a number - if (!Number.isInteger(highestNonce)) localNonce = 0 - const pendingCount = this._getPendingTransactionCount(address) - const confirmedCount = confirmedTransactions.length - if ( - // the local nonce is not 0 - localNonce || - // or their are pending or confirmed transactions - pendingCount || - confirmedCount - ) ++localNonce - const nonceDetails = { highestNonce, localNonce, pendingCount, confirmedCount } - return { name: 'local', nonce: localNonce, details: nonceDetails } + let nextNonce + // check our local tx history for the highest nonce (if any) + const highestNonce = this._getLocalHighestNonce(address) + 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 } + return { name: 'local', nonce: nextNonce, details: nonceDetails } } _getLocalPendingNonce (address) { const pendingTransactions = this.getPendingTransactions(address) - const localNonce = this._getHighestNonce(pendingTransactions) - return localNonce + const highestNonce = this._getHighestNonce(pendingTransactions) + return highestNonce + } + + _getLocalConfirmedNonce (address) { + const pendingTransactions = this.getConfirmedTransactions(address) + const highestNonce = this._getHighestNonce(pendingTransactions) + return highestNonce + } + + _getLocalHighestNonce (address) { + const confirmedTransactions = this.getConfirmedTransactions(address) + const pendingTransactions = this.getPendingTransactions(address) + const transactions = confirmedTransactions.concat(pendingTransactions) + const highestNonce = this._getHighestNonce(transactions) + return highestNonce } _getPendingTransactionCount (address) { -- cgit v1.2.3 From 6d596bd9e6921b92de62befadebe32b270f2b784 Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 22 Aug 2017 14:17:00 -0700 Subject: nonce-tracker - getlocalNextNonce - add entry to nonceDetails --- app/scripts/lib/nonce-tracker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/scripts/lib/nonce-tracker.js b/app/scripts/lib/nonce-tracker.js index 0fb245f7b..1c51ef647 100644 --- a/app/scripts/lib/nonce-tracker.js +++ b/app/scripts/lib/nonce-tracker.js @@ -105,7 +105,7 @@ class NonceTracker { // no local tx history so next must be first (zero) nextNonce = 0 } - const nonceDetails = { highestNonce } + const nonceDetails = { highestNonce, haveHighestNonce } return { name: 'local', nonce: nextNonce, details: nonceDetails } } -- cgit v1.2.3 From dc3e8d60ef22b268b4146e28e9dcb262aaa63304 Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 22 Aug 2017 14:24:09 -0700 Subject: nonce-tracker - fix var name --- app/scripts/lib/nonce-tracker.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/scripts/lib/nonce-tracker.js b/app/scripts/lib/nonce-tracker.js index 1c51ef647..13fdf27eb 100644 --- a/app/scripts/lib/nonce-tracker.js +++ b/app/scripts/lib/nonce-tracker.js @@ -110,14 +110,14 @@ class NonceTracker { } _getLocalPendingNonce (address) { - const pendingTransactions = this.getPendingTransactions(address) - const highestNonce = this._getHighestNonce(pendingTransactions) + const transactions = this.getPendingTransactions(address) + const highestNonce = this._getHighestNonce(transactions) return highestNonce } _getLocalConfirmedNonce (address) { - const pendingTransactions = this.getConfirmedTransactions(address) - const highestNonce = this._getHighestNonce(pendingTransactions) + const transactions = this.getConfirmedTransactions(address) + const highestNonce = this._getHighestNonce(transactions) return highestNonce } -- cgit v1.2.3 From fb2c6cc8acacfbdf134877beb68090a6fc127b94 Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 22 Aug 2017 14:33:54 -0700 Subject: nonce-tracker - use ethjs-query --- app/scripts/lib/nonce-tracker.js | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/app/scripts/lib/nonce-tracker.js b/app/scripts/lib/nonce-tracker.js index 13fdf27eb..427c09508 100644 --- a/app/scripts/lib/nonce-tracker.js +++ b/app/scripts/lib/nonce-tracker.js @@ -1,4 +1,4 @@ -const EthQuery = require('eth-query') +const EthQuery = require('ethjs-query') const assert = require('assert') const Mutex = require('await-semaphore').Mutex @@ -50,15 +50,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() @@ -86,7 +77,7 @@ class NonceTracker { // and pending count are from the same block const currentBlock = await this._getCurrentBlock() const blockNumber = currentBlock.blockNumber - const baseCountHex = await this._getTxCount(address, currentBlock) + 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 } -- cgit v1.2.3 From 0a93b65a3dea32a85a73674fb40ee40257ed18d7 Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 22 Aug 2017 15:06:54 -0700 Subject: remove unused code --- app/scripts/lib/nonce-tracker.js | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/app/scripts/lib/nonce-tracker.js b/app/scripts/lib/nonce-tracker.js index 427c09508..03bc3c278 100644 --- a/app/scripts/lib/nonce-tracker.js +++ b/app/scripts/lib/nonce-tracker.js @@ -34,9 +34,6 @@ class NonceTracker { 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 currentPendingNonce = this._getLocalPendingNonce(address) - nonceDetails.currentPendingNonce = currentPendingNonce // return nonce and release cb return { nextNonce, nonceDetails, releaseLock } } @@ -100,18 +97,6 @@ class NonceTracker { return { name: 'local', nonce: nextNonce, details: nonceDetails } } - _getLocalPendingNonce (address) { - const transactions = this.getPendingTransactions(address) - const highestNonce = this._getHighestNonce(transactions) - return highestNonce - } - - _getLocalConfirmedNonce (address) { - const transactions = this.getConfirmedTransactions(address) - const highestNonce = this._getHighestNonce(transactions) - return highestNonce - } - _getLocalHighestNonce (address) { const confirmedTransactions = this.getConfirmedTransactions(address) const pendingTransactions = this.getPendingTransactions(address) -- cgit v1.2.3 From 247965ebbe99bd611aaa6b7a5c6e86f33dcdea1d Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 22 Aug 2017 15:34:30 -0700 Subject: nonce-tracker - more debugging numbers for nonceDetails --- app/scripts/lib/nonce-tracker.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/app/scripts/lib/nonce-tracker.js b/app/scripts/lib/nonce-tracker.js index 03bc3c278..30c59fa46 100644 --- a/app/scripts/lib/nonce-tracker.js +++ b/app/scripts/lib/nonce-tracker.js @@ -84,7 +84,13 @@ class NonceTracker { async _getlocalNextNonce (address) { let nextNonce // check our local tx history for the highest nonce (if any) - const highestNonce = this._getLocalHighestNonce(address) + 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 @@ -93,18 +99,10 @@ class NonceTracker { // no local tx history so next must be first (zero) nextNonce = 0 } - const nonceDetails = { highestNonce, haveHighestNonce } + const nonceDetails = { highestNonce, haveHighestNonce, highestConfirmedNonce, highestPendingNonce } return { name: 'local', nonce: nextNonce, details: nonceDetails } } - _getLocalHighestNonce (address) { - const confirmedTransactions = this.getConfirmedTransactions(address) - const pendingTransactions = this.getPendingTransactions(address) - const transactions = confirmedTransactions.concat(pendingTransactions) - const highestNonce = this._getHighestNonce(transactions) - return highestNonce - } - _getPendingTransactionCount (address) { const pendingTransactions = this.getPendingTransactions(address) return this._reduceTxListToUniqueNonces(pendingTransactions).length -- cgit v1.2.3 From a1618ca1b4ef6568cd0c9660c56adeb8659af2bb Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 23 Aug 2017 11:12:44 -0700 Subject: Fix format of changelog --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b26afd47..cb9ea68b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,6 @@ - Fix formatting of eth_sign "Sign Message" view. - Add deprecation warning to eth_sign "Sign Message" view. - ## 3.9.8 2017-8-16 - Reenable token list. -- cgit v1.2.3 From 1d7781da6b895789933c450bdd6d2581d6fff03c Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 23 Aug 2017 11:13:35 -0700 Subject: Bump changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 157af097b..68272404c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Current Master +- Improve nonce calculation, to prevent bug where people are unable to send transactions reliably. + ## 3.9.9 2017-8-18 - Fix bug where some transaction submission errors would show an empty screen. -- cgit v1.2.3