Merge pull request #7016 from gabrielbazan7/feat/wallet

Wallet creation and address generation
This commit is contained in:
Gustavo Maximiliano Cortez 2017-10-31 15:17:52 -03:00 committed by GitHub
commit 2dfd458114
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 149 additions and 67 deletions

View File

@ -14,19 +14,19 @@
<div *ngIf="isShared"> <div *ngIf="isShared">
<ion-item> <ion-item>
<ion-label stacked>Your name</ion-label> <ion-label stacked>Your name</ion-label>
<ion-input type="text" [(ngModel)]="formData.copayerName" formControlName="copayerName" required></ion-input> <ion-input type="text" [(ngModel)]="formData.myName" formControlName="myName" required></ion-input>
</ion-item> </ion-item>
<ion-item> <ion-item>
<ion-label stacked>Total number of copayers</ion-label> <ion-label stacked>Total number of copayers</ion-label>
<ion-select [(ngModel)]="formData.copayerSelected" formControlName="copayerSelected" (ionChange)="copayersChange(formData.copayerSelected)"> <ion-select [(ngModel)]="formData.totalCopayers" formControlName="totalCopayers" (ionChange)="copayersChange(formData.totalCopayers)">
<ion-option *ngFor="let copayer of copayers" [value]="copayer">{{copayer}}</ion-option> <ion-option *ngFor="let copayer of copayers" [value]="copayer">{{copayer}}</ion-option>
</ion-select> </ion-select>
</ion-item> </ion-item>
<ion-item> <ion-item>
<ion-label stacked>Required number of signatures</ion-label> <ion-label stacked>Required number of signatures</ion-label>
<ion-select [(ngModel)]="formData.signatureSelected" formControlName="signatureSelected" (ionChange)="signaturesChange(formData.signatureSelected)"> <ion-select [(ngModel)]="formData.requiredCopayers" formControlName="requiredCopayers" (ionChange)="signaturesChange(formData.requiredCopayers)">
<ion-option *ngFor="let signature of signatures" [value]="signature">{{signature}}</ion-option> <ion-option *ngFor="let signature of signatures" [value]="signature">{{signature}}</ion-option>
</ion-select> </ion-select>
</ion-item> </ion-item>
@ -87,7 +87,7 @@
<ion-item *ngIf="formData.selectedSeed.id == 'new'"> <ion-item *ngIf="formData.selectedSeed.id == 'new'">
<ion-label stacked>Testnet</ion-label> <ion-label stacked>Testnet</ion-label>
<ion-toggle [(ngModel)]="formData.testnet" formControlName="testnet" (ionChange)="setDerivationPath()"></ion-toggle> <ion-toggle [(ngModel)]="formData.testnetEnabled" formControlName="testnetEnabled" (ionChange)="setDerivationPath()"></ion-toggle>
</ion-item> </ion-item>
<ion-item *ngIf="formData.selectedSeed.id == 'set'"> <ion-item *ngIf="formData.selectedSeed.id == 'set'">

View File

@ -3,6 +3,10 @@ import { NavController, NavParams } from 'ionic-angular';
import { Validators, FormBuilder, FormGroup } from '@angular/forms'; import { Validators, FormBuilder, FormGroup } from '@angular/forms';
import { AppProvider } from '../../../providers/app/app'; import { AppProvider } from '../../../providers/app/app';
import { ProfileProvider } from '../../../providers/profile/profile';
import { HomePage } from '../../../pages/home/home';
import * as _ from 'lodash'; import * as _ from 'lodash';
@Component({ @Component({
@ -25,24 +29,25 @@ export class CreateWalletPage implements OnInit{
private createForm: FormGroup; private createForm: FormGroup;
constructor( constructor(
public navCtrl: NavController, private navCtrl: NavController,
public navParams: NavParams, private navParams: NavParams,
private app: AppProvider, private app: AppProvider,
private fb: FormBuilder private fb: FormBuilder,
private profileProvider: ProfileProvider
) { ) {
this.isShared = navParams.get('isShared'); this.isShared = navParams.get('isShared');
this.title = this.isShared ? 'Create shared wallet' : 'Create personal wallet'; this.title = this.isShared ? 'Create shared wallet' : 'Create personal wallet';
this.derivationPathByDefault = "m/44'/0'/0'"; this.derivationPathByDefault = "m/44'/0'/0'";
this.derivationPathForTestnet = "m/44'/1'/0'"; this.derivationPathForTestnet = "m/44'/1'/0'";
this.showAdvOpts = false; this.showAdvOpts = false;
this.COPAYER_PAIR_LIMITS = [2, 3, 4, 5, 6]; this.COPAYER_PAIR_LIMITS = [1, 2, 3, 4, 5, 6];
this.copayers = _.clone(this.COPAYER_PAIR_LIMITS); this.copayers = _.clone(this.COPAYER_PAIR_LIMITS);
this.signatures = _.clone(this.COPAYER_PAIR_LIMITS); this.signatures = _.clone(this.COPAYER_PAIR_LIMITS);
this.formData = { this.formData = {
walletName: null, walletName: null,
copayerName: null, myName: null,
copayerSelected: this.copayers[0], totalCopayers: 1,
signatureSelected: this.copayers[0], requiredCopayers: 1,
bwsURL: 'https://bws.bitpay.com/bws/api', bwsURL: 'https://bws.bitpay.com/bws/api',
recoveryPhrase: null, recoveryPhrase: null,
addPassword: false, addPassword: false,
@ -50,7 +55,7 @@ export class CreateWalletPage implements OnInit{
confirmPassword: null, confirmPassword: null,
recoveryPhraseBackedUp: null, recoveryPhraseBackedUp: null,
derivationPath: this.derivationPathByDefault, derivationPath: this.derivationPathByDefault,
testnet: false, testnetEnabled: false,
singleAddress: false, singleAddress: false,
}; };
this.appName = this.app.info.name; this.appName = this.app.info.name;
@ -60,9 +65,9 @@ export class CreateWalletPage implements OnInit{
ngOnInit() { ngOnInit() {
this.createForm = this.fb.group({ this.createForm = this.fb.group({
walletName: ['', Validators.required], walletName: ['', Validators.required],
copayerName: [''], myName: [''],
copayerSelected: [''], totalCopayers: [''],
signatureSelected: [''], requiredCopayers: [''],
bwsURL: [''], bwsURL: [''],
selectedSeed: [''], selectedSeed: [''],
recoveryPhrase: [''], recoveryPhrase: [''],
@ -71,12 +76,12 @@ export class CreateWalletPage implements OnInit{
confirmPassword: [''], confirmPassword: [''],
recoveryPhraseBackedUp: [''], recoveryPhraseBackedUp: [''],
derivationPath: [''], derivationPath: [''],
testnet: [''], testnetEnabled: [''],
singleAddress: [''], singleAddress: [''],
}); });
if (this.isShared) { if (this.isShared) {
this.createForm.get('copayerName').setValidators([Validators.required]); this.createForm.get('myName').setValidators([Validators.required]);
} }
this.createForm.get('addPassword').valueChanges.subscribe((addPassword: boolean) => { this.createForm.get('addPassword').valueChanges.subscribe((addPassword: boolean) => {
@ -145,6 +150,21 @@ export class CreateWalletPage implements OnInit{
} }
create() { create() {
console.log(this.formData); var opts = {
name: this.formData.walletName,
m: this.formData.requiredCopayers,
n: this.formData.totalCopayers,
myName: this.formData.totalCopayers > 1 ? this.formData.myName : null,
networkName: this.formData.testnetEnabled && this.formData.coin != 'bch' ? 'testnet' : 'livenet',
bwsurl: this.formData.bwsurl,
singleAddress: this.formData.singleAddressEnabled,
walletPrivKey: this.formData._walletPrivKey, // Only for testing
coin: this.formData.coin
};
this.profileProvider.createWallet(opts).then((wallet) => {
this.navCtrl.setRoot(HomePage);
this.navCtrl.popToRoot();
});
} }
} }

View File

@ -27,7 +27,8 @@
<img src="assets/img/icon-wallet.svg" class="icon-wallet"> <img src="assets/img/icon-wallet.svg" class="icon-wallet">
</ion-icon> </ion-icon>
<h2>{{wallet.credentials.walletName}} {{wallet.credentials.m}}-{{wallet.credentials.n}}</h2> <h2>{{wallet.credentials.walletName}} {{wallet.credentials.m}}-{{wallet.credentials.n}}</h2>
<p>{{wallet.credentials.coin}} - 10 BTC</p> <p *ngIf="!wallet.scanning">{{wallet.status.totalBalanceStr ? wallet.status.totalBalanceStr : ( wallet.cachedBalance ? wallet.cachedBalance + (wallet.cachedBalanceUpdatedOn ? ' &middot; ' + ( wallet.cachedBalanceUpdatedOn * 1000 | amTimeAgo) : '') : '' )}}</p>
<span *ngIf="wallet.scanning"> Scanning funds... </span>
</button> </button>
</ion-list> </ion-list>
</ion-card> </ion-card>

View File

@ -3,38 +3,58 @@ import { NavController } from 'ionic-angular';
import { AddPage } from "../add/add"; import { AddPage } from "../add/add";
import { ProfileProvider } from '../../providers/profile/profile'; import { ProfileProvider } from '../../providers/profile/profile';
import { ReleaseProvider } from '../../providers/release/release'; import { ReleaseProvider } from '../../providers/release/release';
import { WalletProvider } from '../../providers/wallet/wallet';
import { BwcErrorProvider } from '../../providers/bwc-error/bwc-error';
import * as _ from 'lodash';
@Component({ @Component({
selector: 'page-home', selector: 'page-home',
templateUrl: 'home.html' templateUrl: 'home.html'
}) })
export class HomePage { export class HomePage {
public wallets; public wallets: any;
constructor( constructor(
public navCtrl: NavController, private navCtrl: NavController,
private profile: ProfileProvider, private profileProvider: ProfileProvider,
private release: ReleaseProvider, private releaseProvider: ReleaseProvider,
private walletProvider: WalletProvider,
private bwcErrorProvider: BwcErrorProvider
) { ) {
this.release.getLatestAppVersion() this.checkUpdate();
.catch((err) => { this.wallets = this.profileProvider.getWallets();
console.log('Error:', err) this.updateAllWallets();
})
.then((version) => {
console.log('Current app version:', version);
var result = this.release.checkForUpdates(version);
console.log('Update available:', result.updateAvailable);
});
} }
ionViewDidLoad() { ionViewDidLoad() {
console.log('ionViewDidLoad HomePage'); console.log('ionViewDidLoad HomePage');
this.wallets = this.profile.getWallets();
console.log('[home.ts:20]', this.wallets); //TODO
} }
goToAddView() { private updateAllWallets(): void {
_.each(this.wallets, (wallet: any) => {
this.walletProvider.getStatus(wallet, {}).then((status: any) => {
wallet.status = status;
this.profileProvider.setLastKnownBalance(wallet.id, wallet.status.totalBalanceStr);
}).catch((err) => {
wallet.error = (err === 'WALLET_NOT_REGISTERED') ? 'Wallet not registered' : this.bwcErrorProvider.msg(err);
console.log(err);
});
});
}
private checkUpdate(): void {
this.releaseProvider.getLatestAppVersion()
.then((version) => {
console.log('Current app version:', version);
var result = this.releaseProvider.checkForUpdates(version);
console.log('Update available:', result.updateAvailable);
})
.catch((err) => {
console.log('Error:', err);
})
}
public goToAddView(): void {
this.navCtrl.push(AddPage); this.navCtrl.push(AddPage);
} }
} }

View File

@ -1,6 +1,10 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { NavController, NavParams } from 'ionic-angular'; import { NavController, NavParams } from 'ionic-angular';
import { AmountPage } from '../send/amount/amount'; import { AmountPage } from '../send/amount/amount';
import { WalletProvider } from '../../providers/wallet/wallet';
import { ProfileProvider } from '../../providers/profile/profile';
import * as _ from 'lodash';
@Component({ @Component({
selector: 'page-receive', selector: 'page-receive',
@ -11,27 +15,57 @@ export class ReceivePage {
public protocolHandler: string; public protocolHandler: string;
public address: string; public address: string;
public qrAddress: string; public qrAddress: string;
public wallets: any;
public wallet: any;
constructor(public navCtrl: NavController, public navParams: NavParams) { constructor(
this.protocolHandler = "bitcoin"; private navCtrl: NavController,
this.address = "1FgGP9dKqtWC1Q9xGhPYVmAeyezeZCFjhf"; private navParams: NavParams,
private profileProvider: ProfileProvider,
private walletProvider: WalletProvider
) {
this.wallets = this.profileProvider.getWallets();
this.updateQrAddress(); this.updateQrAddress();
} }
ionViewDidLoad() { ionViewDidLoad() {
console.log('ionViewDidLoad ReceivePage'); console.log('ionViewDidLoad ReceivePage');
this.onSelect(this.checkSelectedWallet(this.wallet, this.wallets));
} }
requestSpecificAmount() { private onSelect(wallet: any): any {
this.wallet = wallet;
this.setProtocolHandler();
this.setAddress();
}
private setProtocolHandler(): void {
this.protocolHandler = this.walletProvider.getProtocolHandler(this.wallet);
}
private checkSelectedWallet(wallet: any, wallets: any): any {
if (!wallet) return wallets[0];
let w = _.find(wallets, (w: any) => {
return w.id == wallet.id;
});
if (!w) return wallets[0];
return wallet;
}
public requestSpecificAmount(): void {
this.navCtrl.push(AmountPage, { address: this.address, sending: false }); this.navCtrl.push(AmountPage, { address: this.address, sending: false });
} }
setAddress() { private setAddress(newAddr?: boolean): void {
this.address = this.address === "1FgGP9dKqtWC1Q9xGhPYVmAeyezeZCFjhf" ? "1RTes3reeRTs1Q9xGhPYVmQFrdUyCr3EsX" : "1FgGP9dKqtWC1Q9xGhPYVmAeyezeZCFjhf"; this.walletProvider.getAddress(this.wallet, newAddr).then((addr) => {
this.address = addr;
this.updateQrAddress(); this.updateQrAddress();
}).catch((err) => {
console.log(err);
});
} }
updateQrAddress () { private updateQrAddress(): void {
this.qrAddress = this.protocolHandler + ":" + this.address; this.qrAddress = this.protocolHandler + ":" + this.address;
} }

View File

@ -71,7 +71,7 @@ export class ProfileProvider {
private needsBackup(wallet: any): Promise<boolean> { private needsBackup(wallet: any): Promise<boolean> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!this.requiresBackup(wallet)) { if (!this.requiresBackup(wallet)) {
return reject(false); return resolve(false);
} }
this.persistenceProvider.getBackupFlag(wallet.credentials.walletId).then((val: string) => { this.persistenceProvider.getBackupFlag(wallet.credentials.walletId).then((val: string) => {
@ -111,6 +111,7 @@ export class ProfileProvider {
wallet.m = wallet.credentials.m; wallet.m = wallet.credentials.m;
wallet.n = wallet.credentials.n; wallet.n = wallet.credentials.n;
wallet.coin = wallet.credentials.coin; wallet.coin = wallet.credentials.coin;
wallet.status = {};
this.updateWalletSettings(wallet); this.updateWalletSettings(wallet);
this.wallet[walletId] = wallet; this.wallet[walletId] = wallet;
@ -155,7 +156,7 @@ export class ProfileProvider {
wallet.setNotificationsInterval(this.UPDATE_PERIOD); wallet.setNotificationsInterval(this.UPDATE_PERIOD);
wallet.openWallet((err: any) => { wallet.openWallet((err: any) => {
if (wallet.status !== true) if (wallet.status !== true)
this.logger.debug('Wallet + ' + walletId + ' status:' + wallet.status) this.logger.debug('Wallet + ' + walletId + ' status:' + JSON.stringify(wallet.status));
}); });
}); });
@ -209,24 +210,21 @@ export class ProfileProvider {
let now = Math.floor(Date.now() / 1000); let now = Math.floor(Date.now() / 1000);
let showRange = 600; // 10min; let showRange = 600; // 10min;
this.getLastKnownBalance(wallet.id).then((data: string) => { this.getLastKnownBalance(wallet.id).then((data: any) => {
if (data) { if (data) {
let parseData: any = JSON.parse(data); let parseData: any = data;
wallet.cachedBalance = parseData.balance; wallet.cachedBalance = parseData.balance;
wallet.cachedBalanceUpdatedOn = (parseData.updatedOn < now - showRange) ? parseData.updatedOn : null; wallet.cachedBalanceUpdatedOn = (parseData.updatedOn < now - showRange) ? parseData.updatedOn : null;
} }
return resolve(); return resolve();
}).catch((err: any) => { }).catch((err: any) => {
return reject(err); console.log(err);
}); });
}); });
} }
public setLastKnownBalance(wid: string, balance: number): Promise<any> { public setLastKnownBalance(wid: string, balance: number): void {
return new Promise((resolve, reject) => { this.persistenceProvider.setBalanceCache(wid, { balance: balance, updatedOn: Math.floor(Date.now() / 1000) });
this.persistenceProvider.setBalanceCache(wid, { balance: balance, updatedOn: Math.floor(Date.now() / 1000), });
return resolve();
});
} }
private runValidation(wallet: any, delay?: number, retryDelay?: number) { private runValidation(wallet: any, delay?: number, retryDelay?: number) {

View File

@ -144,7 +144,7 @@ export class WalletProvider {
} }
return reject(err); return reject(err);
} }
return resolve(null); return resolve(ret);
}); });
}) })
}; };
@ -223,6 +223,8 @@ export class WalletProvider {
cache.alternativeBalanceAvailable = true; cache.alternativeBalanceAvailable = true;
cache.isRateAvailable = true; cache.isRateAvailable = true;
}).catch((err) => {
console.log(err);
}); });
}; };
@ -246,8 +248,10 @@ export class WalletProvider {
}; };
let _getStatus = (initStatusHash: any, tries: number): Promise<any> => { let _getStatus = (initStatusHash: any, tries: number): Promise<any> => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (isStatusCached() && !opts.force) { if (isStatusCached() && !opts.force) {
this.logger.debug('Wallet status cache hit:' + wallet.id); this.logger.debug('Wallet status cache hit:' + wallet.id);
cacheStatus(wallet.cachedStatus); cacheStatus(wallet.cachedStatus);
processPendingTxps(wallet.cachedStatus); processPendingTxps(wallet.cachedStatus);
@ -258,6 +262,7 @@ export class WalletProvider {
this.logger.debug('Updating Status:', wallet.credentials.walletName, tries); this.logger.debug('Updating Status:', wallet.credentials.walletName, tries);
get().then((status) => { get().then((status) => {
let currentStatusHash = walletStatusHash(status); let currentStatusHash = walletStatusHash(status);
this.logger.debug('Status update. hash:' + currentStatusHash + ' Try:' + tries); this.logger.debug('Status update. hash:' + currentStatusHash + ' Try:' + tries);
if (opts.untilItChanges && initStatusHash == currentStatusHash && tries < this.WALLET_STATUS_MAX_TRIES && walletId == wallet.credentials.walletId) { if (opts.untilItChanges && initStatusHash == currentStatusHash && tries < this.WALLET_STATUS_MAX_TRIES && walletId == wallet.credentials.walletId) {
@ -283,7 +288,11 @@ export class WalletProvider {
}); });
}; };
_getStatus(walletStatusHash(null), 0); _getStatus(walletStatusHash(null), 0).then((status) => {
resolve(status);
}).catch((err) => {
return reject(err);
});
}); });
} }
@ -302,7 +311,7 @@ export class WalletProvider {
}); });
} }
private getAddress(wallet: any, forceNew: boolean): Promise<any> { public getAddress(wallet: any, forceNew: boolean): Promise<any> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.persistenceProvider.getLastAddress(wallet.id).then((addr) => { this.persistenceProvider.getLastAddress(wallet.id).then((addr) => {
if (!forceNew && addr) return resolve(addr); if (!forceNew && addr) return resolve(addr);