aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Finlay <dan@danfinlay.com>2016-06-03 07:52:18 +0800
committerDan Finlay <dan@danfinlay.com>2016-06-03 07:52:18 +0800
commit9cc04be5e467ea6f41584c42cc742680163a6fe6 (patch)
tree1dfa5a964ac95efb3c85a0c5559d1413c6918e94
parent01e63d41ede22a7290553f888261583d22b24eda (diff)
downloadtangerine-wallet-browser-9cc04be5e467ea6f41584c42cc742680163a6fe6.tar
tangerine-wallet-browser-9cc04be5e467ea6f41584c42cc742680163a6fe6.tar.gz
tangerine-wallet-browser-9cc04be5e467ea6f41584c42cc742680163a6fe6.tar.bz2
tangerine-wallet-browser-9cc04be5e467ea6f41584c42cc742680163a6fe6.tar.lz
tangerine-wallet-browser-9cc04be5e467ea6f41584c42cc742680163a6fe6.tar.xz
tangerine-wallet-browser-9cc04be5e467ea6f41584c42cc742680163a6fe6.tar.zst
tangerine-wallet-browser-9cc04be5e467ea6f41584c42cc742680163a6fe6.zip
Added seed word recovery to config screen
-rw-r--r--CHANGELOG.md2
-rw-r--r--app/scripts/background.js8
-rw-r--r--app/scripts/lib/idStore.js11
-rw-r--r--ui/app/actions.js27
-rw-r--r--ui/app/app.js4
-rw-r--r--ui/app/config.js30
-rw-r--r--ui/app/css/index.css4
-rw-r--r--ui/app/recover-seed/confirmation.js150
-rw-r--r--ui/app/reducers/app.js14
9 files changed, 237 insertions, 13 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cd5495c94..c011ea6af 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,8 @@
## Current Master
+- Added seed word recovery to config screen.
+
## 2.2.0 2016-06-02
- Redesigned init, vault create, vault restore and seed confirmation screens.
diff --git a/app/scripts/background.js b/app/scripts/background.js
index bfd1fc92b..432040c53 100644
--- a/app/scripts/background.js
+++ b/app/scripts/background.js
@@ -145,7 +145,7 @@ function setupPublicConfig(stream){
}
function setupProviderConnection(stream, originDomain){
-
+
stream.on('data', function onRpcRequest(payload){
// Append origin to rpc payload
payload.origin = originDomain
@@ -195,6 +195,8 @@ function setupControllerConnection(stream){
exportAccount: idStore.exportAccount.bind(idStore),
revealAccount: idStore.revealAccount.bind(idStore),
saveAccountLabel: idStore.saveAccountLabel.bind(idStore),
+ tryPassword: idStore.tryPassword.bind(idStore),
+ recoverSeed: idStore.recoverSeed.bind(idStore),
})
stream.pipe(dnode).pipe(stream)
dnode.on('remote', function(remote){
@@ -246,7 +248,7 @@ function newUnsignedTransaction(txParams, cb){
})
var txId = idStore.addUnconfirmedTransaction(txParams, cb)
} else {
- addUnconfirmedTx(txParams, cb)
+ addUnconfirmedTx(txParams, cb)
}
}
@@ -258,7 +260,7 @@ function newUnsignedMessage(msgParams, cb){
})
var msgId = idStore.addUnconfirmedMessage(msgParams, cb)
} else {
- addUnconfirmedMsg(msgParams, cb)
+ addUnconfirmedMsg(msgParams, cb)
}
}
diff --git a/app/scripts/lib/idStore.js b/app/scripts/lib/idStore.js
index 4ce4fd6f2..991827603 100644
--- a/app/scripts/lib/idStore.js
+++ b/app/scripts/lib/idStore.js
@@ -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){
this._createIdmgmt(password, seed, null, (err) => {
if (err) return cb(err)
@@ -150,7 +157,7 @@ IdentityStore.prototype.setLocked = function(cb){
}
IdentityStore.prototype.submitPassword = function(password, cb){
- this._tryPassword(password, (err) => {
+ this.tryPassword(password, (err) => {
if (err) return cb(err)
// load identities before returning...
this._loadIdentities()
@@ -366,7 +373,7 @@ IdentityStore.prototype._mayBeFauceting = function(i) {
// keyStore managment - unlocking + deserialization
//
-IdentityStore.prototype._tryPassword = function(password, cb){
+IdentityStore.prototype.tryPassword = function(password, cb){
this._createIdmgmt(password, null, null, cb)
}
diff --git a/ui/app/actions.js b/ui/app/actions.js
index ae6125b20..684d33f3d 100644
--- a/ui/app/actions.js
+++ b/ui/app/actions.js
@@ -29,6 +29,10 @@ var actions = {
createNewVaultInProgress: createNewVaultInProgress,
showNewVaultSeed: showNewVaultSeed,
showInfoPage: showInfoPage,
+ // seed recovery actions
+ REVEAL_SEED_CONFIRMATION: 'REVEAL_SEED_CONFIRMATION',
+ revealSeedConfirmation: revealSeedConfirmation,
+ requestRevealSeed: requestRevealSeed,
// unlock screen
UNLOCK_IN_PROGRESS: 'UNLOCK_IN_PROGRESS',
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) {
return (dispatch) => {
// dispatch(actions.createNewVaultInProgress())
@@ -402,9 +426,10 @@ function previousTx() {
}
}
-function showConfigPage() {
+function showConfigPage(transitionForward = true) {
return {
type: actions.SHOW_CONFIG_PAGE,
+ value: transitionForward,
}
}
diff --git a/ui/app/app.js b/ui/app/app.js
index 7e7ca24ad..094cae76c 100644
--- a/ui/app/app.js
+++ b/ui/app/app.js
@@ -21,6 +21,7 @@ const SendTransactionScreen = require('./send')
const ConfirmTxScreen = require('./conf-tx')
// other views
const ConfigScreen = require('./config')
+const RevealSeedConfirmation = require('./recover-seed/confirmation')
const InfoScreen = require('./info')
const LoadingIndicator = require('./loading')
const txHelper = require('../lib/tx-helper')
@@ -232,6 +233,9 @@ App.prototype.renderPrimary = function(){
case 'config':
return h(ConfigScreen, {key: 'config'})
+ case 'reveal-seed-conf':
+ return h(RevealSeedConfirmation, {key: 'reveal-seed-conf'})
+
case 'info':
return h(InfoScreen, {key: 'info'})
diff --git a/ui/app/config.js b/ui/app/config.js
index ddf158325..c4d473b10 100644
--- a/ui/app/config.js
+++ b/ui/app/config.js
@@ -78,7 +78,7 @@ ConfigScreen.prototype.render = function() {
]),
h('div', [
- h('button', {
+ h('button.spaced', {
style: {
alignSelf: 'center',
},
@@ -86,11 +86,11 @@ ConfigScreen.prototype.render = function() {
event.preventDefault()
state.dispatch(actions.setProviderType('mainnet'))
}
- }, 'Use Main Network')
+ }, 'Use Main Network'),
]),
h('div', [
- h('button', {
+ h('button.spaced', {
style: {
alignSelf: 'center',
},
@@ -98,11 +98,11 @@ ConfigScreen.prototype.render = function() {
event.preventDefault()
state.dispatch(actions.setProviderType('testnet'))
}
- }, 'Use Morden Test Network')
+ }, 'Use Morden Test Network'),
]),
h('div', [
- h('button', {
+ h('button.spaced', {
style: {
alignSelf: 'center',
},
@@ -110,7 +110,25 @@ ConfigScreen.prototype.render = function() {
event.preventDefault()
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')
]),
]),
diff --git a/ui/app/css/index.css b/ui/app/css/index.css
index f56e9fbc4..4fd51ac95 100644
--- a/ui/app/css/index.css
+++ b/ui/app/css/index.css
@@ -45,6 +45,10 @@ button {
transition: transform 50ms ease-in;
}
+button.spaced {
+ margin: 2px;
+}
+
button:hover {
transform: scale(1.1);
}
diff --git a/ui/app/recover-seed/confirmation.js b/ui/app/recover-seed/confirmation.js
new file mode 100644
index 000000000..c7a99ad00
--- /dev/null
+++ b/ui/app/recover-seed/confirmation.js
@@ -0,0 +1,150 @@
+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', {
+ style: {
+ marginTop: '12px',
+ color: state && state.confirmationWrong ? 'red' : 'black',
+ }
+ }, `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/reducers/app.js b/ui/app/reducers/app.js
index 08c2268c1..58303f630 100644
--- a/ui/app/reducers/app.js
+++ b/ui/app/reducers/app.js
@@ -85,7 +85,7 @@ function reduceApp(state, action) {
name: 'config',
context: appState.currentView.context,
},
- transForward: true,
+ transForward: action.value,
})
case actions.SHOW_INFO_PAGE:
@@ -144,6 +144,18 @@ function reduceApp(state, action) {
warning: null,
})
+ // reveal seed words
+
+ case actions.REVEAL_SEED_CONFIRMATION:
+ return extend(appState, {
+ currentView: {
+ name: 'reveal-seed-conf',
+ },
+ transForward: true,
+ warning: null,
+ })
+
+
// accounts
case actions.SET_SELECTED_ACCOUNT: