Merge pull request #4557 from MetaMask/nonce-tracker-mutex-fix

Bug: Mutex locks not released on error
feature/default_network_editable
kumavis 7 years ago committed by GitHub
commit 0740dd6a5b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      app/scripts/controllers/transactions/index.js
  2. 54
      app/scripts/controllers/transactions/nonce-tracker.js
  3. 4
      app/scripts/controllers/transactions/pending-tx-tracker.js
  4. 20
      app/scripts/metamask-controller.js

@ -165,7 +165,7 @@ class TransactionController extends EventEmitter {
// add default tx params
txMeta = await this.addTxGasDefaults(txMeta)
} catch (error) {
console.log(error)
log.warn(error)
this.txStateManager.setTxStatusFailed(txMeta.id, error)
throw error
}
@ -264,7 +264,12 @@ class TransactionController extends EventEmitter {
// must set transaction to submitted/failed before releasing lock
nonceLock.releaseLock()
} catch (err) {
this.txStateManager.setTxStatusFailed(txId, err)
// this is try-catch wrapped so that we can guarantee that the nonceLock is released
try {
this.txStateManager.setTxStatusFailed(txId, err)
} catch (err) {
log.error(err)
}
// must set transaction to submitted/failed before releasing lock
if (nonceLock) nonceLock.releaseLock()
// continue with error chain

@ -49,29 +49,35 @@ class NonceTracker {
await this._globalMutexFree()
// await lock free, then take lock
const releaseLock = await this._takeMutex(address)
// evaluate multiple nextNonce strategies
const nonceDetails = {}
const networkNonceResult = await this._getNetworkNextNonce(address)
const highestLocallyConfirmed = this._getHighestLocallyConfirmed(address)
const nextNetworkNonce = networkNonceResult.nonce
const highestSuggested = Math.max(nextNetworkNonce, highestLocallyConfirmed)
const pendingTxs = this.getPendingTransactions(address)
const localNonceResult = this._getHighestContinuousFrom(pendingTxs, highestSuggested) || 0
nonceDetails.params = {
highestLocallyConfirmed,
highestSuggested,
nextNetworkNonce,
try {
// evaluate multiple nextNonce strategies
const nonceDetails = {}
const networkNonceResult = await this._getNetworkNextNonce(address)
const highestLocallyConfirmed = this._getHighestLocallyConfirmed(address)
const nextNetworkNonce = networkNonceResult.nonce
const highestSuggested = Math.max(nextNetworkNonce, highestLocallyConfirmed)
const pendingTxs = this.getPendingTransactions(address)
const localNonceResult = this._getHighestContinuousFrom(pendingTxs, highestSuggested) || 0
nonceDetails.params = {
highestLocallyConfirmed,
highestSuggested,
nextNetworkNonce,
}
nonceDetails.local = localNonceResult
nonceDetails.network = networkNonceResult
const nextNonce = Math.max(networkNonceResult.nonce, localNonceResult.nonce)
assert(Number.isInteger(nextNonce), `nonce-tracker - nextNonce is not an integer - got: (${typeof nextNonce}) "${nextNonce}"`)
// return nonce and release cb
return { nextNonce, nonceDetails, releaseLock }
} catch (err) {
// release lock if we encounter an error
releaseLock()
throw err
}
nonceDetails.local = localNonceResult
nonceDetails.network = networkNonceResult
const nextNonce = Math.max(networkNonceResult.nonce, localNonceResult.nonce)
assert(Number.isInteger(nextNonce), `nonce-tracker - nextNonce is not an integer - got: (${typeof nextNonce}) "${nextNonce}"`)
// return nonce and release cb
return { nextNonce, nonceDetails, releaseLock }
}
async _getCurrentBlock () {
@ -85,8 +91,8 @@ class NonceTracker {
async _globalMutexFree () {
const globalMutex = this._lookupMutex('global')
const release = await globalMutex.acquire()
release()
const releaseLock = await globalMutex.acquire()
releaseLock()
}
async _takeMutex (lockId) {

@ -196,14 +196,14 @@ class PendingTransactionTracker extends EventEmitter {
async _checkPendingTxs () {
const signedTxList = this.getPendingTransactions()
// in order to keep the nonceTracker accurate we block it while updating pending transactions
const nonceGlobalLock = await this.nonceTracker.getGlobalLock()
const { releaseLock } = await this.nonceTracker.getGlobalLock()
try {
await Promise.all(signedTxList.map((txMeta) => this._checkPendingTx(txMeta)))
} catch (err) {
log.error('PendingTransactionWatcher - Error updating pending transactions')
log.error(err)
}
nonceGlobalLock.releaseLock()
releaseLock()
}
/**

@ -436,28 +436,24 @@ module.exports = class MetamaskController extends EventEmitter {
* @returns {Object} vault
*/
async createNewVaultAndKeychain (password) {
const release = await this.createVaultMutex.acquire()
let vault
const releaseLock = await this.createVaultMutex.acquire()
try {
let vault
const accounts = await this.keyringController.getAccounts()
if (accounts.length > 0) {
vault = await this.keyringController.fullUpdate()
} else {
vault = await this.keyringController.createNewVaultAndKeychain(password)
const accounts = await this.keyringController.getAccounts()
this.preferencesController.setAddresses(accounts)
this.selectFirstIdentity()
}
release()
releaseLock()
return vault
} catch (err) {
release()
releaseLock()
throw err
}
return vault
}
/**
@ -466,7 +462,7 @@ module.exports = class MetamaskController extends EventEmitter {
* @param {} seed
*/
async createNewVaultAndRestore (password, seed) {
const release = await this.createVaultMutex.acquire()
const releaseLock = await this.createVaultMutex.acquire()
try {
// clear known identities
this.preferencesController.setAddresses([])
@ -476,10 +472,10 @@ module.exports = class MetamaskController extends EventEmitter {
const accounts = await this.keyringController.getAccounts()
this.preferencesController.setAddresses(accounts)
this.selectFirstIdentity()
release()
releaseLock()
return vault
} catch (err) {
release()
releaseLock()
throw err
}
}

Loading…
Cancel
Save