feature/default_network_editable
Alexander Tseung 7 years ago
commit 5b2ace3b80
  1. 9
      CHANGELOG.md
  2. 2
      app/manifest.json
  3. 6
      app/scripts/contentscript.js
  4. 59
      app/scripts/controllers/transactions.js
  5. 35
      app/scripts/lib/tx-gas-utils.js
  6. 2
      app/scripts/lib/tx-state-manager.js
  7. 45
      app/scripts/migrations/024.js
  8. 1
      app/scripts/migrations/index.js
  9. 37
      test/unit/migrations/024-test.js
  10. 89
      test/unit/tx-controller-test.js
  11. 42
      test/unit/tx-gas-util-test.js
  12. 4
      ui/app/components/ens-input.js
  13. 3
      ui/app/components/pending-tx/confirm-send-ether.js

@ -2,6 +2,15 @@
## Current Master ## Current Master
## 4.5.3 Wed Apr 04 2018
- Fix bug where checksum address are messing with balance issue [#3843](https://github.com/MetaMask/metamask-extension/issues/3843)
- new ui: fix the confirm transaction screen
## 4.5.2 Wed Apr 04 2018
- Fix overly strict validation where transactions were rejected with hex encoded "chainId"
## 4.5.1 Tue Apr 03 2018 ## 4.5.1 Tue Apr 03 2018
- Fix default network (should be mainnet not Rinkeby) - Fix default network (should be mainnet not Rinkeby)

@ -1,7 +1,7 @@
{ {
"name": "__MSG_appName__", "name": "__MSG_appName__",
"short_name": "__MSG_appName__", "short_name": "__MSG_appName__",
"version": "4.5.1", "version": "4.5.3",
"manifest_version": 2, "manifest_version": 2,
"author": "https://metamask.io", "author": "https://metamask.io",
"description": "__MSG_appDescription__", "description": "__MSG_appDescription__",

@ -131,7 +131,11 @@ function documentElementCheck () {
} }
function blacklistedDomainCheck () { function blacklistedDomainCheck () {
var blacklistedDomains = ['uscourts.gov', 'dropbox.com'] var blacklistedDomains = [
'uscourts.gov',
'dropbox.com',
'webbyawards.com',
]
var currentUrl = window.location.href var currentUrl = window.location.href
var currentRegex var currentRegex
for (let i = 0; i < blacklistedDomains.length; i++) { for (let i = 0; i < blacklistedDomains.length; i++) {

@ -185,7 +185,8 @@ module.exports = class TransactionController extends EventEmitter {
async addUnapprovedTransaction (txParams) { async addUnapprovedTransaction (txParams) {
// validate // validate
await this.txGasUtil.validateTxParams(txParams) this._validateTxParams(txParams)
this._normalizeTxParams(txParams)
// construct txMeta // construct txMeta
let txMeta = this.txStateManager.generateTxMeta({txParams}) let txMeta = this.txStateManager.generateTxMeta({txParams})
this.addTx(txMeta) this.addTx(txMeta)
@ -215,7 +216,6 @@ module.exports = class TransactionController extends EventEmitter {
} }
txParams.gasPrice = ethUtil.addHexPrefix(gasPrice.toString(16)) txParams.gasPrice = ethUtil.addHexPrefix(gasPrice.toString(16))
txParams.value = txParams.value || '0x0' txParams.value = txParams.value || '0x0'
if (txParams.to === null) delete txParams.to
// set gasLimit // set gasLimit
return await this.txGasUtil.analyzeGasUsage(txMeta) return await this.txGasUtil.analyzeGasUsage(txMeta)
} }
@ -314,6 +314,61 @@ module.exports = class TransactionController extends EventEmitter {
// PRIVATE METHODS // PRIVATE METHODS
// //
_normalizeTxParams (txParams) {
delete txParams.chainId
if ( !txParams.to ) {
delete txParams.to
} else {
txParams.to = ethUtil.addHexPrefix(txParams.to)
}
txParams.from = ethUtil.addHexPrefix(txParams.from).toLowerCase()
if (!txParams.data) {
delete txParams.data
} else {
txParams.data = ethUtil.addHexPrefix(txParams.data)
}
if (txParams.value) txParams.value = ethUtil.addHexPrefix(txParams.value)
if (txParams.gas) txParams.gas = ethUtil.addHexPrefix(txParams.gas)
if (txParams.gasPrice) txParams.gas = ethUtil.addHexPrefix(txParams.gas)
}
_validateTxParams (txParams) {
this._validateFrom(txParams)
this._validateRecipient(txParams)
if ('value' in txParams) {
const value = txParams.value.toString()
if (value.includes('-')) {
throw new Error(`Invalid transaction value of ${txParams.value} not a positive number.`)
}
if (value.includes('.')) {
throw new Error(`Invalid transaction value of ${txParams.value} number must be in wei`)
}
}
}
_validateFrom (txParams) {
if ( !(typeof txParams.from === 'string') ) throw new Error(`Invalid from address ${txParams.from} not a string`)
if (!ethUtil.isValidAddress(txParams.from)) throw new Error('Invalid from address')
}
_validateRecipient (txParams) {
if (txParams.to === '0x' || txParams.to === null ) {
if (txParams.data) {
delete txParams.to
} else {
throw new Error('Invalid recipient address')
}
} else if ( txParams.to !== undefined && !ethUtil.isValidAddress(txParams.to) ) {
throw new Error('Invalid recipient address')
}
return txParams
}
_markNonceDuplicatesDropped (txId) { _markNonceDuplicatesDropped (txId) {
this.txStateManager.setTxStatusConfirmed(txId) this.txStateManager.setTxStatusConfirmed(txId)
// get the confirmed transactions nonce and from address // get the confirmed transactions nonce and from address

@ -4,7 +4,7 @@ const {
BnMultiplyByFraction, BnMultiplyByFraction,
bnToHex, bnToHex,
} = require('./util') } = require('./util')
const { addHexPrefix, isValidAddress } = require('ethereumjs-util') const { addHexPrefix } = require('ethereumjs-util')
const SIMPLE_GAS_COST = '0x5208' // Hex for 21000, cost of a simple send. const SIMPLE_GAS_COST = '0x5208' // Hex for 21000, cost of a simple send.
/* /*
@ -100,37 +100,4 @@ module.exports = class TxGasUtil {
// otherwise use blockGasLimit // otherwise use blockGasLimit
return bnToHex(upperGasLimitBn) return bnToHex(upperGasLimitBn)
} }
async validateTxParams (txParams) {
this.validateFrom(txParams)
this.validateRecipient(txParams)
if ('value' in txParams) {
const value = txParams.value.toString()
if (value.includes('-')) {
throw new Error(`Invalid transaction value of ${txParams.value} not a positive number.`)
}
if (value.includes('.')) {
throw new Error(`Invalid transaction value of ${txParams.value} number must be in wei`)
}
}
}
validateFrom (txParams) {
if ( !(typeof txParams.from === 'string') ) throw new Error(`Invalid from address ${txParams.from} not a string`)
if (!isValidAddress(txParams.from)) throw new Error('Invalid from address')
}
validateRecipient (txParams) {
if (txParams.to === '0x' || txParams.to === null ) {
if (txParams.data) {
delete txParams.to
} else {
throw new Error('Invalid recipient address')
}
} else if ( txParams.to !== undefined && !isValidAddress(txParams.to) ) {
throw new Error('Invalid recipient address')
}
return txParams
}
} }

@ -143,7 +143,7 @@ module.exports = class TransactionStateManager extends EventEmitter {
// validate types // validate types
switch (key) { switch (key) {
case 'chainId': case 'chainId':
if (typeof value !== 'number') throw new Error(`${key} in txParams is not a Number. got: (${value})`) if (typeof value !== 'number' && typeof value !== 'string') throw new Error(`${key} in txParams is not a Number or hex string. got: (${value})`)
break break
default: default:
if (typeof value !== 'string') throw new Error(`${key} in txParams is not a string. got: (${value})`) if (typeof value !== 'string') throw new Error(`${key} in txParams is not a string. got: (${value})`)

@ -0,0 +1,45 @@
const version = 24
/*
This migration ensures that the from address in txParams is to lower case for
all unapproved transactions
*/
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, _, txList) => {
if (
txMeta.status === 'unapproved' &&
txMeta.txParams &&
txMeta.txParams.from
) {
txMeta.txParams.from = txMeta.txParams.from.toLowerCase()
}
return txMeta
})
return newState
}

@ -34,4 +34,5 @@ module.exports = [
require('./021'), require('./021'),
require('./022'), require('./022'),
require('./023'), require('./023'),
require('./024'),
] ]

@ -0,0 +1,37 @@
const assert = require('assert')
const migration24 = require('../../../app/scripts/migrations/024')
const properTime = (new Date()).getTime()
const storage = {
"meta": {},
"data": {
"TransactionController": {
"transactions": [
]
},
},
}
const transactions = []
while (transactions.length <= 10) {
transactions.push({ txParams: { from: '0x8aCce2391c0d510a6c5E5d8f819a678f79b7e675' }, status: 'unapproved' })
transactions.push({ txParams: { from: '0x8aCce2391c0d510a6c5E5d8f819a678f79b7e675' }, status: 'confirmed' })
}
storage.data.TransactionController.transactions = transactions
describe('storage is migrated successfully and the txParams.from are lowercase', () => {
it('should lowercase the from for unapproved txs', (done) => {
migration24.migrate(storage)
.then((migratedData) => {
const migratedTransactions = migratedData.data.TransactionController.transactions
migratedTransactions.forEach((tx) => {
if (tx.status === 'unapproved') assert.equal(tx.txParams.from, '0x8acce2391c0d510a6c5e5d8f819a678f79b7e675')
else assert.equal(tx.txParams.from, '0x8aCce2391c0d510a6c5E5d8f819a678f79b7e675')
})
done()
}).catch(done)
})
})

@ -210,29 +210,96 @@ describe('Transaction Controller', function () {
}) })
}) })
describe('#validateTxParams', function () { describe('#_validateTxParams', function () {
it('does not throw for positive values', function (done) { it('does not throw for positive values', function () {
var sample = { var sample = {
from: '0x1678a085c290ebd122dc42cba69373b5953b831d', from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
value: '0x01', value: '0x01',
} }
txController.txGasUtil.validateTxParams(sample).then(() => { txController._validateTxParams(sample)
done()
}).catch(done)
}) })
it('returns error for negative values', function (done) { it('returns error for negative values', function () {
var sample = { var sample = {
from: '0x1678a085c290ebd122dc42cba69373b5953b831d', from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
value: '-0x01', value: '-0x01',
} }
txController.txGasUtil.validateTxParams(sample) try {
.then(() => done('expected to thrown on negativity values but didn\'t')) txController._validateTxParams(sample)
.catch((err) => { } catch (err) {
assert.ok(err, 'error') assert.ok(err, 'error')
done() }
}) })
})
describe('#_normalizeTxParams', () => {
it('should normalize txParams', () => {
let txParams = {
chainId: '0x1',
from: 'a7df1beDBF813f57096dF77FCd515f0B3900e402',
to: null,
data: '68656c6c6f20776f726c64',
}
txController._normalizeTxParams(txParams)
assert(!txParams.chainId, 'their should be no chainId')
assert(!txParams.to, 'their should be no to address if null')
assert.equal(txParams.from.slice(0, 2), '0x', 'from should be hexPrefixd')
assert.equal(txParams.data.slice(0, 2), '0x', 'data should be hexPrefixd')
txParams.to = 'a7df1beDBF813f57096dF77FCd515f0B3900e402'
txController._normalizeTxParams(txParams)
assert.equal(txParams.to.slice(0, 2), '0x', 'to should be hexPrefixd')
})
})
describe('#_validateRecipient', () => {
it('removes recipient for txParams with 0x when contract data is provided', function () {
const zeroRecipientandDataTxParams = {
from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
to: '0x',
data: 'bytecode',
}
const sanitizedTxParams = txController._validateRecipient(zeroRecipientandDataTxParams)
assert.deepEqual(sanitizedTxParams, { from: '0x1678a085c290ebd122dc42cba69373b5953b831d', data: 'bytecode' }, 'no recipient with 0x')
}) })
it('should error when recipient is 0x', function () {
const zeroRecipientTxParams = {
from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
to: '0x',
}
assert.throws(() => { txController._validateRecipient(zeroRecipientTxParams) }, Error, 'Invalid recipient address')
})
})
describe('#_validateFrom', () => {
it('should error when from is not a hex string', function () {
// where from is undefined
const txParams = {}
assert.throws(() => { txController._validateFrom(txParams) }, Error, `Invalid from address ${txParams.from} not a string`)
// where from is array
txParams.from = []
assert.throws(() => { txController._validateFrom(txParams) }, Error, `Invalid from address ${txParams.from} not a string`)
// where from is a object
txParams.from = {}
assert.throws(() => { txController._validateFrom(txParams) }, Error, `Invalid from address ${txParams.from} not a string`)
// where from is a invalid address
txParams.from = 'im going to fail'
assert.throws(() => { txController._validateFrom(txParams) }, Error, `Invalid from address`)
// should run
txParams.from ='0x1678a085c290ebd122dc42cba69373b5953b831d'
txController._validateFrom(txParams)
})
}) })
describe('#addTx', function () { describe('#addTx', function () {

@ -11,46 +11,4 @@ describe('Tx Gas Util', function () {
provider, provider,
}) })
}) })
it('removes recipient for txParams with 0x when contract data is provided', function () {
const zeroRecipientandDataTxParams = {
from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
to: '0x',
data: 'bytecode',
}
const sanitizedTxParams = txGasUtil.validateRecipient(zeroRecipientandDataTxParams)
assert.deepEqual(sanitizedTxParams, { from: '0x1678a085c290ebd122dc42cba69373b5953b831d', data: 'bytecode' }, 'no recipient with 0x')
})
it('should error when recipient is 0x', function () {
const zeroRecipientTxParams = {
from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
to: '0x',
}
assert.throws(() => { txGasUtil.validateRecipient(zeroRecipientTxParams) }, Error, 'Invalid recipient address')
})
it('should error when from is not a hex string', function () {
// where from is undefined
const txParams = {}
assert.throws(() => { txGasUtil.validateFrom(txParams) }, Error, `Invalid from address ${txParams.from} not a string`)
// where from is array
txParams.from = []
assert.throws(() => { txGasUtil.validateFrom(txParams) }, Error, `Invalid from address ${txParams.from} not a string`)
// where from is a object
txParams.from = {}
assert.throws(() => { txGasUtil.validateFrom(txParams) }, Error, `Invalid from address ${txParams.from} not a string`)
// where from is a invalid address
txParams.from = 'im going to fail'
assert.throws(() => { txGasUtil.validateFrom(txParams) }, Error, `Invalid from address`)
// should run
txParams.from ='0x1678a085c290ebd122dc42cba69373b5953b831d'
txGasUtil.validateFrom(txParams)
})
}) })

@ -32,10 +32,10 @@ EnsInput.prototype.render = function () {
const network = this.props.network const network = this.props.network
const networkHasEnsSupport = getNetworkEnsSupport(network) const networkHasEnsSupport = getNetworkEnsSupport(network)
if (!networkHasEnsSupport) return
props.onChange(recipient) props.onChange(recipient)
if (!networkHasEnsSupport) return
if (recipient.match(ensRE) === null) { if (recipient.match(ensRE) === null) {
return this.setState({ return this.setState({
loadingEns: false, loadingEns: false,

@ -242,6 +242,7 @@ ConfirmSendEther.prototype.getData = function () {
const { identities } = this.props const { identities } = this.props
const txMeta = this.gatherTxMeta() const txMeta = this.gatherTxMeta()
const txParams = txMeta.txParams || {} const txParams = txMeta.txParams || {}
const account = identities ? identities[txParams.from] || {} : {}
const { FIAT: gasFeeInFIAT, ETH: gasFeeInETH, gasFeeInHex } = this.getGasFee() const { FIAT: gasFeeInFIAT, ETH: gasFeeInETH, gasFeeInHex } = this.getGasFee()
const { FIAT: amountInFIAT, ETH: amountInETH } = this.getAmount() const { FIAT: amountInFIAT, ETH: amountInETH } = this.getAmount()
@ -257,7 +258,7 @@ ConfirmSendEther.prototype.getData = function () {
return { return {
from: { from: {
address: txParams.from, address: txParams.from,
name: identities[txParams.from].name, name: account.name,
}, },
to: { to: {
address: txParams.to, address: txParams.to,

Loading…
Cancel
Save