From 1cba6543a42561c6691736d58f45e97f4832912b Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 4 Oct 2017 15:35:04 -0700 Subject: [PATCH 01/96] Begin implementing sync injection idea --- app/scripts/contentscript.js | 3 +-- gulpfile.js | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js index b4708189e..59e7f08ce 100644 --- a/app/scripts/contentscript.js +++ b/app/scripts/contentscript.js @@ -7,7 +7,7 @@ const ObjectMultiplex = require('obj-multiplex') const extension = require('extensionizer') const PortStream = require('./lib/port-stream.js') -const inpageText = fs.readFileSync(path.join(__dirname, 'inpage.js')).toString() +const inpageText = fs.readFileSync(path.join(__dirname, '..', '..', 'dist', 'chrome', 'scripts', 'inpage.js')).toString() + '//# sourceURL=' + extension.extension.getURL('scripts/inpage.js') + '\n' // Eventually this streaming injection could be replaced with: // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Language_Bindings/Components.utils.exportFunction @@ -25,7 +25,6 @@ function setupInjection () { try { // inject in-page script var scriptTag = document.createElement('script') - scriptTag.src = extension.extension.getURL('scripts/inpage.js') scriptTag.textContent = inpageText scriptTag.onload = function () { this.parentNode.removeChild(this) } var container = document.head || document.documentElement diff --git a/gulpfile.js b/gulpfile.js index ac36cf983..14e26ed2e 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -186,8 +186,8 @@ jsFiles.forEach((jsFile) => { gulp.task(`build:js:${jsFile}`, bundleTask({ watch: false, label: jsFile, filename: `${jsFile}.js` })) }) -gulp.task('dev:js', gulp.parallel(...jsDevStrings)) -gulp.task('build:js', gulp.parallel(...jsBuildStrings)) +gulp.task('dev:js', gulp.series(jsDevStrings.shift(), gulp.parallel(...jsDevStrings))) +gulp.task('build:js', gulp.series(jsBuildStrings.shift(), gulp.parallel(...jsBuildStrings))) // disc bundle analyzer tasks From e79037261ec4b232299dbef14e6c30fc46c48ac7 Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 10 Oct 2017 10:26:59 -0700 Subject: [PATCH 02/96] metamask controller - breakout getAccounts method --- app/scripts/metamask-controller.js | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 727f48f1c..840012e81 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -225,19 +225,9 @@ module.exports = class MetamaskController extends EventEmitter { web3_clientVersion: `MetaMask/v${version}`, }, // account mgmt - getAccounts: (cb) => { - const isUnlocked = this.keyringController.memStore.getState().isUnlocked - const result = [] - const selectedAddress = this.preferencesController.getSelectedAddress() - - // only show address if account is unlocked - if (isUnlocked && selectedAddress) { - result.push(selectedAddress) - } - cb(null, result) - }, + getAccounts: nodeify(this.getAccounts, this), // tx signing - processTransaction: nodeify(async (txParams) => await this.txController.newUnapprovedTransaction(txParams), this), + processTransaction: nodeify(this.txController.newUnapprovedTransaction, this.txController), // old style msg signing processMessage: this.newUnsignedMessage.bind(this), // personal_sign msg signing @@ -483,6 +473,18 @@ module.exports = class MetamaskController extends EventEmitter { // Opinionated Keyring Management // + async getAccounts () { + const isUnlocked = this.keyringController.memStore.getState().isUnlocked + const result = [] + const selectedAddress = this.preferencesController.getSelectedAddress() + + // only show address if account is unlocked + if (isUnlocked && selectedAddress) { + result.push(selectedAddress) + } + return result + }, + addNewAccount (cb) { const primaryKeyring = this.keyringController.getKeyringsByType('HD Key Tree')[0] if (!primaryKeyring) return cb(new Error('MetamaskController - No HD Key Tree found')) From f7c1bc804d12cfae4ff99b958b793a6fb68f4aa0 Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 10 Oct 2017 10:39:31 -0700 Subject: [PATCH 03/96] metamask controller - simplify provider init --- app/scripts/metamask-controller.js | 38 +++++++++++++----------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 840012e81..df5784571 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -81,8 +81,22 @@ module.exports = class MetamaskController extends EventEmitter { }) this.blacklistController.scheduleUpdates() - // rpc provider - this.provider = this.initializeProvider() + // rpc provider and block tracker + this.provider = this.networkController.initializeProvider({ + static: { + eth_syncing: false, + web3_clientVersion: `MetaMask/v${version}`, + }, + // account mgmt + getAccounts: nodeify(this.getAccounts, this), + // tx signing + processTransaction: nodeify(this.txController.newUnapprovedTransaction, this.txController), + // old style msg signing + processMessage: this.newUnsignedMessage.bind(this), + // personal_sign msg signing + processPersonalMessage: this.newUnsignedPersonalMessage.bind(this), + processTypedMessage: this.newUnsignedTypedMessage.bind(this), + }) this.blockTracker = this.provider._blockTracker // eth data query tools @@ -218,26 +232,6 @@ module.exports = class MetamaskController extends EventEmitter { // Constructor helpers // - initializeProvider () { - const providerOpts = { - static: { - eth_syncing: false, - web3_clientVersion: `MetaMask/v${version}`, - }, - // account mgmt - getAccounts: nodeify(this.getAccounts, this), - // tx signing - processTransaction: nodeify(this.txController.newUnapprovedTransaction, this.txController), - // old style msg signing - processMessage: this.newUnsignedMessage.bind(this), - // personal_sign msg signing - processPersonalMessage: this.newUnsignedPersonalMessage.bind(this), - processTypedMessage: this.newUnsignedTypedMessage.bind(this), - } - const providerProxy = this.networkController.initializeProvider(providerOpts) - return providerProxy - } - initPublicConfigStore () { // get init state const publicConfigStore = new ObservableStore() From ff4e9a0d1122db83221bc956f11c9520bf0e008c Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 10 Oct 2017 10:50:45 -0700 Subject: [PATCH 04/96] metamask controller - define this.newTransaction to ease instantiation order --- app/scripts/metamask-controller.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index df5784571..1292d2a1e 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -90,7 +90,7 @@ module.exports = class MetamaskController extends EventEmitter { // account mgmt getAccounts: nodeify(this.getAccounts, this), // tx signing - processTransaction: nodeify(this.txController.newUnapprovedTransaction, this.txController), + processTransaction: nodeify(this.newTransaction, this), // old style msg signing processMessage: this.newUnsignedMessage.bind(this), // personal_sign msg signing @@ -525,6 +525,11 @@ module.exports = class MetamaskController extends EventEmitter { // Identity Management // + // this function wrappper lets us pass the fn reference before txController is instantiated + async newTransaction (txParams) { + return await this.txController.newUnapprovedTransaction(txParams) + } + newUnsignedMessage (msgParams, cb) { const msgId = this.messageManager.addUnapprovedMessage(msgParams) this.sendUpdate() From efa92a7fc5925533f72e876c9bf84df0a6258d4a Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 10 Oct 2017 14:13:12 -0700 Subject: [PATCH 05/96] network controller - refactor to use eth-rpc-client --- app/scripts/controllers/network.js | 56 ++++++++++++++++-------------- app/scripts/metamask-controller.js | 7 ++-- package.json | 2 ++ 3 files changed, 36 insertions(+), 29 deletions(-) diff --git a/app/scripts/controllers/network.js b/app/scripts/controllers/network.js index 0f9db4d53..412967dbe 100644 --- a/app/scripts/controllers/network.js +++ b/app/scripts/controllers/network.js @@ -5,6 +5,7 @@ const ObservableStore = require('obs-store') const ComposedStore = require('obs-store/lib/composed') const extend = require('xtend') const EthQuery = require('eth-query') +const createEthRpcClient = require('eth-rpc-client') const createEventEmitterProxy = require('../lib/events-proxy.js') const RPC_ADDRESS_LIST = require('../config.js').network const DEFAULT_RPC = RPC_ADDRESS_LIST['rinkeby'] @@ -17,7 +18,8 @@ module.exports = class NetworkController extends EventEmitter { this.networkStore = new ObservableStore('loading') this.providerStore = new ObservableStore(config.provider) this.store = new ComposedStore({ provider: this.providerStore, network: this.networkStore }) - this._proxy = createEventEmitterProxy() + this.providerProxy = createEventEmitterProxy() + this.blockTrackerProxy = createEventEmitterProxy() this.on('networkDidChange', this.lookupNetwork) } @@ -25,12 +27,11 @@ module.exports = class NetworkController extends EventEmitter { initializeProvider (_providerParams) { this._baseProviderParams = _providerParams const rpcUrl = this.getCurrentRpcAddress() - this._configureStandardProvider({ rpcUrl }) - this._proxy.on('block', this._logBlock.bind(this)) - this._proxy.on('error', this.verifyNetwork.bind(this)) - this.ethQuery = new EthQuery(this._proxy) + this._configureStandardClient({ rpcUrl }) + this.providerProxy.on('block', this._logBlock.bind(this)) + this.providerProxy.on('error', this.verifyNetwork.bind(this)) + this.ethQuery = new EthQuery(this.providerProxy) this.lookupNetwork() - return this._proxy } verifyNetwork () { @@ -76,8 +77,10 @@ module.exports = class NetworkController extends EventEmitter { assert(type !== 'rpc', `NetworkController.setProviderType - cannot connect by type "rpc"`) // skip if type already matches if (type === this.getProviderConfig().type) return + // lookup rpcTarget for type const rpcTarget = this.getRpcAddressForType(type) assert(rpcTarget, `NetworkController - unknown rpc address for type "${type}"`) + // update connection this.providerStore.updateState({ type, rpcTarget }) this._switchNetwork({ rpcUrl: rpcTarget }) } @@ -97,32 +100,33 @@ module.exports = class NetworkController extends EventEmitter { _switchNetwork (providerParams) { this.setNetworkState('loading') - this._configureStandardProvider(providerParams) + this._configureStandardClient(providerParams) this.emit('networkDidChange') } - _configureStandardProvider(_providerParams) { + _configureStandardClient(_providerParams) { const providerParams = extend(this._baseProviderParams, _providerParams) - const provider = createMetamaskProvider(providerParams) - this._setProvider(provider) - } - - _setProvider (provider) { - // collect old block tracker events - const oldProvider = this._provider - let blockTrackerHandlers - if (oldProvider) { - // capture old block handlers - blockTrackerHandlers = oldProvider._blockTracker.proxyEventHandlers - // tear down - oldProvider.removeAllListeners() - oldProvider.stop() + const client = createEthRpcClient(providerParams) + this._setClient(client) + } + + _createMetamaskProvider(providerParams) { + const { provider, blockTracker } = createEthRpcClient(providerParams) + } + + _setClient (newClient) { + // teardown old client + const oldClient = this._currentClient + if (oldClient) { + oldClient.blockTracker.stop() + // asyncEventEmitter lacks a "removeAllListeners" method + // oldClient.blockTracker.removeAllListeners + oldClient.blockTracker._events = {} } - // override block tracler - provider._blockTracker = createEventEmitterProxy(provider._blockTracker, blockTrackerHandlers) // set as new provider - this._provider = provider - this._proxy.setTarget(provider) + this._currentClient = newClient + this.providerProxy.setTarget(newClient.provider) + this.blockTrackerProxy.setTarget(newClient.blockTracker) } _logBlock (block) { diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 1292d2a1e..67bbdc15a 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -82,8 +82,8 @@ module.exports = class MetamaskController extends EventEmitter { this.blacklistController.scheduleUpdates() // rpc provider and block tracker - this.provider = this.networkController.initializeProvider({ - static: { + this.networkController.initializeProvider({ + scaffold: { eth_syncing: false, web3_clientVersion: `MetaMask/v${version}`, }, @@ -97,7 +97,8 @@ module.exports = class MetamaskController extends EventEmitter { processPersonalMessage: this.newUnsignedPersonalMessage.bind(this), processTypedMessage: this.newUnsignedTypedMessage.bind(this), }) - this.blockTracker = this.provider._blockTracker + this.provider = this.networkController.providerProxy + this.blockTracker = this.networkController.blockTrackerProxy // eth data query tools this.ethQuery = new EthQuery(this.provider) diff --git a/package.json b/package.json index 225742487..3a6be4c61 100644 --- a/package.json +++ b/package.json @@ -71,9 +71,11 @@ "eth-contract-metadata": "^1.1.4", "eth-hd-keyring": "^1.2.1", "eth-json-rpc-filters": "^1.2.2", + "eth-json-rpc-middleware": "^1.4.1", "eth-keyring-controller": "^2.1.0", "eth-phishing-detect": "^1.1.4", "eth-query": "^2.1.2", + "eth-rpc-client": "^1.0.0", "eth-sig-util": "^1.4.0", "eth-simple-keyring": "^1.1.1", "eth-token-tracker": "^1.1.4", From 4d273d3ceade861d24dceed96a0f5d5f3dc229ae Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 10 Oct 2017 14:14:43 -0700 Subject: [PATCH 06/96] lint fixes --- app/scripts/controllers/network.js | 9 ++------- app/scripts/metamask-controller.js | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/app/scripts/controllers/network.js b/app/scripts/controllers/network.js index 412967dbe..f4665baf8 100644 --- a/app/scripts/controllers/network.js +++ b/app/scripts/controllers/network.js @@ -1,6 +1,5 @@ const assert = require('assert') const EventEmitter = require('events') -const createMetamaskProvider = require('web3-provider-engine/zero.js') const ObservableStore = require('obs-store') const ComposedStore = require('obs-store/lib/composed') const extend = require('xtend') @@ -77,10 +76,10 @@ module.exports = class NetworkController extends EventEmitter { assert(type !== 'rpc', `NetworkController.setProviderType - cannot connect by type "rpc"`) // skip if type already matches if (type === this.getProviderConfig().type) return - // lookup rpcTarget for type + // lookup rpcTarget for typecreateMetamaskProvider const rpcTarget = this.getRpcAddressForType(type) assert(rpcTarget, `NetworkController - unknown rpc address for type "${type}"`) - // update connection + // update connectioncreateMetamaskProvider this.providerStore.updateState({ type, rpcTarget }) this._switchNetwork({ rpcUrl: rpcTarget }) } @@ -110,10 +109,6 @@ module.exports = class NetworkController extends EventEmitter { this._setClient(client) } - _createMetamaskProvider(providerParams) { - const { provider, blockTracker } = createEthRpcClient(providerParams) - } - _setClient (newClient) { // teardown old client const oldClient = this._currentClient diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 67bbdc15a..a742f3cba 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -478,7 +478,7 @@ module.exports = class MetamaskController extends EventEmitter { result.push(selectedAddress) } return result - }, + } addNewAccount (cb) { const primaryKeyring = this.keyringController.getKeyringsByType('HD Key Tree')[0] From e32d75965f848f8b26868b6476265e61b791c768 Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 10 Oct 2017 17:15:14 -0700 Subject: [PATCH 07/96] events-proxy - clean up --- app/scripts/lib/events-proxy.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/scripts/lib/events-proxy.js b/app/scripts/lib/events-proxy.js index d1199a278..840b06b1a 100644 --- a/app/scripts/lib/events-proxy.js +++ b/app/scripts/lib/events-proxy.js @@ -1,6 +1,5 @@ -module.exports = function createEventEmitterProxy(eventEmitter, listeners) { +module.exports = function createEventEmitterProxy(eventEmitter, eventHandlers = {}) { let target = eventEmitter - const eventHandlers = listeners || {} const proxy = new Proxy({}, { get: (obj, name) => { // intercept listeners @@ -14,9 +13,12 @@ module.exports = function createEventEmitterProxy(eventEmitter, listeners) { return true }, }) + proxy.setTarget(eventEmitter) + return proxy + function setTarget (eventEmitter) { target = eventEmitter - // migrate listeners + // migrate eventHandlers Object.keys(eventHandlers).forEach((name) => { eventHandlers[name].forEach((handler) => target.on(name, handler)) }) @@ -26,6 +28,4 @@ module.exports = function createEventEmitterProxy(eventEmitter, listeners) { eventHandlers[name].push(handler) target.on(name, handler) } - if (listeners) proxy.setTarget(eventEmitter) - return proxy } \ No newline at end of file From 7d50a56198f2992e908bc97b871210ec2b52123a Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 10 Oct 2017 17:15:52 -0700 Subject: [PATCH 08/96] util - add obj-proxy --- app/scripts/lib/obj-proxy.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 app/scripts/lib/obj-proxy.js diff --git a/app/scripts/lib/obj-proxy.js b/app/scripts/lib/obj-proxy.js new file mode 100644 index 000000000..29ca1269f --- /dev/null +++ b/app/scripts/lib/obj-proxy.js @@ -0,0 +1,19 @@ +module.exports = function createObjectProxy(obj) { + let target = obj + const proxy = new Proxy({}, { + get: (obj, name) => { + // intercept setTarget + if (name === 'setTarget') return setTarget + return target[name] + }, + set: (obj, name, value) => { + target[name] = value + return true + }, + }) + return proxy + + function setTarget (obj) { + target = obj + } +} \ No newline at end of file From 0f8d7dacb1bada269f38b3f0f73df9e8347bc492 Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 10 Oct 2017 17:26:44 -0700 Subject: [PATCH 09/96] network-controller - use obj-proxy for providerProxy --- app/scripts/controllers/network.js | 7 ++++--- package.json | 4 ++-- test/unit/network-contoller-test.js | 25 +++++-------------------- 3 files changed, 11 insertions(+), 25 deletions(-) diff --git a/app/scripts/controllers/network.js b/app/scripts/controllers/network.js index f4665baf8..64ed4b7c2 100644 --- a/app/scripts/controllers/network.js +++ b/app/scripts/controllers/network.js @@ -6,6 +6,7 @@ const extend = require('xtend') const EthQuery = require('eth-query') const createEthRpcClient = require('eth-rpc-client') const createEventEmitterProxy = require('../lib/events-proxy.js') +const createObjectProxy = require('../lib/obj-proxy.js') const RPC_ADDRESS_LIST = require('../config.js').network const DEFAULT_RPC = RPC_ADDRESS_LIST['rinkeby'] @@ -17,7 +18,7 @@ module.exports = class NetworkController extends EventEmitter { this.networkStore = new ObservableStore('loading') this.providerStore = new ObservableStore(config.provider) this.store = new ComposedStore({ provider: this.providerStore, network: this.networkStore }) - this.providerProxy = createEventEmitterProxy() + this.providerProxy = createObjectProxy() this.blockTrackerProxy = createEventEmitterProxy() this.on('networkDidChange', this.lookupNetwork) @@ -27,8 +28,8 @@ module.exports = class NetworkController extends EventEmitter { this._baseProviderParams = _providerParams const rpcUrl = this.getCurrentRpcAddress() this._configureStandardClient({ rpcUrl }) - this.providerProxy.on('block', this._logBlock.bind(this)) - this.providerProxy.on('error', this.verifyNetwork.bind(this)) + this.blockTrackerProxy.on('block', this._logBlock.bind(this)) + this.blockTrackerProxy.on('error', this.verifyNetwork.bind(this)) this.ethQuery = new EthQuery(this.providerProxy) this.lookupNetwork() } diff --git a/package.json b/package.json index 3a6be4c61..a5a473bed 100644 --- a/package.json +++ b/package.json @@ -71,11 +71,11 @@ "eth-contract-metadata": "^1.1.4", "eth-hd-keyring": "^1.2.1", "eth-json-rpc-filters": "^1.2.2", - "eth-json-rpc-middleware": "^1.4.1", + "eth-json-rpc-middleware": "^1.4.2", "eth-keyring-controller": "^2.1.0", "eth-phishing-detect": "^1.1.4", "eth-query": "^2.1.2", - "eth-rpc-client": "^1.0.0", + "eth-rpc-client": "^1.0.3", "eth-sig-util": "^1.4.0", "eth-simple-keyring": "^1.1.1", "eth-token-tracker": "^1.1.4", diff --git a/test/unit/network-contoller-test.js b/test/unit/network-contoller-test.js index 0b3b5adeb..42ca40c56 100644 --- a/test/unit/network-contoller-test.js +++ b/test/unit/network-contoller-test.js @@ -14,15 +14,15 @@ describe('# Network Controller', function () { }, }) - networkController.initializeProvider(networkControllerProviderInit, dummyProviderConstructor) + networkController.initializeProvider(networkControllerProviderInit) }) describe('network', function () { describe('#provider', function () { it('provider should be updatable without reassignment', function () { - networkController.initializeProvider(networkControllerProviderInit, dummyProviderConstructor) - const proxy = networkController._proxy - proxy.setTarget({ test: true, on: () => {} }) - assert.ok(proxy.test) + networkController.initializeProvider(networkControllerProviderInit) + const providerProxy = networkController.providerProxy + providerProxy.setTarget({ test: true }) + assert.ok(providerProxy.test) }) }) describe('#getNetworkState', function () { @@ -66,19 +66,4 @@ describe('# Network Controller', function () { }) }) -function dummyProviderConstructor() { - return { - // provider - sendAsync: noop, - // block tracker - _blockTracker: {}, - start: noop, - stop: noop, - on: noop, - addListener: noop, - once: noop, - removeAllListeners: noop, - } -} - function noop() {} \ No newline at end of file From 4096ec9f693f668edfba73a98e91a4d0ef3f3e98 Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 10 Oct 2017 20:20:12 -0700 Subject: [PATCH 10/96] deps - bump eth-json-rpc-middleware for fix --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a5a473bed..9874f035e 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "eth-contract-metadata": "^1.1.4", "eth-hd-keyring": "^1.2.1", "eth-json-rpc-filters": "^1.2.2", - "eth-json-rpc-middleware": "^1.4.2", + "eth-json-rpc-middleware": "^1.4.3", "eth-keyring-controller": "^2.1.0", "eth-phishing-detect": "^1.1.4", "eth-query": "^2.1.2", From d31c746210cd3130e3fee467f925987eb9578a73 Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 10 Oct 2017 21:10:35 -0700 Subject: [PATCH 11/96] test - integration - intercept reload attempts --- test/integration/lib/first-time.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/integration/lib/first-time.js b/test/integration/lib/first-time.js index cedb14f6e..ee49d0901 100644 --- a/test/integration/lib/first-time.js +++ b/test/integration/lib/first-time.js @@ -3,6 +3,9 @@ const PASSWORD = 'password123' QUnit.module('first time usage') QUnit.test('render init screen', (assert) => { + // intercept reload attempts + window.onbeforeunload = () => true + const done = assert.async() runFirstTimeUsageTest(assert).then(done).catch((err) => { assert.notOk(err, `Error was thrown: ${err.stack}`) From f0713d4b1a28d608ddca6e251e947d1b2e14b6d3 Mon Sep 17 00:00:00 2001 From: kumavis Date: Wed, 11 Oct 2017 01:01:29 -0700 Subject: [PATCH 13/96] ui - network - fix localhost active status --- ui/app/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/app/app.js b/ui/app/app.js index 613577913..30d3766ab 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -319,7 +319,7 @@ App.prototype.renderNetworkDropdown = function () { [ h('i.fa.fa-question-circle.fa-lg.menu-icon'), 'Localhost 8545', - activeNetwork === 'http://localhost:8545' ? h('.check', '✓') : null, + providerType === 'localhost' ? h('.check', '✓') : null, ] ), From 9f063c320c334c997d0ce10ccad1ff323dac128a Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 11 Oct 2017 15:15:31 -0400 Subject: [PATCH 14/96] Fix link to token support page --- CHANGELOG.md | 1 + ui/app/add-token.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8bb48e9f..42ded93b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Add new support for new eth_signTypedData method per EIP 712. - Fix bug where some transactions would be shown as pending forever, even after successfully mined. - Fix bug where a transaction might be shown as pending forever if another tx with the same nonce was mined. +- Fix link to support article on token addresses. ## 3.10.9 2017-10-5 diff --git a/ui/app/add-token.js b/ui/app/add-token.js index 18adc7eb5..9354a4cad 100644 --- a/ui/app/add-token.js +++ b/ui/app/add-token.js @@ -73,7 +73,7 @@ AddTokenScreen.prototype.render = function () { }, [ h('a', { style: { fontWeight: 'bold', paddingRight: '10px'}, - href: 'https://consensyssupport.happyfox.com/staff/kb/article/24-what-is-a-token-contract-address', + href: 'https://support.metamask.io/kb/article/24-what-is-a-token-contract-address', target: '_blank', }, [ h('span', 'Token Contract Address '), From 4ed00ea2d8db6649144e7ae37672a1edefa1169e Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 11 Oct 2017 15:54:17 -0400 Subject: [PATCH 15/96] Version 3.11.0 --- CHANGELOG.md | 4 +++- app/manifest.json | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42ded93b6..ab5b3cb9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,9 @@ ## Current Master -- Add new support for new eth_signTypedData method per EIP 712. +## 3.11.0 2017-10-11 + +- Add support for new eth_signTypedData method per EIP 712. - Fix bug where some transactions would be shown as pending forever, even after successfully mined. - Fix bug where a transaction might be shown as pending forever if another tx with the same nonce was mined. - Fix link to support article on token addresses. diff --git a/app/manifest.json b/app/manifest.json index c253a5c2b..a0f449c68 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -1,7 +1,7 @@ { "name": "MetaMask", "short_name": "Metamask", - "version": "3.10.9", + "version": "3.11.0", "manifest_version": 2, "author": "https://metamask.io", "description": "Ethereum Browser Extension", From dcf10f3d7559b428cc189fb4406580d816eae365 Mon Sep 17 00:00:00 2001 From: kumavis Date: Wed, 11 Oct 2017 18:33:02 -0700 Subject: [PATCH 16/96] nonce-tracker - use blockTracker directly --- app/scripts/controllers/transactions.js | 1 + app/scripts/lib/nonce-tracker.js | 10 +++------- test/unit/nonce-tracker-test.js | 7 ++++--- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js index ef659a300..d46dee230 100644 --- a/app/scripts/controllers/transactions.js +++ b/app/scripts/controllers/transactions.js @@ -46,6 +46,7 @@ module.exports = class TransactionController extends EventEmitter { this.txStateManager.on('tx:status-update', this.emit.bind(this, 'tx:status-update')) this.nonceTracker = new NonceTracker({ provider: this.provider, + blockTracker: this.blockTracker, getPendingTransactions: this.txStateManager.getPendingTransactions.bind(this.txStateManager), getConfirmedTransactions: (address) => { return this.txStateManager.getFilteredTxList({ diff --git a/app/scripts/lib/nonce-tracker.js b/app/scripts/lib/nonce-tracker.js index 0029ac953..2af40a27f 100644 --- a/app/scripts/lib/nonce-tracker.js +++ b/app/scripts/lib/nonce-tracker.js @@ -4,8 +4,9 @@ const Mutex = require('await-semaphore').Mutex class NonceTracker { - constructor ({ provider, getPendingTransactions, getConfirmedTransactions }) { + constructor ({ provider, blockTracker, getPendingTransactions, getConfirmedTransactions }) { this.provider = provider + this.blockTracker = blockTracker this.ethQuery = new EthQuery(provider) this.getPendingTransactions = getPendingTransactions this.getConfirmedTransactions = getConfirmedTransactions @@ -53,7 +54,7 @@ class NonceTracker { } async _getCurrentBlock () { - const blockTracker = this._getBlockTracker() + const blockTracker = this.blockTracker const currentBlock = blockTracker.getCurrentBlock() if (currentBlock) return currentBlock return await Promise((reject, resolve) => { @@ -139,11 +140,6 @@ class NonceTracker { return { name: 'local', nonce: highest, details: { startPoint, highest } } } - // this is a hotfix for the fact that the blockTracker will - // change when the network changes - _getBlockTracker () { - return this.provider._blockTracker - } } module.exports = NonceTracker diff --git a/test/unit/nonce-tracker-test.js b/test/unit/nonce-tracker-test.js index 8970cf84d..77af2a21c 100644 --- a/test/unit/nonce-tracker-test.js +++ b/test/unit/nonce-tracker-test.js @@ -190,12 +190,13 @@ function generateNonceTrackerWith (pending, confirmed, providerStub = '0x0') { providerResultStub.result = providerStub const provider = { sendAsync: (_, cb) => { cb(undefined, providerResultStub) }, - _blockTracker: { - getCurrentBlock: () => '0x11b568', - }, + } + const blockTracker = { + getCurrentBlock: () => '0x11b568', } return new NonceTracker({ provider, + blockTracker, getPendingTransactions, getConfirmedTransactions, }) From 5c5f9297f78c965b38087b960acfec472e16818b Mon Sep 17 00:00:00 2001 From: kumavis Date: Wed, 11 Oct 2017 18:36:25 -0700 Subject: [PATCH 17/96] deps - bump eth-rpc-client for fix --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9874f035e..2b7b2056a 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "eth-keyring-controller": "^2.1.0", "eth-phishing-detect": "^1.1.4", "eth-query": "^2.1.2", - "eth-rpc-client": "^1.0.3", + "eth-rpc-client": "^1.1.3", "eth-sig-util": "^1.4.0", "eth-simple-keyring": "^1.1.1", "eth-token-tracker": "^1.1.4", From d71f14cb678b48b8666e6835c025ff14193acbb3 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 12 Oct 2017 14:03:42 -0400 Subject: [PATCH 18/96] Increase build readability --- gulpfile.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 6d2ff5fdd..9253949c7 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -186,8 +186,13 @@ jsFiles.forEach((jsFile) => { gulp.task(`build:js:${jsFile}`, bundleTask({ watch: false, label: jsFile, filename: `${jsFile}.js` })) }) -gulp.task('dev:js', gulp.series(jsDevStrings.shift(), gulp.parallel(...jsDevStrings))) -gulp.task('build:js', gulp.series(jsBuildStrings.shift(), gulp.parallel(...jsBuildStrings))) +// inpage must be built before all other scripts: +const firstDevString = jsDevStrings.shift() +gulp.task('dev:js', gulp.series(firstDevString, gulp.parallel(...jsDevStrings))) + +// inpage must be built before all other scripts: +const firstBuildString = jsBuildStrings.shift() +gulp.task('build:js', gulp.series(firstBuildString, gulp.parallel(...jsBuildStrings))) // disc bundle analyzer tasks From c9a984a237ccacf8994e6c4c2f49b7a17da92d6b Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 12 Oct 2017 14:16:40 -0400 Subject: [PATCH 19/96] Break up inpage file read into multiple lines --- app/scripts/contentscript.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js index 59e7f08ce..445608214 100644 --- a/app/scripts/contentscript.js +++ b/app/scripts/contentscript.js @@ -7,7 +7,9 @@ const ObjectMultiplex = require('obj-multiplex') const extension = require('extensionizer') const PortStream = require('./lib/port-stream.js') -const inpageText = fs.readFileSync(path.join(__dirname, '..', '..', 'dist', 'chrome', 'scripts', 'inpage.js')).toString() + '//# sourceURL=' + extension.extension.getURL('scripts/inpage.js') + '\n' +const inpagePath = path.join(__dirname, '..', '..', 'dist', 'chrome', 'scripts', 'inpage.js') +const inpageString = fs.readFileSync(inpagePath).toString() +const inpageText = inpageString + '//# sourceURL=' + extension.extension.getURL('scripts/inpage.js') + '\n' // Eventually this streaming injection could be replaced with: // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Language_Bindings/Components.utils.exportFunction From 53a360b65d6b97fa4551c2953072a21cbe9f708d Mon Sep 17 00:00:00 2001 From: kumavis Date: Thu, 12 Oct 2017 12:51:48 -0700 Subject: [PATCH 20/96] contentscript - fix inpage require and bundling --- app/scripts/contentscript.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js index 445608214..ffbbc73cc 100644 --- a/app/scripts/contentscript.js +++ b/app/scripts/contentscript.js @@ -7,9 +7,9 @@ const ObjectMultiplex = require('obj-multiplex') const extension = require('extensionizer') const PortStream = require('./lib/port-stream.js') -const inpagePath = path.join(__dirname, '..', '..', 'dist', 'chrome', 'scripts', 'inpage.js') -const inpageString = fs.readFileSync(inpagePath).toString() -const inpageText = inpageString + '//# sourceURL=' + extension.extension.getURL('scripts/inpage.js') + '\n' +const inpageContent = fs.readFileSync(path.join(__dirname, '..', '..', 'dist', 'chrome', 'scripts', 'inpage.js')).toString() +const inpageSuffix = '//# sourceURL=' + extension.extension.getURL('scripts/inpage.js') + '\n' +const inpageBundle = inpageContent + inpageSuffix // Eventually this streaming injection could be replaced with: // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Language_Bindings/Components.utils.exportFunction @@ -27,7 +27,7 @@ function setupInjection () { try { // inject in-page script var scriptTag = document.createElement('script') - scriptTag.textContent = inpageText + scriptTag.textContent = inpageBundle scriptTag.onload = function () { this.parentNode.removeChild(this) } var container = document.head || document.documentElement // append as first child From 970fbd797a96a4e86175181ad30d0b7216d9d2c9 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Sat, 14 Oct 2017 12:05:00 +0000 Subject: [PATCH 21/96] fix(package): update react-simple-file-input to version 2.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2b7b2056a..fb1621e29 100644 --- a/package.json +++ b/package.json @@ -128,7 +128,7 @@ "react-markdown": "^2.3.0", "react-redux": "^5.0.5", "react-select": "^1.0.0-rc.2", - "react-simple-file-input": "^1.0.0", + "react-simple-file-input": "^2.0.0", "react-tooltip-component": "^0.3.0", "readable-stream": "^2.3.3", "redux": "^3.0.5", From 06094c914b324e3debf33af374bbaa280d6dc6ef Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Sat, 14 Oct 2017 11:23:44 -0400 Subject: [PATCH 22/96] Move etherscan link logic into module --- package.json | 1 + test/unit/account-link-test.js | 16 ------------- test/unit/explorer-link-test.js | 14 ------------ ui/app/components/account-dropdowns.js | 2 +- ui/app/components/shift-list-item.js | 2 +- ui/app/components/transaction-list-item.js | 2 +- ui/lib/account-link.js | 26 ---------------------- ui/lib/explorer-link.js | 6 ----- 8 files changed, 4 insertions(+), 65 deletions(-) delete mode 100644 test/unit/account-link-test.js delete mode 100644 test/unit/explorer-link-test.js delete mode 100644 ui/lib/account-link.js delete mode 100644 ui/lib/explorer-link.js diff --git a/package.json b/package.json index 2b7b2056a..6f4ac2141 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,7 @@ "ethereumjs-tx": "^1.3.0", "ethereumjs-util": "github:ethereumjs/ethereumjs-util#ac5d0908536b447083ea422b435da27f26615de9", "ethereumjs-wallet": "^0.6.0", + "etherscan-link": "^1.0.2", "ethjs-contract": "^0.1.9", "ethjs-ens": "^2.0.0", "ethjs-query": "^0.2.9", diff --git a/test/unit/account-link-test.js b/test/unit/account-link-test.js deleted file mode 100644 index 47a961d1f..000000000 --- a/test/unit/account-link-test.js +++ /dev/null @@ -1,16 +0,0 @@ -var assert = require('assert') -var linkGen = require('../../ui/lib/account-link') - -describe('account-link', function () { - it('adds ropsten prefix to ropsten test network', function () { - var result = linkGen('account', '3') - assert.notEqual(result.indexOf('ropsten'), -1, 'ropsten included') - assert.notEqual(result.indexOf('account'), -1, 'account included') - }) - - it('adds kovan prefix to kovan test network', function () { - var result = linkGen('account', '42') - assert.notEqual(result.indexOf('kovan'), -1, 'kovan included') - assert.notEqual(result.indexOf('account'), -1, 'account included') - }) -}) diff --git a/test/unit/explorer-link-test.js b/test/unit/explorer-link-test.js deleted file mode 100644 index a02564509..000000000 --- a/test/unit/explorer-link-test.js +++ /dev/null @@ -1,14 +0,0 @@ -var assert = require('assert') -var linkGen = require('../../ui/lib/explorer-link') - -describe('explorer-link', function () { - it('adds ropsten prefix to ropsten test network', function () { - var result = linkGen('hash', '3') - assert.notEqual(result.indexOf('ropsten'), -1, 'ropsten injected') - }) - - it('adds kovan prefix to kovan test network', function () { - var result = linkGen('hash', '42') - assert.notEqual(result.indexOf('kovan'), -1, 'kovan injected') - }) -}) diff --git a/ui/app/components/account-dropdowns.js b/ui/app/components/account-dropdowns.js index b087a40d4..1b46e532a 100644 --- a/ui/app/components/account-dropdowns.js +++ b/ui/app/components/account-dropdowns.js @@ -2,7 +2,7 @@ const Component = require('react').Component const PropTypes = require('react').PropTypes const h = require('react-hyperscript') const actions = require('../actions') -const genAccountLink = require('../../lib/account-link.js') +const genAccountLink = require('etherscan-link').createAccountLink const connect = require('react-redux').connect const Dropdown = require('./dropdown').Dropdown const DropdownMenuItem = require('./dropdown').DropdownMenuItem diff --git a/ui/app/components/shift-list-item.js b/ui/app/components/shift-list-item.js index 079f05e31..b555dee84 100644 --- a/ui/app/components/shift-list-item.js +++ b/ui/app/components/shift-list-item.js @@ -3,7 +3,7 @@ const Component = require('react').Component const h = require('react-hyperscript') const connect = require('react-redux').connect const vreme = new (require('vreme'))() -const explorerLink = require('../../lib/explorer-link') +const explorerLink = require('etherscan-link').createExplorerLink const actions = require('../actions') const addressSummary = require('../util').addressSummary diff --git a/ui/app/components/transaction-list-item.js b/ui/app/components/transaction-list-item.js index a9961f47c..891d5e227 100644 --- a/ui/app/components/transaction-list-item.js +++ b/ui/app/components/transaction-list-item.js @@ -4,7 +4,7 @@ const inherits = require('util').inherits const EthBalance = require('./eth-balance') const addressSummary = require('../util').addressSummary -const explorerLink = require('../../lib/explorer-link') +const explorerLink = require('etherscan-link').createExplorerLink const CopyButton = require('./copyButton') const vreme = new (require('vreme'))() const Tooltip = require('./tooltip') diff --git a/ui/lib/account-link.js b/ui/lib/account-link.js deleted file mode 100644 index 037d990fa..000000000 --- a/ui/lib/account-link.js +++ /dev/null @@ -1,26 +0,0 @@ -module.exports = function (address, network) { - const net = parseInt(network) - let link - switch (net) { - case 1: // main net - link = `https://etherscan.io/address/${address}` - break - case 2: // morden test net - link = `https://morden.etherscan.io/address/${address}` - break - case 3: // ropsten test net - link = `https://ropsten.etherscan.io/address/${address}` - break - case 4: // rinkeby test net - link = `https://rinkeby.etherscan.io/address/${address}` - break - case 42: // kovan test net - link = `https://kovan.etherscan.io/address/${address}` - break - default: - link = '' - break - } - - return link -} diff --git a/ui/lib/explorer-link.js b/ui/lib/explorer-link.js deleted file mode 100644 index 3b82ecd5f..000000000 --- a/ui/lib/explorer-link.js +++ /dev/null @@ -1,6 +0,0 @@ -const prefixForNetwork = require('./etherscan-prefix-for-network') - -module.exports = function (hash, network) { - const prefix = prefixForNetwork(network) - return `http://${prefix}etherscan.io/tx/${hash}` -} From c77bc5d408b3717cb6de66e7458bcd888c526958 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 16 Oct 2017 04:12:51 -0700 Subject: [PATCH 23/96] Bump version on eth-simple-keyring Fixes bug where imported accounts could not use the new `signTypedData` method. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2b7b2056a..1dee72a1d 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "eth-query": "^2.1.2", "eth-rpc-client": "^1.1.3", "eth-sig-util": "^1.4.0", - "eth-simple-keyring": "^1.1.1", + "eth-simple-keyring": "^1.2.0", "eth-token-tracker": "^1.1.4", "ethereumjs-tx": "^1.3.0", "ethereumjs-util": "github:ethereumjs/ethereumjs-util#ac5d0908536b447083ea422b435da27f26615de9", From 10011395f6ab3f0784751dc7dc23074feac35b29 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 16 Oct 2017 04:14:09 -0700 Subject: [PATCH 24/96] Bump changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65f75ee18..11a093621 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Current Master - Fix bug where web3 API was sometimes injected after the page loaded. +- Fix bug where imported accounts could not use new eth_signTypedData method. ## 3.11.0 2017-10-11 From ad970180f2ca00e76302f97a532341502e990e38 Mon Sep 17 00:00:00 2001 From: Daijiro Wachi Date: Tue, 17 Oct 2017 22:03:40 +0200 Subject: [PATCH 25/96] Increase line-height for account nicknames --- ui/app/account-detail.js | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/app/account-detail.js b/ui/app/account-detail.js index a844daf88..d4f707e0b 100644 --- a/ui/app/account-detail.js +++ b/ui/app/account-detail.js @@ -121,6 +121,7 @@ AccountDetailScreen.prototype.render = function () { overflow: 'hidden', textOverflow: 'ellipsis', padding: '5px 0px', + lineHeight: '25px', }, }, [ identity && identity.name, From ab31eb6a17f5ab230fe47df66344cbce59223306 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 17 Oct 2017 13:09:41 -0700 Subject: [PATCH 26/96] Select first account on new vault creation --- app/scripts/metamask-controller.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index a742f3cba..2a45e413b 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -336,7 +336,7 @@ module.exports = class MetamaskController extends EventEmitter { // KeyringController setLocked: nodeify(keyringController.setLocked, keyringController), - createNewVaultAndKeychain: nodeify(keyringController.createNewVaultAndKeychain, keyringController), + createNewVaultAndKeychain: this.createNewVaultAndKeychain.bind(this), createNewVaultAndRestore: nodeify(keyringController.createNewVaultAndRestore, keyringController), addNewKeyring: nodeify(keyringController.addNewKeyring, keyringController), saveAccountLabel: nodeify(keyringController.saveAccountLabel, keyringController), @@ -458,6 +458,17 @@ module.exports = class MetamaskController extends EventEmitter { // Vault Management // + createNewVaultAndKeychain (password, cb) { + this.keyringController.createNewVaultAndKeychain(password) + .then((vault) => { + const { identities } = vault + const address = Object.keys(identities)[0] + this.preferencesController.setSelectedAddress(address) + cb(null, vault) + }) + .catch(reason => cb(reason)) + } + submitPassword (password, cb) { return this.keyringController.submitPassword(password) .then((newState) => { cb(null, newState) }) From d7f384485d2af15ec694208b9ef068c18c7dc91d Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 17 Oct 2017 13:19:57 -0700 Subject: [PATCH 27/96] Select first account when restoring seed Fixes #2348 --- app/scripts/metamask-controller.js | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 2a45e413b..8a51fdd8d 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -337,7 +337,7 @@ module.exports = class MetamaskController extends EventEmitter { // KeyringController setLocked: nodeify(keyringController.setLocked, keyringController), createNewVaultAndKeychain: this.createNewVaultAndKeychain.bind(this), - createNewVaultAndRestore: nodeify(keyringController.createNewVaultAndRestore, keyringController), + createNewVaultAndRestore: this.createNewVaultAndRestore.bind(this), addNewKeyring: nodeify(keyringController.addNewKeyring, keyringController), saveAccountLabel: nodeify(keyringController.saveAccountLabel, keyringController), exportAccount: nodeify(keyringController.exportAccount, keyringController), @@ -461,14 +461,28 @@ module.exports = class MetamaskController extends EventEmitter { createNewVaultAndKeychain (password, cb) { this.keyringController.createNewVaultAndKeychain(password) .then((vault) => { - const { identities } = vault - const address = Object.keys(identities)[0] + this.selectFirstIdentity(vault) this.preferencesController.setSelectedAddress(address) cb(null, vault) }) .catch(reason => cb(reason)) } + createNewVaultAndRestore (password, seed, cb) { + this.keyringController.createNewVaultAndRestore(password, seed) + .then((vault) => { + this.selectFirstIdentity(vault) + cb(null, vault) + }) + .catch(reason => cb(reason)) + } + + selectFirstIdentity (vault) { + const { identities } = vault + const address = Object.keys(identities)[0] + this.preferencesController.setSelectedAddress(address) + } + submitPassword (password, cb) { return this.keyringController.submitPassword(password) .then((newState) => { cb(null, newState) }) From 50e8599988c54bbf9ee0e9f324f79f5835fa6727 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 17 Oct 2017 13:25:27 -0700 Subject: [PATCH 28/96] Promisify metamask-controller vault creating methods --- app/scripts/metamask-controller.js | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 8a51fdd8d..4b11f6024 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -336,8 +336,8 @@ module.exports = class MetamaskController extends EventEmitter { // KeyringController setLocked: nodeify(keyringController.setLocked, keyringController), - createNewVaultAndKeychain: this.createNewVaultAndKeychain.bind(this), - createNewVaultAndRestore: this.createNewVaultAndRestore.bind(this), + createNewVaultAndKeychain: nodeify(this.createNewVaultAndKeychain, this), + createNewVaultAndRestore: nodeify(this.createNewVaultAndRestore, this), addNewKeyring: nodeify(keyringController.addNewKeyring, keyringController), saveAccountLabel: nodeify(keyringController.saveAccountLabel, keyringController), exportAccount: nodeify(keyringController.exportAccount, keyringController), @@ -458,23 +458,16 @@ module.exports = class MetamaskController extends EventEmitter { // Vault Management // - createNewVaultAndKeychain (password, cb) { - this.keyringController.createNewVaultAndKeychain(password) - .then((vault) => { - this.selectFirstIdentity(vault) - this.preferencesController.setSelectedAddress(address) - cb(null, vault) - }) - .catch(reason => cb(reason)) + async createNewVaultAndKeychain (password, cb) { + const vault = await this.keyringController.createNewVaultAndKeychain(password) + this.selectFirstIdentity(vault) + return vault } - createNewVaultAndRestore (password, seed, cb) { - this.keyringController.createNewVaultAndRestore(password, seed) - .then((vault) => { - this.selectFirstIdentity(vault) - cb(null, vault) - }) - .catch(reason => cb(reason)) + async createNewVaultAndRestore (password, seed, cb) { + const vault = await this.keyringController.createNewVaultAndRestore(password, seed) + this.selectFirstIdentity(vault) + return vault } selectFirstIdentity (vault) { From 9c45af3e2567e2afab9162f27fd4919cfa0957c5 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 17 Oct 2017 13:26:16 -0700 Subject: [PATCH 29/96] Bump changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65f75ee18..1c82c59be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Current Master - Fix bug where web3 API was sometimes injected after the page loaded. +- Fix bug where first account was sometimes not selected correctly after creating or restoring a vault. ## 3.11.0 2017-10-11 From 7032edf32b43e94a7f58c7bcb068da63fa6bda1b Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 18 Oct 2017 11:13:14 -0700 Subject: [PATCH 30/96] Stop tracking old account balances after restore vault Per @kgserrano note --- app/scripts/metamask-controller.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 4b11f6024..b6a3749e4 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -459,17 +459,30 @@ module.exports = class MetamaskController extends EventEmitter { // async createNewVaultAndKeychain (password, cb) { + this.forgetOldAccounts() const vault = await this.keyringController.createNewVaultAndKeychain(password) this.selectFirstIdentity(vault) return vault } async createNewVaultAndRestore (password, seed, cb) { + this.forgetOldAccounts() const vault = await this.keyringController.createNewVaultAndRestore(password, seed) this.selectFirstIdentity(vault) return vault } + forgetOldAccounts () { + const { accountTracker } = this + let oldAccounts = [] + try { + oldAccounts = Object.keys(accountTracker.store.getState().accounts) + } catch (e) { + log.warn('Could not load old accounts to forget', e) + } + oldAccounts.forEach(addr => accountTracker.removeAccount(addr)) + } + selectFirstIdentity (vault) { const { identities } = vault const address = Object.keys(identities)[0] From ea79eca8eb19cf7ce375e03ad8cbde010299936c Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 18 Oct 2017 12:21:22 -0700 Subject: [PATCH 31/96] Add validation to balance constructor --- app/scripts/controllers/balance.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/scripts/controllers/balance.js b/app/scripts/controllers/balance.js index 4fa4c78fe..f83f294cc 100644 --- a/app/scripts/controllers/balance.js +++ b/app/scripts/controllers/balance.js @@ -5,7 +5,9 @@ const BN = require('ethereumjs-util').BN class BalanceController { constructor (opts = {}) { + this._validateParams(opts) const { address, accountTracker, txController, blockTracker } = opts + this.address = address this.accountTracker = accountTracker this.txController = txController @@ -65,6 +67,14 @@ class BalanceController { return pending } + _validateParams (opts) { + const { address, accountTracker, txController, blockTracker } = opts + if (!address || !accountTracker || !txController || !blockTracker) { + const error = 'Cannot construct a balance checker without address, accountTracker, txController, and blockTracker.' + throw new Error(error) + } + } + } module.exports = BalanceController From 9cc1e8a6d867b7f0663c55b017b471132f6a719e Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 18 Oct 2017 14:22:04 -0700 Subject: [PATCH 32/96] Refresh computed balances controller when restoring vault --- app/scripts/controllers/computed-balances.js | 4 ++++ app/scripts/metamask-controller.js | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/scripts/controllers/computed-balances.js b/app/scripts/controllers/computed-balances.js index 2479e1b3a..3479eae2b 100644 --- a/app/scripts/controllers/computed-balances.js +++ b/app/scripts/controllers/computed-balances.js @@ -25,6 +25,10 @@ class ComputedbalancesController { } } + forgetAllBalances () { + this.balances = {} + } + _initBalanceUpdating () { const store = this.accountTracker.store.getState() this.addAnyAccountsFromStore(store) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index b6a3749e4..b312106dd 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -473,7 +473,7 @@ module.exports = class MetamaskController extends EventEmitter { } forgetOldAccounts () { - const { accountTracker } = this + const { accountTracker, balancesController } = this let oldAccounts = [] try { oldAccounts = Object.keys(accountTracker.store.getState().accounts) @@ -481,6 +481,7 @@ module.exports = class MetamaskController extends EventEmitter { log.warn('Could not load old accounts to forget', e) } oldAccounts.forEach(addr => accountTracker.removeAccount(addr)) + balancesController.forgetAllBalances() } selectFirstIdentity (vault) { From 75177ce34cac589be26fb8089aac04feccdbae81 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 18 Oct 2017 15:08:34 -0700 Subject: [PATCH 33/96] Make account tracking more reactive We were doing a lot of conditional observation & updating. Pulled out a bunch of that for generic observer/syncers. --- app/scripts/controllers/computed-balances.js | 22 +++++++++++++------- app/scripts/lib/account-tracker.js | 18 ++++++++++++++++ app/scripts/metamask-controller.js | 22 +------------------- 3 files changed, 33 insertions(+), 29 deletions(-) diff --git a/app/scripts/controllers/computed-balances.js b/app/scripts/controllers/computed-balances.js index 3479eae2b..009405d29 100644 --- a/app/scripts/controllers/computed-balances.js +++ b/app/scripts/controllers/computed-balances.js @@ -25,22 +25,28 @@ class ComputedbalancesController { } } - forgetAllBalances () { - this.balances = {} - } - _initBalanceUpdating () { const store = this.accountTracker.store.getState() - this.addAnyAccountsFromStore(store) - this.accountTracker.store.subscribe(this.addAnyAccountsFromStore.bind(this)) + this.syncAllAccountsFromStore(store) + this.accountTracker.store.subscribe(this.syncAllAccountsFromStore.bind(this)) } - addAnyAccountsFromStore(store) { - const balances = store.accounts + syncAllAccountsFromStore(store) { + const upstream = Object.keys(store.accounts) + const balances = Object.keys(this.balances) + .map(address => this.balances[address]) + // Follow new addresses for (let address in balances) { this.trackAddressIfNotAlready(address) } + + // Unfollow old ones + balances.forEach(({ address }) => { + if (!upstream.includes(address)) { + delete this.balances[address] + } + }) } trackAddressIfNotAlready (address) { diff --git a/app/scripts/lib/account-tracker.js b/app/scripts/lib/account-tracker.js index cdc21282d..13dea918f 100644 --- a/app/scripts/lib/account-tracker.js +++ b/app/scripts/lib/account-tracker.js @@ -38,6 +38,24 @@ class AccountTracker extends EventEmitter { // public // + syncWithAddresses (addresses) { + const accounts = this.store.getState().accounts + const locals = Object.keys(accounts) + .map(account => accounts[account.address]) + + addresses.forEach((upstream) => { + if (!locals.includes(upstream)) { + this.addAccount(upstream) + } + }) + + locals.forEach((local) => { + if (!addresses.includes(local)) { + this.removeAccount(local) + } + }) + } + addAccount (address) { const accounts = this.store.getState().accounts accounts[address] = {} diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index b312106dd..eae4478b5 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -123,13 +123,7 @@ module.exports = class MetamaskController extends EventEmitter { const address = addresses[0] this.preferencesController.setSelectedAddress(address) } - }) - this.keyringController.on('newAccount', (address) => { - this.preferencesController.setSelectedAddress(address) - this.accountTracker.addAccount(address) - }) - this.keyringController.on('removedAccount', (address) => { - this.accountTracker.removeAccount(address) + this.accountTracker.syncWithAddresses(addresses) }) // address book controller @@ -459,31 +453,17 @@ module.exports = class MetamaskController extends EventEmitter { // async createNewVaultAndKeychain (password, cb) { - this.forgetOldAccounts() const vault = await this.keyringController.createNewVaultAndKeychain(password) this.selectFirstIdentity(vault) return vault } async createNewVaultAndRestore (password, seed, cb) { - this.forgetOldAccounts() const vault = await this.keyringController.createNewVaultAndRestore(password, seed) this.selectFirstIdentity(vault) return vault } - forgetOldAccounts () { - const { accountTracker, balancesController } = this - let oldAccounts = [] - try { - oldAccounts = Object.keys(accountTracker.store.getState().accounts) - } catch (e) { - log.warn('Could not load old accounts to forget', e) - } - oldAccounts.forEach(addr => accountTracker.removeAccount(addr)) - balancesController.forgetAllBalances() - } - selectFirstIdentity (vault) { const { identities } = vault const address = Object.keys(identities)[0] From 8da0d0b28a52d476da3623774159e8d6a595da2d Mon Sep 17 00:00:00 2001 From: kumavis Date: Wed, 18 Oct 2017 15:09:32 -0700 Subject: [PATCH 34/96] Revert "NetworkController refactor for new EthClient interface" --- app/scripts/controllers/network.js | 54 ++++++++++---------- app/scripts/controllers/transactions.js | 1 - app/scripts/lib/events-proxy.js | 10 ++-- app/scripts/lib/nonce-tracker.js | 10 ++-- app/scripts/lib/obj-proxy.js | 19 ------- app/scripts/metamask-controller.js | 68 ++++++++++++------------- package.json | 2 - test/integration/lib/first-time.js | 3 -- test/unit/network-contoller-test.js | 25 +++++++-- test/unit/nonce-tracker-test.js | 7 ++- ui/app/app.js | 2 +- 11 files changed, 96 insertions(+), 105 deletions(-) delete mode 100644 app/scripts/lib/obj-proxy.js diff --git a/app/scripts/controllers/network.js b/app/scripts/controllers/network.js index 64ed4b7c2..0f9db4d53 100644 --- a/app/scripts/controllers/network.js +++ b/app/scripts/controllers/network.js @@ -1,12 +1,11 @@ const assert = require('assert') const EventEmitter = require('events') +const createMetamaskProvider = require('web3-provider-engine/zero.js') const ObservableStore = require('obs-store') const ComposedStore = require('obs-store/lib/composed') const extend = require('xtend') const EthQuery = require('eth-query') -const createEthRpcClient = require('eth-rpc-client') const createEventEmitterProxy = require('../lib/events-proxy.js') -const createObjectProxy = require('../lib/obj-proxy.js') const RPC_ADDRESS_LIST = require('../config.js').network const DEFAULT_RPC = RPC_ADDRESS_LIST['rinkeby'] @@ -18,8 +17,7 @@ module.exports = class NetworkController extends EventEmitter { this.networkStore = new ObservableStore('loading') this.providerStore = new ObservableStore(config.provider) this.store = new ComposedStore({ provider: this.providerStore, network: this.networkStore }) - this.providerProxy = createObjectProxy() - this.blockTrackerProxy = createEventEmitterProxy() + this._proxy = createEventEmitterProxy() this.on('networkDidChange', this.lookupNetwork) } @@ -27,11 +25,12 @@ module.exports = class NetworkController extends EventEmitter { initializeProvider (_providerParams) { this._baseProviderParams = _providerParams const rpcUrl = this.getCurrentRpcAddress() - this._configureStandardClient({ rpcUrl }) - this.blockTrackerProxy.on('block', this._logBlock.bind(this)) - this.blockTrackerProxy.on('error', this.verifyNetwork.bind(this)) - this.ethQuery = new EthQuery(this.providerProxy) + this._configureStandardProvider({ rpcUrl }) + this._proxy.on('block', this._logBlock.bind(this)) + this._proxy.on('error', this.verifyNetwork.bind(this)) + this.ethQuery = new EthQuery(this._proxy) this.lookupNetwork() + return this._proxy } verifyNetwork () { @@ -77,10 +76,8 @@ module.exports = class NetworkController extends EventEmitter { assert(type !== 'rpc', `NetworkController.setProviderType - cannot connect by type "rpc"`) // skip if type already matches if (type === this.getProviderConfig().type) return - // lookup rpcTarget for typecreateMetamaskProvider const rpcTarget = this.getRpcAddressForType(type) assert(rpcTarget, `NetworkController - unknown rpc address for type "${type}"`) - // update connectioncreateMetamaskProvider this.providerStore.updateState({ type, rpcTarget }) this._switchNetwork({ rpcUrl: rpcTarget }) } @@ -100,29 +97,32 @@ module.exports = class NetworkController extends EventEmitter { _switchNetwork (providerParams) { this.setNetworkState('loading') - this._configureStandardClient(providerParams) + this._configureStandardProvider(providerParams) this.emit('networkDidChange') } - _configureStandardClient(_providerParams) { + _configureStandardProvider(_providerParams) { const providerParams = extend(this._baseProviderParams, _providerParams) - const client = createEthRpcClient(providerParams) - this._setClient(client) - } - - _setClient (newClient) { - // teardown old client - const oldClient = this._currentClient - if (oldClient) { - oldClient.blockTracker.stop() - // asyncEventEmitter lacks a "removeAllListeners" method - // oldClient.blockTracker.removeAllListeners - oldClient.blockTracker._events = {} + const provider = createMetamaskProvider(providerParams) + this._setProvider(provider) + } + + _setProvider (provider) { + // collect old block tracker events + const oldProvider = this._provider + let blockTrackerHandlers + if (oldProvider) { + // capture old block handlers + blockTrackerHandlers = oldProvider._blockTracker.proxyEventHandlers + // tear down + oldProvider.removeAllListeners() + oldProvider.stop() } + // override block tracler + provider._blockTracker = createEventEmitterProxy(provider._blockTracker, blockTrackerHandlers) // set as new provider - this._currentClient = newClient - this.providerProxy.setTarget(newClient.provider) - this.blockTrackerProxy.setTarget(newClient.blockTracker) + this._provider = provider + this._proxy.setTarget(provider) } _logBlock (block) { diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js index d46dee230..ef659a300 100644 --- a/app/scripts/controllers/transactions.js +++ b/app/scripts/controllers/transactions.js @@ -46,7 +46,6 @@ module.exports = class TransactionController extends EventEmitter { this.txStateManager.on('tx:status-update', this.emit.bind(this, 'tx:status-update')) this.nonceTracker = new NonceTracker({ provider: this.provider, - blockTracker: this.blockTracker, getPendingTransactions: this.txStateManager.getPendingTransactions.bind(this.txStateManager), getConfirmedTransactions: (address) => { return this.txStateManager.getFilteredTxList({ diff --git a/app/scripts/lib/events-proxy.js b/app/scripts/lib/events-proxy.js index 840b06b1a..d1199a278 100644 --- a/app/scripts/lib/events-proxy.js +++ b/app/scripts/lib/events-proxy.js @@ -1,5 +1,6 @@ -module.exports = function createEventEmitterProxy(eventEmitter, eventHandlers = {}) { +module.exports = function createEventEmitterProxy(eventEmitter, listeners) { let target = eventEmitter + const eventHandlers = listeners || {} const proxy = new Proxy({}, { get: (obj, name) => { // intercept listeners @@ -13,12 +14,9 @@ module.exports = function createEventEmitterProxy(eventEmitter, eventHandlers = return true }, }) - proxy.setTarget(eventEmitter) - return proxy - function setTarget (eventEmitter) { target = eventEmitter - // migrate eventHandlers + // migrate listeners Object.keys(eventHandlers).forEach((name) => { eventHandlers[name].forEach((handler) => target.on(name, handler)) }) @@ -28,4 +26,6 @@ module.exports = function createEventEmitterProxy(eventEmitter, eventHandlers = eventHandlers[name].push(handler) target.on(name, handler) } + if (listeners) proxy.setTarget(eventEmitter) + return proxy } \ No newline at end of file diff --git a/app/scripts/lib/nonce-tracker.js b/app/scripts/lib/nonce-tracker.js index 2af40a27f..0029ac953 100644 --- a/app/scripts/lib/nonce-tracker.js +++ b/app/scripts/lib/nonce-tracker.js @@ -4,9 +4,8 @@ const Mutex = require('await-semaphore').Mutex class NonceTracker { - constructor ({ provider, blockTracker, getPendingTransactions, getConfirmedTransactions }) { + constructor ({ provider, getPendingTransactions, getConfirmedTransactions }) { this.provider = provider - this.blockTracker = blockTracker this.ethQuery = new EthQuery(provider) this.getPendingTransactions = getPendingTransactions this.getConfirmedTransactions = getConfirmedTransactions @@ -54,7 +53,7 @@ class NonceTracker { } async _getCurrentBlock () { - const blockTracker = this.blockTracker + const blockTracker = this._getBlockTracker() const currentBlock = blockTracker.getCurrentBlock() if (currentBlock) return currentBlock return await Promise((reject, resolve) => { @@ -140,6 +139,11 @@ class NonceTracker { return { name: 'local', nonce: highest, details: { startPoint, highest } } } + // this is a hotfix for the fact that the blockTracker will + // change when the network changes + _getBlockTracker () { + return this.provider._blockTracker + } } module.exports = NonceTracker diff --git a/app/scripts/lib/obj-proxy.js b/app/scripts/lib/obj-proxy.js deleted file mode 100644 index 29ca1269f..000000000 --- a/app/scripts/lib/obj-proxy.js +++ /dev/null @@ -1,19 +0,0 @@ -module.exports = function createObjectProxy(obj) { - let target = obj - const proxy = new Proxy({}, { - get: (obj, name) => { - // intercept setTarget - if (name === 'setTarget') return setTarget - return target[name] - }, - set: (obj, name, value) => { - target[name] = value - return true - }, - }) - return proxy - - function setTarget (obj) { - target = obj - } -} \ No newline at end of file diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index a742f3cba..727f48f1c 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -81,24 +81,9 @@ module.exports = class MetamaskController extends EventEmitter { }) this.blacklistController.scheduleUpdates() - // rpc provider and block tracker - this.networkController.initializeProvider({ - scaffold: { - eth_syncing: false, - web3_clientVersion: `MetaMask/v${version}`, - }, - // account mgmt - getAccounts: nodeify(this.getAccounts, this), - // tx signing - processTransaction: nodeify(this.newTransaction, this), - // old style msg signing - processMessage: this.newUnsignedMessage.bind(this), - // personal_sign msg signing - processPersonalMessage: this.newUnsignedPersonalMessage.bind(this), - processTypedMessage: this.newUnsignedTypedMessage.bind(this), - }) - this.provider = this.networkController.providerProxy - this.blockTracker = this.networkController.blockTrackerProxy + // rpc provider + this.provider = this.initializeProvider() + this.blockTracker = this.provider._blockTracker // eth data query tools this.ethQuery = new EthQuery(this.provider) @@ -233,6 +218,36 @@ module.exports = class MetamaskController extends EventEmitter { // Constructor helpers // + initializeProvider () { + const providerOpts = { + static: { + eth_syncing: false, + web3_clientVersion: `MetaMask/v${version}`, + }, + // account mgmt + getAccounts: (cb) => { + const isUnlocked = this.keyringController.memStore.getState().isUnlocked + const result = [] + const selectedAddress = this.preferencesController.getSelectedAddress() + + // only show address if account is unlocked + if (isUnlocked && selectedAddress) { + result.push(selectedAddress) + } + cb(null, result) + }, + // tx signing + processTransaction: nodeify(async (txParams) => await this.txController.newUnapprovedTransaction(txParams), this), + // old style msg signing + processMessage: this.newUnsignedMessage.bind(this), + // personal_sign msg signing + processPersonalMessage: this.newUnsignedPersonalMessage.bind(this), + processTypedMessage: this.newUnsignedTypedMessage.bind(this), + } + const providerProxy = this.networkController.initializeProvider(providerOpts) + return providerProxy + } + initPublicConfigStore () { // get init state const publicConfigStore = new ObservableStore() @@ -468,18 +483,6 @@ module.exports = class MetamaskController extends EventEmitter { // Opinionated Keyring Management // - async getAccounts () { - const isUnlocked = this.keyringController.memStore.getState().isUnlocked - const result = [] - const selectedAddress = this.preferencesController.getSelectedAddress() - - // only show address if account is unlocked - if (isUnlocked && selectedAddress) { - result.push(selectedAddress) - } - return result - } - addNewAccount (cb) { const primaryKeyring = this.keyringController.getKeyringsByType('HD Key Tree')[0] if (!primaryKeyring) return cb(new Error('MetamaskController - No HD Key Tree found')) @@ -526,11 +529,6 @@ module.exports = class MetamaskController extends EventEmitter { // Identity Management // - // this function wrappper lets us pass the fn reference before txController is instantiated - async newTransaction (txParams) { - return await this.txController.newUnapprovedTransaction(txParams) - } - newUnsignedMessage (msgParams, cb) { const msgId = this.messageManager.addUnapprovedMessage(msgParams) this.sendUpdate() diff --git a/package.json b/package.json index d82766fb5..3843a8a41 100644 --- a/package.json +++ b/package.json @@ -71,11 +71,9 @@ "eth-contract-metadata": "^1.1.4", "eth-hd-keyring": "^1.2.1", "eth-json-rpc-filters": "^1.2.2", - "eth-json-rpc-middleware": "^1.4.3", "eth-keyring-controller": "^2.1.0", "eth-phishing-detect": "^1.1.4", "eth-query": "^2.1.2", - "eth-rpc-client": "^1.1.3", "eth-sig-util": "^1.4.0", "eth-simple-keyring": "^1.2.0", "eth-token-tracker": "^1.1.4", diff --git a/test/integration/lib/first-time.js b/test/integration/lib/first-time.js index ee49d0901..cedb14f6e 100644 --- a/test/integration/lib/first-time.js +++ b/test/integration/lib/first-time.js @@ -3,9 +3,6 @@ const PASSWORD = 'password123' QUnit.module('first time usage') QUnit.test('render init screen', (assert) => { - // intercept reload attempts - window.onbeforeunload = () => true - const done = assert.async() runFirstTimeUsageTest(assert).then(done).catch((err) => { assert.notOk(err, `Error was thrown: ${err.stack}`) diff --git a/test/unit/network-contoller-test.js b/test/unit/network-contoller-test.js index 42ca40c56..0b3b5adeb 100644 --- a/test/unit/network-contoller-test.js +++ b/test/unit/network-contoller-test.js @@ -14,15 +14,15 @@ describe('# Network Controller', function () { }, }) - networkController.initializeProvider(networkControllerProviderInit) + networkController.initializeProvider(networkControllerProviderInit, dummyProviderConstructor) }) describe('network', function () { describe('#provider', function () { it('provider should be updatable without reassignment', function () { - networkController.initializeProvider(networkControllerProviderInit) - const providerProxy = networkController.providerProxy - providerProxy.setTarget({ test: true }) - assert.ok(providerProxy.test) + networkController.initializeProvider(networkControllerProviderInit, dummyProviderConstructor) + const proxy = networkController._proxy + proxy.setTarget({ test: true, on: () => {} }) + assert.ok(proxy.test) }) }) describe('#getNetworkState', function () { @@ -66,4 +66,19 @@ describe('# Network Controller', function () { }) }) +function dummyProviderConstructor() { + return { + // provider + sendAsync: noop, + // block tracker + _blockTracker: {}, + start: noop, + stop: noop, + on: noop, + addListener: noop, + once: noop, + removeAllListeners: noop, + } +} + function noop() {} \ No newline at end of file diff --git a/test/unit/nonce-tracker-test.js b/test/unit/nonce-tracker-test.js index 77af2a21c..8970cf84d 100644 --- a/test/unit/nonce-tracker-test.js +++ b/test/unit/nonce-tracker-test.js @@ -190,13 +190,12 @@ function generateNonceTrackerWith (pending, confirmed, providerStub = '0x0') { providerResultStub.result = providerStub const provider = { sendAsync: (_, cb) => { cb(undefined, providerResultStub) }, - } - const blockTracker = { - getCurrentBlock: () => '0x11b568', + _blockTracker: { + getCurrentBlock: () => '0x11b568', + }, } return new NonceTracker({ provider, - blockTracker, getPendingTransactions, getConfirmedTransactions, }) diff --git a/ui/app/app.js b/ui/app/app.js index 30d3766ab..613577913 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -319,7 +319,7 @@ App.prototype.renderNetworkDropdown = function () { [ h('i.fa.fa-question-circle.fa-lg.menu-icon'), 'Localhost 8545', - providerType === 'localhost' ? h('.check', '✓') : null, + activeNetwork === 'http://localhost:8545' ? h('.check', '✓') : null, ] ), From d89394a7c9a5139ed5708ce7022fbbe2809e612a Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 18 Oct 2017 17:07:22 -0700 Subject: [PATCH 35/96] Make account tracking much more reactive --- app/scripts/lib/account-tracker.js | 11 ++++++++--- app/scripts/metamask-controller.js | 14 +++++--------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/app/scripts/lib/account-tracker.js b/app/scripts/lib/account-tracker.js index 13dea918f..b9959dc25 100644 --- a/app/scripts/lib/account-tracker.js +++ b/app/scripts/lib/account-tracker.js @@ -41,19 +41,24 @@ class AccountTracker extends EventEmitter { syncWithAddresses (addresses) { const accounts = this.store.getState().accounts const locals = Object.keys(accounts) - .map(account => accounts[account.address]) + const toAdd = [] addresses.forEach((upstream) => { if (!locals.includes(upstream)) { - this.addAccount(upstream) + toAdd.push(upstream) } }) + const toRemove = [] locals.forEach((local) => { if (!addresses.includes(local)) { - this.removeAccount(local) + toRemove.push(local) } }) + + toAdd.forEach(upstream => this.addAccount(upstream)) + toRemove.forEach(local=> this.removeAccount(local)) + this._updateAccounts() } addAccount (address) { diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index eae4478b5..11a26df64 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -117,8 +117,10 @@ module.exports = class MetamaskController extends EventEmitter { }) // If only one account exists, make sure it is selected. - this.keyringController.store.subscribe((state) => { - const addresses = Object.keys(state.walletNicknames || {}) + this.keyringController.memStore.subscribe((state) => { + const addresses = state.keyrings.reduce((res, keyring) => { + return res.concat(keyring.accounts) + }, []) if (addresses.length === 1) { const address = addresses[0] this.preferencesController.setSelectedAddress(address) @@ -314,7 +316,7 @@ module.exports = class MetamaskController extends EventEmitter { importAccountWithStrategy: this.importAccountWithStrategy.bind(this), // vault management - submitPassword: this.submitPassword.bind(this), + submitPassword: nodeify(keyringController.submitPassword, keyringController), // network management setProviderType: nodeify(networkController.setProviderType, networkController), @@ -470,12 +472,6 @@ module.exports = class MetamaskController extends EventEmitter { this.preferencesController.setSelectedAddress(address) } - submitPassword (password, cb) { - return this.keyringController.submitPassword(password) - .then((newState) => { cb(null, newState) }) - .catch((reason) => { cb(reason) }) - } - // // Opinionated Keyring Management // From 21bde66e16c3a41a1cb8fca5e9e9e3e97875d23b Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 18 Oct 2017 17:14:26 -0700 Subject: [PATCH 36/96] Remove account-tracker from keyringController --- app/scripts/metamask-controller.js | 1 - package.json | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index dd34cec97..366bb6d98 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -96,7 +96,6 @@ module.exports = class MetamaskController extends EventEmitter { // key mgmt this.keyringController = new KeyringController({ initState: initState.KeyringController, - accountTracker: this.accountTracker, getNetwork: this.networkController.getNetworkState.bind(this.networkController), encryptor: opts.encryptor || undefined, }) diff --git a/package.json b/package.json index 3843a8a41..2e8faadee 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "eth-contract-metadata": "^1.1.4", "eth-hd-keyring": "^1.2.1", "eth-json-rpc-filters": "^1.2.2", - "eth-keyring-controller": "^2.1.0", + "eth-keyring-controller": "^2.1.1", "eth-phishing-detect": "^1.1.4", "eth-query": "^2.1.2", "eth-sig-util": "^1.4.0", From 4dc494fdd10603d023962fe3a3ff844c77c46d4c Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 18 Oct 2017 17:14:42 -0700 Subject: [PATCH 37/96] Fix bug that breaks ui dev mode --- ui/app/reducers.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ui/app/reducers.js b/ui/app/reducers.js index 6a2f44534..1cded7ca7 100644 --- a/ui/app/reducers.js +++ b/ui/app/reducers.js @@ -43,7 +43,12 @@ function rootReducer (state, action) { window.logState = function () { let state = window.METAMASK_CACHED_LOG_STATE - const version = global.platform.getVersion() + let version + try { + version = global.platform.getVersion() + } catch (e) { + version = 'unable to load version.' + } state.version = version let stateString = JSON.stringify(state, removeSeedWords, 2) return stateString From 209db9ae5fef7936e7bd5e1630afdf0e2422bbb8 Mon Sep 17 00:00:00 2001 From: kumavis Date: Wed, 18 Oct 2017 19:53:48 -0700 Subject: [PATCH 38/96] deps - bump eth-json-rpc-filters --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3843a8a41..91e64c10b 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "eth-block-tracker": "^2.2.0", "eth-contract-metadata": "^1.1.4", "eth-hd-keyring": "^1.2.1", - "eth-json-rpc-filters": "^1.2.2", + "eth-json-rpc-filters": "^1.2.3", "eth-keyring-controller": "^2.1.0", "eth-phishing-detect": "^1.1.4", "eth-query": "^2.1.2", From 942de9ba0269a7a7d35bdfcb154a087e86b12b2b Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 19 Oct 2017 09:47:48 -0700 Subject: [PATCH 39/96] Patch sandwich-expando for security update --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2e8faadee..81515741e 100644 --- a/package.json +++ b/package.json @@ -134,7 +134,7 @@ "redux-logger": "^3.0.6", "redux-thunk": "^2.2.0", "request-promise": "^4.2.1", - "sandwich-expando": "^1.0.5", + "sandwich-expando": "^1.1.3", "semaphore": "^1.0.5", "sw-stream": "^2.0.0", "textarea-caret": "^3.0.1", From 0ae406e489a635ea094913ee5c20d1e8f2165db5 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 19 Oct 2017 09:59:57 -0700 Subject: [PATCH 40/96] Allow computed balances to enumerate its own view --- app/scripts/controllers/computed-balances.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/scripts/controllers/computed-balances.js b/app/scripts/controllers/computed-balances.js index 009405d29..9855f715e 100644 --- a/app/scripts/controllers/computed-balances.js +++ b/app/scripts/controllers/computed-balances.js @@ -20,9 +20,10 @@ class ComputedbalancesController { } updateAllBalances () { - for (let address in this.accountTracker.store.getState().accounts) { + Object.keys(this.balances).forEach((balance) => { + const address = balance.address this.balances[address].updateBalance() - } + }) } _initBalanceUpdating () { From 3b4c679ffcd76279221bb7cb6b83c53f0468ee65 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 19 Oct 2017 12:15:26 -0700 Subject: [PATCH 41/96] Fix bug where new account was not immediately selected --- app/scripts/metamask-controller.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 366bb6d98..457d38e26 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -324,7 +324,7 @@ module.exports = class MetamaskController extends EventEmitter { createShapeShiftTx: this.createShapeShiftTx.bind(this), // primary HD keyring management - addNewAccount: this.addNewAccount.bind(this), + addNewAccount: nodeify(this.addNewAccount, this), placeSeedWords: this.placeSeedWords.bind(this), clearSeedWordCache: this.clearSeedWordCache.bind(this), importAccountWithStrategy: this.importAccountWithStrategy.bind(this), @@ -490,10 +490,21 @@ module.exports = class MetamaskController extends EventEmitter { // Opinionated Keyring Management // - addNewAccount (cb) { + async addNewAccount (cb) { const primaryKeyring = this.keyringController.getKeyringsByType('HD Key Tree')[0] if (!primaryKeyring) return cb(new Error('MetamaskController - No HD Key Tree found')) - promiseToCallback(this.keyringController.addNewAccount(primaryKeyring))(cb) + const keyringController = this.keyringController + const oldAccounts = await keyringController.getAccounts() + const keyState = await keyringController.addNewAccount(primaryKeyring) + const newAccounts = await keyringController.getAccounts() + + newAccounts.forEach((address) => { + if (!oldAccounts.includes(address)) { + this.preferencesController.setSelectedAddress(address) + } + }) + + return keyState } // Adds the current vault's seed words to the UI's state tree. From a10a600cced6273047f224c5e19d186de091efe0 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 19 Oct 2017 12:33:43 -0700 Subject: [PATCH 42/96] Linted --- app/scripts/lib/account-tracker.js | 2 +- app/scripts/metamask-controller.js | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/scripts/lib/account-tracker.js b/app/scripts/lib/account-tracker.js index b9959dc25..ce6642150 100644 --- a/app/scripts/lib/account-tracker.js +++ b/app/scripts/lib/account-tracker.js @@ -57,7 +57,7 @@ class AccountTracker extends EventEmitter { }) toAdd.forEach(upstream => this.addAccount(upstream)) - toRemove.forEach(local=> this.removeAccount(local)) + toRemove.forEach(local => this.removeAccount(local)) this._updateAccounts() } diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 457d38e26..ad42a39fb 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -1,6 +1,5 @@ const EventEmitter = require('events') const extend = require('xtend') -const promiseToCallback = require('promise-to-callback') const pump = require('pump') const Dnode = require('dnode') const ObservableStore = require('obs-store') From 69d8359639063fec86e115bbd1bfff277b0d749b Mon Sep 17 00:00:00 2001 From: Chi Kei Chan Date: Thu, 19 Oct 2017 17:48:45 -0700 Subject: [PATCH 43/96] Update eth-keyring-controller dependency --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7b1578f7b..167ca00ce 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "eth-contract-metadata": "^1.1.4", "eth-hd-keyring": "^1.2.1", "eth-json-rpc-filters": "^1.2.2", - "eth-keyring-controller": "^2.1.0", + "eth-keyring-controller": "^2.1.2", "eth-phishing-detect": "^1.1.4", "eth-query": "^2.1.2", "eth-sig-util": "^1.4.0", From 5c902423d9f20699c636d8291b6a5f5071aeae85 Mon Sep 17 00:00:00 2001 From: frankiebee Date: Fri, 20 Oct 2017 02:30:46 -0700 Subject: [PATCH 44/96] mascara - set x-frame-options header to DENY --- mascara/server/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mascara/server/index.js b/mascara/server/index.js index 12b527e5d..24739b43f 100644 --- a/mascara/server/index.js +++ b/mascara/server/index.js @@ -17,7 +17,7 @@ function createMetamascaraServer () { const server = express() // ui window serveBundle(server, '/ui.js', uiBundle) - server.use(express.static(__dirname + '/../ui/')) + server.use(express.static(__dirname + '/../ui/', { setHeaders: (res) => res.set('X-Frame-Options', 'DENY') })) server.use(express.static(__dirname + '/../../dist/chrome')) // metamascara serveBundle(server, '/metamascara.js', metamascaraBundle) From 8472ca406a78468199a18c173ac8c9317dee8518 Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 20 Oct 2017 12:29:12 -0700 Subject: [PATCH 45/96] deps - bump eth-json-rpc-filters for log filter fix --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 91e64c10b..0ecd22db5 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "eth-block-tracker": "^2.2.0", "eth-contract-metadata": "^1.1.4", "eth-hd-keyring": "^1.2.1", - "eth-json-rpc-filters": "^1.2.3", + "eth-json-rpc-filters": "^1.2.4", "eth-keyring-controller": "^2.1.0", "eth-phishing-detect": "^1.1.4", "eth-query": "^2.1.2", From a71c95b98744ce64f1863ea0ae4e4d450e0a606c Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 20 Oct 2017 12:32:15 -0700 Subject: [PATCH 46/96] changelog entry for filter fix --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b24cc161a..f000244ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Current Master +- Fix bug where log filters were not populated correctly - Fix bug where web3 API was sometimes injected after the page loaded. - Fix bug where first account was sometimes not selected correctly after creating or restoring a vault. - Fix bug where imported accounts could not use new eth_signTypedData method. From 56dcf7c011f48c5f49e5339c9d36527d1f68cf93 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 20 Oct 2017 14:07:54 -0700 Subject: [PATCH 47/96] Version 3.11.1 --- CHANGELOG.md | 2 ++ app/manifest.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f000244ca..bbbc2e3be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Current Master +## 3.11.1 2017-10-20 + - Fix bug where log filters were not populated correctly - Fix bug where web3 API was sometimes injected after the page loaded. - Fix bug where first account was sometimes not selected correctly after creating or restoring a vault. diff --git a/app/manifest.json b/app/manifest.json index a0f449c68..c30d90dfa 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -1,7 +1,7 @@ { "name": "MetaMask", "short_name": "Metamask", - "version": "3.11.0", + "version": "3.11.1", "manifest_version": 2, "author": "https://metamask.io", "description": "Ethereum Browser Extension", From 0264ecaad77330b151f4bf4248b66f4659a67cce Mon Sep 17 00:00:00 2001 From: Jacky Chan Date: Fri, 18 Aug 2017 04:11:26 -0700 Subject: [PATCH 48/96] Adding CreatePasswordScreen --- .babelrc | 2 +- mascara/server/util.js | 2 +- .../app/first-time/create-password-screen.js | 13 +++++ mascara/src/app/first-time/index.js | 54 +++++++++++++++++++ ui/app/app.js | 24 +++++++++ ui/app/reducers/metamask.js | 2 + 6 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 mascara/src/app/first-time/create-password-screen.js create mode 100644 mascara/src/app/first-time/index.js diff --git a/.babelrc b/.babelrc index bca3364fc..307583ffd 100644 --- a/.babelrc +++ b/.babelrc @@ -1,4 +1,4 @@ { - "presets": ["es2015", "stage-0"], + "presets": ["es2015", "stage-0", "react"], "plugins": ["transform-runtime", "transform-async-to-generator"] } diff --git a/mascara/server/util.js b/mascara/server/util.js index 6ab41b729..af2daddb9 100644 --- a/mascara/server/util.js +++ b/mascara/server/util.js @@ -23,7 +23,7 @@ function createBundle (entryPoint) { cache: {}, packageCache: {}, plugin: [watchify], - }) + }).transform('babelify') bundler.on('update', bundle) bundle() diff --git a/mascara/src/app/first-time/create-password-screen.js b/mascara/src/app/first-time/create-password-screen.js new file mode 100644 index 000000000..afb1ad8f6 --- /dev/null +++ b/mascara/src/app/first-time/create-password-screen.js @@ -0,0 +1,13 @@ +import React, {Component, PropTypes} from 'react' + +export default class CreatePasswordScreen extends Component { + + render() { + return ( +
+ +
+ ) + } + +} \ No newline at end of file diff --git a/mascara/src/app/first-time/index.js b/mascara/src/app/first-time/index.js new file mode 100644 index 000000000..a2cb8d71c --- /dev/null +++ b/mascara/src/app/first-time/index.js @@ -0,0 +1,54 @@ +import React, {Component, PropTypes} from 'react' +import CreatePasswordScreen from './create-password-screen' + +export default class FirstTimeFlow extends Component { + + static propTypes = { + screenType: PropTypes.string + }; + + static defaultProps = { + screenType: FirstTimeFlow.CREATE_PASSWORD + }; + + static SCREEN_TYPE = { + CREATE_PASSWORD: 'create_password', + UNIQUE_IMAGE: 'unique_image', + TERM_OF_USE: 'term_of_use', + BACK_UP_PHRASE: 'back_up_phrase', + CONFIRM_BACK_UP_PHRASE: 'confirm_back_up_phrase', + BUY_ETHER: 'buy_ether' + }; + + static getScreenType = ({isInitialized, noActiveNotices, seedWords}) => { + const {SCREEN_TYPE} = FirstTimeFlow + + if (!isInitialized) { + return SCREEN_TYPE.CREATE_PASSWORD + } + + if (!noActiveNotices) { + return SCREEN_TYPE.TERM_OF_USE + } + + if (seedWords) { + return SCREEN_TYPE.BACK_UP_PHRASE + } + }; + + renderScreen() { + const {SCREEN_TYPE} = FirstTimeFlow + + switch (this.props.screenType) { + case SCREEN_TYPE.CREATE_PASSWORD: + return + default: + return