From b17a799de6b04e6971eb3a33e7baa76f8236cad9 Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 23 Oct 2017 12:10:49 -0700 Subject: Update network.js --- app/scripts/controllers/network.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/scripts') diff --git a/app/scripts/controllers/network.js b/app/scripts/controllers/network.js index 7f0cbd379..2259efaeb 100644 --- a/app/scripts/controllers/network.js +++ b/app/scripts/controllers/network.js @@ -53,7 +53,7 @@ module.exports = class NetworkController extends EventEmitter { lookupNetwork () { // Prevent firing when provider is not defined. if (!this.ethQuery || !this.ethQuery.sendAsync) { - return + return console.warn('NetworkController - lookupNetwork aborted due to missing ethQuery') } this.ethQuery.sendAsync({ method: 'net_version' }, (err, network) => { if (err) return this.setNetworkState('loading') -- cgit v1.2.3 From 5ce94e69b311428d9d2f9b5502c02d3b960e380e Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 31 Oct 2017 09:59:26 -0700 Subject: Add useful error when duplicate web3 is detected. Fixes #2507 --- app/scripts/inpage.js | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'app/scripts') diff --git a/app/scripts/inpage.js b/app/scripts/inpage.js index b6889b00f..9261e7d64 100644 --- a/app/scripts/inpage.js +++ b/app/scripts/inpage.js @@ -31,6 +31,13 @@ var inpageProvider = new MetamaskInpageProvider(metamaskStream) // setup web3 // +if (typeof window.web3 !== 'undefined') { + throw new Error(`MetaMask detected another web3. + MetaMask will not work reliably with another web3 extension. + This usually happens if you have two MetaMasks installed, + or MetaMask and another web3 extension. Please remove one + and try again.`) +} var web3 = new Web3(inpageProvider) web3.setProvider = function () { log.debug('MetaMask - overrode web3.setProvider') -- cgit v1.2.3 From c0aa7ce8574c6a88c348d86c71678fcd24ac168d Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 20 Nov 2017 13:27:29 -0800 Subject: Add reproduction and mutex code --- app/scripts/metamask-controller.js | 39 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) (limited to 'app/scripts') diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 968589f6e..69cf65554 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -31,6 +31,7 @@ const ConfigManager = require('./lib/config-manager') const nodeify = require('./lib/nodeify') const accountImporter = require('./account-import-strategies') const getBuyEthUrl = require('./lib/buy-eth-url') +const Mutex = require('await-semaphore').Mutex const version = require('../manifest.json').version module.exports = class MetamaskController extends EventEmitter { @@ -38,6 +39,10 @@ module.exports = class MetamaskController extends EventEmitter { constructor (opts) { super() + this.createVaultRequestStart = [] + this.createVaultRequestEnd = [] + this.createVaultMutex = new Mutex() + this.sendUpdate = debounce(this.privateSendUpdate.bind(this), 200) this.opts = opts @@ -467,15 +472,45 @@ module.exports = class MetamaskController extends EventEmitter { // Vault Management // - async createNewVaultAndKeychain (password, cb) { + async createNewVaultAndKeychain (password) { + const release = await this.createVaultMutex.acquire() + this.createVaultRequestStart.push(performance.now()) + this.createVaultRequestEnd.push(0); + const idx = this.createVaultRequestStart.length - 1; + if(idx === 1) { + await this.sleep(8000) + } const vault = await this.keyringController.createNewVaultAndKeychain(password) + if(idx === 0) { + //await this.sleep(3000) + } + console.log({ + "idx": idx, + "when": "before", + "obj": vault + }); this.selectFirstIdentity(vault) + console.log({ + "idx": idx, + "when": "after", + "obj": vault + }); + this.createVaultRequestEnd[idx] = performance.now() + console.log(this.createVaultRequestStart) + console.log(this.createVaultRequestEnd) + release() return vault } - async createNewVaultAndRestore (password, seed, cb) { + sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); + } + + async createNewVaultAndRestore (password, seed) { + const release = await this.createVaultMutex.acquire() const vault = await this.keyringController.createNewVaultAndRestore(password, seed) this.selectFirstIdentity(vault) + release() return vault } -- cgit v1.2.3 From 65cb9704872fe7730285f4185ea6bc1a2d28c149 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 20 Nov 2017 13:47:35 -0800 Subject: Prevent creation of multiple vaults Fixes #2577 by ensuring only one seed phrase can be set for a new vault. Also cleans up logs and reproduction logic. --- app/scripts/metamask-controller.js | 51 +++++++++++++++----------------------- 1 file changed, 20 insertions(+), 31 deletions(-) (limited to 'app/scripts') diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 69cf65554..a4c77e468 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -39,9 +39,6 @@ module.exports = class MetamaskController extends EventEmitter { constructor (opts) { super() - this.createVaultRequestStart = [] - this.createVaultRequestEnd = [] - this.createVaultMutex = new Mutex() this.sendUpdate = debounce(this.privateSendUpdate.bind(this), 200) @@ -54,6 +51,9 @@ module.exports = class MetamaskController extends EventEmitter { // observable state store this.store = new ObservableStore(initState) + // lock to ensure only one vault created at once + this.createVaultMutex = new Mutex() + // network store this.networkController = new NetworkController(initState.NetworkController) @@ -474,36 +474,25 @@ module.exports = class MetamaskController extends EventEmitter { async createNewVaultAndKeychain (password) { const release = await this.createVaultMutex.acquire() - this.createVaultRequestStart.push(performance.now()) - this.createVaultRequestEnd.push(0); - const idx = this.createVaultRequestStart.length - 1; - if(idx === 1) { - await this.sleep(8000) - } - const vault = await this.keyringController.createNewVaultAndKeychain(password) - if(idx === 0) { - //await this.sleep(3000) + let vault + + try { + const accounts = await this.keyringController.getAccounts() + + if (accounts.length > 0) { + vault = await this.keyringController.fullUpdate() + + } else { + let vault = await this.keyringController.createNewVaultAndKeychain(password) + this.selectFirstIdentity(vault) + } + release() + } catch (err) { + release() + throw err } - console.log({ - "idx": idx, - "when": "before", - "obj": vault - }); - this.selectFirstIdentity(vault) - console.log({ - "idx": idx, - "when": "after", - "obj": vault - }); - this.createVaultRequestEnd[idx] = performance.now() - console.log(this.createVaultRequestStart) - console.log(this.createVaultRequestEnd) - release() - return vault - } - sleep(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); + return vault } async createNewVaultAndRestore (password, seed) { -- cgit v1.2.3 From 634102df6331161c21593c0f8cab91a2093209e1 Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 27 Nov 2017 13:59:32 -1000 Subject: network controller - warn via log.warn --- app/scripts/controllers/network.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/scripts') diff --git a/app/scripts/controllers/network.js b/app/scripts/controllers/network.js index 2259efaeb..f3349d9a0 100644 --- a/app/scripts/controllers/network.js +++ b/app/scripts/controllers/network.js @@ -53,7 +53,7 @@ module.exports = class NetworkController extends EventEmitter { lookupNetwork () { // Prevent firing when provider is not defined. if (!this.ethQuery || !this.ethQuery.sendAsync) { - return console.warn('NetworkController - lookupNetwork aborted due to missing ethQuery') + return log.warn('NetworkController - lookupNetwork aborted due to missing ethQuery') } this.ethQuery.sendAsync({ method: 'net_version' }, (err, network) => { if (err) return this.setNetworkState('loading') -- cgit v1.2.3 From e89f82399f1c732c40dc644c496795833691cff0 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 28 Nov 2017 11:14:57 -0800 Subject: Add optional version field to notices Allows notices to only show to users who are on a certain version. --- app/scripts/metamask-controller.js | 11 +++++++++++ app/scripts/notice-controller.js | 10 +++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) (limited to 'app/scripts') diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index a4c77e468..c1b63449a 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -44,6 +44,7 @@ module.exports = class MetamaskController extends EventEmitter { this.opts = opts const initState = opts.initState || {} + this.recordFirstTimeInfo(initState) // platform-specific api this.platform = opts.platform @@ -149,6 +150,7 @@ module.exports = class MetamaskController extends EventEmitter { // notices this.noticeController = new NoticeController({ initState: initState.NoticeController, + version, }) this.noticeController.updateNoticesList() // to be uncommented when retrieving notices from a remote server. @@ -797,4 +799,13 @@ module.exports = class MetamaskController extends EventEmitter { return rpcTarget } + recordFirstTimeInfo (initState) { + if (!('firstTimeInfo' in initState)) { + initState.firstTimeInfo = { + version, + date: Date.now(), + } + } + } + } diff --git a/app/scripts/notice-controller.js b/app/scripts/notice-controller.js index 57aad40c5..bc545127e 100644 --- a/app/scripts/notice-controller.js +++ b/app/scripts/notice-controller.js @@ -1,4 +1,5 @@ const EventEmitter = require('events').EventEmitter +const semver = require('semver') const extend = require('xtend') const ObservableStore = require('obs-store') const hardCodedNotices = require('../../notices/notices.json') @@ -8,6 +9,7 @@ module.exports = class NoticeController extends EventEmitter { constructor (opts) { super() this.noticePoller = null + this.version = opts.version const initState = extend({ noticesList: [], }, opts.initState) @@ -51,7 +53,13 @@ module.exports = class NoticeController extends EventEmitter { } updateNoticesList () { - return this._retrieveNoticeData().then((newNotices) => { + return this._retrieveNoticeData().then((hardNotices) => { + const newNotices = hardNotices.filter((newNotice) => { + if ('version' in newNotice) { + return semver.satisfies(this.version, newNotice.version) + } + return true + }) var oldNotices = this.getNoticesList() var combinedNotices = this._mergeNotices(oldNotices, newNotices) return Promise.resolve(this.setNoticesList(combinedNotices)) -- cgit v1.2.3 From ec4b7de962d0c4913f8f65a21a6cbef9f2ebc261 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 28 Nov 2017 11:22:09 -0800 Subject: Add firstVersion field to notices selector --- app/scripts/metamask-controller.js | 1 + app/scripts/notice-controller.js | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'app/scripts') diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index c1b63449a..0c759010b 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -151,6 +151,7 @@ module.exports = class MetamaskController extends EventEmitter { this.noticeController = new NoticeController({ initState: initState.NoticeController, version, + firstVersion: initState.firstTimeInfo.version, }) this.noticeController.updateNoticesList() // to be uncommented when retrieving notices from a remote server. diff --git a/app/scripts/notice-controller.js b/app/scripts/notice-controller.js index bc545127e..457161ccb 100644 --- a/app/scripts/notice-controller.js +++ b/app/scripts/notice-controller.js @@ -9,7 +9,7 @@ module.exports = class NoticeController extends EventEmitter { constructor (opts) { super() this.noticePoller = null - this.version = opts.version + this.firstVersion = opts.firstVersion const initState = extend({ noticesList: [], }, opts.initState) @@ -58,6 +58,9 @@ module.exports = class NoticeController extends EventEmitter { if ('version' in newNotice) { return semver.satisfies(this.version, newNotice.version) } + if ('firstVersion' in newNotice) { + return semver.satisfies(this.firstVersion, newNotice.firstVersion) + } return true }) var oldNotices = this.getNoticesList() -- cgit v1.2.3 From f64bc3c01afa688ee4eb4ea681bca0d9a95f0300 Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 28 Nov 2017 11:09:18 -1000 Subject: deps - obs-store@3 + migrate stream plumbing --- app/scripts/background.js | 9 +++++---- app/scripts/lib/inpage-provider.js | 4 +++- app/scripts/metamask-controller.js | 3 ++- 3 files changed, 10 insertions(+), 6 deletions(-) (limited to 'app/scripts') diff --git a/app/scripts/background.js b/app/scripts/background.js index 3e560d302..da022c490 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -1,10 +1,11 @@ const urlUtil = require('url') const endOfStream = require('end-of-stream') -const pipe = require('pump') +const pump = require('pump') const log = require('loglevel') const extension = require('extensionizer') const LocalStorageStore = require('obs-store/lib/localStorage') const storeTransform = require('obs-store/lib/transform') +const asStream = require('obs-store/lib/asStream') const ExtensionPlatform = require('./platforms/extension') const Migrator = require('./lib/migrator/') const migrations = require('./migrations/') @@ -72,10 +73,10 @@ function setupController (initState) { global.metamaskController = controller // setup state persistence - pipe( - controller.store, + pump( + asStream(controller.store), storeTransform(versionifyData), - diskStore + asStream(diskStore) ) function versionifyData (state) { diff --git a/app/scripts/lib/inpage-provider.js b/app/scripts/lib/inpage-provider.js index da75c4be2..99cc5d2cf 100644 --- a/app/scripts/lib/inpage-provider.js +++ b/app/scripts/lib/inpage-provider.js @@ -3,6 +3,7 @@ const RpcEngine = require('json-rpc-engine') const createIdRemapMiddleware = require('json-rpc-engine/src/idRemapMiddleware') const createStreamMiddleware = require('json-rpc-middleware-stream') const LocalStorageStore = require('obs-store') +const asStream = require('obs-store/lib/asStream') const ObjectMultiplex = require('obj-multiplex') module.exports = MetamaskInpageProvider @@ -21,9 +22,10 @@ function MetamaskInpageProvider (connectionStream) { // subscribe to metamask public config (one-way) self.publicConfigStore = new LocalStorageStore({ storageKey: 'MetaMask-Config' }) + pump( mux.createStream('publicConfig'), - self.publicConfigStore, + asStream(self.publicConfigStore), (err) => logStreamDisconnectWarning('MetaMask PublicConfigStore', err) ) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index a4c77e468..f9b7b4182 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -3,6 +3,7 @@ const extend = require('xtend') const pump = require('pump') const Dnode = require('dnode') const ObservableStore = require('obs-store') +const asStream = require('obs-store/lib/asStream') const AccountTracker = require('./lib/account-tracker') const EthQuery = require('eth-query') const RpcEngine = require('json-rpc-engine') @@ -456,7 +457,7 @@ module.exports = class MetamaskController extends EventEmitter { setupPublicConfig (outStream) { pump( - this.publicConfigStore, + asStream(this.publicConfigStore), outStream, (err) => { if (err) log.error(err) -- cgit v1.2.3 From f0f6bb28e0e721c0f802354b72f02ed83afbb3b5 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 28 Nov 2017 15:16:04 -0800 Subject: Get notice version filtering working nicely --- app/scripts/metamask-controller.js | 1 + app/scripts/notice-controller.js | 51 ++++++++++++++++++++------------------ 2 files changed, 28 insertions(+), 24 deletions(-) (limited to 'app/scripts') diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 0c759010b..c9eb27fbf 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -45,6 +45,7 @@ module.exports = class MetamaskController extends EventEmitter { this.opts = opts const initState = opts.initState || {} this.recordFirstTimeInfo(initState) + opts.initState.firstTimeInfo.version = '4.5.0' // platform-specific api this.platform = opts.platform diff --git a/app/scripts/notice-controller.js b/app/scripts/notice-controller.js index 457161ccb..efad3e516 100644 --- a/app/scripts/notice-controller.js +++ b/app/scripts/notice-controller.js @@ -3,6 +3,7 @@ const semver = require('semver') const extend = require('xtend') const ObservableStore = require('obs-store') const hardCodedNotices = require('../../notices/notices.json') +const uniqBy = require('lodash.uniqby') module.exports = class NoticeController extends EventEmitter { @@ -10,6 +11,7 @@ module.exports = class NoticeController extends EventEmitter { super() this.noticePoller = null this.firstVersion = opts.firstVersion + this.version = opts.version const initState = extend({ noticesList: [], }, opts.initState) @@ -32,9 +34,9 @@ module.exports = class NoticeController extends EventEmitter { return unreadNotices[unreadNotices.length - 1] } - setNoticesList (noticesList) { + async setNoticesList (noticesList) { this.store.updateState({ noticesList }) - return Promise.resolve(true) + return true } markNoticeRead (noticeToMark, cb) { @@ -52,21 +54,14 @@ module.exports = class NoticeController extends EventEmitter { } } - updateNoticesList () { - return this._retrieveNoticeData().then((hardNotices) => { - const newNotices = hardNotices.filter((newNotice) => { - if ('version' in newNotice) { - return semver.satisfies(this.version, newNotice.version) - } - if ('firstVersion' in newNotice) { - return semver.satisfies(this.firstVersion, newNotice.firstVersion) - } - return true - }) - var oldNotices = this.getNoticesList() - var combinedNotices = this._mergeNotices(oldNotices, newNotices) - return Promise.resolve(this.setNoticesList(combinedNotices)) - }) + async updateNoticesList () { + const newNotices = await this._retrieveNoticeData() + const oldNotices = this.getNoticesList() + const combinedNotices = this._mergeNotices(oldNotices, newNotices) + const filteredNotices = this._filterNotices(combinedNotices) + const result = this.setNoticesList(filteredNotices) + this._updateMemstore() + return result } startPolling () { @@ -79,22 +74,30 @@ module.exports = class NoticeController extends EventEmitter { } _mergeNotices (oldNotices, newNotices) { - var noticeMap = this._mapNoticeIds(oldNotices) - newNotices.forEach((notice) => { - if (noticeMap.indexOf(notice.id) === -1) { - oldNotices.push(notice) + return uniqBy(oldNotices.concat(newNotices), 'id') + } + + _filterNotices( notices ) { + return notices.filter((newNotice) => { + if ('version' in newNotice) { + const satisfied = semver.satisfies(this.version, newNotice.version) + return satisfied + } + if ('firstVersion' in newNotice) { + const satisfied = semver.satisfies(this.firstVersion, newNotice.firstVersion) + return satisfied } + return true }) - return oldNotices } _mapNoticeIds (notices) { return notices.map((notice) => notice.id) } - _retrieveNoticeData () { + async _retrieveNoticeData () { // Placeholder for the API. - return Promise.resolve(hardCodedNotices) + return hardCodedNotices } _updateMemstore () { -- cgit v1.2.3 From 049ec52a95d0323c2b79cbd785582c5d6a90cb52 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 28 Nov 2017 15:29:56 -0800 Subject: Add seed phrase bounty award notice Only shows to people who installed before or equal to MetaMask 3.12.1 --- app/scripts/metamask-controller.js | 1 - 1 file changed, 1 deletion(-) (limited to 'app/scripts') diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index c9eb27fbf..0c759010b 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -45,7 +45,6 @@ module.exports = class MetamaskController extends EventEmitter { this.opts = opts const initState = opts.initState || {} this.recordFirstTimeInfo(initState) - opts.initState.firstTimeInfo.version = '4.5.0' // platform-specific api this.platform = opts.platform -- cgit v1.2.3 From bd82b173106d8b6d4c7b47a4f6c30d59b76bf8c5 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 28 Nov 2017 15:35:20 -0800 Subject: Linted --- app/scripts/metamask-controller.js | 2 +- app/scripts/notice-controller.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'app/scripts') diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 0c759010b..94831e8ef 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -486,7 +486,7 @@ module.exports = class MetamaskController extends EventEmitter { vault = await this.keyringController.fullUpdate() } else { - let vault = await this.keyringController.createNewVaultAndKeychain(password) + vault = await this.keyringController.createNewVaultAndKeychain(password) this.selectFirstIdentity(vault) } release() diff --git a/app/scripts/notice-controller.js b/app/scripts/notice-controller.js index efad3e516..db2b8c4f4 100644 --- a/app/scripts/notice-controller.js +++ b/app/scripts/notice-controller.js @@ -77,7 +77,7 @@ module.exports = class NoticeController extends EventEmitter { return uniqBy(oldNotices.concat(newNotices), 'id') } - _filterNotices( notices ) { + _filterNotices(notices) { return notices.filter((newNotice) => { if ('version' in newNotice) { const satisfied = semver.satisfies(this.version, newNotice.version) -- cgit v1.2.3 From ab35a76a240dc0139761678028aa8c187345ab49 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 28 Nov 2017 16:56:45 -0800 Subject: Migrate old data to include firstVersion info --- app/scripts/migrations/020.js | 40 ++++++++++++++++++++++++++++++++++++++++ app/scripts/migrations/index.js | 1 + 2 files changed, 41 insertions(+) create mode 100644 app/scripts/migrations/020.js (limited to 'app/scripts') diff --git a/app/scripts/migrations/020.js b/app/scripts/migrations/020.js new file mode 100644 index 000000000..59b9be7b3 --- /dev/null +++ b/app/scripts/migrations/020.js @@ -0,0 +1,40 @@ +const version = 20 + +/* + +This migration sets transactions as failed +whos nonce is too high + +*/ + +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 + if ('metamask' in newState && + !('firstTimeInfo' in newState.metamask)) { + newState.metamask.firstTimeInfo = { + version: '3.12.0', + date: Date.now(), + } + } + return newState +} + diff --git a/app/scripts/migrations/index.js b/app/scripts/migrations/index.js index e9cbd7b98..9d0631042 100644 --- a/app/scripts/migrations/index.js +++ b/app/scripts/migrations/index.js @@ -30,4 +30,5 @@ module.exports = [ require('./017'), require('./018'), require('./019'), + require('./020'), ] -- cgit v1.2.3 From 8192c0b58ccdb7b045093656cfbd53b0a7cffae9 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 29 Nov 2017 11:51:27 -0800 Subject: Fix migration comment --- app/scripts/migrations/020.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'app/scripts') diff --git a/app/scripts/migrations/020.js b/app/scripts/migrations/020.js index 59b9be7b3..8159b3e70 100644 --- a/app/scripts/migrations/020.js +++ b/app/scripts/migrations/020.js @@ -2,8 +2,9 @@ const version = 20 /* -This migration sets transactions as failed -whos nonce is too high +This migration ensures previous installations +get a `firstTimeInfo` key on the metamask state, +so that we can version notices in the future. */ -- cgit v1.2.3 From ae2a4d78e8c7733da1963965e38e154351d54a20 Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 5 Dec 2017 17:21:14 -0330 Subject: Exponentional backoff on transaction retry in pending-tx-tracker --- app/scripts/controllers/transactions.js | 6 ++++++ app/scripts/lib/pending-tx-tracker.js | 16 ++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) (limited to 'app/scripts') diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js index a861c0342..063ba8f65 100644 --- a/app/scripts/controllers/transactions.js +++ b/app/scripts/controllers/transactions.js @@ -72,6 +72,12 @@ module.exports = class TransactionController extends EventEmitter { }) this.pendingTxTracker.on('tx:failed', this.txStateManager.setTxStatusFailed.bind(this.txStateManager)) this.pendingTxTracker.on('tx:confirmed', this.txStateManager.setTxStatusConfirmed.bind(this.txStateManager)) + this.pendingTxTracker.on('tx:block-update', (txMeta, latestBlockNumber) => { + if (!txMeta.firstRetryBlockNumber) { + txMeta.firstRetryBlockNumber = latestBlockNumber + this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:retry') + } + }) this.pendingTxTracker.on('tx:retry', (txMeta) => { if (!('retryCount' in txMeta)) txMeta.retryCount = 0 txMeta.retryCount++ diff --git a/app/scripts/lib/pending-tx-tracker.js b/app/scripts/lib/pending-tx-tracker.js index 0d7c6a92c..60c837040 100644 --- a/app/scripts/lib/pending-tx-tracker.js +++ b/app/scripts/lib/pending-tx-tracker.js @@ -65,7 +65,7 @@ module.exports = class PendingTransactionTracker extends EventEmitter { } - resubmitPendingTxs () { + resubmitPendingTxs (block) { const pending = this.getPendingTransactions() // only try resubmitting if their are transactions to resubmit if (!pending.length) return @@ -101,13 +101,25 @@ module.exports = class PendingTransactionTracker extends EventEmitter { })) } - async _resubmitTx (txMeta) { + async _resubmitTx (txMeta, latestBlockNumber) { + if (!txMeta.firstRetryBlockNumber) { + this.emit('tx:block-update', txMeta, latestBlockNumber) + } + if (Date.now() > txMeta.time + this.retryTimePeriod) { const hours = (this.retryTimePeriod / 3.6e+6).toFixed(1) const err = new Error(`Gave up submitting after ${hours} hours.`) return this.emit('tx:failed', txMeta.id, err) } + const firstRetryBlockNumber = txMeta.firstRetryBlockNumber + const txBlockDistance = Number.parseInt(latestBlockNumber, 16) - Number.parseInt(firstRetryBlockNumber, 16) + + const retryCount = txMeta.retryCount || 0 + + // Exponential backoff to limit retries at publishing + if (txBlockDistance <= Math.pow(2, retryCount) - 1) return + // Only auto-submit already-signed txs: if (!('rawTx' in txMeta)) return -- cgit v1.2.3 From 871d9fd9fb8417a2dd47abafe68ae07e4903ba6e Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 6 Dec 2017 13:02:38 -0330 Subject: Fix undefined latestBlockNumber in _resubmitTx --- app/scripts/lib/pending-tx-tracker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/scripts') diff --git a/app/scripts/lib/pending-tx-tracker.js b/app/scripts/lib/pending-tx-tracker.js index 60c837040..62d621ac1 100644 --- a/app/scripts/lib/pending-tx-tracker.js +++ b/app/scripts/lib/pending-tx-tracker.js @@ -69,7 +69,7 @@ module.exports = class PendingTransactionTracker extends EventEmitter { const pending = this.getPendingTransactions() // only try resubmitting if their are transactions to resubmit if (!pending.length) return - pending.forEach((txMeta) => this._resubmitTx(txMeta).catch((err) => { + pending.forEach((txMeta) => this._resubmitTx(txMeta, block.number).catch((err) => { /* Dont marked as failed if the error is a "known" transaction warning "there is already a transaction with the same sender-nonce -- cgit v1.2.3 From ea23da9e75c92cbf82d0daa19847d2035361a656 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 6 Dec 2017 13:07:31 -0330 Subject: Correct note for updateTx after block-update event in transaction.js --- app/scripts/controllers/transactions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/scripts') diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js index 063ba8f65..ce709bd28 100644 --- a/app/scripts/controllers/transactions.js +++ b/app/scripts/controllers/transactions.js @@ -75,7 +75,7 @@ module.exports = class TransactionController extends EventEmitter { this.pendingTxTracker.on('tx:block-update', (txMeta, latestBlockNumber) => { if (!txMeta.firstRetryBlockNumber) { txMeta.firstRetryBlockNumber = latestBlockNumber - this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:retry') + this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:block-update') } }) this.pendingTxTracker.on('tx:retry', (txMeta) => { -- cgit v1.2.3 From f58aae3f2ba3d8a129b2c09dc3b45369c488fd04 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 6 Dec 2017 13:21:09 -0330 Subject: firstRetryBlockNumber defaults to latestBlockNumber if undefined on txMeta in _resubmitTx --- app/scripts/lib/pending-tx-tracker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/scripts') diff --git a/app/scripts/lib/pending-tx-tracker.js b/app/scripts/lib/pending-tx-tracker.js index 62d621ac1..dc6e526fd 100644 --- a/app/scripts/lib/pending-tx-tracker.js +++ b/app/scripts/lib/pending-tx-tracker.js @@ -112,7 +112,7 @@ module.exports = class PendingTransactionTracker extends EventEmitter { return this.emit('tx:failed', txMeta.id, err) } - const firstRetryBlockNumber = txMeta.firstRetryBlockNumber + const firstRetryBlockNumber = txMeta.firstRetryBlockNumber || latestBlockNumber const txBlockDistance = Number.parseInt(latestBlockNumber, 16) - Number.parseInt(firstRetryBlockNumber, 16) const retryCount = txMeta.retryCount || 0 -- cgit v1.2.3 From ec6c3c33bdbe2d90dc71649d0cc5fb3c07d96af7 Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 5 Dec 2017 13:11:59 -0330 Subject: Merge branch 'master' into NewUI-flat-merge-with-master --- app/scripts/background.js | 9 ++++---- app/scripts/controllers/network.js | 2 +- app/scripts/inpage.js | 7 ++++++ app/scripts/lib/inpage-provider.js | 4 +++- app/scripts/metamask-controller.js | 47 ++++++++++++++++++++++++++++++++++---- app/scripts/migrations/020.js | 41 +++++++++++++++++++++++++++++++++ app/scripts/migrations/index.js | 1 + app/scripts/notice-controller.js | 44 +++++++++++++++++++++++------------ 8 files changed, 129 insertions(+), 26 deletions(-) create mode 100644 app/scripts/migrations/020.js (limited to 'app/scripts') diff --git a/app/scripts/background.js b/app/scripts/background.js index 3e560d302..da022c490 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -1,10 +1,11 @@ const urlUtil = require('url') const endOfStream = require('end-of-stream') -const pipe = require('pump') +const pump = require('pump') const log = require('loglevel') const extension = require('extensionizer') const LocalStorageStore = require('obs-store/lib/localStorage') const storeTransform = require('obs-store/lib/transform') +const asStream = require('obs-store/lib/asStream') const ExtensionPlatform = require('./platforms/extension') const Migrator = require('./lib/migrator/') const migrations = require('./migrations/') @@ -72,10 +73,10 @@ function setupController (initState) { global.metamaskController = controller // setup state persistence - pipe( - controller.store, + pump( + asStream(controller.store), storeTransform(versionifyData), - diskStore + asStream(diskStore) ) function versionifyData (state) { diff --git a/app/scripts/controllers/network.js b/app/scripts/controllers/network.js index 23afdc12b..045bfcc5d 100644 --- a/app/scripts/controllers/network.js +++ b/app/scripts/controllers/network.js @@ -53,7 +53,7 @@ module.exports = class NetworkController extends EventEmitter { lookupNetwork () { // Prevent firing when provider is not defined. if (!this.ethQuery || !this.ethQuery.sendAsync) { - return + return log.warn('NetworkController - lookupNetwork aborted due to missing ethQuery') } this.ethQuery.sendAsync({ method: 'net_version' }, (err, network) => { if (err) return this.setNetworkState('loading') diff --git a/app/scripts/inpage.js b/app/scripts/inpage.js index b6889b00f..9261e7d64 100644 --- a/app/scripts/inpage.js +++ b/app/scripts/inpage.js @@ -31,6 +31,13 @@ var inpageProvider = new MetamaskInpageProvider(metamaskStream) // setup web3 // +if (typeof window.web3 !== 'undefined') { + throw new Error(`MetaMask detected another web3. + MetaMask will not work reliably with another web3 extension. + This usually happens if you have two MetaMasks installed, + or MetaMask and another web3 extension. Please remove one + and try again.`) +} var web3 = new Web3(inpageProvider) web3.setProvider = function () { log.debug('MetaMask - overrode web3.setProvider') diff --git a/app/scripts/lib/inpage-provider.js b/app/scripts/lib/inpage-provider.js index da75c4be2..99cc5d2cf 100644 --- a/app/scripts/lib/inpage-provider.js +++ b/app/scripts/lib/inpage-provider.js @@ -3,6 +3,7 @@ const RpcEngine = require('json-rpc-engine') const createIdRemapMiddleware = require('json-rpc-engine/src/idRemapMiddleware') const createStreamMiddleware = require('json-rpc-middleware-stream') const LocalStorageStore = require('obs-store') +const asStream = require('obs-store/lib/asStream') const ObjectMultiplex = require('obj-multiplex') module.exports = MetamaskInpageProvider @@ -21,9 +22,10 @@ function MetamaskInpageProvider (connectionStream) { // subscribe to metamask public config (one-way) self.publicConfigStore = new LocalStorageStore({ storageKey: 'MetaMask-Config' }) + pump( mux.createStream('publicConfig'), - self.publicConfigStore, + asStream(self.publicConfigStore), (err) => logStreamDisconnectWarning('MetaMask PublicConfigStore', err) ) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 4dce89e3a..a96d20d3b 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -3,6 +3,7 @@ const extend = require('xtend') const pump = require('pump') const Dnode = require('dnode') const ObservableStore = require('obs-store') +const asStream = require('obs-store/lib/asStream') const AccountTracker = require('./lib/account-tracker') const EthQuery = require('eth-query') const RpcEngine = require('json-rpc-engine') @@ -31,6 +32,7 @@ const ConfigManager = require('./lib/config-manager') const nodeify = require('./lib/nodeify') const accountImporter = require('./account-import-strategies') const getBuyEthUrl = require('./lib/buy-eth-url') +const Mutex = require('await-semaphore').Mutex const version = require('../manifest.json').version module.exports = class MetamaskController extends EventEmitter { @@ -38,10 +40,12 @@ module.exports = class MetamaskController extends EventEmitter { constructor (opts) { super() + this.sendUpdate = debounce(this.privateSendUpdate.bind(this), 200) this.opts = opts const initState = opts.initState || {} + this.recordFirstTimeInfo(initState) // platform-specific api this.platform = opts.platform @@ -49,6 +53,9 @@ module.exports = class MetamaskController extends EventEmitter { // observable state store this.store = new ObservableStore(initState) + // lock to ensure only one vault created at once + this.createVaultMutex = new Mutex() + // network store this.networkController = new NetworkController(initState.NetworkController) @@ -144,6 +151,8 @@ module.exports = class MetamaskController extends EventEmitter { // notices this.noticeController = new NoticeController({ initState: initState.NoticeController, + version, + firstVersion: initState.firstTimeInfo.version, }) this.noticeController.updateNoticesList() // to be uncommented when retrieving notices from a remote server. @@ -453,7 +462,7 @@ module.exports = class MetamaskController extends EventEmitter { setupPublicConfig (outStream) { pump( - this.publicConfigStore, + asStream(this.publicConfigStore), outStream, (err) => { if (err) log.error(err) @@ -469,15 +478,34 @@ module.exports = class MetamaskController extends EventEmitter { // Vault Management // - async createNewVaultAndKeychain (password, cb) { - const vault = await this.keyringController.createNewVaultAndKeychain(password) - this.selectFirstIdentity(vault) + async createNewVaultAndKeychain (password) { + const release = await this.createVaultMutex.acquire() + let vault + + try { + const accounts = await this.keyringController.getAccounts() + + if (accounts.length > 0) { + vault = await this.keyringController.fullUpdate() + + } else { + vault = await this.keyringController.createNewVaultAndKeychain(password) + this.selectFirstIdentity(vault) + } + release() + } catch (err) { + release() + throw err + } + return vault } - async createNewVaultAndRestore (password, seed, cb) { + async createNewVaultAndRestore (password, seed) { + const release = await this.createVaultMutex.acquire() const vault = await this.keyringController.createNewVaultAndRestore(password, seed) this.selectFirstIdentity(vault) + release() return vault } @@ -784,4 +812,13 @@ module.exports = class MetamaskController extends EventEmitter { } } + recordFirstTimeInfo (initState) { + if (!('firstTimeInfo' in initState)) { + initState.firstTimeInfo = { + version, + date: Date.now(), + } + } + } + } diff --git a/app/scripts/migrations/020.js b/app/scripts/migrations/020.js new file mode 100644 index 000000000..8159b3e70 --- /dev/null +++ b/app/scripts/migrations/020.js @@ -0,0 +1,41 @@ +const version = 20 + +/* + +This migration ensures previous installations +get a `firstTimeInfo` key on the metamask state, +so that we can version notices in the future. + +*/ + +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 + if ('metamask' in newState && + !('firstTimeInfo' in newState.metamask)) { + newState.metamask.firstTimeInfo = { + version: '3.12.0', + date: Date.now(), + } + } + return newState +} + diff --git a/app/scripts/migrations/index.js b/app/scripts/migrations/index.js index e9cbd7b98..9d0631042 100644 --- a/app/scripts/migrations/index.js +++ b/app/scripts/migrations/index.js @@ -30,4 +30,5 @@ module.exports = [ require('./017'), require('./018'), require('./019'), + require('./020'), ] diff --git a/app/scripts/notice-controller.js b/app/scripts/notice-controller.js index 57aad40c5..db2b8c4f4 100644 --- a/app/scripts/notice-controller.js +++ b/app/scripts/notice-controller.js @@ -1,13 +1,17 @@ const EventEmitter = require('events').EventEmitter +const semver = require('semver') const extend = require('xtend') const ObservableStore = require('obs-store') const hardCodedNotices = require('../../notices/notices.json') +const uniqBy = require('lodash.uniqby') module.exports = class NoticeController extends EventEmitter { constructor (opts) { super() this.noticePoller = null + this.firstVersion = opts.firstVersion + this.version = opts.version const initState = extend({ noticesList: [], }, opts.initState) @@ -30,9 +34,9 @@ module.exports = class NoticeController extends EventEmitter { return unreadNotices[unreadNotices.length - 1] } - setNoticesList (noticesList) { + async setNoticesList (noticesList) { this.store.updateState({ noticesList }) - return Promise.resolve(true) + return true } markNoticeRead (noticeToMark, cb) { @@ -50,12 +54,14 @@ module.exports = class NoticeController extends EventEmitter { } } - updateNoticesList () { - return this._retrieveNoticeData().then((newNotices) => { - var oldNotices = this.getNoticesList() - var combinedNotices = this._mergeNotices(oldNotices, newNotices) - return Promise.resolve(this.setNoticesList(combinedNotices)) - }) + async updateNoticesList () { + const newNotices = await this._retrieveNoticeData() + const oldNotices = this.getNoticesList() + const combinedNotices = this._mergeNotices(oldNotices, newNotices) + const filteredNotices = this._filterNotices(combinedNotices) + const result = this.setNoticesList(filteredNotices) + this._updateMemstore() + return result } startPolling () { @@ -68,22 +74,30 @@ module.exports = class NoticeController extends EventEmitter { } _mergeNotices (oldNotices, newNotices) { - var noticeMap = this._mapNoticeIds(oldNotices) - newNotices.forEach((notice) => { - if (noticeMap.indexOf(notice.id) === -1) { - oldNotices.push(notice) + return uniqBy(oldNotices.concat(newNotices), 'id') + } + + _filterNotices(notices) { + return notices.filter((newNotice) => { + if ('version' in newNotice) { + const satisfied = semver.satisfies(this.version, newNotice.version) + return satisfied + } + if ('firstVersion' in newNotice) { + const satisfied = semver.satisfies(this.firstVersion, newNotice.firstVersion) + return satisfied } + return true }) - return oldNotices } _mapNoticeIds (notices) { return notices.map((notice) => notice.id) } - _retrieveNoticeData () { + async _retrieveNoticeData () { // Placeholder for the API. - return Promise.resolve(hardCodedNotices) + return hardCodedNotices } _updateMemstore () { -- cgit v1.2.3