Merge branch 'master' into uat

This commit is contained in:
Dan 2018-02-07 16:32:47 -03:30
commit d8896a8c31
18 changed files with 269 additions and 26 deletions

View File

@ -2,6 +2,23 @@
## Current Master ## Current Master
## 3.14.1 2018-2-1
- Further fix scrolling for Firefox.
## 3.14.0 2018-2-1
- Removed unneeded data from storage
- Add a "reset account" feature to Settings
- Add warning for importing some kinds of files.
- Scrollable Setting view for Firefox.
## 3.13.8 2018-1-29
- Fix provider for Kovan network.
- Bump limit for EventEmitter listeners before warning.
- Display Error when empty string is entered as a token address.
## 3.13.7 2018-1-22 ## 3.13.7 2018-1-22
- Add ability to bypass gas estimation loading indicator. - Add ability to bypass gas estimation loading indicator.

View File

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

View File

@ -152,6 +152,10 @@ module.exports = class TransactionController extends EventEmitter {
} }
} }
wipeTransactions (address) {
this.txStateManager.wipeTransactions(address)
}
// Adds a tx to the txlist // Adds a tx to the txlist
addTx (txMeta) { addTx (txMeta) {
this.txStateManager.addTx(txMeta) this.txStateManager.addTx(txMeta)

View File

@ -221,6 +221,17 @@ module.exports = class TransactionStateManger extends EventEmitter {
this._setTxStatus(txId, 'failed') this._setTxStatus(txId, 'failed')
} }
wipeTransactions (address) {
// network only tx
const txs = this.getFullTxList()
const network = this.getNetwork()
// Filter out the ones from the current account and network
const otherAccountTxs = txs.filter((txMeta) => !(txMeta.txParams.from === address && txMeta.metamaskNetworkId === network))
// Update state
this._saveTxList(otherAccountTxs)
}
// //
// PRIVATE METHODS // PRIVATE METHODS
// //

View File

@ -43,6 +43,8 @@ module.exports = class MetamaskController extends EventEmitter {
constructor (opts) { constructor (opts) {
super() super()
this.defaultMaxListeners = 20
this.sendUpdate = debounce(this.privateSendUpdate.bind(this), 200) this.sendUpdate = debounce(this.privateSendUpdate.bind(this), 200)
this.opts = opts this.opts = opts
@ -84,9 +86,7 @@ module.exports = class MetamaskController extends EventEmitter {
}) })
this.infuraController.scheduleInfuraNetworkCheck() this.infuraController.scheduleInfuraNetworkCheck()
this.blacklistController = new BlacklistController({ this.blacklistController = new BlacklistController()
initState: initState.BlacklistController,
})
this.blacklistController.scheduleUpdates() this.blacklistController.scheduleUpdates()
// rpc provider // rpc provider
@ -198,12 +198,7 @@ module.exports = class MetamaskController extends EventEmitter {
this.networkController.store.subscribe((state) => { this.networkController.store.subscribe((state) => {
this.store.updateState({ NetworkController: state }) this.store.updateState({ NetworkController: state })
}) })
this.blacklistController.store.subscribe((state) => {
this.store.updateState({ BlacklistController: state })
})
this.recentBlocksController.store.subscribe((state) => {
this.store.updateState({ RecentBlocks: state })
})
this.infuraController.store.subscribe((state) => { this.infuraController.store.subscribe((state) => {
this.store.updateState({ InfuraController: state }) this.store.updateState({ InfuraController: state })
}) })
@ -347,6 +342,7 @@ module.exports = class MetamaskController extends EventEmitter {
addNewAccount: nodeify(this.addNewAccount, this), addNewAccount: nodeify(this.addNewAccount, this),
placeSeedWords: this.placeSeedWords.bind(this), placeSeedWords: this.placeSeedWords.bind(this),
clearSeedWordCache: this.clearSeedWordCache.bind(this), clearSeedWordCache: this.clearSeedWordCache.bind(this),
resetAccount: this.resetAccount.bind(this),
importAccountWithStrategy: this.importAccountWithStrategy.bind(this), importAccountWithStrategy: this.importAccountWithStrategy.bind(this),
// vault management // vault management
@ -607,6 +603,13 @@ module.exports = class MetamaskController extends EventEmitter {
cb(null, this.preferencesController.getSelectedAddress()) cb(null, this.preferencesController.getSelectedAddress())
} }
resetAccount (cb) {
const selectedAddress = this.preferencesController.getSelectedAddress()
this.txController.wipeTransactions(selectedAddress)
cb(null, selectedAddress)
}
importAccountWithStrategy (strategy, args, cb) { importAccountWithStrategy (strategy, args, cb) {
accountImporter.importAccount(strategy, args) accountImporter.importAccount(strategy, args)
.then((privateKey) => { .then((privateKey) => {

View File

@ -0,0 +1,34 @@
const version = 21
/*
This migration removes the BlackListController from disk state
*/
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
delete newState.BlacklistController
delete newState.RecentBlocks
return newState
}

View File

@ -31,4 +31,5 @@ module.exports = [
require('./018'), require('./018'),
require('./019'), require('./019'),
require('./020'), require('./020'),
require('./021'),
] ]

View File

@ -303,7 +303,7 @@ gulp.task('apply-prod-environment', function(done) {
gulp.task('dev', gulp.series('build:scss', 'dev:js', 'copy', gulp.parallel('watch:scss', 'copy:watch', 'dev:reload'))) gulp.task('dev', gulp.series('build:scss', 'dev:js', 'copy', gulp.parallel('watch:scss', 'copy:watch', 'dev:reload')))
gulp.task('build', gulp.series('clean', 'build:scss', gulp.parallel('build:js', 'copy', 'deps'))) gulp.task('build', gulp.series('clean', 'build:scss', gulp.parallel('build:js', 'copy')))
gulp.task('dist', gulp.series('apply-prod-environment', 'build', 'zip')) gulp.task('dist', gulp.series('apply-prod-environment', 'build', 'zip'))
// task generators // task generators

View File

@ -25,7 +25,7 @@ inherits(AddTokenScreen, Component)
function AddTokenScreen () { function AddTokenScreen () {
this.state = { this.state = {
warning: null, warning: null,
address: null, address: '',
symbol: 'TOKEN', symbol: 'TOKEN',
decimals: 18, decimals: 18,
} }
@ -190,7 +190,7 @@ AddTokenScreen.prototype.validateInputs = function () {
const validAddress = ethUtil.isValidAddress(address) const validAddress = ethUtil.isValidAddress(address)
if (!validAddress) { if (!validAddress) {
msg += 'Address is invalid. ' msg += 'Address is invalid.'
} }
const validDecimals = decimals >= 0 && decimals < 36 const validDecimals = decimals >= 0 && decimals < 36

View File

@ -30,7 +30,12 @@ ConfigScreen.prototype.render = function () {
var warning = state.warning var warning = state.warning
return ( return (
h('.flex-column.flex-grow', [ h('.flex-column.flex-grow', {
style:{
maxHeight: '465px',
overflowY: 'auto',
},
}, [
h(Modal, {}, []), h(Modal, {}, []),
@ -57,6 +62,7 @@ ConfigScreen.prototype.render = function () {
h('.flex-space-around', { h('.flex-space-around', {
style: { style: {
padding: '20px', padding: '20px',
overflow: 'auto',
}, },
}, [ }, [
@ -144,6 +150,40 @@ ConfigScreen.prototype.render = function () {
}, 'Reveal Seed Words'), }, 'Reveal Seed Words'),
]), ]),
h('hr.horizontal-line'),
h('div', {
style: {
marginTop: '20px',
},
}, [
h('p', {
style: {
fontFamily: 'Montserrat Light',
fontSize: '13px',
},
}, [
'Resetting is for developer use only. ',
h('a', {
href: 'http://metamask.helpscoutdocs.com/article/36-resetting-an-account',
target: '_blank',
onClick (event) { this.navigateTo(event.target.href) },
}, 'Read more.'),
]),
h('br'),
h('button', {
style: {
alignSelf: 'center',
},
onClick (event) {
event.preventDefault()
state.dispatch(actions.resetAccount())
},
}, 'Reset Account'),
]),
]), ]),
]), ]),
]) ])
@ -220,3 +260,7 @@ function currentProviderDisplay (metamaskState) {
h('span', value), h('span', value),
]) ])
} }
ConfigScreen.prototype.navigateTo = function (url) {
global.platform.openWindow({ url })
}

View File

@ -78,7 +78,7 @@
"eth-bin-to-ops": "^1.0.1", "eth-bin-to-ops": "^1.0.1",
"eth-block-tracker": "^2.3.0", "eth-block-tracker": "^2.3.0",
"eth-json-rpc-filters": "^1.2.5", "eth-json-rpc-filters": "^1.2.5",
"eth-json-rpc-infura": "^2.0.11", "eth-json-rpc-infura": "^3.0.0",
"eth-keyring-controller": "^2.1.4", "eth-keyring-controller": "^2.1.4",
"eth-contract-metadata": "^1.1.5", "eth-contract-metadata": "^1.1.5",
"eth-hd-keyring": "^1.2.1", "eth-hd-keyring": "^1.2.1",
@ -192,7 +192,7 @@
"deep-freeze-strict": "^1.1.1", "deep-freeze-strict": "^1.1.1",
"del": "^3.0.0", "del": "^3.0.0",
"envify": "^4.0.0", "envify": "^4.0.0",
"enzyme": "^3.2.0", "enzyme": "^3.3.0",
"enzyme-adapter-react-15": "^1.0.5", "enzyme-adapter-react-15": "^1.0.5",
"eslint-plugin-chai": "0.0.1", "eslint-plugin-chai": "0.0.1",
"eslint-plugin-mocha": "^4.9.0", "eslint-plugin-mocha": "^4.9.0",
@ -201,6 +201,7 @@
"fs-promise": "^2.0.3", "fs-promise": "^2.0.3",
"gulp": "github:gulpjs/gulp#6d71a658c61edb3090221579d8f97dbe086ba2ed", "gulp": "github:gulpjs/gulp#6d71a658c61edb3090221579d8f97dbe086ba2ed",
"gulp-babel": "^7.0.0", "gulp-babel": "^7.0.0",
"gulp-eslint": "^4.0.0",
"gulp-if": "^2.0.2", "gulp-if": "^2.0.2",
"gulp-json-editor": "^2.2.1", "gulp-json-editor": "^2.2.1",
"gulp-livereload": "^3.8.1", "gulp-livereload": "^3.8.1",
@ -211,9 +212,8 @@
"gulp-uglify": "^3.0.0", "gulp-uglify": "^3.0.0",
"gulp-uglify-es": "^1.0.0", "gulp-uglify-es": "^1.0.0",
"gulp-util": "^3.0.7", "gulp-util": "^3.0.7",
"gulp-watch": "^4.3.5", "gulp-watch": "^5.0.0",
"gulp-zip": "^4.0.0", "gulp-zip": "^4.0.0",
"gulp-eslint": "^4.0.0",
"isomorphic-fetch": "^2.2.1", "isomorphic-fetch": "^2.2.1",
"jsdom": "^11.1.0", "jsdom": "^11.1.0",
"jsdom-global": "^3.0.2", "jsdom-global": "^3.0.2",
@ -224,7 +224,7 @@
"karma-firefox-launcher": "^1.0.1", "karma-firefox-launcher": "^1.0.1",
"karma-qunit": "^1.2.1", "karma-qunit": "^1.2.1",
"lodash.assign": "^4.0.6", "lodash.assign": "^4.0.6",
"mocha": "^4.0.0", "mocha": "^5.0.0",
"mocha-eslint": "^4.0.0", "mocha-eslint": "^4.0.0",
"mocha-jsdom": "^1.1.0", "mocha-jsdom": "^1.1.0",
"mocha-sinon": "^2.0.0", "mocha-sinon": "^2.0.0",
@ -237,11 +237,11 @@
"react-addons-test-utils": "^15.5.1", "react-addons-test-utils": "^15.5.1",
"react-test-renderer": "^15.6.2", "react-test-renderer": "^15.6.2",
"react-testutils-additions": "^15.2.0", "react-testutils-additions": "^15.2.0",
"redux-test-utils": "^0.1.3", "redux-test-utils": "^0.2.2",
"sinon": "^4.0.0", "sinon": "^4.0.0",
"stylelint-config-standard": "^17.0.0", "stylelint-config-standard": "^17.0.0",
"tape": "^4.5.1", "tape": "^4.5.1",
"testem": "^1.10.3", "testem": "^2.0.0",
"uglifyify": "^4.0.2", "uglifyify": "^4.0.2",
"vinyl-buffer": "^1.0.1", "vinyl-buffer": "^1.0.1",
"vinyl-source-stream": "^2.0.0", "vinyl-source-stream": "^2.0.0",

File diff suppressed because one or more lines are too long

View File

@ -1,16 +1,20 @@
const { shallow, mount } = require('enzyme') const { shallow, mount } = require('enzyme')
exports.shallowWithStore = function shallowWithStore (component, store) { module.exports = {
shallowWithStore,
mountWithStore,
}
function shallowWithStore (component, store) {
const context = { const context = {
store, store,
} }
return shallow(component, {context})
return shallow(component, { context })
} }
exports.mountWithStore = function mountWithStore (component, store) { function mountWithStore (component, store) {
const context = { const context = {
store, store,
} }
return mount(component, { context }) return mount(component, {context})
} }

View File

@ -0,0 +1,16 @@
const assert = require('assert')
const wallet2 = require('../../lib/migrations/002.json')
const migration21 = require('../../../app/scripts/migrations/021')
describe('wallet2 is migrated successfully with out the BlacklistController', () => {
it('should delete BlacklistController key', (done) => {
migration21.migrate(wallet2)
.then((migratedData) => {
assert.equal(migratedData.meta.version, 21)
assert(!migratedData.data.BlacklistController)
assert(!migratedData.data.RecentBlocks)
done()
}).catch(done)
})
})

View File

@ -238,4 +238,47 @@ describe('TransactionStateManger', function () {
assert.equal(txStateManager.getFilteredTxList(filterParams).length, 5, `getFilteredTxList - ${JSON.stringify(filterParams)}`) assert.equal(txStateManager.getFilteredTxList(filterParams).length, 5, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
}) })
}) })
describe('#wipeTransactions', function () {
const specificAddress = '0xaa'
const otherAddress = '0xbb'
it('should remove only the transactions from a specific address', function () {
const txMetas = [
{ id: 0, status: 'unapproved', txParams: { from: specificAddress, to: otherAddress }, metamaskNetworkId: currentNetworkId },
{ id: 1, status: 'confirmed', txParams: { from: otherAddress, to: specificAddress }, metamaskNetworkId: currentNetworkId },
{ id: 2, status: 'confirmed', txParams: { from: otherAddress, to: specificAddress }, metamaskNetworkId: currentNetworkId },
]
txMetas.forEach((txMeta) => txStateManager.addTx(txMeta, noop))
txStateManager.wipeTransactions(specificAddress)
const transactionsFromCurrentAddress = txStateManager.getTxList().filter((txMeta) => txMeta.txParams.from === specificAddress)
const transactionsFromOtherAddresses = txStateManager.getTxList().filter((txMeta) => txMeta.txParams.from !== specificAddress)
assert.equal(transactionsFromCurrentAddress.length, 0)
assert.equal(transactionsFromOtherAddresses.length, 2)
})
it('should not remove the transactions from other networks', function () {
const txMetas = [
{ id: 0, status: 'unapproved', txParams: { from: specificAddress, to: otherAddress }, metamaskNetworkId: currentNetworkId },
{ id: 1, status: 'confirmed', txParams: { from: specificAddress, to: otherAddress }, metamaskNetworkId: otherNetworkId },
{ id: 2, status: 'confirmed', txParams: { from: specificAddress, to: otherAddress }, metamaskNetworkId: otherNetworkId },
]
txMetas.forEach((txMeta) => txStateManager.addTx(txMeta, noop))
txStateManager.wipeTransactions(specificAddress)
const txsFromCurrentNetworkAndAddress = txStateManager.getTxList().filter((txMeta) => txMeta.txParams.from === specificAddress)
const txFromOtherNetworks = txStateManager.getFullTxList().filter((txMeta) => txMeta.metamaskNetworkId === otherNetworkId)
assert.equal(txsFromCurrentNetworkAndAddress.length, 0)
assert.equal(txFromOtherNetworks.length, 2)
})
})
}) })

View File

@ -0,0 +1,43 @@
const assert = require('assert')
const { createMockStore } = require('redux-test-utils')
const h = require('react-hyperscript')
const { shallowWithStore } = require('../../lib/shallow-with-store')
const AddTokenScreen = require('../../../ui/app/add-token')
describe('Add Token Screen', function () {
let addTokenComponent, store, component
const mockState = {
metamask: {
identities: {
'0x7d3517b0d011698406d6e0aed8453f0be2697926': {
'address': '0x7d3517b0d011698406d6e0aed8453f0be2697926',
'name': 'Add Token Name',
},
},
},
}
beforeEach(function () {
store = createMockStore(mockState)
component = shallowWithStore(h(AddTokenScreen), store)
addTokenComponent = component.dive()
})
describe('#ValidateInputs', function () {
it('Default State', function () {
addTokenComponent.instance().validateInputs()
const state = addTokenComponent.state()
assert.equal(state.warning, 'Address is invalid.')
})
it('Address is a Metamask Identity', function () {
addTokenComponent.setState({
address: '0x7d3517b0d011698406d6e0aed8453f0be2697926',
})
addTokenComponent.instance().validateInputs()
const state = addTokenComponent.state()
assert.equal(state.warning, 'Personal address detected. Input the token contract address.')
})
})
})

View File

@ -81,6 +81,12 @@ JsonImportSubview.prototype.createKeyringOnEnter = function (event) {
JsonImportSubview.prototype.createNewKeychain = function () { JsonImportSubview.prototype.createNewKeychain = function () {
const state = this.state const state = this.state
if (!state) {
const message = 'You must select a valid file to import.'
return this.props.dispatch(actions.displayWarning(message))
}
const { fileContents } = state const { fileContents } = state
if (!fileContents) { if (!fileContents) {

View File

@ -69,12 +69,14 @@ var actions = {
addNewAccount, addNewAccount,
NEW_ACCOUNT_SCREEN: 'NEW_ACCOUNT_SCREEN', NEW_ACCOUNT_SCREEN: 'NEW_ACCOUNT_SCREEN',
navigateToNewAccountScreen, navigateToNewAccountScreen,
resetAccount,
showNewVaultSeed: showNewVaultSeed, showNewVaultSeed: showNewVaultSeed,
showInfoPage: showInfoPage, showInfoPage: showInfoPage,
// seed recovery actions // seed recovery actions
REVEAL_SEED_CONFIRMATION: 'REVEAL_SEED_CONFIRMATION', REVEAL_SEED_CONFIRMATION: 'REVEAL_SEED_CONFIRMATION',
revealSeedConfirmation: revealSeedConfirmation, revealSeedConfirmation: revealSeedConfirmation,
requestRevealSeed: requestRevealSeed, requestRevealSeed: requestRevealSeed,
// unlock screen // unlock screen
UNLOCK_IN_PROGRESS: 'UNLOCK_IN_PROGRESS', UNLOCK_IN_PROGRESS: 'UNLOCK_IN_PROGRESS',
UNLOCK_FAILED: 'UNLOCK_FAILED', UNLOCK_FAILED: 'UNLOCK_FAILED',
@ -396,6 +398,20 @@ function requestRevealSeed (password) {
} }
} }
function resetAccount () {
return (dispatch) => {
background.resetAccount((err, account) => {
dispatch(actions.hideLoadingIndication())
if (err) {
dispatch(actions.displayWarning(err.message))
}
log.info('Transaction history reset for ' + account)
dispatch(actions.showAccountsPage())
})
}
}
function addNewKeyring (type, opts) { function addNewKeyring (type, opts) {
return (dispatch) => { return (dispatch) => {
dispatch(actions.showLoadingIndication()) dispatch(actions.showLoadingIndication())