mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
transactions/pending - buffer 3 blocks before dropping a tx (#7483)
* transactions/pending - buffer 3 blocks before dropping a tx * transactions/pending - only increment for dropped txs
This commit is contained in:
parent
eeb329adf7
commit
71a89df8ee
@ -502,7 +502,7 @@ class TransactionController extends EventEmitter {
|
|||||||
* @param {number} txId - The tx's ID
|
* @param {number} txId - The tx's ID
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async confirmTransaction (txId) {
|
async confirmTransaction (txId, txReceipt) {
|
||||||
// get the txReceipt before marking the transaction confirmed
|
// get the txReceipt before marking the transaction confirmed
|
||||||
// to ensure the receipt is gotten before the ui revives the tx
|
// to ensure the receipt is gotten before the ui revives the tx
|
||||||
const txMeta = this.txStateManager.getTx(txId)
|
const txMeta = this.txStateManager.getTx(txId)
|
||||||
@ -512,7 +512,6 @@ class TransactionController extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const txReceipt = await this.query.getTransactionReceipt(txMeta.hash)
|
|
||||||
|
|
||||||
// It seems that sometimes the numerical values being returned from
|
// It seems that sometimes the numerical values being returned from
|
||||||
// this.query.getTransactionReceipt are BN instances and not strings.
|
// this.query.getTransactionReceipt are BN instances and not strings.
|
||||||
@ -626,7 +625,7 @@ class TransactionController extends EventEmitter {
|
|||||||
this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:warning')
|
this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:warning')
|
||||||
})
|
})
|
||||||
this.pendingTxTracker.on('tx:failed', this.txStateManager.setTxStatusFailed.bind(this.txStateManager))
|
this.pendingTxTracker.on('tx:failed', this.txStateManager.setTxStatusFailed.bind(this.txStateManager))
|
||||||
this.pendingTxTracker.on('tx:confirmed', (txId) => this.confirmTransaction(txId))
|
this.pendingTxTracker.on('tx:confirmed', (txId, transactionReceipt) => this.confirmTransaction(txId, transactionReceipt))
|
||||||
this.pendingTxTracker.on('tx:dropped', this.txStateManager.setTxStatusDropped.bind(this.txStateManager))
|
this.pendingTxTracker.on('tx:dropped', this.txStateManager.setTxStatusDropped.bind(this.txStateManager))
|
||||||
this.pendingTxTracker.on('tx:block-update', (txMeta, latestBlockNumber) => {
|
this.pendingTxTracker.on('tx:block-update', (txMeta, latestBlockNumber) => {
|
||||||
if (!txMeta.firstRetryBlockNumber) {
|
if (!txMeta.firstRetryBlockNumber) {
|
||||||
|
@ -132,6 +132,7 @@ class PendingTransactionTracker extends EventEmitter {
|
|||||||
Ask the network for the transaction to see if it has been include in a block
|
Ask the network for the transaction to see if it has been include in a block
|
||||||
@param txMeta {Object} - the txMeta object
|
@param txMeta {Object} - the txMeta object
|
||||||
@emits tx:failed
|
@emits tx:failed
|
||||||
|
@emits tx:dropped
|
||||||
@emits tx:confirmed
|
@emits tx:confirmed
|
||||||
@emits tx:warning
|
@emits tx:warning
|
||||||
*/
|
*/
|
||||||
@ -153,6 +154,9 @@ class PendingTransactionTracker extends EventEmitter {
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// *note to self* hard failure point
|
||||||
|
const transactionReceipt = await this.query.getTransactionReceipt(txHash)
|
||||||
|
|
||||||
|
|
||||||
// If another tx with the same nonce is mined, set as dropped.
|
// If another tx with the same nonce is mined, set as dropped.
|
||||||
const taken = await this._checkIfNonceIsTaken(txMeta)
|
const taken = await this._checkIfNonceIsTaken(txMeta)
|
||||||
@ -161,16 +165,23 @@ class PendingTransactionTracker extends EventEmitter {
|
|||||||
// check the network if the nonce is ahead the tx
|
// check the network if the nonce is ahead the tx
|
||||||
// and the tx has not been mined into a block
|
// and the tx has not been mined into a block
|
||||||
|
|
||||||
dropped = await this._checkIftxWasDropped(txMeta)
|
dropped = await this._checkIftxWasDropped(txMeta, transactionReceipt)
|
||||||
// the dropped buffer is in case we ask a node for the tx
|
// the dropped buffer is in case we ask a node for the tx
|
||||||
// that is behind the node we asked for tx count
|
// that is behind the node we asked for tx count
|
||||||
// IS A SECURITY FOR HITTING NODES IN INFURA THAT COULD GO OUT
|
// IS A SECURITY FOR HITTING NODES IN INFURA THAT COULD GO OUT
|
||||||
// OF SYNC.
|
// OF SYNC.
|
||||||
// on the next block event it will return fire as dropped
|
// on the next block event it will return fire as dropped
|
||||||
if (dropped && !this.droppedBuffer[txHash]) {
|
if (typeof this.droppedBuffer[txHash] !== 'number') {
|
||||||
this.droppedBuffer[txHash] = true
|
this.droppedBuffer[txHash] = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3 block count buffer
|
||||||
|
if (dropped && this.droppedBuffer[txHash] < 3) {
|
||||||
dropped = false
|
dropped = false
|
||||||
} else if (dropped && this.droppedBuffer[txHash]) {
|
++this.droppedBuffer[txHash]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dropped && this.droppedBuffer[txHash] === 3) {
|
||||||
// clean up
|
// clean up
|
||||||
delete this.droppedBuffer[txHash]
|
delete this.droppedBuffer[txHash]
|
||||||
}
|
}
|
||||||
@ -184,9 +195,9 @@ class PendingTransactionTracker extends EventEmitter {
|
|||||||
|
|
||||||
// get latest transaction status
|
// get latest transaction status
|
||||||
try {
|
try {
|
||||||
const { blockNumber } = await this.query.getTransactionReceipt(txHash) || {}
|
const { blockNumber } = transactionReceipt
|
||||||
if (blockNumber) {
|
if (blockNumber) {
|
||||||
this.emit('tx:confirmed', txId)
|
this.emit('tx:confirmed', txId, transactionReceipt)
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
txMeta.warning = {
|
txMeta.warning = {
|
||||||
@ -199,14 +210,14 @@ class PendingTransactionTracker extends EventEmitter {
|
|||||||
/**
|
/**
|
||||||
checks to see if if the tx's nonce has been used by another transaction
|
checks to see if if the tx's nonce has been used by another transaction
|
||||||
@param txMeta {Object} - txMeta object
|
@param txMeta {Object} - txMeta object
|
||||||
|
@param transactionReceipt {Object} - transactionReceipt object
|
||||||
@emits tx:dropped
|
@emits tx:dropped
|
||||||
@returns {boolean}
|
@returns {boolean}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async _checkIftxWasDropped (txMeta) {
|
async _checkIftxWasDropped (txMeta, { blockNumber }) {
|
||||||
const { txParams: { nonce, from }, hash } = txMeta
|
const { txParams: { nonce, from } } = txMeta
|
||||||
const nextNonce = await this.query.getTransactionCount(from)
|
const nextNonce = await this.query.getTransactionCount(from)
|
||||||
const { blockNumber } = await this.query.getTransactionReceipt(hash) || {}
|
|
||||||
if (!blockNumber && parseInt(nextNonce) > parseInt(nonce)) {
|
if (!blockNumber && parseInt(nextNonce) > parseInt(nonce)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -214,7 +225,7 @@ class PendingTransactionTracker extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
checks to see if a confirmed txMeta has the same nonce
|
checks local txs to see if a confirmed txMeta has the same nonce
|
||||||
@param txMeta {Object} - txMeta object
|
@param txMeta {Object} - txMeta object
|
||||||
@returns {boolean}
|
@returns {boolean}
|
||||||
*/
|
*/
|
||||||
@ -224,6 +235,9 @@ class PendingTransactionTracker extends EventEmitter {
|
|||||||
const address = txMeta.txParams.from
|
const address = txMeta.txParams.from
|
||||||
const completed = this.getCompletedTransactions(address)
|
const completed = this.getCompletedTransactions(address)
|
||||||
const sameNonce = completed.filter((otherMeta) => {
|
const sameNonce = completed.filter((otherMeta) => {
|
||||||
|
if (otherMeta.id === txMeta.id) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return otherMeta.txParams.nonce === txMeta.txParams.nonce
|
return otherMeta.txParams.nonce === txMeta.txParams.nonce
|
||||||
})
|
})
|
||||||
return sameNonce.length > 0
|
return sameNonce.length > 0
|
||||||
|
@ -74,16 +74,14 @@ describe('PendingTransactionTracker', function () {
|
|||||||
value: '0x01',
|
value: '0x01',
|
||||||
hash: '0xbad',
|
hash: '0xbad',
|
||||||
status: 'confirmed',
|
status: 'confirmed',
|
||||||
nonce: '0x01',
|
}, { count: 1, fromNonce: '0x01' })
|
||||||
}, { count: 1 })
|
|
||||||
|
|
||||||
const pending = txGen.generate({
|
const pending = txGen.generate({
|
||||||
id: '123',
|
id: '123',
|
||||||
value: '0x02',
|
value: '0x02',
|
||||||
hash: '0xfad',
|
hash: '0x2a919d2512ec963f524bfd9730fb66b6d5a2e399d1dd957abb5e2b544a12644b',
|
||||||
status: 'submitted',
|
status: 'submitted',
|
||||||
nonce: '0x01',
|
}, { count: 1, fromNonce: '0x01' })[0]
|
||||||
}, { count: 1 })[0]
|
|
||||||
|
|
||||||
stub = sinon.stub(pendingTxTracker, 'getCompletedTransactions')
|
stub = sinon.stub(pendingTxTracker, 'getCompletedTransactions')
|
||||||
.returns(txGen.txs)
|
.returns(txGen.txs)
|
||||||
@ -112,7 +110,7 @@ describe('PendingTransactionTracker', function () {
|
|||||||
pendingTxTracker._checkPendingTx(txMetaNoHash)
|
pendingTxTracker._checkPendingTx(txMetaNoHash)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should emit tx:dropped with the txMetas id only after the second call', function (done) {
|
it('should emit tx:dropped with the txMetas id only after the fourth call', function (done) {
|
||||||
txMeta = {
|
txMeta = {
|
||||||
id: 1,
|
id: 1,
|
||||||
hash: '0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
|
hash: '0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
|
||||||
@ -126,20 +124,32 @@ describe('PendingTransactionTracker', function () {
|
|||||||
rawTx: '0xf86c808504a817c800827b0d940c62bb85faa3311a998d3aba8098c1235c564966880de0b6b3a7640000802aa08ff665feb887a25d4099e40e11f0fef93ee9608f404bd3f853dd9e84ed3317a6a02ec9d3d1d6e176d4d2593dd760e74ccac753e6a0ea0d00cc9789d0d7ff1f471d',
|
rawTx: '0xf86c808504a817c800827b0d940c62bb85faa3311a998d3aba8098c1235c564966880de0b6b3a7640000802aa08ff665feb887a25d4099e40e11f0fef93ee9608f404bd3f853dd9e84ed3317a6a02ec9d3d1d6e176d4d2593dd760e74ccac753e6a0ea0d00cc9789d0d7ff1f471d',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let counter = 0
|
||||||
providerResultStub['eth_getTransactionCount'] = '0x02'
|
providerResultStub['eth_getTransactionCount'] = '0x02'
|
||||||
providerResultStub['eth_getTransactionByHash'] = {}
|
providerResultStub['eth_getTransactionReceipt'] = {}
|
||||||
pendingTxTracker.once('tx:dropped', (id) => {
|
pendingTxTracker.once('tx:dropped', (id) => {
|
||||||
if (id === txMeta.id) {
|
if (id === txMeta.id) {
|
||||||
delete providerResultStub['eth_getTransactionCount']
|
delete providerResultStub['eth_getTransactionCount']
|
||||||
delete providerResultStub['eth_getTransactionByHash']
|
delete providerResultStub['eth_getTransactionReceipt']
|
||||||
return done()
|
if (counter === 3) {
|
||||||
|
return done()
|
||||||
|
} else {
|
||||||
|
return done(new Error(`Counter does not equal 3 got ${counter} instead`))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
done(new Error('wrong tx Id'))
|
done(new Error('wrong tx Id'))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
pendingTxTracker._checkPendingTx(txMeta).then(() => {
|
pendingTxTracker._checkPendingTx(txMeta).then(() => {
|
||||||
pendingTxTracker._checkPendingTx(txMeta).catch(done)
|
++counter
|
||||||
|
pendingTxTracker._checkPendingTx(txMeta).then(() => {
|
||||||
|
++counter
|
||||||
|
pendingTxTracker._checkPendingTx(txMeta).then(() => {
|
||||||
|
++counter
|
||||||
|
pendingTxTracker._checkPendingTx(txMeta)
|
||||||
|
})
|
||||||
|
})
|
||||||
}).catch(done)
|
}).catch(done)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -344,8 +354,8 @@ describe('PendingTransactionTracker', function () {
|
|||||||
}
|
}
|
||||||
it('should return false when the nonce is the suggested network nonce', (done) => {
|
it('should return false when the nonce is the suggested network nonce', (done) => {
|
||||||
providerResultStub['eth_getTransactionCount'] = '0x01'
|
providerResultStub['eth_getTransactionCount'] = '0x01'
|
||||||
providerResultStub['eth_getTransactionByHash'] = {}
|
providerResultStub['eth_getTransactionReceipt'] = {}
|
||||||
pendingTxTracker._checkIftxWasDropped(txMeta).then((dropped) => {
|
pendingTxTracker._checkIftxWasDropped(txMeta, {}).then((dropped) => {
|
||||||
assert(!dropped, 'should be false')
|
assert(!dropped, 'should be false')
|
||||||
done()
|
done()
|
||||||
}).catch(done)
|
}).catch(done)
|
||||||
@ -353,8 +363,8 @@ describe('PendingTransactionTracker', function () {
|
|||||||
|
|
||||||
it('should return true when the network nonce is higher then the txMeta nonce', function (done) {
|
it('should return true when the network nonce is higher then the txMeta nonce', function (done) {
|
||||||
providerResultStub['eth_getTransactionCount'] = '0x02'
|
providerResultStub['eth_getTransactionCount'] = '0x02'
|
||||||
providerResultStub['eth_getTransactionByHash'] = {}
|
providerResultStub['eth_getTransactionReceipt'] = {}
|
||||||
pendingTxTracker._checkIftxWasDropped(txMeta).then((dropped) => {
|
pendingTxTracker._checkIftxWasDropped(txMeta, {}).then((dropped) => {
|
||||||
assert(dropped, 'should be true')
|
assert(dropped, 'should be true')
|
||||||
done()
|
done()
|
||||||
}).catch(done)
|
}).catch(done)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user