FIX: Backup flow for wallets with password on their mnemonics

This commit is contained in:
Gabriel Bazán 2018-01-16 13:00:57 -03:00
parent bb6c087fe7
commit eb91cc9927
3 changed files with 47 additions and 50 deletions

View File

@ -9,14 +9,19 @@
<ion-content> <ion-content>
<div *ngIf="deleted"> <div *ngIf="deleted">
<h1 translate>Wallet recovery phrase not available.</h1> <h1 class="deleted-title">{{'Wallet recovery phrase not available' |translate}}</h1>
<p translate>You can still export it from Advanced &gt; Export.</p> <ion-item-divider text-wrap>
{{'You can still export it from Advanced &gt; Export.' |translate}}
<span *ngIf="wallet.coin == 'bch'" translate>
Note: if this BCH wallet was duplicated from a BTC wallet, they share the same recovery phrase.
</span>
</ion-item-divider>
</div> </div>
<ion-slides pager="true" *ngIf="!deleted"> <ion-slides pager="true" *ngIf="!deleted">
<ion-slide> <ion-slide>
<div *ngIf="mnemonicWords && mnemonicWords[0] || !credentialsEncrypted"> <div *ngIf="mnemonicWords && mnemonicWords[0] || !credentialsEncrypted">
<h4 translate>Please carefully write down this phrase.</h4> <h4 class="slide-title" translate>Please carefully write down this phrase.</h4>
<div copy-to-clipboard="{{ copyRecoveryPhrase() }}" class="phrase"> <div copy-to-clipboard="{{ copyRecoveryPhrase() }}" class="phrase">
<span *ngFor="let word of mnemonicWords"> <span *ngFor="let word of mnemonicWords">
<span>{{word}}</span> <span>{{word}}</span>
@ -25,20 +30,20 @@
</div> </div>
<div class="bottom-absolute"> <div class="bottom-absolute">
<div class="tldr" translate>We'll confirm on the next screen.</div> <ion-item-divider>{{"We'll confirm on the next screen." | translate}}</ion-item-divider>
<button ion-button block [disabled]="credentialsEncrypted || error" (click)="slideNext()" translate>I've written it down</button> <button ion-button block [disabled]="credentialsEncrypted || error" (click)="slideNext()" translate>I've written it down</button>
</div> </div>
</div> </div>
</ion-slide> </ion-slide>
<ion-slide> <ion-slide>
<h1 translate>Let's verify your backup phrase.</h1> <h4 class="slide-title" translate>Let's verify your backup phrase.</h4>
<div class="phrase"> <div class="phrase">
<button ion-button outline *ngFor="let customWord of customWords; let i = index" (click)="removeButton(i, customWord)">{{customWord.word}}</button> <button ion-button outline *ngFor="let customWord of customWords; let i = index" (click)="removeButton(i, customWord)">{{customWord.word}}</button>
</div> </div>
<div class="bottom-absolute"> <div class="bottom-absolute">
<div *ngIf="!selectComplete"> <div *ngIf="!selectComplete">
<div class="tldr" translate>Please tap each word in the correct order.</div> <ion-item-divider>{{'Please tap each word in the correct order.' | translate}}</ion-item-divider>
<div> <div>
<button ion-button *ngFor="let shuffledWord of shuffledMnemonicWords; let i = index" (click)="addButton(i, shuffledWord)" <button ion-button *ngFor="let shuffledWord of shuffledMnemonicWords; let i = index" (click)="addButton(i, shuffledWord)"
[disabled]="shuffledWord.selected">{{shuffledWord.word}} [disabled]="shuffledWord.selected">{{shuffledWord.word}}
@ -47,7 +52,7 @@
</div> </div>
<div *ngIf="selectComplete"> <div *ngIf="selectComplete">
<div class="tldr" translate>Is this correct?</div> <ion-item-divider>{{'Is this correct?' | translate}}</ion-item-divider>
<div> <div>
<button ion-button block (click)="slideNext();" translate>Confirm</button> <button ion-button block (click)="slideNext();" translate>Confirm</button>
<button ion-button block outline (click)="setFlow();" translate>Clear</button> <button ion-button block outline (click)="setFlow();" translate>Clear</button>
@ -57,18 +62,19 @@
</ion-slide> </ion-slide>
<ion-slide *ngIf="wallet.mnemonicHasPassphrase"> <ion-slide *ngIf="wallet.mnemonicHasPassphrase">
<h1 translate>Enter your password</h1> <h4 translate>Enter your password</h4>
<p translate>In order to verify your wallet backup, please type your password.</p> <ion-item-divider text-wrap>{{'In order to verify your wallet backup, please type your password.' | translate}}</ion-item-divider>
<div class="input"> <ion-item>
<input type="password" id="passphrase" [(ngModel)]="passphrase" autocapitalize="off" spellcheck="false" /> <ion-label stacked translate>Password</ion-label>
</div> <ion-input type="password" id="password" [(ngModel)]="password" autocapitalize="off" spellcheck="false"></ion-input>
</ion-item>
<div class="password-required" translate> <ion-item-divider text-wrap class="assertive">
This recovery phrase was created with a password. To recover this wallet both the recovery phrase and password are needed. {{'This recovery phrase was created with a password. To recover this wallet both the recovery phrase and password are needed.'
</div> | translate}}
</ion-item-divider>
<div class="bottom-absolute"> <div class="bottom-absolute">
<button ion-button block [disabled]="!passphrase" (click)="finalStep();" translate>Confirm</button> <button ion-button block [disabled]="!password" (click)="finalStep()" translate>Confirm</button>
</div> </div>
</ion-slide> </ion-slide>
</ion-slides> </ion-slides>

View File

@ -1,9 +1,9 @@
page-backup-game { page-backup-game {
p { .deleted-title {
line-height: 1.6; text-align: center;
font-size: 18px; }
margin-right: 10px; .slide-title {
margin-left: 10px; padding: 20px;
} }
.swiper-pagination { .swiper-pagination {
display: none; display: none;
@ -11,11 +11,6 @@ page-backup-game {
.slide-zoom { .slide-zoom {
height: 100%; height: 100%;
} }
.password-required {
padding: 2rem;
font-size: 14px;
color: color($colors, danger);
}
.password { .password {
background-color: color($colors, light); background-color: color($colors, light);
padding: 2rem; padding: 2rem;
@ -23,19 +18,10 @@ page-backup-game {
.phrase { .phrase {
display: -webkit-box; display: -webkit-box;
background: color($colors, light); background: color($colors, light);
border: 2px dashed #d9d9d9;
border-radius: 3px;
color: color($colors, secondary);
text-align: center;
max-width: 500px; max-width: 500px;
min-height: 12rem; min-height: 12rem;
align-items: center; align-items: center;
margin: 20px auto;
padding: 15px 0;
line-height: 170%; line-height: 170%;
word-spacing: 10px; word-spacing: 10px;
} }
.tldr {
padding: 1rem;
}
} }

View File

@ -1,11 +1,16 @@
import { Component, ViewChild } from '@angular/core'; import { Component, ViewChild } from '@angular/core';
import { NavController, Slides, Navbar, AlertController, NavParams } from 'ionic-angular'; import { NavController, Slides, Navbar, AlertController, NavParams } from 'ionic-angular';
import { Logger } from '@nsalaun/ng-logger';
import * as _ from 'lodash';
//pahes
import { DisclaimerPage } from '../../onboarding/disclaimer/disclaimer'; import { DisclaimerPage } from '../../onboarding/disclaimer/disclaimer';
//providers
import { ProfileProvider } from '../../../providers/profile/profile'; import { ProfileProvider } from '../../../providers/profile/profile';
import { WalletProvider } from '../../../providers/wallet/wallet'; import { WalletProvider } from '../../../providers/wallet/wallet';
import { BwcProvider } from '../../../providers/bwc/bwc'; import { BwcProvider } from '../../../providers/bwc/bwc';
import { Logger } from '@nsalaun/ng-logger'; import { OnGoingProcessProvider } from '../../../providers/on-going-process/on-going-process';
import * as _ from 'lodash';
@Component({ @Component({
selector: 'page-backup-game', selector: 'page-backup-game',
@ -21,14 +26,13 @@ export class BackupGamePage {
public deleted: boolean; public deleted: boolean;
public mnemonicWords: Array<String>; public mnemonicWords: Array<String>;
public shuffledMnemonicWords: Array<any>; public shuffledMnemonicWords: Array<any>;
public passphrase: String; public password: String;
public customWords: Array<any>; public customWords: Array<any>;
public selectComplete: boolean; public selectComplete: boolean;
public error: boolean; public error: boolean;
public credentialsEncrypted: boolean; public credentialsEncrypted: boolean;
private mnemonicHasPassphrase: any; private mnemonicHasPassphrase: any;
private data: any;
private walletId: string; private walletId: string;
private wallet: any; private wallet: any;
private keys: any; private keys: any;
@ -41,7 +45,8 @@ export class BackupGamePage {
private logger: Logger, private logger: Logger,
private profileProvider: ProfileProvider, private profileProvider: ProfileProvider,
private walletProvider: WalletProvider, private walletProvider: WalletProvider,
private bwcProvider: BwcProvider private bwcProvider: BwcProvider,
private onGoingProcessProvider: OnGoingProcessProvider
) { ) {
this.walletId = this.navParams.get('walletId'); this.walletId = this.navParams.get('walletId');
this.fromOnboarding = this.navParams.get('fromOnboarding'); this.fromOnboarding = this.navParams.get('fromOnboarding');
@ -70,12 +75,13 @@ export class BackupGamePage {
ngOnInit() { ngOnInit() {
this.currentIndex = 0; this.currentIndex = 0;
this.navBar.backButtonClick = (e: UIEvent) => { this.navBar.backButtonClick = (e: UIEvent) => {
this.slidePrev(); if (this.slides) this.slidePrev();
else this.navCtrl.pop();
} }
} }
ionViewDidLoad() { ionViewDidLoad() {
this.slides.lockSwipes(true); if (this.slides) this.slides.lockSwipes(true);
} }
private shuffledWords(words: Array<String>) { private shuffledWords(words: Array<String>) {
@ -197,13 +203,12 @@ export class BackupGamePage {
if (!this.keys) return; if (!this.keys) return;
let words = this.keys.mnemonic; let words = this.keys.mnemonic;
this.data = {};
this.mnemonicWords = words.split(/[\u3000\s]+/); this.mnemonicWords = words.split(/[\u3000\s]+/);
this.shuffledMnemonicWords = this.shuffledWords(this.mnemonicWords); this.shuffledMnemonicWords = this.shuffledWords(this.mnemonicWords);
this.mnemonicHasPassphrase = this.wallet.mnemonicHasPassphrase(); this.mnemonicHasPassphrase = this.wallet.mnemonicHasPassphrase();
this.useIdeograms = words.indexOf("\u3000") >= 0; this.useIdeograms = words.indexOf("\u3000") >= 0;
this.data['passphrase'] = null; this.password = '';
this.customWords = []; this.customWords = [];
this.selectComplete = false; this.selectComplete = false;
this.error = false; this.error = false;
@ -234,12 +239,12 @@ export class BackupGamePage {
let walletClient = this.bwcProvider.getClient(); let walletClient = this.bwcProvider.getClient();
let separator = this.useIdeograms ? '\u3000' : ' '; let separator = this.useIdeograms ? '\u3000' : ' ';
let customSentence = customWordList.join(separator); let customSentence = customWordList.join(separator);
let passphrase = this.data.passphrase || ''; let password = this.password || '';
try { try {
walletClient.seedFromMnemonic(customSentence, { walletClient.seedFromMnemonic(customSentence, {
network: this.wallet.credentials.network, network: this.wallet.credentials.network,
passphrase: passphrase, password: password,
account: this.wallet.credentials.account account: this.wallet.credentials.account
}); });
} catch (err) { } catch (err) {
@ -259,9 +264,9 @@ export class BackupGamePage {
}; };
private finalStep(): void { private finalStep(): void {
//ongoingProcess.set('validatingWords', true); this.onGoingProcessProvider.set('validatingWords', true);
this.confirm().then(() => { this.confirm().then(() => {
//ongoingProcess.set('validatingWords', false); this.onGoingProcessProvider.set('validatingWords', false);
this.showBackupResult(); this.showBackupResult();
}).catch((err) => { }).catch((err) => {
this.backupError(err); this.backupError(err);