From ad9531d521057da3540a47bfa592dee6fd225d2e Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 27 Jan 2017 19:35:03 -0800 Subject: metamask - organize methods --- app/scripts/metamask-controller.js | 246 +++++++++++++++++++------------------ 1 file changed, 126 insertions(+), 120 deletions(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 3ce9c2373..30fc61168 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -41,7 +41,7 @@ module.exports = class MetamaskController extends EventEmitter { // rpc provider this.provider = this.initializeProvider(opts) - this.provider.on('block', this.processBlock.bind(this)) + this.provider.on('block', this.logBlock.bind(this)) this.provider.on('error', this.getNetwork.bind(this)) // eth data query tools @@ -142,9 +142,7 @@ module.exports = class MetamaskController extends EventEmitter { const result = { selectedAccount: undefined } try { result.selectedAccount = state.config.selectedAccount - } catch (_) { - // thats fine, im sure it will be there next time... - } + } catch (_) {} return result } @@ -269,6 +267,13 @@ module.exports = class MetamaskController extends EventEmitter { } } + setupPublicConfig (outStream) { + pipe( + this.publicConfigStore, + outStream + ) + } + sendUpdate () { this.getState() .then((state) => { @@ -374,31 +379,90 @@ module.exports = class MetamaskController extends EventEmitter { this.opts.showUnconfirmedMessage(msgParams, msgId) } - setupPublicConfig (outStream) { - pipe( - this.publicConfigStore, - outStream - ) + + markAccountsFound (cb) { + this.configManager.setLostAccounts([]) + this.sendUpdate() + cb(null, this.getState()) } - // Log blocks - processBlock (block) { - if (global.METAMASK_DEBUG) { - console.log(`BLOCK CHANGED: #${block.number.toString('hex')} 0x${block.hash.toString('hex')}`) + // Migrate Old Vault If Any + // @string password + // + // returns Promise() + // + // Temporary step used when logging in. + // Checks if old style (pre-3.0.0) Metamask Vault exists. + // If so, persists that vault in the new vault format + // with the provided password, so the other unlock steps + // may be completed without interruption. + migrateOldVaultIfAny (password) { + + if (!this.checkIfShouldMigrate()) { + return Promise.resolve(password) } - this.verifyNetwork() + + const keyringController = this.keyringController + + return this.idStoreMigrator.migratedVaultForPassword(password) + .then(this.restoreOldVaultAccounts.bind(this)) + .then(this.restoreOldLostAccounts.bind(this)) + .then(keyringController.persistAllKeyrings.bind(keyringController, password)) + .then(() => password) } - verifyNetwork () { - // Check network when restoring connectivity: - if (this.state.network === 'loading') { - this.getNetwork() + checkIfShouldMigrate() { + return !!this.configManager.getWallet() && !this.configManager.getVault() + } + + restoreOldVaultAccounts(migratorOutput) { + const { serialized } = migratorOutput + return this.keyringController.restoreKeyring(serialized) + .then(() => migratorOutput) + } + + restoreOldLostAccounts(migratorOutput) { + const { lostAccounts } = migratorOutput + if (lostAccounts) { + this.configManager.setLostAccounts(lostAccounts.map(acct => acct.address)) + return this.importLostAccounts(migratorOutput) } + return Promise.resolve(migratorOutput) } - // config + // IMPORT LOST ACCOUNTS + // @Object with key lostAccounts: @Array accounts <{ address, privateKey }> + // Uses the array's private keys to create a new Simple Key Pair keychain + // and add it to the keyring controller. + importLostAccounts ({ lostAccounts }) { + const privKeys = lostAccounts.map(acct => acct.privateKey) + return this.keyringController.restoreKeyring({ + type: 'Simple Key Pair', + data: privKeys, + }) + } + + // + // disclaimer // + agreeToDisclaimer (cb) { + try { + this.configManager.setConfirmedDisclaimer(true) + cb() + } catch (err) { + cb(err) + } + } + + resetDisclaimer () { + try { + this.configManager.setConfirmedDisclaimer(false) + } catch (e) { + console.error(e) + } + } + setTOSHash (hash) { try { this.configManager.setTOSHash(hash) @@ -419,23 +483,16 @@ module.exports = class MetamaskController extends EventEmitter { } } - // disclaimer - - agreeToDisclaimer (cb) { - try { - this.configManager.setConfirmedDisclaimer(true) - cb() - } catch (err) { - cb(err) - } - } + // + // config + // - resetDisclaimer () { - try { - this.configManager.setConfirmedDisclaimer(false) - } catch (e) { - console.error(e) + // Log blocks + logBlock (block) { + if (global.METAMASK_DEBUG) { + console.log(`BLOCK CHANGED: #${block.number.toString('hex')} 0x${block.hash.toString('hex')}`) } + this.verifyNetwork() } setCurrentFiat (fiat, cb) { @@ -463,24 +520,6 @@ module.exports = class MetamaskController extends EventEmitter { }, 300000) } - // called from popup - setRpcTarget (rpcTarget) { - this.configManager.setRpcTarget(rpcTarget) - extension.runtime.reload() - this.getNetwork() - } - - setProviderType (type) { - this.configManager.setProviderType(type) - extension.runtime.reload() - this.getNetwork() - } - - useEtherscanProvider () { - this.configManager.useEtherscanProvider() - extension.runtime.reload() - } - buyEth (address, amount) { if (!amount) amount = '5' @@ -500,25 +539,6 @@ module.exports = class MetamaskController extends EventEmitter { this.configManager.createShapeShiftTx(depositAddress, depositType) } - getNetwork (err) { - if (err) { - this.state.network = 'loading' - this.sendUpdate() - } - - this.ethQuery.sendAsync({ method: 'net_version' }, (err, network) => { - if (err) { - this.state.network = 'loading' - return this.sendUpdate() - } - if (global.METAMASK_DEBUG) { - console.log('web3.getNetwork returned ' + network) - } - this.state.network = network - this.sendUpdate() - }) - } - setGasMultiplier (gasMultiplier, cb) { try { this.configManager.setGasMultiplier(gasMultiplier) @@ -528,69 +548,55 @@ module.exports = class MetamaskController extends EventEmitter { } } - getStateNetwork () { - return this.state.network - } - - markAccountsFound (cb) { - this.configManager.setLostAccounts([]) - this.sendUpdate() - cb(null, this.getState()) - } - - // Migrate Old Vault If Any - // @string password // - // returns Promise() + // network // - // Temporary step used when logging in. - // Checks if old style (pre-3.0.0) Metamask Vault exists. - // If so, persists that vault in the new vault format - // with the provided password, so the other unlock steps - // may be completed without interruption. - migrateOldVaultIfAny (password) { - if (!this.checkIfShouldMigrate()) { - return Promise.resolve(password) + verifyNetwork () { + // Check network when restoring connectivity: + if (this.state.network === 'loading') { + this.getNetwork() } + } - const keyringController = this.keyringController + setRpcTarget (rpcTarget) { + this.configManager.setRpcTarget(rpcTarget) + extension.runtime.reload() + this.getNetwork() + } - return this.idStoreMigrator.migratedVaultForPassword(password) - .then(this.restoreOldVaultAccounts.bind(this)) - .then(this.restoreOldLostAccounts.bind(this)) - .then(keyringController.persistAllKeyrings.bind(keyringController, password)) - .then(() => password) + setProviderType (type) { + this.configManager.setProviderType(type) + extension.runtime.reload() + this.getNetwork() } - checkIfShouldMigrate() { - return !!this.configManager.getWallet() && !this.configManager.getVault() + useEtherscanProvider () { + this.configManager.useEtherscanProvider() + extension.runtime.reload() } - restoreOldVaultAccounts(migratorOutput) { - const { serialized } = migratorOutput - return this.keyringController.restoreKeyring(serialized) - .then(() => migratorOutput) + getStateNetwork () { + return this.state.network } - restoreOldLostAccounts(migratorOutput) { - const { lostAccounts } = migratorOutput - if (lostAccounts) { - this.configManager.setLostAccounts(lostAccounts.map(acct => acct.address)) - return this.importLostAccounts(migratorOutput) + getNetwork (err) { + if (err) { + this.state.network = 'loading' + this.sendUpdate() } - return Promise.resolve(migratorOutput) - } - // IMPORT LOST ACCOUNTS - // @Object with key lostAccounts: @Array accounts <{ address, privateKey }> - // Uses the array's private keys to create a new Simple Key Pair keychain - // and add it to the keyring controller. - importLostAccounts ({ lostAccounts }) { - const privKeys = lostAccounts.map(acct => acct.privateKey) - return this.keyringController.restoreKeyring({ - type: 'Simple Key Pair', - data: privKeys, + this.ethQuery.sendAsync({ method: 'net_version' }, (err, network) => { + if (err) { + this.state.network = 'loading' + return this.sendUpdate() + } + if (global.METAMASK_DEBUG) { + console.log('web3.getNetwork returned ' + network) + } + this.state.network = network + this.sendUpdate() }) } + } -- cgit From 3afd0ef27d69ed5d130f4ea37e3e856cadba34d9 Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 27 Jan 2017 23:04:34 -0800 Subject: tests - remove persistence and start with initial state --- app/scripts/lib/config-manager.js | 10 +----- test/unit/idStore-migration-test.js | 58 ++---------------------------- test/unit/metamask-controller-test.js | 68 +++++------------------------------ 3 files changed, 12 insertions(+), 124 deletions(-) diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js index fd4ac511a..357e081b1 100644 --- a/app/scripts/lib/config-manager.js +++ b/app/scripts/lib/config-manager.js @@ -29,15 +29,7 @@ ConfigManager.prototype.setConfig = function (config) { ConfigManager.prototype.getConfig = function () { var data = this.getData() - if ('config' in data) { - return data.config - } else { - return { - provider: { - type: 'testnet', - }, - } - } + return data.config } ConfigManager.prototype.setRpcTarget = function (rpcUrl) { diff --git a/test/unit/idStore-migration-test.js b/test/unit/idStore-migration-test.js index 38667fc3e..47894a458 100644 --- a/test/unit/idStore-migration-test.js +++ b/test/unit/idStore-migration-test.js @@ -4,12 +4,13 @@ const ObservableStore = require('obs-store') const ethUtil = require('ethereumjs-util') const BN = ethUtil.BN const ConfigManager = require('../../app/scripts/lib/config-manager') +const firstTimeState = require('../../app/scripts/first-time-state') const delegateCallCode = require('../lib/example-code.json').delegateCallCode +const clone = require('clone') // The old way: const IdentityStore = require('../../app/scripts/lib/idStore') const STORAGE_KEY = 'metamask-config' -const extend = require('xtend') // The new ways: var KeyringController = require('../../app/scripts/keyring-controller') @@ -42,12 +43,9 @@ describe('IdentityStore to KeyringController migration', function() { // and THEN create a new one, before we can run tests on it. beforeEach(function(done) { this.sinon = sinon.sandbox.create() - window.localStorage = {} // Hacking localStorage support into JSDom - let store = new ObservableStore(loadData()) - store.subscribe(setData) + let store = new ObservableStore(clone(firstTimeState)) configManager = new ConfigManager({ store }) - idStore = new IdentityStore({ configManager: configManager, ethStore: { @@ -94,53 +92,3 @@ describe('IdentityStore to KeyringController migration', function() { }) }) }) - -function loadData () { - var oldData = getOldStyleData() - var newData - try { - newData = JSON.parse(window.localStorage[STORAGE_KEY]) - } catch (e) {} - - var data = extend({ - meta: { - version: 0, - }, - data: { - config: { - provider: { - type: 'testnet', - }, - }, - }, - }, oldData || null, newData || null) - return data -} - -function setData (data) { - window.localStorage[STORAGE_KEY] = JSON.stringify(data) -} - -function getOldStyleData () { - var config, wallet, seedWords - - var result = { - meta: { version: 0 }, - data: {}, - } - - try { - config = JSON.parse(window.localStorage['config']) - result.data.config = config - } catch (e) {} - try { - wallet = JSON.parse(window.localStorage['lightwallet']) - result.data.wallet = wallet - } catch (e) {} - try { - seedWords = window.localStorage['seedWords'] - result.data.seedWords = seedWords - } catch (e) {} - - return result -} diff --git a/test/unit/metamask-controller-test.js b/test/unit/metamask-controller-test.js index 24d9ddd67..78b9e9df7 100644 --- a/test/unit/metamask-controller-test.js +++ b/test/unit/metamask-controller-test.js @@ -1,7 +1,9 @@ -var assert = require('assert') -var MetaMaskController = require('../../app/scripts/metamask-controller') -var sinon = require('sinon') -var extend = require('xtend') +const assert = require('assert') +const sinon = require('sinon') +const clone = require('clone') +const MetaMaskController = require('../../app/scripts/metamask-controller') +const firstTimeState = require('../../app/scripts/first-time-state') + const STORAGE_KEY = 'metamask-config' describe('MetaMaskController', function() { @@ -11,15 +13,12 @@ describe('MetaMaskController', function() { unlockAccountMessage: noop, showUnapprovedTx: noop, // initial state - initState: loadData(), + initState: clone(firstTimeState), }) - // setup state persistence - controller.store.subscribe(setData) beforeEach(function() { // sinon allows stubbing methods that are easily verified this.sinon = sinon.sandbox.create() - window.localStorage = {} // Hacking localStorage support into JSDom }) afterEach(function() { @@ -27,55 +26,4 @@ describe('MetaMaskController', function() { this.sinon.restore() }) -}) - - -function loadData () { - var oldData = getOldStyleData() - var newData - try { - newData = JSON.parse(window.localStorage[STORAGE_KEY]) - } catch (e) {} - - var data = extend({ - meta: { - version: 0, - }, - data: { - config: { - provider: { - type: 'testnet', - }, - }, - }, - }, oldData || null, newData || null) - return data -} - -function getOldStyleData () { - var config, wallet, seedWords - - var result = { - meta: { version: 0 }, - data: {}, - } - - try { - config = JSON.parse(window.localStorage['config']) - result.data.config = config - } catch (e) {} - try { - wallet = JSON.parse(window.localStorage['lightwallet']) - result.data.wallet = wallet - } catch (e) {} - try { - seedWords = window.localStorage['seedWords'] - result.data.seedWords = seedWords - } catch (e) {} - - return result -} - -function setData (data) { - window.localStorage[STORAGE_KEY] = JSON.stringify(data) -} +}) \ No newline at end of file -- cgit From 1fd17467442fb60f308b116fea53f87a4cc16dca Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 27 Jan 2017 23:05:29 -0800 Subject: metamask - adopt shapeshift txs from keyringController --- app/scripts/keyring-controller.js | 1 - app/scripts/metamask-controller.js | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/scripts/keyring-controller.js b/app/scripts/keyring-controller.js index a4bee8ae1..e1e1342aa 100644 --- a/app/scripts/keyring-controller.js +++ b/app/scripts/keyring-controller.js @@ -87,7 +87,6 @@ module.exports = class KeyringController extends EventEmitter { unconfMsgs: messageManager.unconfirmedMsgs(), messages: messageManager.getMsgList(), selectedAccount: address, - shapeShiftTxList: this.configManager.getShapeShiftTxList(), currentFiat: this.configManager.getCurrentFiat(), conversionRate: this.configManager.getConversionRate(), conversionDate: this.configManager.getConversionDate(), diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 30fc61168..bef791ec9 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -162,7 +162,9 @@ module.exports = class MetamaskController extends EventEmitter { this.configManager.getConfig(), this.txManager.getState(), keyringControllerState, - this.noticeController.getState(), { + this.noticeController.getState(), + { + shapeShiftTxList: this.configManager.getShapeShiftTxList(), lostAccounts: this.configManager.getLostAccounts(), } ) -- cgit From e9cdbf4f89085cc6c31991416eaac7ca91f9c1d4 Mon Sep 17 00:00:00 2001 From: kumavis Date: Sat, 28 Jan 2017 13:12:12 -0800 Subject: keyring - add and initialize obs-store --- app/scripts/keyring-controller.js | 38 +++++++++++++++++++++++++++++++++----- app/scripts/metamask-controller.js | 4 +++- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/app/scripts/keyring-controller.js b/app/scripts/keyring-controller.js index e1e1342aa..460c71e27 100644 --- a/app/scripts/keyring-controller.js +++ b/app/scripts/keyring-controller.js @@ -1,12 +1,14 @@ const ethUtil = require('ethereumjs-util') +const BN = ethUtil.BN const bip39 = require('bip39') const EventEmitter = require('events').EventEmitter +const ObservableStore = require('obs-store') const filter = require('promise-filter') const encryptor = require('browser-passworder') - +const createId = require('./lib/random-id') const normalize = require('./lib/sig-util').normalize const messageManager = require('./lib/message-manager') -const BN = ethUtil.BN +function noop () {} // Keyrings: const SimpleKeyring = require('./keyrings/simple') @@ -16,9 +18,8 @@ const keyringTypes = [ HdKeyring, ] -const createId = require('./lib/random-id') -module.exports = class KeyringController extends EventEmitter { +class KeyringController extends EventEmitter { // PUBLIC METHODS // @@ -29,6 +30,8 @@ module.exports = class KeyringController extends EventEmitter { constructor (opts) { super() + const initState = opts.initState || {} + this.store = new ObservableStore(initState) this.configManager = opts.configManager this.ethStore = opts.ethStore this.encryptor = encryptor @@ -643,5 +646,30 @@ module.exports = class KeyringController extends EventEmitter { } +// +// Static Class Methods +// + +KeyringController.selectFromState = (state) => { + const config = state.config + return { + vault: state.vault, + selectedAccount: config.selectedAccount, + walletNicknames: state.walletNicknames, + } +} + +KeyringController.flattenToOldState = (state) => { + const data = { + vault: state.vault, + walletNicknames: state.walletNicknames, + config: { + selectedAccount: state.selectedAccount, + }, + } + return data +} + + +module.exports = KeyringController -function noop () {} diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index bef791ec9..f3da6f53e 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -29,9 +29,10 @@ module.exports = class MetamaskController extends EventEmitter { super() this.opts = opts this.state = { network: 'loading' } + let initState = opts.initState || {} // observable state store - this.store = new ObservableStore(opts.initState) + this.store = new ObservableStore(initState) // config manager this.configManager = new ConfigManager({ @@ -50,6 +51,7 @@ module.exports = class MetamaskController extends EventEmitter { // key mgmt this.keyringController = new KeyringController({ + initState: initState.KeyringController, ethStore: this.ethStore, configManager: this.configManager, getNetwork: this.getStateNetwork.bind(this), -- cgit From 4dd6ba9c1b6704dafcf9d10e1db02a3bb3071cb6 Mon Sep 17 00:00:00 2001 From: kumavis Date: Sat, 28 Jan 2017 19:19:03 -0800 Subject: migration 5 - move keyring controller state to substate --- app/scripts/keyring-controller.js | 120 ++++++++++++++++------------------- app/scripts/migrations/005.js | 41 ++++++++++++ app/scripts/migrations/index.js | 1 + package.json | 2 +- test/unit/idStore-migration-test.js | 3 + test/unit/keyring-controller-test.js | 56 +++++----------- ui/app/account-detail.js | 5 +- ui/app/accounts/account-list-item.js | 5 +- ui/app/first-time/init-menu.js | 1 - 9 files changed, 122 insertions(+), 112 deletions(-) create mode 100644 app/scripts/migrations/005.js diff --git a/app/scripts/keyring-controller.js b/app/scripts/keyring-controller.js index 460c71e27..68fc6e882 100644 --- a/app/scripts/keyring-controller.js +++ b/app/scripts/keyring-controller.js @@ -6,7 +6,7 @@ const ObservableStore = require('obs-store') const filter = require('promise-filter') const encryptor = require('browser-passworder') const createId = require('./lib/random-id') -const normalize = require('./lib/sig-util').normalize +const normalizeAddress = require('./lib/sig-util').normalize const messageManager = require('./lib/message-manager') function noop () {} @@ -74,28 +74,31 @@ class KeyringController extends EventEmitter { // in this class, but will need to be Promisified when we move our // persistence to an async model. getState () { - const configManager = this.configManager - const address = configManager.getSelectedAccount() - const wallet = configManager.getWallet() // old style vault - const vault = configManager.getVault() // new style vault - const keyrings = this.keyrings - - return Promise.all(keyrings.map(this.displayForKeyring)) + return Promise.all(this.keyrings.map(this.displayForKeyring)) .then((displayKeyrings) => { + const state = this.store.getState() + // old wallet + const wallet = this.configManager.getWallet() return { + // computed + isInitialized: (!!wallet || !!state.vault), + isUnlocked: (!!this.password), + keyrings: displayKeyrings, + // hard coded + keyringTypes: this.keyringTypes.map(krt => krt.type), + // memStore + identities: this.identities, + // diskStore + selectedAccount: state.selectedAccount, + // configManager seedWords: this.configManager.getSeedWords(), - isInitialized: (!!wallet || !!vault), - isUnlocked: Boolean(this.password), isDisclaimerConfirmed: this.configManager.getConfirmedDisclaimer(), - unconfMsgs: messageManager.unconfirmedMsgs(), - messages: messageManager.getMsgList(), - selectedAccount: address, currentFiat: this.configManager.getCurrentFiat(), conversionRate: this.configManager.getConversionRate(), conversionDate: this.configManager.getConversionDate(), - keyringTypes: this.keyringTypes.map(krt => krt.type), - identities: this.identities, - keyrings: displayKeyrings, + // messageManager + unconfMsgs: messageManager.unconfirmedMsgs(), + messages: messageManager.getMsgList(), } }) } @@ -148,8 +151,8 @@ class KeyringController extends EventEmitter { .then((accounts) => { const firstAccount = accounts[0] if (!firstAccount) throw new Error('KeyringController - First Account not found.') - const hexAccount = normalize(firstAccount) - this.configManager.setSelectedAccount(hexAccount) + const hexAccount = normalizeAddress(firstAccount) + this.setSelectedAccount(hexAccount) return this.setupAccounts(accounts) }) .then(this.persistAllKeyrings.bind(this, password)) @@ -235,9 +238,9 @@ class KeyringController extends EventEmitter { // // Sets the state's `selectedAccount` value // to the specified address. - setSelectedAccount (address) { - var addr = normalize(address) - this.configManager.setSelectedAccount(addr) + setSelectedAccount (account) { + var address = normalizeAddress(account) + this.store.updateState({ selectedAccount: address }) return this.fullUpdate() } @@ -249,11 +252,19 @@ class KeyringController extends EventEmitter { // // Persists a nickname equal to `label` for the specified account. saveAccountLabel (account, label) { - const address = normalize(account) - const configManager = this.configManager - configManager.setNicknameForWallet(address, label) - this.identities[address].name = label - return Promise.resolve(label) + try { + const hexAddress = normalizeAddress(account) + // update state on diskStore + const state = this.store.getState() + const walletNicknames = state.walletNicknames || {} + walletNicknames[hexAddress] = label + this.store.updateState({ walletNicknames }) + // update state on memStore + this.identities[hexAddress].name = label + return Promise.resolve(label) + } catch (err) { + return Promise.reject(err) + } } // Export Account @@ -269,7 +280,7 @@ class KeyringController extends EventEmitter { try { return this.getKeyringForAccount(address) .then((keyring) => { - return keyring.exportAccount(normalize(address)) + return keyring.exportAccount(normalizeAddress(address)) }) } catch (e) { return Promise.reject(e) @@ -283,7 +294,7 @@ class KeyringController extends EventEmitter { // TX Manager to update the state after signing signTransaction (ethTx, _fromAddress) { - const fromAddress = normalize(_fromAddress) + const fromAddress = normalizeAddress(_fromAddress) return this.getKeyringForAccount(fromAddress) .then((keyring) => { return keyring.signTransaction(fromAddress, ethTx) @@ -356,7 +367,7 @@ class KeyringController extends EventEmitter { delete msgParams.metamaskId const approvalCb = this._unconfMsgCbs[msgId] || noop - const address = normalize(msgParams.from) + const address = normalizeAddress(msgParams.from) return this.getKeyringForAccount(address) .then((keyring) => { return keyring.signMessage(address, msgParams.data) @@ -394,8 +405,8 @@ class KeyringController extends EventEmitter { .then((accounts) => { const firstAccount = accounts[0] if (!firstAccount) throw new Error('KeyringController - No account found on keychain.') - const hexAccount = normalize(firstAccount) - this.configManager.setSelectedAccount(hexAccount) + const hexAccount = normalizeAddress(firstAccount) + this.setSelectedAccount(hexAccount) this.emit('newAccount', hexAccount) return this.setupAccounts(accounts) }) @@ -431,7 +442,7 @@ class KeyringController extends EventEmitter { if (!account) { throw new Error('Problem loading account.') } - const address = normalize(account) + const address = normalizeAddress(account) this.ethStore.addAccount(address) return this.createNickname(address) } @@ -443,10 +454,11 @@ class KeyringController extends EventEmitter { // // Takes an address, and assigns it an incremented nickname, persisting it. createNickname (address) { - const hexAddress = normalize(address) - var i = Object.keys(this.identities).length - const oldNickname = this.configManager.nicknameForWallet(address) - const name = oldNickname || `Account ${++i}` + const hexAddress = normalizeAddress(address) + const currentIdentityCount = Object.keys(this.identities).length + 1 + const nicknames = this.store.getState().walletNicknames || {} + const existingNickname = nicknames[hexAddress] + const name = existingNickname || `Account ${currentIdentityCount}` this.identities[hexAddress] = { address: hexAddress, name, @@ -481,7 +493,7 @@ class KeyringController extends EventEmitter { return this.encryptor.encrypt(this.password, serializedKeyrings) }) .then((encryptedString) => { - this.configManager.setVault(encryptedString) + this.store.updateState({ vault: encryptedString }) return true }) } @@ -494,7 +506,7 @@ class KeyringController extends EventEmitter { // Attempts to unlock the persisted encrypted storage, // initializing the persisted keyrings to RAM. unlockKeyrings (password) { - const encryptedVault = this.configManager.getVault() + const encryptedVault = this.store.getState().vault if (!encryptedVault) { throw new Error('Cannot unlock without a previous vault.') } @@ -574,7 +586,7 @@ class KeyringController extends EventEmitter { // Returns the currently initialized keyring that manages // the specified `address` if one exists. getKeyringForAccount (address) { - const hexed = normalize(address) + const hexed = normalizeAddress(address) return Promise.all(this.keyrings.map((keyring) => { return Promise.all([ @@ -583,7 +595,7 @@ class KeyringController extends EventEmitter { ]) })) .then(filter((candidate) => { - const accounts = candidate[1].map(normalize) + const accounts = candidate[1].map(normalizeAddress) return accounts.includes(hexed) })) .then((winners) => { @@ -641,35 +653,9 @@ class KeyringController extends EventEmitter { this.keyrings = [] this.identities = {} - this.configManager.setSelectedAccount() + this.setSelectedAccount() } } -// -// Static Class Methods -// - -KeyringController.selectFromState = (state) => { - const config = state.config - return { - vault: state.vault, - selectedAccount: config.selectedAccount, - walletNicknames: state.walletNicknames, - } -} - -KeyringController.flattenToOldState = (state) => { - const data = { - vault: state.vault, - walletNicknames: state.walletNicknames, - config: { - selectedAccount: state.selectedAccount, - }, - } - return data -} - - module.exports = KeyringController - diff --git a/app/scripts/migrations/005.js b/app/scripts/migrations/005.js new file mode 100644 index 000000000..65f62a861 --- /dev/null +++ b/app/scripts/migrations/005.js @@ -0,0 +1,41 @@ +const version = 5 + +/* + +This migration moves state from the flat state trie into KeyringController substate + +*/ + +const extend = require('xtend') + +module.exports = { + version, + + migrate: function (versionedData) { + versionedData.meta.version = version + try { + const state = versionedData.data + const newState = selectSubstateForKeyringController(state) + versionedData.data = newState + } catch (err) { + console.warn('MetaMask Migration #5' + err.stack) + } + return Promise.resolve(versionedData) + }, +} + +function selectSubstateForKeyringController (state) { + const config = state.config + const newState = extend(state, { + KeyringController: { + vault: state.vault, + selectedAccount: config.selectedAccount, + walletNicknames: state.walletNicknames, + }, + }) + delete newState.vault + delete newState.walletNicknames + delete newState.config.selectedAccount + + return newState +} diff --git a/app/scripts/migrations/index.js b/app/scripts/migrations/index.js index d2ac221b9..a7ce745e7 100644 --- a/app/scripts/migrations/index.js +++ b/app/scripts/migrations/index.js @@ -15,4 +15,5 @@ module.exports = [ require('./002'), require('./003'), require('./004'), + require('./005'), ] diff --git a/package.json b/package.json index bf4b3986c..2a46ab6bd 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "mississippi": "^1.2.0", "mkdirp": "^0.5.1", "multiplex": "^6.7.0", - "obs-store": "^2.2.3", + "obs-store": "^2.3.0", "once": "^1.3.3", "ping-pong-stream": "^1.0.0", "pojo-migrator": "^2.1.0", diff --git a/test/unit/idStore-migration-test.js b/test/unit/idStore-migration-test.js index 47894a458..3aaf4bb94 100644 --- a/test/unit/idStore-migration-test.js +++ b/test/unit/idStore-migration-test.js @@ -89,6 +89,9 @@ describe('IdentityStore to KeyringController migration', function() { assert(!state.lostAccounts, 'no lost accounts') done() }) + .catch((err) => { + done(err) + }) }) }) }) diff --git a/test/unit/keyring-controller-test.js b/test/unit/keyring-controller-test.js index d6d2db817..347aa2bdf 100644 --- a/test/unit/keyring-controller-test.js +++ b/test/unit/keyring-controller-test.js @@ -1,6 +1,6 @@ -var assert = require('assert') -var KeyringController = require('../../app/scripts/keyring-controller') -var configManagerGen = require('../lib/mock-config-manager') +const assert = require('assert') +const KeyringController = require('../../app/scripts/keyring-controller') +const configManagerGen = require('../lib/mock-config-manager') const ethUtil = require('ethereumjs-util') const BN = ethUtil.BN const async = require('async') @@ -55,17 +55,16 @@ describe('KeyringController', function() { this.timeout(10000) it('should set a vault on the configManager', function(done) { - keyringController.configManager.setVault(null) - assert(!keyringController.configManager.getVault(), 'no previous vault') + keyringController.store.updateState({ vault: null }) + assert(!keyringController.store.getState().vault, 'no previous vault') keyringController.createNewVaultAndKeychain(password) .then(() => { - const vault = keyringController.configManager.getVault() + const vault = keyringController.store.getState().vault assert(vault, 'vault created') done() }) .catch((reason) => { - assert.ifError(reason) - done() + done(reason) }) }) }) @@ -96,8 +95,7 @@ describe('KeyringController', function() { done() }) .catch((reason) => { - assert.ifError(reason) - done() + done(reason) }) }) }) @@ -109,9 +107,6 @@ describe('KeyringController', function() { const identities = keyringController.identities const identity = identities[fakeAddress] assert.equal(identity.address, fakeAddress) - - const nick = keyringController.configManager.nicknameForWallet(fakeAddress) - assert.equal(typeof nick, 'string') }) }) @@ -122,34 +117,17 @@ describe('KeyringController', function() { keyringController.identities[ethUtil.addHexPrefix(account)] = {} keyringController.saveAccountLabel(account, nick) .then((label) => { - assert.equal(label, nick) - const persisted = keyringController.configManager.nicknameForWallet(account) - assert.equal(persisted, nick) - done() - }) - .catch((reason) => { - assert.ifError(reason) - done() - }) - }) - - this.timeout(10000) - it('retrieves the persisted nickname', function(done) { - const account = addresses[0] - var nick = 'Test nickname' - keyringController.configManager.setNicknameForWallet(account, nick) - keyringController.createNewVaultAndRestore(password, seedWords) - .then((state) => { - - const identity = keyringController.identities['0x' + account] - assert.equal(identity.name, nick) - - assert(accounts) - done() + try { + assert.equal(label, nick) + const persisted = keyringController.store.getState().walletNicknames[account] + assert.equal(persisted, nick) + done() + } catch (err) { + done() + } }) .catch((reason) => { - assert.ifError(reason) - done() + done(reason) }) }) }) diff --git a/ui/app/account-detail.js b/ui/app/account-detail.js index 387a1720a..0bcfbcaab 100644 --- a/ui/app/account-detail.js +++ b/ui/app/account-detail.js @@ -41,6 +41,7 @@ function AccountDetailScreen () { AccountDetailScreen.prototype.render = function () { var props = this.props var selected = props.address || Object.keys(props.accounts)[0] + var checksumAddress = selected && ethUtil.toChecksumAddress(selected) var identity = props.identities[selected] var account = props.accounts[selected] const { network } = props @@ -116,7 +117,7 @@ AccountDetailScreen.prototype.render = function () { marginBottom: '15px', color: '#AEAEAE', }, - }, ethUtil.toChecksumAddress(selected)), + }, checksumAddress), // copy and export @@ -129,7 +130,7 @@ AccountDetailScreen.prototype.render = function () { h(AccountInfoLink, { selected, network }), h(CopyButton, { - value: ethUtil.toChecksumAddress(selected), + value: checksumAddress, }), h(Tooltip, { diff --git a/ui/app/accounts/account-list-item.js b/ui/app/accounts/account-list-item.js index 16019c88a..74ecef07f 100644 --- a/ui/app/accounts/account-list-item.js +++ b/ui/app/accounts/account-list-item.js @@ -17,6 +17,7 @@ function AccountListItem () { AccountListItem.prototype.render = function () { const { identity, selectedAccount, accounts, onShowDetail } = this.props + const checksumAddress = identity && identity.address && ethUtil.toChecksumAddress(identity.address) const isSelected = selectedAccount === identity.address const account = accounts[identity.address] const selectedClass = isSelected ? '.selected' : '' @@ -48,7 +49,7 @@ AccountListItem.prototype.render = function () { overflow: 'hidden', textOverflow: 'ellipsis', }, - }, ethUtil.toChecksumAddress(identity.address)), + }, checksumAddress), h(EthBalance, { value: account && account.balance, style: { @@ -65,7 +66,7 @@ AccountListItem.prototype.render = function () { }, }, [ h(CopyButton, { - value: ethUtil.toChecksumAddress(identity.address), + value: checksumAddress, }), ]), ]) diff --git a/ui/app/first-time/init-menu.js b/ui/app/first-time/init-menu.js index 152d28809..cc7c51bd3 100644 --- a/ui/app/first-time/init-menu.js +++ b/ui/app/first-time/init-menu.js @@ -152,7 +152,6 @@ InitializeMenuScreen.prototype.createNewVaultAndKeychain = function () { var password = passwordBox.value var passwordConfirmBox = document.getElementById('password-box-confirm') var passwordConfirm = passwordConfirmBox.value - // var entropy = document.getElementById('entropy-text-entry').value if (password.length < 8) { this.warning = 'password not long enough' -- cgit From 13e20034698ae0477240e88eab9a3483e5daccd6 Mon Sep 17 00:00:00 2001 From: kumavis Date: Sat, 28 Jan 2017 22:12:15 -0800 Subject: metamask - subscribe metamask store to keyringController substate --- app/scripts/metamask-controller.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index f3da6f53e..cf58d2477 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -98,6 +98,9 @@ module.exports = class MetamaskController extends EventEmitter { this.ethStore.on('update', this.sendUpdate.bind(this)) this.keyringController.on('update', this.sendUpdate.bind(this)) this.txManager.on('update', this.sendUpdate.bind(this)) + this.keyringController.store.subscribe((state) => { + this.store.updateState({ KeyringController: state }) + }) } // -- cgit