From db356a181a3fde4ad528c699f6da517053171866 Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Tue, 1 Nov 2016 11:25:38 -0700 Subject: [PATCH] Made progress on parity for MultiVault - Deleted some unused items - Renamed files and paths to match with new locations. - Modified keyring controller logic to separate concerns. - Fix account naming issues. - Enable creation of new vault with default HD keyring. - Formatting issues. --- app/scripts/background.js | 2 +- app/scripts/keyring-controller.js | 65 ++++++--- app/scripts/keyrings/hd.js | 20 ++- app/scripts/metamask-controller.js | 3 +- ui/app/actions.js | 23 +++- ui/app/app.js | 20 +-- ui/app/first-time/create-vault.js | 129 ------------------ ui/app/first-time/init-menu.js | 28 ++-- .../keychains/hd/recover-seed/confirmation.js | 4 +- ui/app/keychains/hd/restore-vault.js | 10 +- 10 files changed, 104 insertions(+), 200 deletions(-) delete mode 100644 ui/app/first-time/create-vault.js diff --git a/app/scripts/background.js b/app/scripts/background.js index 27c9179a7..7cb25d8bf 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -30,7 +30,7 @@ function triggerUi () { // On first install, open a window to MetaMask website to how-it-works. extension.runtime.onInstalled.addListener(function (details) { - if ((details.reason === 'install') && (!METAMASK_DEBUG)) { + if ((details.reason === 'install') && (!METAMASK_DEBUG)) { extension.tabs.create({url: 'https://metamask.io/#how-it-works'}) } }) diff --git a/app/scripts/keyring-controller.js b/app/scripts/keyring-controller.js index ee6445121..f6e71b005 100644 --- a/app/scripts/keyring-controller.js +++ b/app/scripts/keyring-controller.js @@ -70,6 +70,23 @@ module.exports = class KeyringController extends EventEmitter { this.ethStore = ethStore } + createNewVaultAndKeychain(password, entropy, cb) { + this.createNewVault(password, entropy, (err, serialized) => { + if (err) return cb(err) + this.createFirstKeyTree(serialized, password, cb) + }) + } + + createNewVaultAndRestore(password, seed, cb) { + this.createNewVault(password, '', (err) => { + if (err) return cb(err) + this.addNewKeyring('HD Key Tree', { + mnemonic: seed, + n: 0, + }, cb) + }) + } + createNewVault(password, entropy, cb) { const salt = this.encryptor.generateSalt() this.configManager.setSalt(salt) @@ -89,22 +106,7 @@ module.exports = class KeyringController extends EventEmitter { }) .then((encryptedString) => { this.configManager.setVault(encryptedString) - - if (!serialized) { - this.addNewKeyring('HD Key Tree', null, (err, newState) => { - const firstKeyring = this.keyrings[0] - const firstAccount = firstKeyring.getAccounts()[0] - const hexAccount = ethUtil.addHexPrefix(firstAccount) - const seedWords = firstKeyring.serialize().mnemonic - this.configManager.setSelectedAccount(hexAccount) - this.configManager.setSeedWords(seedWords) - autoFaucet(hexAccount) - cb(err, newState) - }) - } else { - return this.submitPassword(password, cb) - } - + cb(null, serialized) // NORMAL BEHAVIOR: // return cb(null, this.getState()) }) @@ -113,6 +115,23 @@ module.exports = class KeyringController extends EventEmitter { }) } + createFirstKeyTree(serialized, password, cb) { + if (!serialized) { + this.addNewKeyring('HD Key Tree', {n: 1}, (err, newState) => { + const firstKeyring = this.keyrings[0] + const firstAccount = firstKeyring.getAccounts()[0] + const hexAccount = ethUtil.addHexPrefix(firstAccount) + const seedWords = firstKeyring.serialize().mnemonic + this.configManager.setSelectedAccount(hexAccount) + this.configManager.setSeedWords(seedWords) + autoFaucet(hexAccount) + cb(err, this.getState()) + }) + } else { + return this.submitPassword(password, cb) + } + } + submitPassword(password, cb) { this.loadKey(password) .then((key) => { @@ -139,10 +158,10 @@ module.exports = class KeyringController extends EventEmitter { addNewKeyring(type, opts, cb) { const Keyring = this.getKeyringClassForType(type) const keyring = new Keyring(opts) - const accounts = keyring.addAccounts(1) + const accounts = keyring.getAccounts() - this.setupAccounts(accounts) this.keyrings.push(keyring) + this.setupAccounts(accounts) this.persistAllKeyrings() .then(() => { cb(null, this.getState()) @@ -160,17 +179,21 @@ module.exports = class KeyringController extends EventEmitter { } setupAccounts(accounts) { - const i = this.getAccounts().length accounts.forEach((account) => { - this.loadBalanceAndNickname(account, i) + this.loadBalanceAndNickname(account) }) } // Takes an account address and an iterator representing // the current number of named accounts. - loadBalanceAndNickname(account, i) { + loadBalanceAndNickname(account) { const address = ethUtil.addHexPrefix(account) this.ethStore.addAccount(address) + this.createNickname(address) + } + + createNickname(address) { + var i = Object.keys(this.identities).length const oldNickname = this.configManager.nicknameForWallet(address) const name = oldNickname || `Account ${++i}` this.identities[address] = { diff --git a/app/scripts/keyrings/hd.js b/app/scripts/keyrings/hd.js index 61df8f28e..69b8d25bc 100644 --- a/app/scripts/keyrings/hd.js +++ b/app/scripts/keyrings/hd.js @@ -17,13 +17,22 @@ module.exports = class HdKeyring extends EventEmitter { super() this.type = type this.opts = opts || {} - this.wallets = [] - this.mnemonic = null + this.deserialize(opts) } - deserialize({ mnemonic, n }) { - this.initFromMnemonic(mnemonic || bip39.generateMnemonic()) - this.addAccounts(n) + deserialize(opts) { + this.wallets = [] + this.mnemonic = null + this.root = null + + if ('mnemonic' in opts) { + this.initFromMnemonic(opts.mnemonic) + } + + if ('n' in opts) { + this.addAccounts(opts.n) + } + } initFromMnemonic(mnemonic) { @@ -83,4 +92,3 @@ module.exports = class HdKeyring extends EventEmitter { } } - diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 48c56d915..7ce22b642 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -59,7 +59,8 @@ module.exports = class MetamaskController { setGasMultiplier: this.setGasMultiplier.bind(this), // forward directly to keyringController - createNewVault: keyringController.createNewVault.bind(keyringController), + createNewVaultAndKeychain: keyringController.createNewVaultAndKeychain.bind(keyringController), + createNewVaultAndRestore: keyringController.createNewVaultAndRestore.bind(keyringController), clearSeedWordCache: keyringController.clearSeedWordCache.bind(keyringController), addNewKeyring: keyringController.addNewKeyring.bind(keyringController), addNewAccount: keyringController.addNewAccount.bind(keyringController), diff --git a/ui/app/actions.js b/ui/app/actions.js index 5068d1848..458145380 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -23,7 +23,8 @@ var actions = { showCreateVault: showCreateVault, showRestoreVault: showRestoreVault, showInitializeMenu: showInitializeMenu, - createNewVault: createNewVault, + createNewVaultAndKeychain: createNewVaultAndKeychain, + createNewVaultAndRestore: createNewVaultAndRestore, createNewVaultInProgress: createNewVaultInProgress, addNewKeyring, addNewAccount, @@ -188,17 +189,27 @@ function confirmSeedWords () { } } -function createNewVault (password, entropy) { +function createNewVaultAndRestore (password, seed) { return (dispatch) => { - // dispatch(actions.createNewVaultInProgress()) - background.createNewVault(password, entropy, (err, newState) => { + dispatch(actions.showLoadingIndication()) + background.createNewVaultAndRestore(password, seed, (err, newState) => { + dispatch(actions.hideLoadingIndication()) + if (err) return dispatch(actions.displayWarning(err.message)) + + dispatch(this.updateMetamaskState(newState)) + }) + } +} + +function createNewVaultAndKeychain (password, entropy) { + return (dispatch) => { + background.createNewVaultAndKeychain(password, entropy, (err, newState) => { if (err) { return dispatch(actions.showWarning(err.message)) } dispatch(this.updateMetamaskState(newState)) - dispatch(this.showAccountsPage()) - dispatch(this.hideLoadingIndication()) + dispatch(this.showNewVaultSeed()) }) } } diff --git a/ui/app/app.js b/ui/app/app.js index a593af0a2..b40219763 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -7,7 +7,6 @@ const ReactCSSTransitionGroup = require('react-addons-css-transition-group') // init const DisclaimerScreen = require('./first-time/disclaimer') const InitializeMenuScreen = require('./first-time/init-menu') -const CreateVaultScreen = require('./first-time/create-vault') const NewKeyChainScreen = require('./new-keychain') // unlock const UnlockScreen = require('./unlock') @@ -28,6 +27,7 @@ const Tooltip = require('./components/tooltip') const BuyView = require('./components/buy-button-subview') const QrView = require('./components/qr-code') const HDCreateVaultComplete = require('./keychains/hd/create-vault-complete') +const HDRestoreVaultScreen = require('./keychains/hd/restore-vault') module.exports = connect(mapStateToProps)(App) @@ -349,16 +349,6 @@ App.prototype.renderBackToInitButton = function () { } else if (props.isInitialized) { var style switch (props.currentView.name) { - case 'createVault': - style = { - position: 'absolute', - top: '41px', - left: '80px', - fontSize: '21px', - fontFamily: 'Montserrat Bold', - color: 'rgb(174, 174, 174)', - } - return this.renderBackButton(style, true) case 'restoreVault': style = { position: 'absolute', @@ -403,8 +393,9 @@ App.prototype.renderPrimary = function () { // show current view switch (props.currentView.name) { - case 'createVault': - return h(CreateVaultScreen, {key: 'createVault'}) + + case 'restoreVault': + return h(HDRestoreVaultScreen, {key: 'HDRestoreVaultScreen'}) default: return h(InitializeMenuScreen, {key: 'menuScreenInit'}) @@ -440,9 +431,6 @@ App.prototype.renderPrimary = function () { case 'info': return h(InfoScreen, {key: 'info'}) - case 'createVault': - return h(CreateVaultScreen, {key: 'createVault'}) - case 'buyEth': return h(BuyView, {key: 'buyEthView'}) diff --git a/ui/app/first-time/create-vault.js b/ui/app/first-time/create-vault.js deleted file mode 100644 index 33ae62179..000000000 --- a/ui/app/first-time/create-vault.js +++ /dev/null @@ -1,129 +0,0 @@ -const inherits = require('util').inherits - -const Component = require('react').Component -const connect = require('react-redux').connect -const h = require('react-hyperscript') -const actions = require('../actions') - -module.exports = connect(mapStateToProps)(CreateVaultScreen) - -inherits(CreateVaultScreen, Component) -function CreateVaultScreen () { - Component.call(this) -} - -function mapStateToProps (state) { - return { - warning: state.appState.warning, - } -} - -CreateVaultScreen.prototype.render = function () { - var state = this.props - return ( - - h('.initialize-screen.flex-column.flex-center.flex-grow', [ - - h('h3.flex-center.text-transform-uppercase', { - style: { - background: '#EBEBEB', - color: '#AEAEAE', - marginBottom: 24, - width: '100%', - fontSize: '20px', - padding: 6, - }, - }, [ - 'Create Vault', - ]), - - // password - h('input.large-input.letter-spacey', { - type: 'password', - id: 'password-box', - placeholder: 'New Password (min 8 chars)', - style: { - width: 260, - marginTop: 12, - }, - }), - - // confirm password - h('input.large-input.letter-spacey', { - type: 'password', - id: 'password-box-confirm', - placeholder: 'Confirm Password', - onKeyPress: this.createVaultOnEnter.bind(this), - style: { - width: 260, - marginTop: 16, - }, - }), - - h('.flex-row.flex-space-between', { - style: { - marginTop: 30, - width: '50%', - }, - }, [ - - // cancel - h('button.primary', { - onClick: this.showInitializeMenu.bind(this), - }, 'CANCEL'), - - // submit - h('button.primary', { - onClick: this.createNewVault.bind(this), - }, 'OK'), - - ]), - - (!state.inProgress && state.warning) && ( - h('span.in-progress-notification', state.warning) - ), - - state.inProgress && ( - h('span.in-progress-notification', 'Generating Seed...') - ), - ]) - ) -} - -CreateVaultScreen.prototype.componentDidMount = function () { - document.getElementById('password-box').focus() -} - -CreateVaultScreen.prototype.showInitializeMenu = function () { - this.props.dispatch(actions.showInitializeMenu()) -} - -// create vault - -CreateVaultScreen.prototype.createVaultOnEnter = function (event) { - if (event.key === 'Enter') { - event.preventDefault() - this.createNewVault() - } -} - -CreateVaultScreen.prototype.createNewVault = function () { - var passwordBox = document.getElementById('password-box') - 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' - this.props.dispatch(actions.displayWarning(this.warning)) - return - } - if (password !== passwordConfirm) { - this.warning = 'passwords don\'t match' - this.props.dispatch(actions.displayWarning(this.warning)) - return - } - - this.props.dispatch(actions.createNewVault(password, ''/* entropy*/)) -} diff --git a/ui/app/first-time/init-menu.js b/ui/app/first-time/init-menu.js index b1155e2f6..897051818 100644 --- a/ui/app/first-time/init-menu.js +++ b/ui/app/first-time/init-menu.js @@ -50,10 +50,10 @@ InitializeMenuScreen.prototype.renderMenu = function (state) { h('h1', { style: { - fontSize: '1.7em', + fontSize: '1.3em', textTransform: 'uppercase', color: '#7F8082', - marginBottom: 20, + marginBottom: 10, }, }, 'MetaMask'), @@ -82,6 +82,8 @@ InitializeMenuScreen.prototype.renderMenu = function (state) { ]), ]), + h('span.in-progress-notification', state.warning), + // password h('input.large-input.letter-spacey', { type: 'password', @@ -91,7 +93,6 @@ InitializeMenuScreen.prototype.renderMenu = function (state) { style: { width: 260, marginTop: 12, - textAlign: 'center', }, }), @@ -105,25 +106,22 @@ InitializeMenuScreen.prototype.renderMenu = function (state) { style: { width: 260, marginTop: 16, - textAlign: 'center', }, }), h('button.primary', { - onClick: this.createNewVault.bind(this), + onClick: this.createNewVaultAndKeychain.bind(this), style: { margin: 12, }, }, 'Create'), - (!state.inProgress && state.warning) && ( - h('span.in-progress-notification', state.warning) - ), - /* + h('.flex-row.flex-center.flex-grow', [ h('p.pointer', { + onClick: this.showRestoreVault.bind(this), style: { fontSize: '0.8em', color: 'rgb(247, 134, 28)', @@ -131,7 +129,7 @@ InitializeMenuScreen.prototype.renderMenu = function (state) { }, }, 'I already have a DEN that I would like to import'), ]), - */ + ]) ) @@ -140,7 +138,7 @@ InitializeMenuScreen.prototype.renderMenu = function (state) { InitializeMenuScreen.prototype.createVaultOnEnter = function (event) { if (event.key === 'Enter') { event.preventDefault() - this.createNewVault() + this.createNewVaultAndKeychain() } } @@ -148,7 +146,11 @@ InitializeMenuScreen.prototype.componentDidMount = function () { document.getElementById('password-box').focus() } -InitializeMenuScreen.prototype.createNewVault = function () { +InitializeMenuScreen.prototype.showRestoreVault = function () { + this.props.dispatch(actions.showRestoreVault()) +} + +InitializeMenuScreen.prototype.createNewVaultAndKeychain = function () { var passwordBox = document.getElementById('password-box') var password = passwordBox.value var passwordConfirmBox = document.getElementById('password-box-confirm') @@ -166,7 +168,7 @@ InitializeMenuScreen.prototype.createNewVault = function () { return } - this.props.dispatch(actions.createNewVault(password, ''/* entropy*/)) + this.props.dispatch(actions.createNewVaultAndKeychain(password, ''/* entropy*/)) } InitializeMenuScreen.prototype.inputChanged = function (event) { diff --git a/ui/app/keychains/hd/recover-seed/confirmation.js b/ui/app/keychains/hd/recover-seed/confirmation.js index 55b18025f..83dbc270f 100644 --- a/ui/app/keychains/hd/recover-seed/confirmation.js +++ b/ui/app/keychains/hd/recover-seed/confirmation.js @@ -3,7 +3,7 @@ const inherits = require('util').inherits const Component = require('react').Component const connect = require('react-redux').connect const h = require('react-hyperscript') -const actions = require('../actions') +const actions = require('../../../actions') module.exports = connect(mapStateToProps)(RevealSeedConfirmatoin) @@ -68,7 +68,7 @@ RevealSeedConfirmatoin.prototype.render = function () { style: { marginTop: '12px', }, - }, 'Enter the phrase "I understand" to proceed.'), + }, `Enter the phrase "${this.confirmationPhrase}" to proceed.`), // confirm confirmation h('input.large-input.letter-spacey', { diff --git a/ui/app/keychains/hd/restore-vault.js b/ui/app/keychains/hd/restore-vault.js index 4c1f21008..15690a159 100644 --- a/ui/app/keychains/hd/restore-vault.js +++ b/ui/app/keychains/hd/restore-vault.js @@ -1,8 +1,8 @@ const inherits = require('util').inherits -const PersistentForm = require('../../lib/persistent-form') +const PersistentForm = require('../../../lib/persistent-form') const connect = require('react-redux').connect const h = require('react-hyperscript') -const actions = require('../actions') +const actions = require('../../actions') module.exports = connect(mapStateToProps)(RestoreVaultScreen) @@ -96,7 +96,7 @@ RestoreVaultScreen.prototype.render = function () { // submit h('button.primary', { - onClick: this.restoreVault.bind(this), + onClick: this.createNewVaultAndRestore.bind(this), }, 'OK'), ]), @@ -116,7 +116,7 @@ RestoreVaultScreen.prototype.onMaybeCreate = function (event) { } } -RestoreVaultScreen.prototype.restoreVault = function () { +RestoreVaultScreen.prototype.createNewVaultAndRestore = function () { // check password var passwordBox = document.getElementById('password-box') var password = passwordBox.value @@ -144,5 +144,5 @@ RestoreVaultScreen.prototype.restoreVault = function () { // submit this.warning = null this.props.dispatch(actions.displayWarning(this.warning)) - this.props.dispatch(actions.recoverFromSeed(password, seed)) + this.props.dispatch(actions.createNewVaultAndRestore(password, seed)) }