Merge pull request #4518 from JDonadio/feat/import-qr

Export-Import Wallet using QR code
This commit is contained in:
Gustavo Maximiliano Cortez 2016-06-30 16:56:24 -03:00 committed by GitHub
commit a77df6f458
7 changed files with 433 additions and 247 deletions

View file

@ -5,89 +5,118 @@
</div>
<div class="content preferences" ng-controller="exportController" ng-init="init(index.prevState)">
<h4></h4>
<div ng-show="!backupWalletPlainText">
<div class="text-warning size-14 m20b" ng-show="error">
<i class="fi-alert size-12"></i>
<span translate>Failed to export</span>
<div ng-show="!index.canSign"><h4></h4></div>
<div ng-show="index.canSign" class="create-tab small-only-text-center">
<div class="row">
<div class="tab-container small-6 columns"
ng-class="{'selected':!exportQR}"
ng-style="{'border-color':!exportQR ? index.backgroundColor: 'inherit'}"
ng-click="exportQR = false">
<a href ng-style="{'color':!exportQR ? index.backgroundColor: 'inherit'}" translate>File/Text</a>
</div>
<div class="tab-container small-6 columns"
ng-class="{'selected':exportQR}"
ng-style="{'border-color':exportQR ? index.backgroundColor: 'inherit'}"
ng-click="exportQR = true">
<a href ng-style="{'color':exportQR ? index.backgroundColor: 'inherit'}" translate>QR Code</a>
</div>
</div>
</div>
<div ng-show="!backupWalletPlainText">
<div class="text-warning size-14 m20b" ng-show="error">
<i class="fi-alert size-12"></i>
<span translate>Failed to export</span>
</div>
<form ng-show="!exportQR">
<div class="row">
<div class="columns">
<div class="text-warning size-14 m20b" ng-show="isEncrypted">
<i class="fi-alert size-12"></i>
<span translate> A spending password is set for this wallet. Exporting keeps the spending password in the export archive.</span>
<label for="password" translate>Set up a password </label>
<div class="input">
<input type="password" class="form-control" placeholder="{{'Your password'|translate}}" name="password" ng-model="password">
</div>
<label for="password" translate>Repeat the password</label>
<div class="input">
<input type="password" class="form-control" placeholder="{{'Repeat password'|translate}}" name="password" ng-model="repeatpassword">
</div>
</div>
</div>
</form>
<form ng-hide="touchidEnabled && !touchidSuccess">
<div class="row">
<div class="columns">
<label for="password" translate>Set up a password </label>
<div class="input">
<input type="password" class="form-control" placeholder="{{'Your password'|translate}}" name="password" ng-model="password">
</div>
<div class="m20t text-gray" ng-show="exportQR && supported">
<div class="text-center m20b">
<qrcode size="220" version="8" error-correction-level="M" data="{{exportWalletInfo}}"></qrcode>
</div>
<div class="text-center size-12 m10" translate>From the destination device, go to Add wallet > Import wallet and scan this QR code</div>
</div>
<label for="password" translate>Repeat the password</label>
<div class="input">
<input type="password" class="form-control" placeholder="{{'Repeat password'|translate}}" name="password" ng-model="repeatpassword">
</div>
</div>
</div>
</form>
<div class="m20t text-gray" ng-show="exportQR && !supported">
<div class="text-center size-12 m10" translate>Exporting via QR not supported for this wallet</div>
</div>
<h4></h4>
<div class="row" ng-show="!exportQR && index.canSign">
<div class="columns m15t">
<a class="button outline light-gray expand tiny p10i" ng-click="showAdvanced = !showAdvanced">
<i class="fi-widget m3r"></i>
<span translate ng-hide="showAdvanced">Show advanced options</span>
<span translate ng-hide="!showAdvanced">Hide advanced options</span>
<i ng-if="!showAdvanced" class="icon-arrow-down4"></i>
<i ng-if="showAdvanced" class="icon-arrow-up4"></i>
</a>
</div>
</div>
<ion-toggle ng-model="noSignEnabled" toggle-class="toggle-balanced" class="r0">
<div ng-show="showAdvanced">
<ion-toggle ng-model="noSignEnabled" toggle-class="toggle-balanced" class="r0" ng-change="noSignEnabledChange()">
<span class="toggle-label" translate>Do not include private key</span>
</ion-toggle>
</div>
<div class="box-notification" ng-show="!index.canSign">
<span class="text-warning size-14">
<i class="fi-alert"></i>
<span translate>
<div class="box-notification p15l" ng-show="!index.canSign">
<span class="text-warning size-14">
<i class="fi-alert"></i>
<span translate>
WARNING: The private key of this wallet is not available. The export allows to check the wallet balance, transaction history, and create spend proposals from the export. However, does not allow to approve (sign) proposals, so <b>funds will not be accessible from the export</b>.
</span>
</span>
</div>
</span>
</div>
<div class="box-notification" ng-show="noSign">
<span class="text-warning size-14">
<i class="fi-alert"></i>
<span translate>
WARNING: Not including the private key allows to check the wallet balance, transaction history, and create spend proposals from the export. However, does not allow to approve (sign) proposals, so <b>funds will not be accessible from the export</b>.
</span>
<div class="box-notification p15l" ng-show="noSignEnabled">
<span class="text-warning size-14">
<i class="fi-alert"></i>
<span translate>
WARNING: Not including the private key allows to check the wallet balance, transaction history, and create spend proposals from the export. However, does not allow to approve (sign) proposals, so <b>funds will not be accessible from the export</b>.
</span>
</div>
</span>
</div>
<div class="row">
<div class="columns">
<button class="black round expand m20t" ng-click="downloadWalletBackup()"
ng-disabled="(!password || password != repeatpassword)"
<div class="row" ng-show="!exportQR">
<div class="columns">
<button class="black round expand m20t" ng-click="downloadWalletBackup()"
ng-disabled="(!password || password != repeatpassword)"
ng-style="{'background-color':index.backgroundColor}"
ng-show="!isSafari && !isCordova"><i class="fi-download"></i>
<span translate>Download</span></button>
<button class="black round expand m20t" ng-click="viewWalletBackup()"
ng-disabled="(!password || password != repeatpassword)"
ng-style="{'background-color':index.backgroundColor}"
ng-show="isSafari && !isCordova"><i class="fi-eye"></i>
<span translate>View</span></button>
<div ng-show="isCordova">
<h4 translate>Export options</h4>
<button class="black round expand" ng-disabled="(!password || password != repeatpassword)"
ng-style="{'background-color':index.backgroundColor}"
ng-show="!index.isSafari && !index.isCordova"><i class="fi-download"></i>
<span translate>Download</span></button>
<button class="black round expand m20t" ng-click="viewWalletBackup()"
ng-disabled="(!password || password != repeatpassword)"
ng-click="copyWalletBackup()"><i class="fi-clipboard-pencil"></i>
<span translate>Copy to clipboard</span></button>
<button class="black round expand" ng-disabled="(!password || password != repeatpassword)"
ng-style="{'background-color':index.backgroundColor}"
ng-show="index.isSafari && !index.isCordova"><i class="fi-eye"></i>
<span translate>View</span></button>
<div ng-show="index.isCordova">
<h4 translate>Export options</h4>
<button class="black round expand" ng-disabled="(!password || password != repeatpassword)"
ng-style="{'background-color':index.backgroundColor}"
ng-click="copyWalletBackup()"><i class="fi-clipboard-pencil"></i>
<span translate>Copy to clipboard</span></button>
<button class="black round expand" ng-disabled="(!password || password != repeatpassword)"
ng-style="{'background-color':index.backgroundColor}"
ng-click="sendWalletBackup()"><i class="fi-mail"></i>
<span translate>Send by email</span></button>
</div>
ng-click="sendWalletBackup()"><i class="fi-mail"></i>
<span translate>Send by email</span></button>
</div>
</div>
</div>
</div>
<div class="row" ng-show="backupWalletPlainText">
@ -102,6 +131,5 @@
</div>
</div>
</div>
</div>
<div class="extra-margin-bottom"></div>

View file

@ -4,17 +4,17 @@
ng-init="titleSection='Import wallet'; goBackToState = 'add'; noColor = true">
</div>
<div class="content p20b" ng-controller="importController as import" ng-init="type='12'">
<div class="content p20b" ng-controller="importController" ng-init="type='12'">
<div class="create-tab pr small-only-text-center" ng-hide="create.hideTabs">
<div class="row">
<div class="tab-container small-4 medium-4 large-4" ng-class="{'selected': type =='12'}">
<a href ng-click="import.setType('12')" translate>Recovery Phrase</a>
<a href ng-click="setType('12')" translate>Recovery Phrase</a>
</div>
<div class="tab-container small-4 medium-4 large-4" ng-class="{'selected': type=='file'}">
<a href ng-click="import.setType('file')" translate>File/Text Backup</a>
<a href ng-click="setType('file')" translate>File/Text Backup</a>
</div>
<div class="tab-container small-4 medium-4 large-4" ng-class="{'selected': type=='hwWallet'}">
<a href ng-click="import.setType('hwWallet')" translate>Hardware Wallet</a>
<a href ng-click="setType('hwWallet')" translate>Hardware Wallet</a>
</div>
</div>
</div>
@ -23,84 +23,87 @@
<div class="row">
<div class="large-12 columns">
<div class="box-notification m20b" ng-show="import.importErr">
<div ng-show="import.importErr" class="text-warning">
<div class="m10 text-bold" translate>Could not access the wallet at the server. Please check:</div>
<ul class="size-12">
<li translate>The password of the recovery phrase (if set)
</li>
<li translate>The derivation path
</li>
<li translate>The wallet service URL
</ul>
<div class="m15l">
<span translate>NOTE: To import a wallet from a 3rd party software, please go to Add Wallet &gt; Create Wallet, and specify the Recovery Phrase there.</span><br>
</div>
<div class="box-notification m20b" ng-show="importErr">
<div ng-show="importErr" class="text-warning">
<div class="m10 text-bold" translate>Could not access the wallet at the server. Please check:</div>
<ul class="size-12">
<li translate>The password of the recovery phrase (if set)</li>
<li translate>The derivation path</li>
<li translate>The wallet service URL</li>
</ul>
<div class="m15l">
<span translate>NOTE: To import a wallet from a 3rd party software, please go to Add Wallet &gt; Create Wallet, and specify the Recovery Phrase there.</span><br>
</div>
</div>
</div>
<div class="box-notification m20b" ng-show="import.error">
<div class="text-warning">
{{import.error|translate}}
</div>
<div class="box-notification m20b" ng-show="error">
<div class="text-warning">{{error|translate}}</div>
</div>
</div>
</div>
<div class="row">
<div class="large-12 columns">
<form name="importForm12" ng-submit="import.importMnemonic(importForm12)" novalidate>
<form name="importForm12" ng-submit="importMnemonic(importForm12)" novalidate>
<label for="words" class="m25r">
<span translate>Type the Recovery Phrase (usually 12 words)</span>:
</label>
<div >
<label for="words">
<span translate>Type the Recovery Phrase (usually 12 words)</span>:
</label>
<textarea class="form-control" name="words" ng-model="import.words" rows="2" autocapitalize="off" spellcheck="false"></textarea>
<div class="qr-scanner-input-import" ng-show="!dataFromQR">
<qr-scanner on-scan="processWalletInfo(data)"></qr-scanner>
</div>
<div class="lock-fromQR" ng-show="dataFromQR">
<div class="left m10l">
<i class="fi-lock color-greeni"></i>
</div>
<div class="m10t oh" ng-init="hideAdv=true">
<a class="button outline light-gray expand tiny p10i" ng-click="hideAdv=!hideAdv">
<i class="fi-widget m3r"></i>
<span translate ng-hide="!hideAdv">Show advanced options</span>
<span translate ng-hide="hideAdv">Hide advanced options</span>
<i ng-if="hideAdv" class="icon-arrow-down4"></i>
<i ng-if="!hideAdv" class="icon-arrow-up4"></i>
</a>
<div class="right icon-close-import" ng-click="dataFromQR = null">
<i class="icon-close-circle size-14"></i>
</div>
<div ng-hide="hideAdv" class="row">
<div class="large-12 columns">
</div>
<textarea class="form-control m10t" ng-disabled="dataFromQR" name="words" ng-model="words" rows="2" autocapitalize="off" spellcheck="false"></textarea>
<label for="passphrase" class="oh"><span translate>Password</span> <small translate>The Wallet Recovery Phrase could require a password to be imported</small>
<div class="m10t oh" ng-init="hideAdv=true">
<a class="button outline light-gray expand tiny p10i" ng-click="hideAdv=!hideAdv">
<i class="fi-widget m3r"></i>
<span translate ng-hide="!hideAdv">Show advanced options</span>
<span translate ng-hide="hideAdv">Hide advanced options</span>
<i ng-if="hideAdv" class="icon-arrow-down4"></i>
<i ng-if="!hideAdv" class="icon-arrow-up4"></i>
</a>
</div>
<div ng-hide="hideAdv" class="row">
<div class="large-12 columns">
<label for="passphrase" class="oh"><span translate>Password</span> <small translate>The Wallet Recovery Phrase could require a password to be imported</small>
<div class="input">
<input type="password" class="form-control" placeholder="{{'Password'|translate}}"
name="passphrase" ng-model="import.passphrase">
name="passphrase" ng-model="passphrase">
</div>
</label>
<div>
<label class="oh"><span translate>Derivation Path</span> <small translate>BIP32 path for address derivation</small>
<input type="text" class="form-control" name="derivationPath" ng-model="derivationPath">
</label>
</div>
<div>
<label class="oh"><span translate>Derivation Path</span> <small translate>BIP32 path for address derivation</small>
<input type="text" class="form-control" name="derivationPath" ng-model="derivationPath">
</label>
</div>
<label for="bws" class="oh">
<span>Wallet Service URL</span>
<input type="text" id="bwsurl" name="bwsurl" ng-model="bwsurl">
</label>
<label for="bws" class="oh">
<span>Wallet Service URL</span>
<input type="text" id="bwsurl" name="bwsurl" ng-model="bwsurl">
</label>
<div class="oh">
<ion-toggle ng-model="testnetEnabled" ng-change="setDerivationPath()" toggle-class="toggle-balanced" class="bct">
<span class="toggle-label">Testnet</span>
</ion-toggle>
</div>
<div class="oh">
<ion-toggle ng-model="testnetEnabled" ng-change="setDerivationPath()" toggle-class="toggle-balanced" class="bct">
<span class="toggle-label">Testnet</span>
</ion-toggle>
</div>
</div>
</div>
<button translate type="submit" class="button round expand black m10t"
ng-disabled="importForm12.$invalid ">
Import
</button>
</form>
<button translate type="submit" class="button round expand black m10t" ng-disabled="importForm12.$invalid">Import</button>
</form>
</div>
</div>
</div>
@ -108,33 +111,33 @@
<div ng-show="type == 'file' ">
<div class="row">
<div class="large-12 columns">
<div class="box-notification m20b" ng-show="import.error">
<div class="box-notification m20b" ng-show="error">
<span class="text-warning size-14">
{{import.error|translate}}
{{error|translate}}
</span>
</div>
<form name="importForm" ng-submit="import.importBlob(importForm)" novalidate>
<form name="importForm" ng-submit="importBlob(importForm)" novalidate>
<div ng-show="!index.isSafari && !index.isCordova" class="line-b m10b">
<label for="backupFile">
<span translate>Choose a backup file from your computer</span> <i class="fi-laptop"></i>
</label>
<input type="file" class="form-control" placeholder="{{'Select a backup file'|translate}}"
name="backupFile" ng-model="import.backupFile" ng-file-select>
name="backupFile" ng-model="backupFile" ng-file-select>
</div>
<div ng-show="index.isSafari || index.isCordova">
<label for="backupText">
<span translate>Paste the backup plain text code</span> <i class="fi-clipboard"></i>
</label>
<textarea class="form-control" name="backupText" ng-model="import.backupText" rows="5"></textarea>
<textarea class="form-control" name="backupText" ng-model="backupText" rows="5"></textarea>
</div>
<label for="password"><span translate>Password</span>
</label>
<div class="input">
<input type="password" class="form-control" placeholder="{{'Your password'|translate}}"
name="password" ng-model="import.password">
name="password" ng-model="password">
</div>
<div class="m10t oh" ng-init="hideAdv=true">
@ -157,7 +160,7 @@
</div>
<button translate type="submit" class="button round expand black"
ng-disabled="importForm.$invalid || !import.password ">
ng-disabled="importForm.$invalid || !password ">
Import backup
</button>
</form>
@ -168,65 +171,62 @@
<div ng-show="type == 'hwWallet'">
<div class="row">
<div class="large-12 columns">
<div class="box-notification m20b" ng-show="import.error">
<div class="box-notification m20b" ng-show="error">
<span class="text-warning size-14">
{{import.error|translate}}
{{error|translate}}
</span>
</div>
<form name="importForm3" ng-submit="import.importHW(importForm3)" novalidate>
<div class="large-12 columns">
<div ng-show="!import.seedOptions[0]">
<span translate>No hardware wallets supported on this device</span>
</div>
<div ng-show="import.seedOptions[0]">
<div>
<label><span translate>Wallet Recovery Phrase</span>
<select class="m10t" ng-model="seedSource"
ng-options="seed as seed.label for seed in import.seedOptions"
ng-change="import.setSeedSource()">
</select>
</label>
</div>
<div ng-show="import.seedSourceId == 'trezor' || import.seedSourceId == 'ledger'">
<label class="oh"><span translate>Account Number</span>
<input type="number" id="account" ng-model="account">
</label>
</div>
<div class="oh" ng-show="import.seedSourceId == 'trezor'">
<ion-toggle ng-model="isMultisig" toggle-class="toggle-balanced" class="bct">
<span class="toggle-label" translate>Shared Wallet</span>
</ion-toggle>
</div>
<div class="m10t oh" ng-init="hideAdv=true">
<a class="button outline light-gray expand tiny p10i" ng-click="hideAdv=!hideAdv">
<i class="fi-widget m3r"></i>
<span translate ng-hide="!hideAdv">Show advanced options</span>
<span translate ng-hide="hideAdv">Hide advanced options</span>
<i ng-if="hideAdv" class="icon-arrow-down4"></i>
<i ng-if="!hideAdv" class="icon-arrow-up4"></i>
</a>
</div>
<div ng-hide="hideAdv" class="row">
<div class="large-12 columns">
<label for="bws" class="oh">
<span>Wallet Service URL</span>
<input type="text" id="bwsurl" name="bwsurl" ng-model="bwsurl">
</label>
</div>
</div>
<button translate type="submit" class="button round expand black">
Import
</button>
</div> <!-- seedoptions show -->
<form name="importForm3" ng-submit="importHW(importForm3)" novalidate>
<div ng-show="!seedOptions[0]">
<span translate>No hardware wallets supported on this device</span>
</div>
<div ng-show="seedOptions[0]">
<div>
<label><span translate>Wallet Recovery Phrase</span>
<select class="m10t" ng-model="seedSource"
ng-options="seed as seed.label for seed in seedOptions"
ng-change="setSeedSource()">
</select>
</label>
</div>
<div ng-show="seedSourceId == 'trezor' || seedSourceId == 'ledger'">
<label class="oh"><span translate>Account Number</span>
<input type="number" id="account" ng-model="account">
</label>
</div>
<div class="oh" ng-show="seedSourceId == 'trezor'">
<ion-toggle ng-model="isMultisig" toggle-class="toggle-balanced" class="bct">
<span class="toggle-label" translate>Shared Wallet</span>
</ion-toggle>
</div>
<div class="m10t oh" ng-init="hideAdv=true">
<a class="button outline light-gray expand tiny p10i" ng-click="hideAdv=!hideAdv">
<i class="fi-widget m3r"></i>
<span translate ng-hide="!hideAdv">Show advanced options</span>
<span translate ng-hide="hideAdv">Hide advanced options</span>
<i ng-if="hideAdv" class="icon-arrow-down4"></i>
<i ng-if="!hideAdv" class="icon-arrow-up4"></i>
</a>
</div>
<div ng-hide="hideAdv" class="row">
<div class="large-12 columns">
<label for="bws" class="oh">
<span>Wallet Service URL</span>
<input type="text" id="bwsurl" name="bwsurl" ng-model="bwsurl">
</label>
</div>
</div>
<button translate type="submit" class="button round expand black">
Import
</button>
</div> <!-- seedoptions show -->
</form>
</div>
</div>

View file

@ -23,8 +23,8 @@ angular.module('copayApp.controllers').controller('backupController',
handleEncryptedWallet(fc, function(err) {
if (err) {
$scope.error = bwsError.msg(err, gettext('Could not decrypt'));
$log.warn('Error decrypting credentials:', $scope.error);
go.path(prevState);
return;
}
$scope.credentialsEncrypted = false;
@ -69,10 +69,13 @@ angular.module('copayApp.controllers').controller('backupController',
};
$scope.goBack = function() {
walletService.lock(fc);
go.path(prevState || 'walletHome');
};
$scope.$on('$destroy', function() {
walletService.lock(fc);
});
$scope.goToStep = function(n) {
if (n == 1)
$scope.initFlow();

View file

@ -1,39 +1,111 @@
'use strict';
angular.module('copayApp.controllers').controller('exportController',
function($scope, $timeout, $log, backupService, fingerprintService, configService, storageService, profileService, platformInfo, notification, go, gettext, gettextCatalog) {
function($rootScope, $scope, $timeout, $log, lodash, backupService, walletService, fingerprintService, configService, storageService, profileService, platformInfo, notification, go, gettext, gettextCatalog) {
var prevState;
var isWP = platformInfo.isWP;
var isAndroid = platformInfo.isAndroid;
var isCordova = platformInfo.isCordova;
$scope.error = null;
$scope.success = null;
var fc = profileService.focusedClient;
$scope.isEncrypted = fc.isPrivKeyEncrypted();
$scope.touchidSuccess = null;
$scope.touchidEnabled = null;
$scope.isCordova = platformInfo.isCordova;
$scope.isSafari = platformInfo.isSafari;
$scope.error = null;
$scope.init = function(state) {
if (!isCordova) return;
var config = configService.getSync();
var touchidAvailable = fingerprintService.isAvailable();
var touchidEnabled = $scope.touchidEnabled = config.touchIdFor ? config.touchIdFor[fc.credentials.walletId] : null;
if (!touchidAvailable || !touchidEnabled) return;
$scope.supported = true;
$scope.exportQR = false;
$scope.noSignEnabled = false;
$scope.showAdvanced = false;
prevState = state || 'walletHome';
fingerprintService.check(fc, function(err) {
if (err)
go.path(state || 'walletHome');
if (err) {
go.path(prevState);
return;
}
$scope.touchidSuccess = true;
$timeout(function() {
$scope.$apply();
}, 10);
handleEncryptedWallet(fc, function(err) {
if (err) {
go.path(prevState);
return;
}
$scope.exportWalletInfo = encodeWalletInfo();
$timeout(function() {
$scope.$apply();
}, 1);
});
});
};
/*
EXPORT WITHOUT PRIVATE KEY - PENDING
$scope.noSignEnabledChange = function() {
$scope.exportWalletInfo = encodeWalletInfo();
$timeout(function() {
$scope.$apply();
}, 1);
};
*/
$scope.$on('$destroy', function() {
walletService.lock(fc);
});
function handleEncryptedWallet(client, cb) {
if (!walletService.isEncrypted(client)) {
$scope.credentialsEncrypted = false;
return cb();
}
$rootScope.$emit('Local/NeedsPassword', false, function(err, password) {
if (err) return cb(err);
return cb(walletService.unlock(client, password));
});
};
function encodeWalletInfo() {
var c = fc.credentials;
var derivationPath = fc.credentials.getBaseAddressDerivationPath();
var encodingType = {
mnemonic: 1,
xpriv: 2,
xpub: 3
};
var info;
$scope.supported = (c.derivationStrategy == 'BIP44' && c.canSign());
if ($scope.supported) {
if (c.mnemonic) {
info = {
type: encodingType.mnemonic,
data: c.mnemonic,
}
} else {
info = {
type: encodingType.xpriv,
data: c.xPrivKey
}
}
} else {
/*
EXPORT WITHOUT PRIVATE KEY - PENDING
info = {
type: encodingType.xpub,
data: c.xPubKey
}
*/
return null;
}
var code = info.type + '|' + info.data + '|' + c.network.toLowerCase() + '|' + derivationPath + '|' + (c.mnemonicHasPassphrase);
return code;
};
$scope.downloadWalletBackup = function() {
$scope.getAddressbook(function(err, localAddressBook) {
if (err) {

View file

@ -1,58 +1,89 @@
'use strict';
angular.module('copayApp.controllers').controller('importController',
function($scope, $rootScope, $timeout, $log, profileService, configService, notification, go, sjcl, gettext, lodash, ledger, trezor, derivationPathHelper, platformInfo, bwsError, bwcService, ongoingProcess) {
function($scope, $rootScope, $timeout, $log, profileService, configService, notification, go, sjcl, gettext, ledger, trezor, derivationPathHelper, platformInfo, bwcService, ongoingProcess) {
var isChromeApp = platformInfo.isChromeApp;
var isDevel = platformInfo.isDevel;
var self = this;
var reader = new FileReader();
var defaults = configService.getDefaults();
var errors = bwcService.getErrors();
$scope.dataFromQR = null;
$scope.bwsurl = defaults.bws.url;
$scope.derivationPath = derivationPathHelper.default;
$scope.account = 1;
self.importErr = false;
$scope.importErr = false;
var updateSeedSourceSelect = function() {
self.seedOptions = [];
$scope.seedOptions = [];
if (isChromeApp) {
self.seedOptions.push({
$scope.seedOptions.push({
id: 'ledger',
label: 'Ledger Hardware Wallet',
});
}
if (isChromeApp || isDevel) {
self.seedOptions.push({
$scope.seedOptions.push({
id: 'trezor',
label: 'Trezor Hardware Wallet',
});
$scope.seedSource = self.seedOptions[0];
$scope.seedSource = $scope.seedOptions[0];
}
};
this.setType = function(type) {
$scope.processWalletInfo = function(code) {
$scope.dataFromQR = null;
$scope.importErr = false;
$scope.error = null;
var parsedCode = code.split('|');
if (parsedCode.length != 5) {
$scope.error = gettext('Cannot read the code properly. Missing parameters');
return;
}
var info = {
type: parsedCode[0],
data: parsedCode[1],
network: parsedCode[2],
derivationPath: parsedCode[3],
hasPassphrase: parsedCode[4] == 'true' ? true : false
};
if (info.type == 1 && info.hasPassphrase)
$scope.error = gettext('Password required. Make sure to enter your password in advanced options');
$scope.derivationPath = info.derivationPath;
$scope.testnetEnabled = info.network == 'testnet' ? true : false;
$timeout(function() {
$scope.words = null;
$scope.dataFromQR = info.data;
$rootScope.$apply();
}, 1);
};
$scope.setType = function(type) {
$scope.type = type;
this.error = null;
$scope.error = null;
$timeout(function() {
$rootScope.$apply();
});
}, 1);
};
var _importBlob = function(str, opts) {
var str2, err;
try {
str2 = sjcl.decrypt(self.password, str);
str2 = sjcl.decrypt($scope.password, str);
} catch (e) {
err = gettext('Could not decrypt file, check your password');
$log.warn(e);
};
if (err) {
self.error = err;
$scope.error = err;
$timeout(function() {
$rootScope.$apply();
});
@ -67,7 +98,7 @@ angular.module('copayApp.controllers').controller('importController',
profileService.importWallet(str2, opts, function(err, walletId) {
ongoingProcess.set('importingWallet', false);
if (err) {
self.error = err;
$scope.error = err;
} else {
$rootScope.$emit('Local/WalletImported', walletId);
notification.success(gettext('Success'), gettext('Your wallet has been imported correctly'));
@ -84,9 +115,9 @@ angular.module('copayApp.controllers').controller('importController',
ongoingProcess.set('importingWallet', false);
if (err) {
if (err instanceof errors.NOT_AUTHORIZED) {
self.importErr = true;
$scope.importErr = true;
} else {
self.error = err;
$scope.error = err;
}
return $timeout(function() {
$scope.$apply();
@ -100,6 +131,28 @@ angular.module('copayApp.controllers').controller('importController',
}, 100);
};
/*
IMPORT FROM PUBLIC KEY - PENDING
var _importExtendedPublicKey = function(xPubKey, opts) {
ongoingProcess.set('importingWallet', true);
$timeout(function() {
profileService.importExtendedPublicKey(opts, function(err, walletId) {
ongoingProcess.set('importingWallet', false);
if (err) {
$scope.error = err;
return $timeout(function() {
$scope.$apply();
});
}
$rootScope.$emit('Local/WalletImported', walletId);
notification.success(gettext('Success'), gettext('Your wallet has been imported correctly'));
go.walletHome();
});
}, 100);
};
*/
var _importMnemonic = function(words, opts) {
ongoingProcess.set('importingWallet', true);
@ -109,9 +162,9 @@ angular.module('copayApp.controllers').controller('importController',
if (err) {
if (err instanceof errors.NOT_AUTHORIZED) {
self.importErr = true;
$scope.importErr = true;
} else {
self.error = err;
$scope.error = err;
}
return $timeout(function() {
$scope.$apply();
@ -130,7 +183,7 @@ angular.module('copayApp.controllers').controller('importController',
$scope.derivationPath = derivationPathHelper.defaultTestnet;
else
$scope.derivationPath = derivationPathHelper.default;
}
};
$scope.getFile = function() {
// If we use onloadend, we need to check the readyState.
@ -143,9 +196,9 @@ angular.module('copayApp.controllers').controller('importController',
}
};
this.importBlob = function(form) {
$scope.importBlob = function(form) {
if (form.$invalid) {
this.error = gettext('There is an error in the form');
$scope.error = gettext('There is an error in the form');
$timeout(function() {
$scope.$apply();
});
@ -157,7 +210,7 @@ angular.module('copayApp.controllers').controller('importController',
var password = form.password.$modelValue;
if (!backupFile && !backupText) {
this.error = gettext('Please, select your backup file');
$scope.error = gettext('Please, select your backup file');
$timeout(function() {
$scope.$apply();
});
@ -174,9 +227,9 @@ angular.module('copayApp.controllers').controller('importController',
}
};
this.importMnemonic = function(form) {
$scope.importMnemonic = function(form) {
if (form.$invalid) {
this.error = gettext('There is an error in the form');
$scope.error = gettext('There is an error in the form');
$timeout(function() {
$scope.$apply();
});
@ -187,32 +240,33 @@ angular.module('copayApp.controllers').controller('importController',
if ($scope.bwsurl)
opts.bwsurl = $scope.bwsurl;
var pathData = derivationPathHelper.parse($scope.derivationPath);
if (!pathData) {
this.error = gettext('Invalid derivation path');
$scope.error = gettext('Invalid derivation path');
return;
}
opts.account = pathData.account;
opts.networkName = pathData.networkName;
opts.derivationStrategy = pathData.derivationStrategy;
var words = form.words.$modelValue;
this.error = null;
var words = form.words.$modelValue || $scope.dataFromQR;
$scope.error = null;
if (!words) {
this.error = gettext('Please enter the recovery phrase');
$scope.error = gettext('Please enter the recovery phrase');
} else if (words.indexOf('xprv') == 0 || words.indexOf('tprv') == 0) {
return _importExtendedPrivateKey(words, opts);
} else if (words.indexOf('xpub') == 0 || words.indexOf('tpuv') == 0) {
return _importExtendedPublicKey(words, opts);
} else {
var wordList = words.split(/[\u3000\s]+/);
if ((wordList.length % 3) != 0) {
this.error = gettext('Wrong number of recovery words:') + wordList.length;
$scope.error = gettext('Wrong number of recovery words:') + wordList.length;
}
}
if (this.error) {
if ($scope.error) {
$timeout(function() {
$scope.$apply();
});
@ -225,12 +279,12 @@ angular.module('copayApp.controllers').controller('importController',
_importMnemonic(words, opts);
};
this.importTrezor = function(account, isMultisig) {
var self = this;
$scope.importTrezor = function(account, isMultisig) {
var $scope = $scope;
trezor.getInfoForNewWallet(isMultisig, account, function(err, lopts) {
ongoingProcess.clear();
if (err) {
self.error = err;
$scope.error = err;
$scope.$apply();
return;
}
@ -243,7 +297,7 @@ angular.module('copayApp.controllers').controller('importController',
profileService.importExtendedPublicKey(lopts, function(err, walletId) {
ongoingProcess.set('importingWallet', false);
if (err) {
self.error = err;
$scope.error = err;
return $timeout(function() {
$scope.$apply();
});
@ -255,56 +309,56 @@ angular.module('copayApp.controllers').controller('importController',
}, 100);
};
this.importHW = function(form) {
$scope.importHW = function(form) {
if (form.$invalid || $scope.account < 0) {
this.error = gettext('There is an error in the form');
$scope.error = gettext('There is an error in the form');
$timeout(function() {
$scope.$apply();
});
return;
}
this.error = '';
this.importErr = false;
$scope.error = '';
$scope.importErr = false;
var account = +$scope.account;
if (self.seedSourceId == 'trezor') {
if ($scope.seedSourceId == 'trezor') {
if (account < 1) {
this.error = gettext('Invalid account number');
$scope.error = gettext('Invalid account number');
return;
}
account = account - 1;
}
switch (self.seedSourceId) {
switch ($scope.seedSourceId) {
case ('ledger'):
ongoingProcess.set('connectingledger', true);
self.importLedger(account);
$scope.importLedger(account);
break;
case ('trezor'):
ongoingProcess.set('connectingtrezor', true);
self.importTrezor(account, $scope.isMultisig);
$scope.importTrezor(account, $scope.isMultisig);
break;
default:
throw ('Error: bad source id');
};
};
this.setSeedSource = function() {
$scope.setSeedSource = function() {
if (!$scope.seedSource) return;
self.seedSourceId = $scope.seedSource.id;
$scope.seedSourceId = $scope.seedSource.id;
$timeout(function() {
$rootScope.$apply();
});
};
this.importLedger = function(account) {
var self = this;
$scope.importLedger = function(account) {
var $scope = $scope;
ledger.getInfoForNewWallet(true, account, function(err, lopts) {
ongoingProcess.clear();
if (err) {
self.error = err;
$scope.error = err;
$scope.$apply();
return;
}
@ -317,7 +371,7 @@ angular.module('copayApp.controllers').controller('importController',
profileService.importExtendedPublicKey(lopts, function(err, walletId) {
ongoingProcess.set('importingWallet', false);
if (err) {
self.error = err;
$scope.error = err;
return $timeout(function() {
$scope.$apply();
});
@ -330,5 +384,5 @@ angular.module('copayApp.controllers').controller('importController',
};
updateSeedSourceSelect();
self.setSeedSource('new');
$scope.setSeedSource('new');
});

View file

@ -28,6 +28,7 @@ angular.module('copayApp.services').factory('ongoingProcess', function($log, $ti
'importingWallet': gettext('Importing Wallet...'),
'sweepingWallet': gettext('Sweeping Wallet...'),
'deletingWallet': gettext('Deleting Wallet...'),
'extractingWalletInfo': gettext('Extracting Wallet Information...'),
};
root.clear = function() {

View file

@ -29,6 +29,16 @@ body {
right: 0;
}
.qr-scanner-input-import {
position: absolute;
top: -5px;
right: 0;
}
.icon-close-import {
padding: 0px 40px 5px 10px;
}
h1, h2, h3, h4, h5, h6 {
color: #2C3E50;
}
@ -606,6 +616,14 @@ ul.manage li {
margin-right: 10px;
}
.m40r {
margin-right: 40px;
}
.m25r {
margin-right: 25px;
}
.m10l {
margin-left: 10px;
}
@ -695,6 +713,10 @@ ul.manage li {
padding-left: 25px;
}
.p15l {
padding-left: 15px;
}
.p15 {
padding: 15px;
}
@ -869,6 +891,12 @@ ul.manage li {
background-color: #1ABC9C;
}
.lock-fromQR {
position: absolute;
width: 100%;
margin-top: 20px;
}
.tx-proposal i {
padding: .1rem .3rem;
background-color: #A5B2BF;