From 96643c222a74552d98218fe1f9fc81e493a1960f Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Mon, 31 Oct 2016 11:35:09 -0700 Subject: Implement seed word confirmation page. Remove logs. Move HD render files to ui/app. --- app/scripts/keyring-controller.js | 18 ++- app/scripts/lib/config-manager.js | 15 ++- app/scripts/lib/idStore-migrator.js | 5 - app/scripts/metamask-controller.js | 1 + keychains/bip44/create-vault-complete.js | 74 ------------ keychains/bip44/recover-seed/confirmation.js | 148 ----------------------- keychains/bip44/restore-vault.js | 148 ----------------------- ui/app/actions.js | 16 +++ ui/app/app.js | 7 ++ ui/app/keychains/hd/create-vault-complete.js | 73 +++++++++++ ui/app/keychains/hd/recover-seed/confirmation.js | 148 +++++++++++++++++++++++ ui/app/keychains/hd/restore-vault.js | 148 +++++++++++++++++++++++ 12 files changed, 421 insertions(+), 380 deletions(-) delete mode 100644 keychains/bip44/create-vault-complete.js delete mode 100644 keychains/bip44/recover-seed/confirmation.js delete mode 100644 keychains/bip44/restore-vault.js create mode 100644 ui/app/keychains/hd/create-vault-complete.js create mode 100644 ui/app/keychains/hd/recover-seed/confirmation.js create mode 100644 ui/app/keychains/hd/restore-vault.js diff --git a/app/scripts/keyring-controller.js b/app/scripts/keyring-controller.js index e7b9612bf..ee6445121 100644 --- a/app/scripts/keyring-controller.js +++ b/app/scripts/keyring-controller.js @@ -47,6 +47,7 @@ module.exports = class KeyringController extends EventEmitter { getState() { return { + seedWords: this.configManager.getSeedWords(), isInitialized: !!this.configManager.getVault(), isUnlocked: !!this.key, isConfirmed: true, // AUDIT this.configManager.getConfirmed(), @@ -90,10 +91,14 @@ module.exports = class KeyringController extends EventEmitter { this.configManager.setVault(encryptedString) if (!serialized) { - // TEMPORARY SINGLE-KEYRING CONFIG: - return this.addNewKeyring('HD Key Tree', null, (err, newState) => { - const firstAccount = this.keyrings[0].getAccounts()[0] - autoFaucet(ethUtil.addHexPrefix(firstAccount)) + 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 { @@ -470,6 +475,11 @@ module.exports = class KeyringController extends EventEmitter { return ethUtil.addHexPrefix(result.toString(16)) } + clearSeedWordCache(cb) { + this.configManager.setSeedWords(null) + cb(null, this.configManager.getSelectedAccount()) + } + } function noop () {} diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js index abe5dfc87..8f5590738 100644 --- a/app/scripts/lib/config-manager.js +++ b/app/scripts/lib/config-manager.js @@ -2,6 +2,7 @@ const Migrator = require('pojo-migrator') const MetamaskConfig = require('../config.js') const migrations = require('./migrations') const rp = require('request-promise') +const ethUtil = require('ethereumjs-util') const TESTNET_RPC = MetamaskConfig.network.testnet const MAINNET_RPC = MetamaskConfig.network.mainnet @@ -138,7 +139,7 @@ ConfigManager.prototype.getSelectedAccount = function () { ConfigManager.prototype.setSelectedAccount = function (address) { var config = this.getConfig() - config.selectedAccount = address + config.selectedAccount = ethUtil.addHexPrefix(address) this.setConfig(config) } @@ -153,11 +154,23 @@ ConfigManager.prototype.setShowSeedWords = function (should) { this.setData(data) } + ConfigManager.prototype.getShouldShowSeedWords = function () { var data = this.migrator.getData() return data.showSeedWords } +ConfigManager.prototype.setSeedWords = function (words) { + var data = this.getData() + data.seedWords = words + this.setData(data) +} + +ConfigManager.prototype.getSeedWords = function () { + var data = this.getData() + return ('seedWords' in data) && data.seedWords +} + ConfigManager.prototype.getCurrentRpcAddress = function () { var provider = this.getProvider() if (!provider) return null diff --git a/app/scripts/lib/idStore-migrator.js b/app/scripts/lib/idStore-migrator.js index f8f7cb51a..c81e7ddfe 100644 --- a/app/scripts/lib/idStore-migrator.js +++ b/app/scripts/lib/idStore-migrator.js @@ -11,9 +11,6 @@ module.exports = class IdentityStoreMigrator { oldSeedForPassword( password ) { const isOldVault = this.hasOldVault() if (!isOldVault) { - console.log('does not seem to have old vault') - console.log('THE DATA:') - console.log(this.configManager.getData()) return Promise.resolve(null) } @@ -31,7 +28,6 @@ module.exports = class IdentityStoreMigrator { serializeVault() { const mnemonic = this.idStore._idmgmt.getSeed() - console.dir(this.idStore._idmgmt) const n = this.idStore._getAddresses().length return { @@ -42,7 +38,6 @@ module.exports = class IdentityStoreMigrator { hasOldVault() { const wallet = this.configManager.getWallet() - console.log('found old wallet: ' + wallet) return wallet } } diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index a7d9a8ec2..48c56d915 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -60,6 +60,7 @@ module.exports = class MetamaskController { // forward directly to keyringController createNewVault: keyringController.createNewVault.bind(keyringController), + clearSeedWordCache: keyringController.clearSeedWordCache.bind(keyringController), addNewKeyring: keyringController.addNewKeyring.bind(keyringController), addNewAccount: keyringController.addNewAccount.bind(keyringController), submitPassword: keyringController.submitPassword.bind(keyringController), diff --git a/keychains/bip44/create-vault-complete.js b/keychains/bip44/create-vault-complete.js deleted file mode 100644 index 2b5413955..000000000 --- a/keychains/bip44/create-vault-complete.js +++ /dev/null @@ -1,74 +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)(CreateVaultCompleteScreen) - -inherits(CreateVaultCompleteScreen, Component) -function CreateVaultCompleteScreen () { - Component.call(this) -} - -function mapStateToProps (state) { - return { - seed: state.appState.currentView.seedWords, - cachedSeed: state.metamask.seedWords, - } -} - -CreateVaultCompleteScreen.prototype.render = function () { - var state = this.props - var seed = state.seed || state.cachedSeed - - return ( - - h('.initialize-screen.flex-column.flex-center.flex-grow', [ - - // // subtitle and nav - // h('.section-title.flex-row.flex-center', [ - // h('h2.page-subtitle', 'Vault Created'), - // ]), - - h('h3.flex-center.text-transform-uppercase', { - style: { - background: '#EBEBEB', - color: '#AEAEAE', - marginTop: 36, - marginBottom: 8, - width: '100%', - fontSize: '20px', - padding: 6, - }, - }, [ - 'Vault Created', - ]), - - h('span.error', { // Error for the right red - style: { - padding: '12px 20px 0px 20px', - textAlign: 'center', - }, - }, 'These 12 words can restore all of your MetaMask accounts for this vault.\nSave them somewhere safe and secret.'), - - h('textarea.twelve-word-phrase', { - readOnly: true, - value: seed, - }), - - h('button.primary', { - onClick: () => this.confirmSeedWords(), - style: { - margin: '24px', - fontSize: '0.9em', - }, - }, 'I\'ve copied it somewhere safe'), - ]) - ) -} - -CreateVaultCompleteScreen.prototype.confirmSeedWords = function () { - this.props.dispatch(actions.confirmSeedWords()) -} - diff --git a/keychains/bip44/recover-seed/confirmation.js b/keychains/bip44/recover-seed/confirmation.js deleted file mode 100644 index 55b18025f..000000000 --- a/keychains/bip44/recover-seed/confirmation.js +++ /dev/null @@ -1,148 +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)(RevealSeedConfirmatoin) - -inherits(RevealSeedConfirmatoin, Component) -function RevealSeedConfirmatoin () { - Component.call(this) -} - -function mapStateToProps (state) { - return { - warning: state.appState.warning, - } -} - -RevealSeedConfirmatoin.prototype.confirmationPhrase = 'I understand' - -RevealSeedConfirmatoin.prototype.render = function () { - const props = this.props - const state = this.state - - 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, - }, - }, [ - 'Reveal Seed Words', - ]), - - h('.div', { - style: { - display: 'flex', - flexDirection: 'column', - padding: '20px', - justifyContent: 'center', - }, - }, [ - - h('h4', 'Do not recover your seed words in a public place! These words can be used to steal all your accounts.'), - - // confirmation - h('input.large-input.letter-spacey', { - type: 'password', - id: 'password-box', - placeholder: 'Enter your password to confirm', - onKeyPress: this.checkConfirmation.bind(this), - style: { - width: 260, - marginTop: '12px', - }, - }), - - h(`h4${state && state.confirmationWrong ? '.error' : ''}`, { - style: { - marginTop: '12px', - }, - }, 'Enter the phrase "I understand" to proceed.'), - - // confirm confirmation - h('input.large-input.letter-spacey', { - type: 'text', - id: 'confirm-box', - placeholder: this.confirmationPhrase, - onKeyPress: this.checkConfirmation.bind(this), - style: { - width: 260, - marginTop: 16, - }, - }), - - h('.flex-row.flex-space-between', { - style: { - marginTop: 30, - width: '50%', - }, - }, [ -// cancel - h('button.primary', { - onClick: this.goHome.bind(this), - }, 'CANCEL'), - - // submit - h('button.primary', { - onClick: this.revealSeedWords.bind(this), - }, 'OK'), - - ]), - - (props.warning) && ( - h('span.error', { - style: { - margin: '20px', - }, - }, props.warning.split('-')) - ), - - props.inProgress && ( - h('span.in-progress-notification', 'Generating Seed...') - ), - ]), - ]) - ) -} - -RevealSeedConfirmatoin.prototype.componentDidMount = function () { - document.getElementById('password-box').focus() -} - -RevealSeedConfirmatoin.prototype.goHome = function () { - this.props.dispatch(actions.showConfigPage(false)) -} - -// create vault - -RevealSeedConfirmatoin.prototype.checkConfirmation = function (event) { - if (event.key === 'Enter') { - event.preventDefault() - this.revealSeedWords() - } -} - -RevealSeedConfirmatoin.prototype.revealSeedWords = function () { - this.setState({ confirmationWrong: false }) - - const confirmBox = document.getElementById('confirm-box') - const confirmation = confirmBox.value - if (confirmation !== this.confirmationPhrase) { - confirmBox.value = '' - return this.setState({ confirmationWrong: true }) - } - - var password = document.getElementById('password-box').value - this.props.dispatch(actions.requestRevealSeed(password)) -} diff --git a/keychains/bip44/restore-vault.js b/keychains/bip44/restore-vault.js deleted file mode 100644 index 4c1f21008..000000000 --- a/keychains/bip44/restore-vault.js +++ /dev/null @@ -1,148 +0,0 @@ -const inherits = require('util').inherits -const PersistentForm = require('../../lib/persistent-form') -const connect = require('react-redux').connect -const h = require('react-hyperscript') -const actions = require('../actions') - -module.exports = connect(mapStateToProps)(RestoreVaultScreen) - -inherits(RestoreVaultScreen, PersistentForm) -function RestoreVaultScreen () { - PersistentForm.call(this) -} - -function mapStateToProps (state) { - return { - warning: state.appState.warning, - } -} - -RestoreVaultScreen.prototype.render = function () { - var state = this.props - this.persistentFormParentId = 'restore-vault-form' - - 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, - }, - }, [ - 'Restore Vault', - ]), - - // wallet seed entry - h('h3', 'Wallet Seed'), - h('textarea.twelve-word-phrase.letter-spacey', { - dataset: { - persistentFormId: 'wallet-seed', - }, - placeholder: 'Enter your secret twelve word phrase here to restore your vault.', - }), - - // password - h('input.large-input.letter-spacey', { - type: 'password', - id: 'password-box', - placeholder: 'New Password (min 8 chars)', - dataset: { - persistentFormId: 'password', - }, - style: { - width: 260, - marginTop: 12, - }, - }), - - // confirm password - h('input.large-input.letter-spacey', { - type: 'password', - id: 'password-box-confirm', - placeholder: 'Confirm Password', - onKeyPress: this.onMaybeCreate.bind(this), - dataset: { - persistentFormId: 'password-confirmation', - }, - style: { - width: 260, - marginTop: 16, - }, - }), - - (state.warning) && ( - h('span.error.in-progress-notification', state.warning) - ), - - // submit - - 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.restoreVault.bind(this), - }, 'OK'), - - ]), - - ]) - - ) -} - -RestoreVaultScreen.prototype.showInitializeMenu = function () { - this.props.dispatch(actions.showInitializeMenu()) -} - -RestoreVaultScreen.prototype.onMaybeCreate = function (event) { - if (event.key === 'Enter') { - this.restoreVault() - } -} - -RestoreVaultScreen.prototype.restoreVault = function () { - // check password - var passwordBox = document.getElementById('password-box') - var password = passwordBox.value - var passwordConfirmBox = document.getElementById('password-box-confirm') - var passwordConfirm = passwordConfirmBox.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 - } - // check seed - var seedBox = document.querySelector('textarea.twelve-word-phrase') - var seed = seedBox.value.trim() - if (seed.split(' ').length !== 12) { - this.warning = 'seed phrases are 12 words long' - this.props.dispatch(actions.displayWarning(this.warning)) - return - } - // submit - this.warning = null - this.props.dispatch(actions.displayWarning(this.warning)) - this.props.dispatch(actions.recoverFromSeed(password, seed)) -} diff --git a/ui/app/actions.js b/ui/app/actions.js index a2f59cb3c..5068d1848 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -80,6 +80,7 @@ var actions = { viewPendingTx: viewPendingTx, VIEW_PENDING_TX: 'VIEW_PENDING_TX', // app messages + confirmSeedWords: confirmSeedWords, showAccountDetail: showAccountDetail, BACK_TO_ACCOUNT_DETAIL: 'BACK_TO_ACCOUNT_DETAIL', backToAccountDetail: backToAccountDetail, @@ -172,6 +173,21 @@ function tryUnlockMetamask (password) { } } +function confirmSeedWords () { + return (dispatch) => { + dispatch(actions.showLoadingIndication()) + background.clearSeedWordCache((err, account) => { + dispatch(actions.hideLoadingIndication()) + if (err) { + return dispatch(actions.showWarning(err.message)) + } + + console.log('Seed word cache cleared. ' + account) + dispatch(actions.showAccountDetail(account)) + }) + } +} + function createNewVault (password, entropy) { return (dispatch) => { // dispatch(actions.createNewVaultInProgress()) diff --git a/ui/app/app.js b/ui/app/app.js index 588aa9896..f3ad8efc3 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -27,6 +27,8 @@ const NetworkIndicator = require('./components/network') 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') + module.exports = connect(mapStateToProps)(App) inherits(App, Component) @@ -35,6 +37,7 @@ function App () { Component.call(this) } function mapStateToProps (state) { return { // state from plugin + seedWords: state.metamask.seedWords, isLoading: state.appState.isLoading, isConfirmed: state.metamask.isConfirmed, isInitialized: state.metamask.isInitialized, @@ -392,6 +395,10 @@ App.prototype.renderPrimary = function () { return h(DisclaimerScreen, {key: 'disclaimerScreen'}) } + if (props.seedWords) { + return h(HDCreateVaultComplete, {key: 'HDCreateVaultComplete'}) + } + // show initialize screen if (!props.isInitialized || props.forgottenPassword) { diff --git a/ui/app/keychains/hd/create-vault-complete.js b/ui/app/keychains/hd/create-vault-complete.js new file mode 100644 index 000000000..7272ebdbd --- /dev/null +++ b/ui/app/keychains/hd/create-vault-complete.js @@ -0,0 +1,73 @@ +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)(CreateVaultCompleteScreen) + +inherits(CreateVaultCompleteScreen, Component) +function CreateVaultCompleteScreen () { + Component.call(this) +} + +function mapStateToProps (state) { + return { + seed: state.appState.currentView.seedWords, + cachedSeed: state.metamask.seedWords, + } +} + +CreateVaultCompleteScreen.prototype.render = function () { + var state = this.props + var seed = state.seed || state.cachedSeed + + return ( + + h('.initialize-screen.flex-column.flex-center.flex-grow', [ + + // // subtitle and nav + // h('.section-title.flex-row.flex-center', [ + // h('h2.page-subtitle', 'Vault Created'), + // ]), + + h('h3.flex-center.text-transform-uppercase', { + style: { + background: '#EBEBEB', + color: '#AEAEAE', + marginTop: 36, + marginBottom: 8, + width: '100%', + fontSize: '20px', + padding: 6, + }, + }, [ + 'Vault Created', + ]), + + h('span.error', { // Error for the right red + style: { + padding: '12px 20px 0px 20px', + textAlign: 'center', + }, + }, 'These 12 words can restore all of your MetaMask accounts for this vault.\nSave them somewhere safe and secret.'), + + h('textarea.twelve-word-phrase', { + readOnly: true, + value: seed, + }), + + h('button.primary', { + onClick: () => this.confirmSeedWords(), + style: { + margin: '24px', + fontSize: '0.9em', + }, + }, 'I\'ve copied it somewhere safe'), + ]) + ) +} + +CreateVaultCompleteScreen.prototype.confirmSeedWords = function () { + this.props.dispatch(actions.confirmSeedWords()) +} diff --git a/ui/app/keychains/hd/recover-seed/confirmation.js b/ui/app/keychains/hd/recover-seed/confirmation.js new file mode 100644 index 000000000..55b18025f --- /dev/null +++ b/ui/app/keychains/hd/recover-seed/confirmation.js @@ -0,0 +1,148 @@ +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)(RevealSeedConfirmatoin) + +inherits(RevealSeedConfirmatoin, Component) +function RevealSeedConfirmatoin () { + Component.call(this) +} + +function mapStateToProps (state) { + return { + warning: state.appState.warning, + } +} + +RevealSeedConfirmatoin.prototype.confirmationPhrase = 'I understand' + +RevealSeedConfirmatoin.prototype.render = function () { + const props = this.props + const state = this.state + + 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, + }, + }, [ + 'Reveal Seed Words', + ]), + + h('.div', { + style: { + display: 'flex', + flexDirection: 'column', + padding: '20px', + justifyContent: 'center', + }, + }, [ + + h('h4', 'Do not recover your seed words in a public place! These words can be used to steal all your accounts.'), + + // confirmation + h('input.large-input.letter-spacey', { + type: 'password', + id: 'password-box', + placeholder: 'Enter your password to confirm', + onKeyPress: this.checkConfirmation.bind(this), + style: { + width: 260, + marginTop: '12px', + }, + }), + + h(`h4${state && state.confirmationWrong ? '.error' : ''}`, { + style: { + marginTop: '12px', + }, + }, 'Enter the phrase "I understand" to proceed.'), + + // confirm confirmation + h('input.large-input.letter-spacey', { + type: 'text', + id: 'confirm-box', + placeholder: this.confirmationPhrase, + onKeyPress: this.checkConfirmation.bind(this), + style: { + width: 260, + marginTop: 16, + }, + }), + + h('.flex-row.flex-space-between', { + style: { + marginTop: 30, + width: '50%', + }, + }, [ +// cancel + h('button.primary', { + onClick: this.goHome.bind(this), + }, 'CANCEL'), + + // submit + h('button.primary', { + onClick: this.revealSeedWords.bind(this), + }, 'OK'), + + ]), + + (props.warning) && ( + h('span.error', { + style: { + margin: '20px', + }, + }, props.warning.split('-')) + ), + + props.inProgress && ( + h('span.in-progress-notification', 'Generating Seed...') + ), + ]), + ]) + ) +} + +RevealSeedConfirmatoin.prototype.componentDidMount = function () { + document.getElementById('password-box').focus() +} + +RevealSeedConfirmatoin.prototype.goHome = function () { + this.props.dispatch(actions.showConfigPage(false)) +} + +// create vault + +RevealSeedConfirmatoin.prototype.checkConfirmation = function (event) { + if (event.key === 'Enter') { + event.preventDefault() + this.revealSeedWords() + } +} + +RevealSeedConfirmatoin.prototype.revealSeedWords = function () { + this.setState({ confirmationWrong: false }) + + const confirmBox = document.getElementById('confirm-box') + const confirmation = confirmBox.value + if (confirmation !== this.confirmationPhrase) { + confirmBox.value = '' + return this.setState({ confirmationWrong: true }) + } + + var password = document.getElementById('password-box').value + this.props.dispatch(actions.requestRevealSeed(password)) +} diff --git a/ui/app/keychains/hd/restore-vault.js b/ui/app/keychains/hd/restore-vault.js new file mode 100644 index 000000000..4c1f21008 --- /dev/null +++ b/ui/app/keychains/hd/restore-vault.js @@ -0,0 +1,148 @@ +const inherits = require('util').inherits +const PersistentForm = require('../../lib/persistent-form') +const connect = require('react-redux').connect +const h = require('react-hyperscript') +const actions = require('../actions') + +module.exports = connect(mapStateToProps)(RestoreVaultScreen) + +inherits(RestoreVaultScreen, PersistentForm) +function RestoreVaultScreen () { + PersistentForm.call(this) +} + +function mapStateToProps (state) { + return { + warning: state.appState.warning, + } +} + +RestoreVaultScreen.prototype.render = function () { + var state = this.props + this.persistentFormParentId = 'restore-vault-form' + + 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, + }, + }, [ + 'Restore Vault', + ]), + + // wallet seed entry + h('h3', 'Wallet Seed'), + h('textarea.twelve-word-phrase.letter-spacey', { + dataset: { + persistentFormId: 'wallet-seed', + }, + placeholder: 'Enter your secret twelve word phrase here to restore your vault.', + }), + + // password + h('input.large-input.letter-spacey', { + type: 'password', + id: 'password-box', + placeholder: 'New Password (min 8 chars)', + dataset: { + persistentFormId: 'password', + }, + style: { + width: 260, + marginTop: 12, + }, + }), + + // confirm password + h('input.large-input.letter-spacey', { + type: 'password', + id: 'password-box-confirm', + placeholder: 'Confirm Password', + onKeyPress: this.onMaybeCreate.bind(this), + dataset: { + persistentFormId: 'password-confirmation', + }, + style: { + width: 260, + marginTop: 16, + }, + }), + + (state.warning) && ( + h('span.error.in-progress-notification', state.warning) + ), + + // submit + + 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.restoreVault.bind(this), + }, 'OK'), + + ]), + + ]) + + ) +} + +RestoreVaultScreen.prototype.showInitializeMenu = function () { + this.props.dispatch(actions.showInitializeMenu()) +} + +RestoreVaultScreen.prototype.onMaybeCreate = function (event) { + if (event.key === 'Enter') { + this.restoreVault() + } +} + +RestoreVaultScreen.prototype.restoreVault = function () { + // check password + var passwordBox = document.getElementById('password-box') + var password = passwordBox.value + var passwordConfirmBox = document.getElementById('password-box-confirm') + var passwordConfirm = passwordConfirmBox.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 + } + // check seed + var seedBox = document.querySelector('textarea.twelve-word-phrase') + var seed = seedBox.value.trim() + if (seed.split(' ').length !== 12) { + this.warning = 'seed phrases are 12 words long' + this.props.dispatch(actions.displayWarning(this.warning)) + return + } + // submit + this.warning = null + this.props.dispatch(actions.displayWarning(this.warning)) + this.props.dispatch(actions.recoverFromSeed(password, seed)) +} -- cgit