Skip to content
This repository has been archived by the owner on Nov 17, 2023. It is now read-only.

Commit

Permalink
feat(onboarding): ability to import encrypted seed
Browse files Browse the repository at this point in the history
If the import fails with an error, check the error message to determine
the reason for the failure. If the reason was that an invalid passphrase
was provided, present the user with a password field where they can
enter the cipher seed passphrase.

This does not change the standard import flow. It adds a new step to the
end of the process only if we detect that the import failed due to a
missing passphrase.

Fix #2071
  • Loading branch information
mrfelton committed Apr 24, 2019
1 parent 9d5da9f commit 8ed8241
Show file tree
Hide file tree
Showing 10 changed files with 283 additions and 48 deletions.
23 changes: 22 additions & 1 deletion renderer/components/Onboarding/Onboarding.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class Onboarding extends React.Component {
// STATE
autopilot: PropTypes.bool, // eslint-disable-line react/boolean-prop-naming
clearStartLndError: PropTypes.func.isRequired,
clearWalletRecoveryError: PropTypes.func.isRequired,
connectionCert: PropTypes.string,
connectionHost: PropTypes.string,
connectionMacaroon: PropTypes.string,
Expand All @@ -35,10 +36,12 @@ class Onboarding extends React.Component {
fetchSeed: PropTypes.func.isRequired,
isFetchingSeed: PropTypes.bool,
isLightningGrpcActive: PropTypes.bool,
isRecoveringWallet: PropTypes.bool,
isWalletUnlockerGrpcActive: PropTypes.bool,
lndConnect: PropTypes.string,
name: PropTypes.string,
network: PropTypes.string,
passphrase: PropTypes.string,
recoverOldWallet: PropTypes.func.isRequired,
resetOnboarding: PropTypes.func.isRequired,

Expand All @@ -53,6 +56,7 @@ class Onboarding extends React.Component {
setLndconnect: PropTypes.func.isRequired,
setName: PropTypes.func.isRequired,
setNetwork: PropTypes.func.isRequired,
setPassphrase: PropTypes.func.isRequired,
setPassword: PropTypes.func.isRequired,
setSeed: PropTypes.func.isRequired,
setUnlockWalletError: PropTypes.func.isRequired,
Expand All @@ -66,6 +70,7 @@ class Onboarding extends React.Component {
validateCert: PropTypes.func.isRequired,
validateHost: PropTypes.func.isRequired,
validateMacaroon: PropTypes.func.isRequired,
walletRecoveryError: PropTypes.string,
}

componentWillUnmount() {
Expand Down Expand Up @@ -106,15 +111,19 @@ class Onboarding extends React.Component {
connectionString,
isLightningGrpcActive,
isWalletUnlockerGrpcActive,
passphrase,
seed,
startLndHostError,
startLndCertError,
startLndMacaroonError,
unlockWalletError,
isFetchingSeed,
isRecoveringWallet,
walletRecoveryError,
lndConnect,

// DISPATCH
clearWalletRecoveryError,
setAutopilot,
setConnectionType,
setConnectionHost,
Expand All @@ -125,6 +134,7 @@ class Onboarding extends React.Component {
setNetwork,
setUnlockWalletError,
setPassword,
setPassphrase,
setSeed,
clearStartLndError,
setLndconnect,
Expand Down Expand Up @@ -181,7 +191,18 @@ class Onboarding extends React.Component {
{...{ network, setNetwork, setAutopilot }}
/>,
<Wizard.Step key="Autopilot" component={Autopilot} {...{ autopilot, setAutopilot }} />,
<Wizard.Step key="WalletRecover" component={WalletRecover} {...{ recoverOldWallet }} />,
<Wizard.Step
key="WalletRecover"
component={WalletRecover}
{...{
clearWalletRecoveryError,
isRecoveringWallet,
passphrase,
recoverOldWallet,
setPassphrase,
walletRecoveryError,
}}
/>,
]
break

Expand Down
125 changes: 108 additions & 17 deletions renderer/components/Onboarding/Steps/WalletRecover.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import React from 'react'
import PropTypes from 'prop-types'
import { FormattedMessage } from 'react-intl'
import { Form, Spinner, Text } from 'components/UI'
import { FormattedMessage, intlShape, injectIntl } from 'react-intl'
import { Bar, Form, Header, Message, PasswordInput, Spinner, Text } from 'components/UI'
import messages from './messages'

const isInvalidPassphrase = error => error === 'invalid passphrase'

class WalletRecover extends React.Component {
static propTypes = {
clearWalletRecoveryError: PropTypes.func.isRequired,
intl: intlShape.isRequired,
isRecoveringWallet: PropTypes.bool,
passphrase: PropTypes.string,
recoverOldWallet: PropTypes.func.isRequired,
setPassphrase: PropTypes.func.isRequired,
walletRecoveryError: PropTypes.string,
wizardApi: PropTypes.object,
wizardState: PropTypes.object,
}
Expand All @@ -20,18 +28,67 @@ class WalletRecover extends React.Component {
this.formApi.submitForm()
}

handleSubmit = () => {
const { recoverOldWallet } = this.props
componentDidUpdate(prevProps) {
const { isRecoveringWallet, walletRecoveryError } = this.props

// Handle success case.
if (!walletRecoveryError && !isRecoveringWallet && prevProps.isRecoveringWallet) {
this.handleSuccess()
}

// Handle failure case.
if (walletRecoveryError && !isRecoveringWallet && prevProps.isRecoveringWallet) {
this.handleError()
}
}

componentWillUnmount() {
const { clearWalletRecoveryError } = this.props
clearWalletRecoveryError()
}

handleSubmit = values => {
const { recoverOldWallet, setPassphrase } = this.props
const { passphrase } = values
if (passphrase) {
setPassphrase(passphrase)
}
recoverOldWallet()
}

handleSuccess = () => {
const { wizardApi } = this.props
wizardApi.onSubmit()
}

handleError = () => {
const { passphrase, wizardApi, walletRecoveryError } = this.props
wizardApi.onSubmitFailure()

// If the user entered an incorrect passphrase, set the error on the passphrase form element.
if (passphrase && walletRecoveryError && isInvalidPassphrase(walletRecoveryError)) {
this.formApi.setError('passphrase', walletRecoveryError)
}
}

setFormApi = formApi => {
this.formApi = formApi
}

render() {
const { wizardApi, wizardState, recoverOldWallet, ...rest } = this.props
const { getApi, onChange, onSubmit, onSubmitFailure } = wizardApi
const {
wizardApi,
wizardState,
passphrase,
recoverOldWallet,
setPassphrase,
clearWalletRecoveryError,
isRecoveringWallet,
walletRecoveryError,
intl,
...rest
} = this.props
const { getApi, onChange, onSubmitFailure } = wizardApi
const { currentItem } = wizardState
return (
<Form
Expand All @@ -43,21 +100,55 @@ class WalletRecover extends React.Component {
}
}}
onChange={onChange && (formState => onChange(formState, currentItem))}
onSubmit={values => {
this.handleSubmit(values)
if (onSubmit) {
onSubmit(values)
}
}}
onSubmit={this.handleSubmit}
onSubmitFailure={onSubmitFailure}
>
<Text textAlign="center">
<Spinner />
<FormattedMessage {...messages.importing_wallet} />
</Text>
{({ formState }) => {
const shouldValidateInline = formState.submits > 0
return (
<>
<Header
align="left"
subtitle={<FormattedMessage {...messages.importing_wallet_subtitle} />}
title={<FormattedMessage {...messages.importing_wallet_title} />}
/>

<Bar my={4} />

{isRecoveringWallet && (
<Text textAlign="center">
<Spinner />
<FormattedMessage {...messages.importing_wallet} />
</Text>
)}

{!isRecoveringWallet && walletRecoveryError && (
<>
{isInvalidPassphrase(walletRecoveryError) ? (
<PasswordInput
autoComplete="current-password"
description={intl.formatMessage({ ...messages.passphrase_description })}
field="passphrase"
isRequired
label={<FormattedMessage {...messages.passphrase_label} />}
minLength={1}
name="passphrase"
placeholder={intl.formatMessage({ ...messages.passphrase_placeholder })}
validateOnBlur={shouldValidateInline}
validateOnChange={shouldValidateInline}
willAutoFocus
/>
) : (
<Message variant="error">{walletRecoveryError}</Message>
)}
</>
)}
</>
)
}}
</Form>
)
}
}

export default WalletRecover
export default injectIntl(WalletRecover)
7 changes: 7 additions & 0 deletions renderer/components/Onboarding/Steps/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ export default defineMessages({
password_placeholder: 'Enter your password',
password_description:
'You would have set your password when first creating your wallet. This is separate from your 24 word seed.',
passphrase_label: 'Passphrase',
passphrase_placeholder: 'Enter your cipher seed passphrase',
passphrase_description:
'Your seed is encrypted. Please enter the passphrase that you set when creating the wallet.',
retype_seed_description:
"Your seed is important! If you lose your seed you'll have no way to recover your wallet. To make sure that you have properly saved your seed, please retype words {word1}, {word2} & {word3}",
retype_seed_title: 'Retype your seed',
Expand Down Expand Up @@ -90,4 +94,7 @@ export default defineMessages({
word_placeholder: 'word',
generating_seed: 'Generating Seed...',
importing_wallet: 'Importing wallet...',
importing_wallet_title: 'Importing wallet',
importing_wallet_subtitle:
'Please wait whilst we start the wallet import process. This should only take a moment.',
})
7 changes: 7 additions & 0 deletions renderer/containers/Onboarding.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
setName,
setNetwork,
setPassword,
setPassphrase,
setSeed,
setLndconnect,
validateHost,
Expand All @@ -28,6 +29,7 @@ import {
startLnd,
stopLnd,
fetchSeed,
clearWalletRecoveryError,
createNewWallet,
recoverOldWallet,
clearStartLndError,
Expand All @@ -46,8 +48,11 @@ const mapStateToProps = state => ({
connectionString: state.onboarding.connectionString,
lndConnect: state.onboarding.lndConnect,
network: state.onboarding.network,
isRecoveringWallet: state.lnd.isRecoveringWallet,
walletRecoveryError: state.lnd.walletRecoveryError,
isLightningGrpcActive: state.lnd.isLightningGrpcActive,
isWalletUnlockerGrpcActive: state.lnd.isWalletUnlockerGrpcActive,
passphrase: state.onboarding.passphrase,
startLndHostError: lndSelectors.startLndHostError(state),
startLndCertError: lndSelectors.startLndCertError(state),
startLndMacaroonError: lndSelectors.startLndMacaroonError(state),
Expand All @@ -57,6 +62,7 @@ const mapStateToProps = state => ({
})

const mapDispatchToProps = {
clearWalletRecoveryError,
setAlias,
setAutopilot,
setConnectionType,
Expand All @@ -67,6 +73,7 @@ const mapDispatchToProps = {
setName,
setNetwork,
setPassword,
setPassphrase,
setSeed,
clearStartLndError,
setUnlockWalletError,
Expand Down
Loading

0 comments on commit 8ed8241

Please sign in to comment.