Skip to content
This repository was archived by the owner on Feb 23, 2021. It is now read-only.

Commit 2794795

Browse files
authored
Merge pull request #612 from lightninglabs/restore-wallet
Restore wallet
2 parents e28354b + d778154 commit 2794795

File tree

15 files changed

+402
-27
lines changed

15 files changed

+402
-27
lines changed

src/action/nav.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ class NavAction {
1313
this._store.route = 'Loader';
1414
}
1515

16+
goSelectSeed() {
17+
this._store.route = 'SelectSeed';
18+
}
19+
1620
goSeed() {
1721
this._store.route = 'Seed';
1822
}
@@ -21,8 +25,12 @@ class NavAction {
2125
this._store.route = 'SeedVerify';
2226
}
2327

24-
goRestoreWallet() {
25-
// this._store.route = 'RestoreWallet';
28+
goRestoreSeed() {
29+
this._store.route = 'RestoreSeed';
30+
}
31+
32+
goRestorePassword() {
33+
this._store.route = 'RestorePassword';
2634
}
2735

2836
goSeedSuccess() {

src/action/wallet.js

Lines changed: 83 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@
55

66
import { observe, when } from 'mobx';
77
import { toBuffer, parseSat, checkHttpStatus, nap, poll } from '../helper';
8-
import { MIN_PASSWORD_LENGTH, NOTIFICATION_DELAY, RATE_DELAY } from '../config';
8+
import {
9+
MIN_PASSWORD_LENGTH,
10+
NOTIFICATION_DELAY,
11+
RATE_DELAY,
12+
RECOVERY_WINDOW,
13+
} from '../config';
914
import * as log from './log';
1015

1116
class WalletAction {
@@ -41,6 +46,16 @@ class WalletAction {
4146
this._store.wallet.seedVerify[index] = word.toLowerCase();
4247
}
4348

49+
/**
50+
* Set the restore seed input by the seed word and
51+
* seed index.
52+
* @param {string} options.word The seed word
53+
* @param {number} options.index The seed index
54+
*/
55+
setRestoreSeed({ word, index }) {
56+
this._store.wallet.restoreSeed[index] = word;
57+
}
58+
4459
//
4560
// Wallet Password actions
4661
//
@@ -82,6 +97,14 @@ class WalletAction {
8297
this._store.wallet.passwordVerify = password;
8398
}
8499

100+
/**
101+
* Set whether or not we're restoring the wallet.
102+
* @param {boolean} options.restoring Whether or not we're restoring.
103+
*/
104+
setRestoringWallet({ restoring }) {
105+
this._store.wallet.restoring = restoring;
106+
}
107+
85108
//
86109
// Wallet actions
87110
//
@@ -98,7 +121,7 @@ class WalletAction {
98121
this._store.firstStart = true;
99122
this._nav.goLoader();
100123
await nap(NOTIFICATION_DELAY);
101-
this._nav.goSeed();
124+
this._nav.goSelectSeed();
102125
} catch (err) {
103126
this.initPassword();
104127
}
@@ -181,18 +204,60 @@ class WalletAction {
181204
* screen.
182205
* @param {string} options.walletPassword The user chosen password
183206
* @param {Array} options.seedMnemonic The seed words to generate the wallet
207+
* @param {number} options.recoveryWindow The number of addresses to recover
184208
* @return {Promise<undefined>}
185209
*/
186-
async initWallet({ walletPassword, seedMnemonic }) {
210+
async initWallet({ walletPassword, seedMnemonic, recoveryWindow = 0 }) {
187211
try {
188212
await this._grpc.sendUnlockerCommand('InitWallet', {
189213
wallet_password: toBuffer(walletPassword),
190214
cipher_seed_mnemonic: seedMnemonic,
215+
recovery_window: recoveryWindow,
191216
});
192217
this._store.walletUnlocked = true;
193218
this._nav.goSeedSuccess();
194219
} catch (err) {
195-
this._notification.display({ msg: 'Initializing wallet failed', err });
220+
this._notification.display({
221+
type: 'error',
222+
msg: `Initializing wallet failed: ${err.details}`,
223+
});
224+
}
225+
}
226+
227+
/**
228+
* Initialize the restore wallet view by resetting input values and then
229+
* navigating to the view.
230+
* @return {undefined}
231+
*/
232+
initRestoreWallet() {
233+
this._store.wallet.restoreIndex = 0;
234+
this._nav.goRestoreSeed();
235+
}
236+
237+
/**
238+
* Initialize the next restore wallet view by setting a new restoreIndex or,
239+
* if all seed words have been entered, navigating to the password entry
240+
* view.
241+
* @return {undefined}
242+
*/
243+
initNextRestorePage() {
244+
if (this._store.wallet.restoreIndex < 21) {
245+
this._store.wallet.restoreIndex += 3;
246+
} else {
247+
this._nav.goRestorePassword();
248+
}
249+
}
250+
251+
/**
252+
* Initialize the previous restore wallet view by setting a new restoreIndex
253+
* or, if on the first seed entry page, navigating to the select seed view.
254+
* @return {undefined}
255+
*/
256+
initPrevRestorePage() {
257+
if (this._store.wallet.restoreIndex >= 3) {
258+
this._store.wallet.restoreIndex -= 3;
259+
} else {
260+
this._nav.goSelectSeed();
196261
}
197262
}
198263

@@ -205,6 +270,20 @@ class WalletAction {
205270
await this.unlockWallet({ walletPassword: password });
206271
}
207272

273+
/**
274+
* Initialize the wallet with the password input the seed that was already
275+
* inputted, and the default recovery window.
276+
* @return {Promise<undefined>}
277+
*/
278+
async restoreWallet() {
279+
const { password, restoreSeed } = this._store.wallet;
280+
await this.initWallet({
281+
walletPassword: password,
282+
seedMnemonic: restoreSeed.toJSON(),
283+
recoveryWindow: RECOVERY_WINDOW,
284+
});
285+
}
286+
208287
/**
209288
* Unlock the wallet by calling the grpc api with the user chosen password.
210289
* @param {string} options.walletPassword The password used to encrypt the wallet

src/computed/seed.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,19 @@ const ComputedSeed = store => {
1717
const c2 = formatOrdinal(seedVerifyIndexes[2]);
1818
return `Type the ${c0}, ${c1}, and ${c2} words of your recovery phrase.`;
1919
}),
20+
restoreIndexes: computed(() => [...Array(24).keys()].map(x => ++x)),
21+
restoreVerifyIndexes: computed(() => {
22+
const { restoreIndexes } = store;
23+
const { restoreIndex } = store.wallet;
24+
return restoreIndexes.slice(restoreIndex, restoreIndex + 3);
25+
}),
26+
restoreVerifyCopy: computed(() => {
27+
const { restoreVerifyIndexes } = store;
28+
const c0 = formatOrdinal(restoreVerifyIndexes[0]);
29+
const c1 = formatOrdinal(restoreVerifyIndexes[1]);
30+
const c2 = formatOrdinal(restoreVerifyIndexes[2]);
31+
return `Type the ${c0}, ${c1}, and ${c2} words of your recovery phrase.`;
32+
}),
2033
});
2134
};
2235

src/config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ module.exports.PREFIX_URI = `${prefixName}:`;
2222
module.exports.DEFAULT_ROUTE = 'Welcome';
2323
module.exports.MIN_PASSWORD_LENGTH = 8;
2424
module.exports.MAX_LOG_LENGTH = 10000;
25+
module.exports.RECOVERY_WINDOW = 250;
2526

2627
module.exports.UNITS = {
2728
sat: { display: 'SAT', displayLong: 'Satoshi', denominator: 1 },

src/store.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ export class Store {
4040
password: '',
4141
passwordVerify: '',
4242
seedVerify: ['', '', ''],
43+
restoring: false,
44+
restoreIndex: 0,
45+
restoreSeed: Array(24).fill(''),
4346
},
4447
transactions: [],
4548
selectedTransaction: null,

src/view/main.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@ import Container from '../component/container';
44
import { NotificationBar } from '../component/notification';
55
import Welcome from './welcome';
66
import Loader from './loader';
7+
import SelectSeed from './select-seed';
78
import Seed from './seed';
89
import SeedVerify from './seed-verify';
910
import SeedSuccess from './seed-success';
1011
import SetPassword from './set-password';
12+
import RestoreSeed from './restore-seed';
13+
import RestorePassword from './restore-password';
1114
import Password from './password';
1215
import NewAddress from './new-address';
1316
import LoaderSyncing from './loader-syncing';
@@ -56,6 +59,9 @@ class MainView extends Component {
5659
/>
5760
{route === 'Welcome' && <Welcome />}
5861
{route === 'Loader' && <Loader />}
62+
{route === 'SelectSeed' && (
63+
<SelectSeed store={store} wallet={wallet} nav={nav} />
64+
)}
5965
{route === 'Seed' && <Seed store={store} wallet={wallet} />}
6066
{route === 'SeedVerify' && (
6167
<SeedVerify store={store} nav={nav} wallet={wallet} />
@@ -64,6 +70,12 @@ class MainView extends Component {
6470
{route === 'SetPassword' && (
6571
<SetPassword store={store} wallet={wallet} />
6672
)}
73+
{route === 'RestoreSeed' && (
74+
<RestoreSeed store={store} wallet={wallet} />
75+
)}
76+
{route === 'RestorePassword' && (
77+
<RestorePassword store={store} wallet={wallet} nav={nav} />
78+
)}
6779
{route === 'Password' && <Password store={store} wallet={wallet} />}
6880
{route === 'NewAddress' && (
6981
<NewAddress store={store} invoice={invoice} info={info} />

src/view/restore-password.js

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import React from 'react';
2+
import { StyleSheet, View } from 'react-native';
3+
import { observer } from 'mobx-react';
4+
import PropTypes from 'prop-types';
5+
import Background from '../component/background';
6+
import MainContent from '../component/main-content';
7+
import { H1Text } from '../component/text';
8+
import { Header } from '../component/header';
9+
import { Button, BackButton, GlasButton } from '../component/button';
10+
import { InputField } from '../component/field';
11+
import Card from '../component/card';
12+
import { FormSubText, FormStretcher } from '../component/form';
13+
14+
//
15+
// Restore Wallet Password View
16+
//
17+
18+
const styles = StyleSheet.create({
19+
content: {
20+
justifyContent: 'flex-end',
21+
},
22+
title: {
23+
textAlign: 'center',
24+
marginBottom: 20,
25+
},
26+
card: {
27+
maxHeight: 350,
28+
maxWidth: 680,
29+
paddingLeft: 45,
30+
paddingRight: 45,
31+
paddingBottom: 50,
32+
},
33+
});
34+
35+
const RestorePasswordView = ({ store, wallet, nav }) => (
36+
<Background image="purple-gradient-bg">
37+
<Header>
38+
<BackButton onPress={() => nav.goSelectSeed()} />
39+
<Button disabled onPress={() => {}} />
40+
</Header>
41+
<MainContent style={styles.content}>
42+
<View>
43+
<H1Text style={styles.title}>Restore wallet</H1Text>
44+
</View>
45+
<Card style={styles.card}>
46+
<FormSubText>Please enter your password.</FormSubText>
47+
<FormStretcher>
48+
<InputField
49+
style={styles.input}
50+
placeholder="Password"
51+
secureTextEntry={true}
52+
autoFocus={true}
53+
value={store.wallet.password}
54+
onChangeText={password => wallet.setPassword({ password })}
55+
onSubmitEditing={() => wallet.restoreWallet()}
56+
/>
57+
</FormStretcher>
58+
</Card>
59+
<GlasButton onPress={() => wallet.restoreWallet()}>Restore</GlasButton>
60+
</MainContent>
61+
</Background>
62+
);
63+
64+
RestorePasswordView.propTypes = {
65+
store: PropTypes.object.isRequired,
66+
wallet: PropTypes.object.isRequired,
67+
nav: PropTypes.object.isRequired,
68+
};
69+
70+
export default observer(RestorePasswordView);

src/view/restore-wallet.js renamed to src/view/restore-seed.js

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { Header } from '../component/header';
1212
import Card from '../component/card';
1313

1414
//
15-
// Restore Wallet View
15+
// Restore Wallet Seed View
1616
//
1717

1818
const styles = StyleSheet.create({
@@ -32,38 +32,39 @@ const styles = StyleSheet.create({
3232
},
3333
});
3434

35-
const RestoreWalletView = ({ store, nav, wallet }) => (
35+
const RestoreSeedView = ({ store, wallet }) => (
3636
<Background image="purple-gradient-bg">
3737
<Header>
38-
<BackButton onPress={() => nav.goSeed()} />
38+
<BackButton onPress={() => wallet.initPrevRestorePage()} />
3939
<Button disabled onPress={() => {}} />
4040
</Header>
4141
<MainContent style={styles.content}>
4242
<View>
4343
<H1Text style={styles.title}>Restore your wallet</H1Text>
4444
</View>
4545
<Card style={styles.card}>
46-
<FormSubText>{store.seedVerifyCopy}</FormSubText>
47-
{store.seedVerifyIndexes.map((seedIndex, i) => (
46+
<FormSubText>{store.restoreVerifyCopy}</FormSubText>
47+
{store.restoreVerifyIndexes.map((seedIndex, i) => (
4848
<SeedEntry
4949
seedIndex={seedIndex}
50-
value={store.wallet.seedVerify[i]}
51-
onChangeText={word => wallet.setSeedVerify({ word, index: i })}
50+
value={store.wallet.restoreSeed[seedIndex - 1]}
51+
onChangeText={word =>
52+
wallet.setRestoreSeed({ word, index: seedIndex - 1 })
53+
}
5254
key={i}
5355
autoFocus={i === 0}
54-
onSubmitEditing={() => wallet.checkSeed()}
56+
onSubmitEditing={() => wallet.initNextRestorePage()}
5557
/>
5658
))}
5759
</Card>
58-
<GlasButton onPress={() => wallet.checkSeed()}>Next</GlasButton>
60+
<GlasButton onPress={() => wallet.initNextRestorePage()}>Next</GlasButton>
5961
</MainContent>
6062
</Background>
6163
);
6264

63-
RestoreWalletView.propTypes = {
65+
RestoreSeedView.propTypes = {
6466
store: PropTypes.object.isRequired,
65-
nav: PropTypes.object.isRequired,
6667
wallet: PropTypes.object.isRequired,
6768
};
6869

69-
export default observer(RestoreWalletView);
70+
export default observer(RestoreSeedView);

0 commit comments

Comments
 (0)