Merge pull request #240 from MetaMask/RecoverSeed

reveal Vault Recovery Seed
feature/default_network_editable
kumavis 9 years ago
commit 3b3c472dee
  1. 2
      CHANGELOG.md
  2. 2
      app/scripts/background.js
  3. 11
      app/scripts/lib/idStore.js
  4. 27
      ui/app/actions.js
  5. 4
      ui/app/app.js
  6. 30
      ui/app/config.js
  7. 4
      ui/app/css/index.css
  8. 2
      ui/app/first-time/create-vault-complete.js
  9. 149
      ui/app/recover-seed/confirmation.js
  10. 20
      ui/app/reducers/app.js

@ -2,6 +2,8 @@
## Current Master ## Current Master
- Added seed word recovery to config screen.
## 2.2.0 2016-06-02 ## 2.2.0 2016-06-02
- Redesigned init, vault create, vault restore and seed confirmation screens. - Redesigned init, vault create, vault restore and seed confirmation screens.

@ -195,6 +195,8 @@ function setupControllerConnection(stream){
exportAccount: idStore.exportAccount.bind(idStore), exportAccount: idStore.exportAccount.bind(idStore),
revealAccount: idStore.revealAccount.bind(idStore), revealAccount: idStore.revealAccount.bind(idStore),
saveAccountLabel: idStore.saveAccountLabel.bind(idStore), saveAccountLabel: idStore.saveAccountLabel.bind(idStore),
tryPassword: idStore.tryPassword.bind(idStore),
recoverSeed: idStore.recoverSeed.bind(idStore),
}) })
stream.pipe(dnode).pipe(stream) stream.pipe(dnode).pipe(stream)
dnode.on('remote', function(remote){ dnode.on('remote', function(remote){

@ -59,6 +59,13 @@ IdentityStore.prototype.createNewVault = function(password, entropy, cb){
}) })
} }
IdentityStore.prototype.recoverSeed = function(cb){
configManager.setShowSeedWords(true)
if (!this._idmgmt) return cb(new Error('Unauthenticated. Please sign in.'))
var seedWords = this._idmgmt.getSeed()
cb(null, seedWords)
}
IdentityStore.prototype.recoverFromSeed = function(password, seed, cb){ IdentityStore.prototype.recoverFromSeed = function(password, seed, cb){
this._createIdmgmt(password, seed, null, (err) => { this._createIdmgmt(password, seed, null, (err) => {
if (err) return cb(err) if (err) return cb(err)
@ -150,7 +157,7 @@ IdentityStore.prototype.setLocked = function(cb){
} }
IdentityStore.prototype.submitPassword = function(password, cb){ IdentityStore.prototype.submitPassword = function(password, cb){
this._tryPassword(password, (err) => { this.tryPassword(password, (err) => {
if (err) return cb(err) if (err) return cb(err)
// load identities before returning... // load identities before returning...
this._loadIdentities() this._loadIdentities()
@ -366,7 +373,7 @@ IdentityStore.prototype._mayBeFauceting = function(i) {
// keyStore managment - unlocking + deserialization // keyStore managment - unlocking + deserialization
// //
IdentityStore.prototype._tryPassword = function(password, cb){ IdentityStore.prototype.tryPassword = function(password, cb){
this._createIdmgmt(password, null, null, cb) this._createIdmgmt(password, null, null, cb)
} }

@ -29,6 +29,10 @@ var actions = {
createNewVaultInProgress: createNewVaultInProgress, createNewVaultInProgress: createNewVaultInProgress,
showNewVaultSeed: showNewVaultSeed, showNewVaultSeed: showNewVaultSeed,
showInfoPage: showInfoPage, showInfoPage: showInfoPage,
// seed recovery actions
REVEAL_SEED_CONFIRMATION: 'REVEAL_SEED_CONFIRMATION',
revealSeedConfirmation: revealSeedConfirmation,
requestRevealSeed: requestRevealSeed,
// unlock screen // unlock screen
UNLOCK_IN_PROGRESS: 'UNLOCK_IN_PROGRESS', UNLOCK_IN_PROGRESS: 'UNLOCK_IN_PROGRESS',
UNLOCK_FAILED: 'UNLOCK_FAILED', UNLOCK_FAILED: 'UNLOCK_FAILED',
@ -155,6 +159,26 @@ function createNewVault(password, entropy) {
} }
} }
function revealSeedConfirmation() {
return {
type: this.REVEAL_SEED_CONFIRMATION,
}
}
function requestRevealSeed(password) {
return (dispatch) => {
dispatch(actions.showLoadingIndication())
_accountManager.tryPassword(password, (err, seed) => {
dispatch(actions.hideLoadingIndication())
if (err) return dispatch(actions.displayWarning(err.message))
_accountManager.recoverSeed((err, seed) => {
if (err) return dispatch(actions.displayWarning(err.message))
dispatch(actions.showNewVaultSeed(seed))
})
})
}
}
function recoverFromSeed(password, seed) { function recoverFromSeed(password, seed) {
return (dispatch) => { return (dispatch) => {
// dispatch(actions.createNewVaultInProgress()) // dispatch(actions.createNewVaultInProgress())
@ -402,9 +426,10 @@ function previousTx() {
} }
} }
function showConfigPage() { function showConfigPage(transitionForward = true) {
return { return {
type: actions.SHOW_CONFIG_PAGE, type: actions.SHOW_CONFIG_PAGE,
value: transitionForward,
} }
} }

@ -21,6 +21,7 @@ const SendTransactionScreen = require('./send')
const ConfirmTxScreen = require('./conf-tx') const ConfirmTxScreen = require('./conf-tx')
// other views // other views
const ConfigScreen = require('./config') const ConfigScreen = require('./config')
const RevealSeedConfirmation = require('./recover-seed/confirmation')
const InfoScreen = require('./info') const InfoScreen = require('./info')
const LoadingIndicator = require('./loading') const LoadingIndicator = require('./loading')
const txHelper = require('../lib/tx-helper') const txHelper = require('../lib/tx-helper')
@ -232,6 +233,9 @@ App.prototype.renderPrimary = function(){
case 'config': case 'config':
return h(ConfigScreen, {key: 'config'}) return h(ConfigScreen, {key: 'config'})
case 'reveal-seed-conf':
return h(RevealSeedConfirmation, {key: 'reveal-seed-conf'})
case 'info': case 'info':
return h(InfoScreen, {key: 'info'}) return h(InfoScreen, {key: 'info'})

@ -78,7 +78,7 @@ ConfigScreen.prototype.render = function() {
]), ]),
h('div', [ h('div', [
h('button', { h('button.spaced', {
style: { style: {
alignSelf: 'center', alignSelf: 'center',
}, },
@ -86,11 +86,11 @@ ConfigScreen.prototype.render = function() {
event.preventDefault() event.preventDefault()
state.dispatch(actions.setProviderType('mainnet')) state.dispatch(actions.setProviderType('mainnet'))
} }
}, 'Use Main Network') }, 'Use Main Network'),
]), ]),
h('div', [ h('div', [
h('button', { h('button.spaced', {
style: { style: {
alignSelf: 'center', alignSelf: 'center',
}, },
@ -98,11 +98,11 @@ ConfigScreen.prototype.render = function() {
event.preventDefault() event.preventDefault()
state.dispatch(actions.setProviderType('testnet')) state.dispatch(actions.setProviderType('testnet'))
} }
}, 'Use Morden Test Network') }, 'Use Morden Test Network'),
]), ]),
h('div', [ h('div', [
h('button', { h('button.spaced', {
style: { style: {
alignSelf: 'center', alignSelf: 'center',
}, },
@ -110,7 +110,25 @@ ConfigScreen.prototype.render = function() {
event.preventDefault() event.preventDefault()
state.dispatch(actions.setRpcTarget('http://localhost:8545/')) state.dispatch(actions.setRpcTarget('http://localhost:8545/'))
} }
}, 'Use http://localhost:8545') }, 'Use http://localhost:8545'),
]),
h('hr.horizontal-line'),
h('div', {
style: {
marginTop: '20px',
}
}, [
h('button', {
style: {
alignSelf: 'center',
},
onClick(event) {
event.preventDefault()
state.dispatch(actions.revealSeedConfirmation())
}
}, 'Reveal Seed Words')
]), ]),
]), ]),

@ -45,6 +45,10 @@ button {
transition: transform 50ms ease-in; transition: transform 50ms ease-in;
} }
button.spaced {
margin: 2px;
}
button:hover { button:hover {
transform: scale(1.1); transform: scale(1.1);
} }

@ -14,7 +14,7 @@ function CreateVaultCompleteScreen() {
function mapStateToProps(state) { function mapStateToProps(state) {
return { return {
seed: state.appState.currentView.context, seed: state.appState.currentView.seedWords,
cachedSeed: state.metamask.seedWords, cachedSeed: state.metamask.seedWords,
} }
} }

@ -0,0 +1,149 @@
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))
}

@ -25,10 +25,11 @@ function reduceApp(state, action) {
} }
// confirm seed words // confirm seed words
var seedWords = state.metamask.seedWords
var seedConfView = { var seedConfView = {
name: 'createVaultComplete', name: 'createVaultComplete',
seedWords,
} }
var seedWords = state.metamask.seedWords
var appState = extend({ var appState = extend({
menuOpen: false, menuOpen: false,
@ -85,7 +86,7 @@ function reduceApp(state, action) {
name: 'config', name: 'config',
context: appState.currentView.context, context: appState.currentView.context,
}, },
transForward: true, transForward: action.value,
}) })
case actions.SHOW_INFO_PAGE: case actions.SHOW_INFO_PAGE:
@ -111,7 +112,7 @@ function reduceApp(state, action) {
return extend(appState, { return extend(appState, {
currentView: { currentView: {
name: 'createVaultComplete', name: 'createVaultComplete',
context: action.value, seedWords: action.value,
}, },
transForward: true, transForward: true,
isLoading: false, isLoading: false,
@ -144,6 +145,18 @@ function reduceApp(state, action) {
warning: null, warning: null,
}) })
// reveal seed words
case actions.REVEAL_SEED_CONFIRMATION:
return extend(appState, {
currentView: {
name: 'reveal-seed-conf',
},
transForward: true,
warning: null,
})
// accounts // accounts
case actions.SET_SELECTED_ACCOUNT: case actions.SET_SELECTED_ACCOUNT:
@ -198,6 +211,7 @@ function reduceApp(state, action) {
return extend(appState, { return extend(appState, {
currentView: { currentView: {
name: seedWords ? 'createVaultComplete' : 'accounts', name: seedWords ? 'createVaultComplete' : 'accounts',
seedWords,
}, },
transForward: true, transForward: true,
isLoading: false, isLoading: false,

Loading…
Cancel
Save