Merge branch 'master' into nonce-tracker

feature/default_network_editable
frankiebee 8 years ago
commit 971d25a8ab
  1. 7
      CHANGELOG.md
  2. 2
      app/manifest.json
  3. 36
      app/scripts/controllers/transactions.js
  4. 9
      app/scripts/lib/tx-utils.js
  5. 41
      app/scripts/migrations/016.js
  6. 1
      app/scripts/migrations/index.js
  7. 38
      test/unit/tx-utils-test.js

@ -2,6 +2,13 @@
## Current Master ## Current Master
- No longer validate nonce client-side in retry loop.
- Fix bug where insufficient balance error was sometimes shown on successful transactions.
## 3.8.4 2017-7-7
- Improve transaction resubmit logic to fail more eagerly when a user would expect it to.
## 3.8.3 2017-7-6 ## 3.8.3 2017-7-6
- Re-enable default token list. - Re-enable default token list.

@ -1,7 +1,7 @@
{ {
"name": "MetaMask", "name": "MetaMask",
"short_name": "Metamask", "short_name": "Metamask",
"version": "3.8.3", "version": "3.8.4",
"manifest_version": 2, "manifest_version": 2,
"author": "https://metamask.io", "author": "https://metamask.io",
"description": "Ethereum Browser Extension", "description": "Ethereum Browser Extension",

@ -432,46 +432,42 @@ module.exports = class TransactionController extends EventEmitter {
// only try resubmitting if their are transactions to resubmit // only try resubmitting if their are transactions to resubmit
if (!pending.length) return if (!pending.length) return
const resubmit = denodeify(this._resubmitTx.bind(this)) const resubmit = denodeify(this._resubmitTx.bind(this))
pending.forEach((txMeta) => resubmit(txMeta) pending.forEach((txMeta) => resubmit(txMeta).catch((err) => {
.catch((reason) => {
/* /*
Dont marked as failed if the error is a "known" transaction warning Dont marked as failed if the error is a "known" transaction warning
"there is already a transaction with the same sender-nonce "there is already a transaction with the same sender-nonce
but higher/same gas price" but higher/same gas price"
*/ */
const errorMessage = reason.message.toLowerCase() const errorMessage = err.message.toLowerCase()
const isKnownTx = ( const isKnownTx = (
// geth // geth
errorMessage === 'replacement transaction underpriced' errorMessage.includes('replacement transaction underpriced')
|| errorMessage.startsWith('known transaction') || errorMessage.includes('known transaction')
// parity // parity
|| errorMessage === 'gas price too low to replace' || errorMessage.includes('gas price too low to replace')
|| errorMessage.includes('transaction with the same hash was already imported')
// other
|| errorMessage.includes('gateway timeout')
) )
// ignore resubmit warnings, return early // ignore resubmit warnings, return early
if (!isKnownTx) this.setTxStatusFailed(txMeta.id, reason.message) if (isKnownTx) return
// encountered real error - transition to error state
this.setTxStatusFailed(txMeta.id, {
errCode: err.errCode || err,
message: err.message,
})
})) }))
} }
_resubmitTx (txMeta, cb) { _resubmitTx (txMeta, cb) {
const address = txMeta.txParams.from const address = txMeta.txParams.from
const balance = this.ethStore.getState().accounts[address].balance const balance = this.ethStore.getState().accounts[address].balance
const nonce = Number.parseInt(this.ethStore.getState().accounts[address].nonce)
const txNonce = Number.parseInt(txMeta.txParams.nonce)
const gtBalance = Number.parseInt(txMeta.txParams.value) > Number.parseInt(balance)
if (!('retryCount' in txMeta)) txMeta.retryCount = 0 if (!('retryCount' in txMeta)) txMeta.retryCount = 0
// if the value of the transaction is greater then the balance, fail. // if the value of the transaction is greater then the balance, fail.
if (gtBalance) { if (!this.txProviderUtils.sufficientBalance(txMeta.txParams, balance)) {
const message = 'Insufficient balance.' const message = 'Insufficient balance.'
this.setTxStatusFailed(txMeta.id, message) this.setTxStatusFailed(txMeta.id, { message })
cb()
return log.error(message)
}
// if the nonce of the transaction is lower then the accounts nonce, fail.
if (txNonce < nonce) {
const message = 'Invalid nonce.'
this.setTxStatusFailed(txMeta.id, message)
cb() cb()
return log.error(message) return log.error(message)
} }

@ -118,6 +118,15 @@ module.exports = class txProviderUtils {
} }
} }
sufficientBalance (tx, hexBalance) {
const balance = hexToBn(hexBalance)
const value = hexToBn(tx.value)
const gasLimit = hexToBn(tx.gas)
const gasPrice = hexToBn(tx.gasPrice)
const maxCost = value.add(gasLimit.mul(gasPrice))
return balance.gte(maxCost)
}
} }

@ -0,0 +1,41 @@
const version = 16
/*
This migration sets transactions with the 'Gave up submitting tx.' err message
to a 'failed' stated
*/
const clone = require('clone')
module.exports = {
version,
migrate: function (originalVersionedData) {
const versionedData = clone(originalVersionedData)
versionedData.meta.version = version
try {
const state = versionedData.data
const newState = transformState(state)
versionedData.data = newState
} catch (err) {
console.warn(`MetaMask Migration #${version}` + err.stack)
}
return Promise.resolve(versionedData)
},
}
function transformState (state) {
const newState = state
const transactions = newState.TransactionController.transactions
newState.TransactionController.transactions = transactions.map((txMeta) => {
if (!txMeta.err) return txMeta
if (txMeta.err === 'transaction with the same hash was already imported.') {
txMeta.status = 'submitted'
delete txMeta.err
}
return txMeta
})
return newState
}

@ -26,4 +26,5 @@ module.exports = [
require('./013'), require('./013'),
require('./014'), require('./014'),
require('./015'), require('./015'),
require('./016'),
] ]

@ -16,6 +16,44 @@ describe('txUtils', function () {
})) }))
}) })
describe('#sufficientBalance', function () {
it('returns true if max tx cost is equal to balance.', function () {
const tx = {
'value': '0x1',
'gas': '0x2',
'gasPrice': '0x3',
}
const balance = '0x8'
const result = txUtils.sufficientBalance(tx, balance)
assert.ok(result, 'sufficient balance found.')
})
it('returns true if max tx cost is less than balance.', function () {
const tx = {
'value': '0x1',
'gas': '0x2',
'gasPrice': '0x3',
}
const balance = '0x9'
const result = txUtils.sufficientBalance(tx, balance)
assert.ok(result, 'sufficient balance found.')
})
it('returns false if max tx cost is more than balance.', function () {
const tx = {
'value': '0x1',
'gas': '0x2',
'gasPrice': '0x3',
}
const balance = '0x6'
const result = txUtils.sufficientBalance(tx, balance)
assert.ok(!result, 'insufficient balance found.')
})
})
describe('chain Id', function () { describe('chain Id', function () {
it('prepares a transaction with the provided chainId', function () { it('prepares a transaction with the provided chainId', function () {
const txParams = { const txParams = {

Loading…
Cancel
Save