diff options
migration 5 - move keyring controller state to substate
-rw-r--r-- | app/scripts/keyring-controller.js | 120 | ||||
-rw-r--r-- | app/scripts/migrations/005.js | 41 | ||||
-rw-r--r-- | app/scripts/migrations/index.js | 1 | ||||
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | test/unit/idStore-migration-test.js | 3 | ||||
-rw-r--r-- | test/unit/keyring-controller-test.js | 56 | ||||
-rw-r--r-- | ui/app/account-detail.js | 5 | ||||
-rw-r--r-- | ui/app/accounts/account-list-item.js | 5 | ||||
-rw-r--r-- | ui/app/first-time/init-menu.js | 1 |
9 files changed, 122 insertions, 112 deletions
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' |