Browse Source

feat(retype-seed): select 3 random seed indexes for the user to enter

renovate/lint-staged-8.x
Jack Mallers 7 years ago
committed by Ben Woosley
parent
commit
c446567dda
No known key found for this signature in database GPG Key ID: 6EE5F3785F78B345
  1. 9
      app/components/Onboarding/Onboarding.js
  2. 67
      app/components/Onboarding/ReEnterSeed.js
  3. 20
      app/components/Onboarding/ReEnterSeed.scss
  4. 10
      app/components/Onboarding/RecoverForm.js
  5. 18
      app/containers/Root.js
  6. 74
      app/reducers/onboarding.js

9
app/components/Onboarding/Onboarding.js

@ -163,7 +163,7 @@ const Onboarding = ({
description="Enter your cipherseed passphrase (or just submit if you don't have one)"
back={() => changeStep(5)}
next={() => {
const recoverySeed = recoverFormProps.seedInput.map(input => input.word)
const recoverySeed = recoverFormProps.recoverSeedInput.map(input => input.word)
submitNewWallet(createWalletPassword, recoverySeed, aezeedPassword)
}}
@ -185,8 +185,11 @@ const Onboarding = ({
case 7:
return (
<FormContainer
title="Re-enter your seed"
description="Yeah I know, might be annoying, but just to be safe!"
title="Retype your 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 ${
reEnterSeedProps.seedIndexesArr[0]
}, ${reEnterSeedProps.seedIndexesArr[1]} and ${reEnterSeedProps.seedIndexesArr[2]}`}
back={() => changeStep(6)}
next={() => {
// don't allow them to move on if they havent re-entered the seed correctly

67
app/components/Onboarding/ReEnterSeed.js

@ -2,36 +2,49 @@ import React from 'react'
import PropTypes from 'prop-types'
import styles from './ReEnterSeed.scss'
const ReEnterSeed = ({ seed, seedInput, updateSeedInput }) => (
<div className={styles.container}>
<ul className={styles.seedContainer}>
{seed.map((word, index) => (
<li key={index}>
<section>
<label htmlFor={word}>{index + 1}</label>
</section>
<section>
<input
type="text"
id={word}
placeholder="word"
value={seedInput[index] ? seedInput[index].word : ''}
onChange={event => updateSeedInput({ word: event.target.value, index })}
className={`${styles.word} ${
seedInput[index] && word === seedInput[index].word ? styles.valid : styles.invalid
}`}
/>
</section>
</li>
))}
</ul>
</div>
)
class ReEnterSeed extends React.Component {
componentWillMount() {
this.props.setReEnterSeedIndexes()
}
render() {
const { seed, reEnterSeedInput, updateReEnterSeedInput, seedIndexesArr } = this.props
return (
<div className={styles.container}>
<ul className={styles.seedContainer}>
{seedIndexesArr.map(index => (
<li key={index}>
<section>
<label htmlFor={index}>{index}</label>
</section>
<section>
<input
type="text"
id={index}
value={reEnterSeedInput[index] ? reEnterSeedInput[index] : ''}
onChange={event => updateReEnterSeedInput({ word: event.target.value, index })}
className={`${styles.word} ${
reEnterSeedInput[index] && seed[index - 1] === reEnterSeedInput[index]
? styles.valid
: styles.invalid
}`}
/>
</section>
</li>
))}
</ul>
</div>
)
}
}
ReEnterSeed.propTypes = {
seed: PropTypes.array.isRequired,
seedInput: PropTypes.array.isRequired,
updateSeedInput: PropTypes.func.isRequired
reEnterSeedInput: PropTypes.object.isRequired,
updateReEnterSeedInput: PropTypes.func.isRequired,
setReEnterSeedIndexes: PropTypes.func.isRequired,
seedIndexesArr: PropTypes.array.isRequired
}
export default ReEnterSeed

20
app/components/Onboarding/ReEnterSeed.scss

@ -1,14 +1,19 @@
@import '../../variables.scss';
.seedContainer {
position: relative;
display: inline-block;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
font-size: 12px;
margin-top: 10%;
li {
display: inline-block;
margin: 5px 0;
margin: 10px;
width: 25%;
border: 0.2px solid #ccc;
padding: 5px 10px;
section {
display: inline-block;
@ -16,30 +21,25 @@
color: $white;
&:nth-child(1) {
width: 15%;
text-align: center;
opacity: 0.5;
}
&:nth-child(2) {
width: calc(85% - 10px);
margin: 0 5px;
}
}
}
}
.word {
margin: 0 3px;
background-color: #1c1e26;
background-color: inherit;
outline: 0;
border: none;
padding: 5px 10px;
color: $white;
font-family: courier;
font-family: 'Courier';
font-family: 'Courier', courier, sans-serif;
&.valid {
color: $green;

10
app/components/Onboarding/RecoverForm.js

@ -2,7 +2,7 @@ import React from 'react'
import PropTypes from 'prop-types'
import styles from './RecoverForm.scss'
const RecoverForm = ({ seedInput, updateSeedInput }) => (
const RecoverForm = ({ recoverSeedInput, updateRecoverSeedInput }) => (
<div className={styles.container}>
<ul className={styles.seedContainer}>
{Array(24)
@ -17,8 +17,8 @@ const RecoverForm = ({ seedInput, updateSeedInput }) => (
type="text"
id={index}
placeholder="word"
value={seedInput[index] ? seedInput[index].word : ''}
onChange={event => updateSeedInput({ word: event.target.value, index })}
value={recoverSeedInput[index] ? recoverSeedInput[index].word : ''}
onChange={event => updateRecoverSeedInput({ word: event.target.value, index })}
className={styles.word}
/>
</section>
@ -29,8 +29,8 @@ const RecoverForm = ({ seedInput, updateSeedInput }) => (
)
RecoverForm.propTypes = {
seedInput: PropTypes.array.isRequired,
updateSeedInput: PropTypes.func.isRequired
recoverSeedInput: PropTypes.array.isRequired,
updateRecoverSeedInput: PropTypes.func.isRequired
}
export default RecoverForm

18
app/containers/Root.js

@ -27,7 +27,9 @@ import {
unlockWallet,
setSignupCreate,
setSignupImport,
updateSeedInput
updateReEnterSeedInput,
updateRecoverSeedInput,
setReEnterSeedIndexes
} from '../reducers/onboarding'
import { fetchBlockHeight, lndSelectors } from '../reducers/lnd'
import { newAddress } from '../reducers/address'
@ -52,8 +54,10 @@ const mapDispatchToProps = {
unlockWallet,
setSignupCreate,
setSignupImport,
updateSeedInput,
newAddress,
updateReEnterSeedInput,
updateRecoverSeedInput,
setReEnterSeedIndexes,
fetchBlockHeight
}
@ -151,15 +155,17 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
}
const recoverFormProps = {
seedInput: stateProps.onboarding.seedInput,
updateSeedInput: dispatchProps.updateSeedInput
recoverSeedInput: stateProps.onboarding.recoverSeedInput,
updateRecoverSeedInput: dispatchProps.updateRecoverSeedInput
}
const reEnterSeedProps = {
seed: stateProps.onboarding.seed,
seedInput: stateProps.onboarding.seedInput,
reEnterSeedInput: stateProps.onboarding.reEnterSeedInput,
seedIndexesArr: stateProps.onboarding.seedIndexesArr,
reEnterSeedChecker: stateProps.reEnterSeedChecker,
updateSeedInput: dispatchProps.updateSeedInput
updateReEnterSeedInput: dispatchProps.updateReEnterSeedInput,
setReEnterSeedIndexes: dispatchProps.setReEnterSeedIndexes
}
const onboardingProps = {

74
app/reducers/onboarding.js

@ -19,7 +19,8 @@ export const UPDATE_CREATE_WALLET_PASSWORD_CONFIRMATION =
'UPDATE_CREATE_WALLET_PASSWORD_CONFIRMATION'
export const UPDATE_AEZEED_PASSWORD = 'UPDATE_AEZEED_PASSWORD'
export const UPDATE_AEZEED_PASSWORD_CONFIRMATION = 'UPDATE_AEZEED_PASSWORD_CONFIRMATION'
export const UPDATE_SEED_INPUT = 'UPDATE_SEED_INPUT'
export const UPDATE_RE_ENTER_SEED_INPUT = 'UPDATE_RE_ENTER_SEED_INPUT'
export const UPDATE_RECOVER_SEED_INPUT = 'UPDATE_RECOVER_SEED_INPUT'
export const CHANGE_STEP = 'CHANGE_STEP'
@ -28,6 +29,7 @@ export const SET_AUTOPILOT = 'SET_AUTOPILOT'
export const FETCH_SEED = 'FETCH_SEED'
export const SET_SEED = 'SET_SEED'
export const SET_HAS_SEED = 'SET_HAS_SEED'
export const SET_RE_ENTER_SEED_INDEXES = 'SET_RE_ENTER_SEED_INDEXES'
export const ONBOARDING_STARTED = 'ONBOARDING_STARTED'
export const ONBOARDING_FINISHED = 'ONBOARDING_FINISHED'
@ -114,9 +116,16 @@ export function updateAezeedPasswordConfirmation(aezeedPasswordConfirmation) {
}
}
export function updateSeedInput(inputSeedObj) {
export function updateReEnterSeedInput(inputSeedObj) {
return {
type: UPDATE_SEED_INPUT,
type: UPDATE_RE_ENTER_SEED_INPUT,
inputSeedObj
}
}
export function updateRecoverSeedInput(inputSeedObj) {
return {
type: UPDATE_RECOVER_SEED_INPUT,
inputSeedObj
}
}
@ -156,6 +165,28 @@ export function startLnd(options) {
}
}
export function setReEnterSeedIndexes() {
// we only want the user to have to verify 3 random indexes from the seed they were just given
const INDEX_AMOUNT = 3
const seedIndexesArr = []
while (seedIndexesArr.length < INDEX_AMOUNT) {
// add 1 because we dont want this to be 0 index based
const ranNum = Math.floor(Math.random() * 24) + 1
if (seedIndexesArr.indexOf(ranNum) > -1) {
continue
}
seedIndexesArr[seedIndexesArr.length] = ranNum
}
return {
type: SET_RE_ENTER_SEED_INDEXES,
seedIndexesArr
}
}
export const submitNewWallet = (
wallet_password,
cipher_seed_mnemonic,
@ -238,15 +269,22 @@ const ACTION_HANDLERS = {
...state,
aezeedPasswordConfirmation
}),
[UPDATE_SEED_INPUT]: (state, { inputSeedObj }) => ({
[UPDATE_RE_ENTER_SEED_INPUT]: (state, { inputSeedObj }) => ({
...state,
reEnterSeedInput: { ...state.reEnterSeedInput, [inputSeedObj.index]: inputSeedObj.word }
}),
[UPDATE_RECOVER_SEED_INPUT]: (state, { inputSeedObj }) => ({
...state,
seedInput: Object.assign([], state.seedInput, { [inputSeedObj.index]: inputSeedObj })
recoverSeedInput: Object.assign([], state.recoverSeedInput, {
[inputSeedObj.index]: inputSeedObj
})
}),
[SET_AUTOPILOT]: (state, { autopilot }) => ({ ...state, autopilot }),
[SET_HAS_SEED]: (state, { hasSeed }) => ({ ...state, hasSeed }),
[SET_SEED]: (state, { seed }) => ({ ...state, seed, fetchingSeed: false }),
[SET_RE_ENTER_SEED_INDEXES]: (state, { seedIndexesArr }) => ({ ...state, seedIndexesArr }),
[CHANGE_STEP]: (state, { step }) => ({ ...state, step }),
@ -285,7 +323,8 @@ const aezeedPasswordSelector = state => state.onboarding.aezeedPassword
const aezeedPasswordConfirmationSelector = state => state.onboarding.aezeedPasswordConfirmation
const seedSelector = state => state.onboarding.seed
const seedInputSelector = state => state.onboarding.seedInput
const seedIndexesArrSelector = state => state.onboarding.seedIndexesArr
const reEnterSeedInputSelector = state => state.onboarding.reEnterSeedInput
onboardingSelectors.passwordIsValid = createSelector(
passwordSelector,
@ -306,9 +345,13 @@ onboardingSelectors.showAezeedPasswordConfirmationError = createSelector(
onboardingSelectors.reEnterSeedChecker = createSelector(
seedSelector,
seedInputSelector,
(seed, seedInput) =>
seed.length === seedInput.length && seed.every((word, i) => word === seedInput[i].word)
seedIndexesArrSelector,
reEnterSeedInputSelector,
(seed, seedIndexArr, reEnterSeedInput) =>
seedIndexArr.length === Object.keys(reEnterSeedInput).length &&
seedIndexArr.every(
index => reEnterSeedInput[index] && reEnterSeedInput[index] === seed[index - 1]
)
)
export { onboardingSelectors }
@ -346,10 +389,15 @@ const initialState = {
message: ''
},
// array of inputs for when the user re-enters their seed
// object has a word attr and a index attr:
// { word: 'foo', index: 0 }
seedInput: [],
seedIndexesArr: [],
// object of inputs for when the user re-enters their seed
// {
// index: word,
// index: word,
// index: word
// }
reEnterSeedInput: {},
recoverSeedInput: [],
// step where the user decides whether they want a newly created seed or to import an existing one
signupForm: {
create: false,

Loading…
Cancel
Save