mirror of https://github.com/BTCPrivate/copay.git
Initial Intel TEE integration.
This commit is contained in:
parent
62cc46e455
commit
0c69dfb061
|
@ -56,7 +56,7 @@
|
||||||
"bezier-easing": "^2.0.3",
|
"bezier-easing": "^2.0.3",
|
||||||
"bhttp": "^1.2.1",
|
"bhttp": "^1.2.1",
|
||||||
"bitauth": "https://github.com/gabrielbazan7/bitauth.git#copay",
|
"bitauth": "https://github.com/gabrielbazan7/bitauth.git#copay",
|
||||||
"bitcore-wallet-client": "5.1.2",
|
"bitcore-wallet-client": "git://github.com/isocolsky/bitcore-wallet-client#369eaede49cd64836015fb9b8a502092ee84dc7c",
|
||||||
"bower": "^1.7.9",
|
"bower": "^1.7.9",
|
||||||
"cordova-android": "5.1.1",
|
"cordova-android": "5.1.1",
|
||||||
"cordova-custom-config": "^3.0.5",
|
"cordova-custom-config": "^3.0.5",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('copayApp.controllers').controller('createController',
|
angular.module('copayApp.controllers').controller('createController',
|
||||||
function($scope, $rootScope, $timeout, $log, lodash, $state, $ionicScrollDelegate, $ionicHistory, profileService, configService, gettextCatalog, ledger, trezor, platformInfo, derivationPathHelper, ongoingProcess, walletService, storageService, popupService, appConfigService) {
|
function($scope, $rootScope, $timeout, $log, lodash, $state, $ionicScrollDelegate, $ionicHistory, profileService, configService, gettextCatalog, ledger, trezor, intelTEE, platformInfo, derivationPathHelper, ongoingProcess, walletService, storageService, popupService, appConfigService) {
|
||||||
|
|
||||||
var isChromeApp = platformInfo.isChromeApp;
|
var isChromeApp = platformInfo.isChromeApp;
|
||||||
var isCordova = platformInfo.isCordova;
|
var isCordova = platformInfo.isCordova;
|
||||||
|
@ -67,6 +67,9 @@ angular.module('copayApp.controllers').controller('createController',
|
||||||
var seedOptions = [{
|
var seedOptions = [{
|
||||||
id: 'new',
|
id: 'new',
|
||||||
label: gettextCatalog.getString('Random'),
|
label: gettextCatalog.getString('Random'),
|
||||||
|
}, {
|
||||||
|
id: walletService.externalSource.intelTEE.id,
|
||||||
|
label: gettextCatalog(walletService.externalSource.intelTEE.name),
|
||||||
}, {
|
}, {
|
||||||
id: 'set',
|
id: 'set',
|
||||||
label: gettextCatalog.getString('Specify Recovery Phrase...'),
|
label: gettextCatalog.getString('Specify Recovery Phrase...'),
|
||||||
|
@ -81,16 +84,16 @@ angular.module('copayApp.controllers').controller('createController',
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (appConfigService.name == 'copay') {
|
if (appConfigService.name == 'copay') {
|
||||||
if (n > 1 && isChromeApp) {
|
if (n > 1 && isChromeApp)
|
||||||
seedOptions.push({
|
seedOptions.push({
|
||||||
id: 'ledger',
|
id: walletService.externalSource.ledger.id,
|
||||||
label: 'Ledger Hardware Wallet',
|
label: walletService.externalSource.ledger.longName,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
if (isChromeApp || isDevel) {
|
if (isChromeApp || isDevel) {
|
||||||
seedOptions.push({
|
seedOptions.push({
|
||||||
id: 'trezor',
|
id: walletService.externalSource.trezor.id,
|
||||||
label: 'Trezor Hardware Wallet',
|
label: walletService.externalSource.trezor.longName,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -151,22 +154,36 @@ angular.module('copayApp.controllers').controller('createController',
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($scope.seedSource.id == 'ledger' || $scope.seedSource.id == 'trezor') {
|
if ($scope.seedSource.id == walletService.externalSource.ledger.id || $scope.seedSource.id == walletService.externalSource.trezor.id || self.seedSourceId == walletService.externalSource.intelTEE.id) {
|
||||||
var account = $scope.formData.account;
|
var account = $scope.formData.account;
|
||||||
if (!account || account < 1) {
|
if (!account || account < 1) {
|
||||||
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Invalid account number'));
|
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Invalid account number'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($scope.seedSource.id == 'trezor')
|
if ($scope.seedSource.id == walletService.externalSource.trezor.id || self.seedSource.id == walletService.externalSource.intelTEE.id)
|
||||||
account = account - 1;
|
account = account - 1;
|
||||||
|
|
||||||
opts.account = account;
|
opts.account = account;
|
||||||
ongoingProcess.set('connecting' + $scope.seedSource.id, true);
|
ongoingProcess.set('connecting' + $scope.seedSource.id, true);
|
||||||
|
|
||||||
var src = $scope.seedSource.id == 'ledger' ? ledger : trezor;
|
var src;
|
||||||
|
switch (self.seedSourceId) {
|
||||||
|
case walletService.externalSource.ledger.id:
|
||||||
|
src = legder;
|
||||||
|
break;
|
||||||
|
case walletService.externalSource.trezor.id:
|
||||||
|
src = trezor;
|
||||||
|
break;
|
||||||
|
case walletService.externalSource.intelTEE.id:
|
||||||
|
src = intelTEE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.error = gettextCatalog('Invalid seed source id: ' + self.seedSourceId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
src.getInfoForNewWallet(opts.n > 1, account, function(err, lopts) {
|
src.getInfoForNewWallet(opts, function(err, lopts) {
|
||||||
ongoingProcess.set('connecting' + $scope.seedSource.id, false);
|
ongoingProcess.set('connecting' + $scope.seedSource.id, false);
|
||||||
if (err) {
|
if (err) {
|
||||||
popupService.showAlert(gettextCatalog.getString('Error'), err);
|
popupService.showAlert(gettextCatalog.getString('Error'), err);
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
angular.module('copayApp.controllers').controller('importController',
|
angular.module('copayApp.controllers').controller('importController',
|
||||||
function($scope, $timeout, $log, $state, $stateParams, $ionicHistory, $ionicScrollDelegate, profileService, configService, sjcl, ledger, trezor, derivationPathHelper, platformInfo, bwcService, ongoingProcess, walletService, popupService, gettextCatalog, appConfigService) {
|
function($scope, $timeout, $log, $state, $stateParams, $ionicHistory, $ionicScrollDelegate, profileService, configService, sjcl, ledger, trezor, derivationPathHelper, platformInfo, bwcService, ongoingProcess, walletService, popupService, gettextCatalog, appConfigService) {
|
||||||
|
|
||||||
|
var isChromeApp = platformInfo.isChromeApp;
|
||||||
|
var isDevel = platformInfo.isDevel;
|
||||||
var reader = new FileReader();
|
var reader = new FileReader();
|
||||||
var defaults = configService.getDefaults();
|
var defaults = configService.getDefaults();
|
||||||
var errors = bwcService.getErrors();
|
var errors = bwcService.getErrors();
|
||||||
|
@ -25,15 +27,15 @@ angular.module('copayApp.controllers').controller('importController',
|
||||||
|
|
||||||
if ($scope.isChromeApp) {
|
if ($scope.isChromeApp) {
|
||||||
$scope.seedOptions.push({
|
$scope.seedOptions.push({
|
||||||
id: 'ledger',
|
id: walletService.externalSource.ledger.id,
|
||||||
label: 'Ledger Hardware Wallet',
|
label: walletService.externalSource.ledger.longName,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($scope.isChromeApp || $scope.isDevel) {
|
if ($scope.isChromeApp || $scope.isDevel) {
|
||||||
$scope.seedOptions.push({
|
$scope.seedOptions.push({
|
||||||
id: 'trezor',
|
id: walletService.externalSource.trezor.id,
|
||||||
label: 'Trezor Hardware Wallet',
|
label: walletService.externalSource.ledger.longName,
|
||||||
});
|
});
|
||||||
$scope.formData.seedSource = $scope.seedOptions[0];
|
$scope.formData.seedSource = $scope.seedOptions[0];
|
||||||
}
|
}
|
||||||
|
@ -267,7 +269,7 @@ angular.module('copayApp.controllers').controller('importController',
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
lopts.externalSource = 'trezor';
|
lopts.externalSource = walletService.externalSource.trezor.id;
|
||||||
lopts.bwsurl = $scope.formData.bwsurl;
|
lopts.bwsurl = $scope.formData.bwsurl;
|
||||||
ongoingProcess.set('importingWallet', true);
|
ongoingProcess.set('importingWallet', true);
|
||||||
$log.debug('Import opts', lopts);
|
$log.debug('Import opts', lopts);
|
||||||
|
@ -293,7 +295,7 @@ angular.module('copayApp.controllers').controller('importController',
|
||||||
|
|
||||||
var account = $scope.formData.account;
|
var account = $scope.formData.account;
|
||||||
|
|
||||||
if ($scope.formData.seedSource.id == 'trezor') {
|
if ($scope.formData.seedSource.id == walletService.externalSource.trezor.id) {
|
||||||
if (account < 1) {
|
if (account < 1) {
|
||||||
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Invalid account number'));
|
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Invalid account number'));
|
||||||
return;
|
return;
|
||||||
|
@ -302,11 +304,11 @@ angular.module('copayApp.controllers').controller('importController',
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($scope.formData.seedSource.id) {
|
switch ($scope.formData.seedSource.id) {
|
||||||
case ('ledger'):
|
case (walletService.externalSource.ledger.id):
|
||||||
ongoingProcess.set('connectingledger', true);
|
ongoingProcess.set('connectingledger', true);
|
||||||
$scope.importLedger(account);
|
$scope.importLedger(account);
|
||||||
break;
|
break;
|
||||||
case ('trezor'):
|
case (walletService.externalSource.trezor.id):
|
||||||
ongoingProcess.set('connectingtrezor', true);
|
ongoingProcess.set('connectingtrezor', true);
|
||||||
$scope.importTrezor(account, $scope.formData.isMultisig);
|
$scope.importTrezor(account, $scope.formData.isMultisig);
|
||||||
break;
|
break;
|
||||||
|
@ -323,7 +325,7 @@ angular.module('copayApp.controllers').controller('importController',
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
lopts.externalSource = 'ledger';
|
lopts.externalSource = lopts.externalSource = walletService.externalSource.ledger.id;
|
||||||
lopts.bwsurl = $scope.formData.bwsurl;
|
lopts.bwsurl = $scope.formData.bwsurl;
|
||||||
ongoingProcess.set('importingWallet', true);
|
ongoingProcess.set('importingWallet', true);
|
||||||
$log.debug('Import opts', lopts);
|
$log.debug('Import opts', lopts);
|
||||||
|
|
|
@ -66,15 +66,15 @@ angular.module('copayApp.controllers').controller('joinController',
|
||||||
if (appConfigService.name == 'copay') {
|
if (appConfigService.name == 'copay') {
|
||||||
if (isChromeApp) {
|
if (isChromeApp) {
|
||||||
self.seedOptions.push({
|
self.seedOptions.push({
|
||||||
id: 'ledger',
|
id: walletService.externalSource.ledger.id,
|
||||||
label: 'Ledger Hardware Wallet',
|
label: walletService.externalSource.ledger.longName,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isChromeApp || isDevel) {
|
if (isChromeApp || isDevel) {
|
||||||
self.seedOptions.push({
|
self.seedOptions.push({
|
||||||
id: 'trezor',
|
id: walletService.externalSource.trezor.id,
|
||||||
label: 'Trezor Hardware Wallet',
|
label: walletService.externalSource.trezor.longName,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,19 +130,19 @@ angular.module('copayApp.controllers').controller('joinController',
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.seedSourceId == 'ledger' || self.seedSourceId == 'trezor') {
|
if (self.seedSourceId == walletService.externalSource.ledger.id || self.seedSourceId == walletService.externalSource.trezor.id) {
|
||||||
var account = $scope.account;
|
var account = $scope.account;
|
||||||
if (!account || account < 1) {
|
if (!account || account < 1) {
|
||||||
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Invalid account number'));
|
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Invalid account number'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.seedSourceId == 'trezor')
|
if (self.seedSourceId == walletService.externalSource.trezor.id)
|
||||||
account = account - 1;
|
account = account - 1;
|
||||||
|
|
||||||
opts.account = account;
|
opts.account = account;
|
||||||
ongoingProcess.set('connecting' + self.seedSourceId, true);
|
ongoingProcess.set('connecting' + self.seedSourceId, true);
|
||||||
var src = self.seedSourceId == 'ledger' ? ledger : trezor;
|
var src = self.seedSourceId == walletService.externalSource.ledger.id ? ledger : trezor;
|
||||||
|
|
||||||
src.getInfoForNewWallet(true, account, function(err, lopts) {
|
src.getInfoForNewWallet(true, account, function(err, lopts) {
|
||||||
ongoingProcess.set('connecting' + self.seedSourceId, false);
|
ongoingProcess.set('connecting' + self.seedSourceId, false);
|
||||||
|
|
|
@ -89,9 +89,6 @@ angular.module('copayApp.controllers').controller('preferencesController',
|
||||||
value: $scope.wallet.balanceHidden
|
value: $scope.wallet.balanceHidden
|
||||||
};
|
};
|
||||||
|
|
||||||
if (wallet.isPrivKeyExternal)
|
|
||||||
$scope.externalSource = wallet.getPrivKeyExternalSourceName() == 'ledger' ? 'Ledger' : 'Trezor';
|
|
||||||
|
|
||||||
$scope.touchIdAvailable = fingerprintService.isAvailable();
|
$scope.touchIdAvailable = fingerprintService.isAvailable();
|
||||||
$scope.touchIdEnabled = {
|
$scope.touchIdEnabled = {
|
||||||
value: config.touchIdFor ? config.touchIdFor[walletId] : null
|
value: config.touchIdFor ? config.touchIdFor[walletId] : null
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.module('copayApp.controllers').controller('preferencesExternalController', function($scope, lodash, profileService, walletService) {
|
||||||
|
var wallet = profileService.getWallet($stateParams.walletId);
|
||||||
|
|
||||||
|
$scope.externalSource = lodash.find(walletService.externalSource, function(source) {
|
||||||
|
return source.id == wallet.getPrivKeyExternalSourceName();
|
||||||
|
}).name;
|
||||||
|
|
||||||
|
$scope.showMneumonicFromHardwarePopup = function() {
|
||||||
|
var title = gettextCatalog.getString('Warning!');
|
||||||
|
var message = gettextCatalog.getString('Are you being watched? Anyone with your recovery phrase can access or spend your bitcoin.');
|
||||||
|
popupService.showConfirm(title, message, null, null, function(res) {
|
||||||
|
if (res) {
|
||||||
|
walletService.showMneumonicFromHardware(wallet, function(err) {
|
||||||
|
if (err) {
|
||||||
|
popupService.showAlert(gettextCatalog.getString('Error'), err.message || err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
|
@ -44,5 +44,13 @@ angular.module('copayApp.controllers').controller('preferencesInformation',
|
||||||
$scope.M = c.m;
|
$scope.M = c.m;
|
||||||
$scope.N = c.n;
|
$scope.N = c.n;
|
||||||
$scope.pubKeys = lodash.pluck(c.publicKeyRing, 'xPubKey');
|
$scope.pubKeys = lodash.pluck(c.publicKeyRing, 'xPubKey');
|
||||||
|
$scope.externalSource = null;
|
||||||
|
|
||||||
|
if (wallet.isPrivKeyExternal()) {
|
||||||
|
$scope.externalSource = lodash.find(walletService.externalSource, function(source) {
|
||||||
|
return source.id == wallet.getPrivKeyExternalSourceName();
|
||||||
|
}).name;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -145,6 +145,22 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.shouldShowReceiveAddressFromHardware = function() {
|
||||||
|
var wallet = $scope.wallet;
|
||||||
|
if (wallet.isPrivKeyExternal() && wallet.credentials.hwInfo) {
|
||||||
|
return (wallet.credentials.hwInfo.name == walletService.externalSource.intelTEE.id);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.showReceiveAddressFromHardware = function() {
|
||||||
|
var wallet = $scope.wallet;
|
||||||
|
if (wallet.isPrivKeyExternal() && wallet.credentials.hwInfo) {
|
||||||
|
walletService.showReceiveAddressFromHardware(wallet, $scope.addr, function(){});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
$scope.$on("$ionicView.beforeEnter", function(event, data) {
|
$scope.$on("$ionicView.beforeEnter", function(event, data) {
|
||||||
$scope.wallets = profileService.getWallets();
|
$scope.wallets = profileService.getWallets();
|
||||||
|
|
||||||
|
|
|
@ -579,6 +579,15 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.state('tabs.preferences.preferencesExternal', {
|
||||||
|
url: '/preferencesExternal',
|
||||||
|
views: {
|
||||||
|
'tab-settings@tabs': {
|
||||||
|
controller: 'preferencesExternalController',
|
||||||
|
templateUrl: 'views/preferencesExternal.html'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
.state('tabs.preferences.delete', {
|
.state('tabs.preferences.delete', {
|
||||||
url: '/delete',
|
url: '/delete',
|
||||||
views: {
|
views: {
|
||||||
|
|
|
@ -6,9 +6,11 @@ angular.module('copayApp.services')
|
||||||
|
|
||||||
// Ledger magic number to get xPub without user confirmation
|
// Ledger magic number to get xPub without user confirmation
|
||||||
root.ENTROPY_INDEX_PATH = "0xb11e/";
|
root.ENTROPY_INDEX_PATH = "0xb11e/";
|
||||||
|
root.M = 'm/';
|
||||||
root.UNISIG_ROOTPATH = 44;
|
root.UNISIG_ROOTPATH = 44;
|
||||||
root.MULTISIG_ROOTPATH = 48;
|
root.MULTISIG_ROOTPATH = 48;
|
||||||
root.LIVENET_PATH = 0;
|
root.LIVENET_PATH = 0;
|
||||||
|
root.TESTNET_PATH = 1;
|
||||||
|
|
||||||
root._err = function(data) {
|
root._err = function(data) {
|
||||||
var msg = data.error || data.message || 'unknown';
|
var msg = data.error || data.message || 'unknown';
|
||||||
|
@ -17,26 +19,49 @@ angular.module('copayApp.services')
|
||||||
|
|
||||||
|
|
||||||
root.getRootPath = function(device, isMultisig, account) {
|
root.getRootPath = function(device, isMultisig, account) {
|
||||||
if (!isMultisig) return root.UNISIG_ROOTPATH;
|
var path;
|
||||||
|
if (isMultisig) {
|
||||||
// Compat
|
path = root.MULTISIG_ROOTPATH;
|
||||||
if (device == 'ledger' && account == 0) return root.UNISIG_ROOTPATH;
|
} else {
|
||||||
|
if (device == 'ledger' && account > 0) {
|
||||||
return root.MULTISIG_ROOTPATH;
|
path = root.MULTISIG_ROOTPATH;
|
||||||
|
} else {
|
||||||
|
path = root.UNISIG_ROOTPATH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (device == 'intelTEE') {
|
||||||
|
path = root.M + path;
|
||||||
|
}
|
||||||
|
return path;
|
||||||
};
|
};
|
||||||
|
|
||||||
root.getAddressPath = function(device, isMultisig, account) {
|
root.getAddressPath = function(device, isMultisig, account, network) {
|
||||||
return root.getRootPath(device, isMultisig, account) + "'/" + root.LIVENET_PATH + "'/" + account + "'";
|
network = network || 'livenet';
|
||||||
|
var networkPath = root.LIVENET_PATH;
|
||||||
|
if (network == 'testnet') {
|
||||||
|
networkPath = root.TESTNET_PATH;
|
||||||
}
|
}
|
||||||
|
return root.getRootPath(device, isMultisig, account) + "'/" + networkPath + "'/" + account + "'";
|
||||||
|
};
|
||||||
|
|
||||||
root.getEntropyPath = function(device, isMultisig, account) {
|
root.getEntropyPath = function(device, isMultisig, account) {
|
||||||
var path;
|
var path = root.ENTROPY_INDEX_PATH;
|
||||||
|
if (isMultisig) {
|
||||||
|
path = path + "48'/"
|
||||||
|
} else {
|
||||||
|
path = path + "44'/"
|
||||||
|
}
|
||||||
|
|
||||||
// Old ledger wallet compat
|
// Old ledger wallet compat
|
||||||
if (device == 'ledger' && account == 0)
|
if (device == 'ledger' && account == 0) {
|
||||||
return root.ENTROPY_INDEX_PATH + "0'";
|
return path + "0'/";
|
||||||
|
}
|
||||||
|
|
||||||
return root.ENTROPY_INDEX_PATH + root.getRootPath(device, isMultisig, account) + "'/" + account + "'";
|
if (device == 'intelTEE') {
|
||||||
|
path = root.M + path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return path + account + "'";
|
||||||
};
|
};
|
||||||
|
|
||||||
root.pubKeyToEntropySource = function(xPubKey) {
|
root.pubKeyToEntropySource = function(xPubKey) {
|
||||||
|
|
|
@ -0,0 +1,191 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.module('copayApp.services')
|
||||||
|
.factory('intelTEE', function($log, $timeout, gettext, lodash, bitcore, hwWallet, bwcService, platformInfo) {
|
||||||
|
|
||||||
|
var root = {};
|
||||||
|
|
||||||
|
if (!platformInfo.isIntelTEE) {
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
var IntelWallet = require('intelWalletCon');
|
||||||
|
var TEE_APP_ID = '63279de1b6cb4dcf8c206716bd318092f8c206716bd31809263279de1b6cb4dc';
|
||||||
|
|
||||||
|
root.description = {
|
||||||
|
id: 'intelTEE',
|
||||||
|
name: 'Intel TEE',
|
||||||
|
longName: 'Intel TEE Hardware Wallet',
|
||||||
|
derivationStrategy: 'BIP44'
|
||||||
|
};
|
||||||
|
|
||||||
|
root.walletEnclave = new IntelWallet.Wallet();
|
||||||
|
var walletEnclaveStatus = root.walletEnclave.initializeEnclave();
|
||||||
|
if (walletEnclaveStatus != 0) {
|
||||||
|
$log.error('Failed to create Intel Wallet enclave');
|
||||||
|
}
|
||||||
|
|
||||||
|
root.getInfoForNewWallet = function(opts, callback) {
|
||||||
|
initSource(opts, function(err, opts) {
|
||||||
|
if (err) return callback(err);
|
||||||
|
|
||||||
|
var isMultisig = opts.n > 1;
|
||||||
|
root.getEntropySource(opts.hwInfo.id, isMultisig, opts.account, function(err, entropySource) {
|
||||||
|
if (err) return callback(err);
|
||||||
|
|
||||||
|
opts.entropySource = entropySource;
|
||||||
|
root.getXPubKey(opts.hwInfo.id, hwWallet.getAddressPath(root.description.id, isMultisig, opts.account, opts.networkName), function(data) {
|
||||||
|
if (!data.success) {
|
||||||
|
$log.warn(data.message);
|
||||||
|
return callback(data);
|
||||||
|
}
|
||||||
|
opts.extendedPublicKey = data.xpubkey;
|
||||||
|
opts.externalSource = root.description.id;
|
||||||
|
opts.derivationStrategy = root.description.derivationStrategy;
|
||||||
|
|
||||||
|
return callback(null, opts);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
root.getXPubKey = function(teeWalletId, path, callback) {
|
||||||
|
$log.debug('TEE deriving xPub path:', path);
|
||||||
|
|
||||||
|
// Expected to be a extended public key.
|
||||||
|
var xpubkey = root.walletEnclave.getPublicKey(teeWalletId, path);
|
||||||
|
|
||||||
|
// Error messages returned in value.
|
||||||
|
var result = {
|
||||||
|
success: false,
|
||||||
|
message: xpubkey.ExtendedPublicKey
|
||||||
|
};
|
||||||
|
|
||||||
|
// Success indicated by status being equal to the tee wallet id.
|
||||||
|
if (xpubkey.Status == teeWalletId) {
|
||||||
|
result.success = true;
|
||||||
|
result.message = 'OK';
|
||||||
|
result.xpubkey = xpubkey.ExtendedPublicKey;
|
||||||
|
} else {
|
||||||
|
$log.error('Failed to get xpubkey from TEE wallet: ' + result.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(result);
|
||||||
|
};
|
||||||
|
|
||||||
|
root.getEntropySource = function(teeWalletId, isMultisig, account, callback) {
|
||||||
|
root.getXPubKey(teeWalletId, hwWallet.getEntropyPath(root.description.id, isMultisig, account), function(data) {
|
||||||
|
if (!data.success)
|
||||||
|
return callback(hwWallet._err(data));
|
||||||
|
|
||||||
|
return callback(null, hwWallet.pubKeyToEntropySource(data.xpubkey));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
root.showMneumonic = function(teeWalletId, cb) {
|
||||||
|
var result = root.walletEnclave.displayWordList(teeWalletId, 'en');
|
||||||
|
if (result != teeWalletId) {
|
||||||
|
cb(result);
|
||||||
|
} else {
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
root.showReceiveAddress = function(teeWalletId, address, cb) {
|
||||||
|
var isMultisig = false; // TODO
|
||||||
|
var account = 0; // TODO
|
||||||
|
var basePath = hwWallet.getAddressPath(root.description.id, isMultisig, account, address.network);
|
||||||
|
var keyPath = address.path.replace('m', basePath);
|
||||||
|
|
||||||
|
var result = root.walletEnclave.displayReceiveAddress(teeWalletId, keyPath);
|
||||||
|
if (result != teeWalletId) {
|
||||||
|
cb(result);
|
||||||
|
} else {
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
root.signTx = function(teeWalletId, txp, callback) {
|
||||||
|
var account = 0; // TODO
|
||||||
|
var isMultisig = txp.requiredSignatures > 1;
|
||||||
|
var basePath = hwWallet.getAddressPath(root.description.id, isMultisig, account, txp.network);
|
||||||
|
|
||||||
|
var rawTx = bwcService.Client.getRawTx(txp);
|
||||||
|
var keypaths = lodash.map(lodash.pluck(txp.inputs, 'path'), function(path) {
|
||||||
|
return path.replace('m', basePath);
|
||||||
|
});
|
||||||
|
var publicKeys = lodash.pluck(txp.inputs, 'publicKeys');
|
||||||
|
var changePublicKeys = txp.changeAddress.publicKeys;
|
||||||
|
publicKeys.push(changePublicKeys);
|
||||||
|
|
||||||
|
var changeaddrpath;
|
||||||
|
if (txp.changeAddress) {
|
||||||
|
changeaddrpath = txp.changeAddress.path.replace('m', basePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
var result;
|
||||||
|
if (txp.requiredSignatures == 1) {
|
||||||
|
result = root.walletEnclave.signTransaction(teeWalletId, rawTx, changeaddrpath, keypaths);
|
||||||
|
} else {
|
||||||
|
result = root.walletEnclave.signTransaction(teeWalletId, rawTx, changeaddrpath, keypaths, publicKeys, txp.requiredSignatures, changePublicKeys, txp.requiredSignatures);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.Status != teeWalletId) {
|
||||||
|
return callback('TEE failed to sign transction: ' + result.Status);
|
||||||
|
}
|
||||||
|
return callback(null, result);
|
||||||
|
};
|
||||||
|
|
||||||
|
function initSource(opts, callback) {
|
||||||
|
var args = {
|
||||||
|
"Testnet" : (opts.networkName == 'livenet'? false : true),
|
||||||
|
"PINUnlockRequired" : false,
|
||||||
|
"PINSignatureDataRequired" : false,
|
||||||
|
"PINSignatureTransaction" : 0,
|
||||||
|
"ExportCount" : 10,
|
||||||
|
"MaxPINAttempts" : 3,
|
||||||
|
"PINTimeout" : 30
|
||||||
|
};
|
||||||
|
|
||||||
|
var teeStatus = root.walletEnclave.createWallet(TEE_APP_ID, args);
|
||||||
|
switch (teeStatus) {
|
||||||
|
case "CREATE WALLET FAILURE":
|
||||||
|
case "CREATE WALLET FAILED TO INITIALIZE":
|
||||||
|
case "CREATE WALLET FAILURE BAD INPUT":
|
||||||
|
case "CREATE WALLET FAILURE case SERIALIZATION":
|
||||||
|
case "DELETE_WALLET_AUTHORIZATION_UNSUCCESSFUL":
|
||||||
|
case "LOAD_WALLET_FAILTURE":
|
||||||
|
case "IMPORT WORD LIST FAILTURE":
|
||||||
|
case "IMPORT WORD LIST FAILURE BAD INPUT":
|
||||||
|
case "IMPORT WORD NOT IN DICTIONARY":
|
||||||
|
case "INVALID PIN":
|
||||||
|
case "INVALID APPLICATION ID":
|
||||||
|
case "DISPLAY WORD LIST FAILURE":
|
||||||
|
case "DELETE WALLET NO SUCH APPLICATION ID":
|
||||||
|
case "SIGN DATA FAILURE":
|
||||||
|
case "SIGN DATA INVALID HASH":
|
||||||
|
case "SIGN DATA BUFFER TOO SMALL":
|
||||||
|
case "SIGN DATA INVALID PIN":
|
||||||
|
case "RECEIVE ADDRESS INVALID INPUT":
|
||||||
|
case "RECEIVE ADDRESS NULL":
|
||||||
|
case "RECEIVE ADDRESS BUFFER TOO SMALL":
|
||||||
|
case "PUBLIC KEY BUFFER TOO SMALL":
|
||||||
|
case "LOAD WALLET FAILURE":
|
||||||
|
case "PUBLIC KEY FAILURE":
|
||||||
|
case "PUBLIC KEY FAIL TO SERIALIZE":
|
||||||
|
case "UKNOWN ERROR CODE":
|
||||||
|
$log.error(teeStatus);
|
||||||
|
return callback(teeStatus); // TODO: translate error text for display
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
opts.hwInfo = {
|
||||||
|
name: root.description.id,
|
||||||
|
id: teeStatus
|
||||||
|
};
|
||||||
|
$log.debug('TEE wallet created: ' + opts.hwInfo);
|
||||||
|
return callback(null, opts);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return root;
|
||||||
|
});
|
|
@ -5,6 +5,12 @@ angular.module('copayApp.services')
|
||||||
var root = {};
|
var root = {};
|
||||||
var LEDGER_CHROME_ID = "kkdpmhnladdopljabkgpacgpliggeeaf";
|
var LEDGER_CHROME_ID = "kkdpmhnladdopljabkgpacgpliggeeaf";
|
||||||
|
|
||||||
|
root.description = {
|
||||||
|
id: 'ledger',
|
||||||
|
name: 'Ledger',
|
||||||
|
longName: 'Ledger Hardware Wallet'
|
||||||
|
};
|
||||||
|
|
||||||
root.callbacks = {};
|
root.callbacks = {};
|
||||||
root.hasSession = function() {
|
root.hasSession = function() {
|
||||||
root._message({
|
root._message({
|
||||||
|
@ -13,7 +19,7 @@ angular.module('copayApp.services')
|
||||||
}
|
}
|
||||||
|
|
||||||
root.getEntropySource = function(isMultisig, account, callback) {
|
root.getEntropySource = function(isMultisig, account, callback) {
|
||||||
root.getXPubKey(hwWallet.getEntropyPath('ledger', isMultisig, account), function(data) {
|
root.getXPubKey(hwWallet.getEntropyPath(root.description.id, isMultisig, account), function(data) {
|
||||||
if (!data.success)
|
if (!data.success)
|
||||||
return callback(hwWallet._err(data));
|
return callback(hwWallet._err(data));
|
||||||
|
|
||||||
|
@ -30,21 +36,27 @@ angular.module('copayApp.services')
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
root.getInfoForNewWallet = function(isMultisig, account, callback) {
|
root.initSource = function(opts, callback) {
|
||||||
root.getEntropySource(isMultisig, account, function(err, entropySource) {
|
// No initialization for this hardware source.
|
||||||
|
return callback(opts);
|
||||||
|
};
|
||||||
|
|
||||||
|
root.getInfoForNewWallet = function(opts, callback) {
|
||||||
|
var isMultisig = opts.n > 1;
|
||||||
|
root.getEntropySource(isMultisig, opts.account, function(err, entropySource) {
|
||||||
if (err) return callback(err);
|
if (err) return callback(err);
|
||||||
|
|
||||||
root.getXPubKey(hwWallet.getAddressPath('ledger', isMultisig, account), function(data) {
|
|
||||||
if (!data.success) return callback(data);
|
|
||||||
|
|
||||||
var opts = {};
|
|
||||||
opts.entropySource = entropySource;
|
opts.entropySource = entropySource;
|
||||||
|
root.getXPubKey(hwWallet.getAddressPath(root.description.id, isMultisig, opts.account), function(data) {
|
||||||
|
if (!data.success) {
|
||||||
|
$log.warn(data.message);
|
||||||
|
return callback(data);
|
||||||
|
}
|
||||||
opts.extendedPublicKey = data.xpubkey;
|
opts.extendedPublicKey = data.xpubkey;
|
||||||
opts.externalSource = 'ledger';
|
opts.externalSource = root.description.id;
|
||||||
opts.account = account;
|
|
||||||
|
|
||||||
// Old ledger compat
|
// Old ledger compat
|
||||||
opts.derivationStrategy = account ? 'BIP48' : 'BIP44';
|
opts.derivationStrategy = opts.account ? 'BIP48' : 'BIP44';
|
||||||
return callback(null, opts);
|
return callback(null, opts);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -57,7 +69,7 @@ angular.module('copayApp.services')
|
||||||
var tx = bwcService.getUtils().buildTx(txp);
|
var tx = bwcService.getUtils().buildTx(txp);
|
||||||
for (var i = 0; i < tx.inputs.length; i++) {
|
for (var i = 0; i < tx.inputs.length; i++) {
|
||||||
redeemScripts.push(new ByteString(tx.inputs[i].redeemScript.toBuffer().toString('hex'), GP.HEX).toString());
|
redeemScripts.push(new ByteString(tx.inputs[i].redeemScript.toBuffer().toString('hex'), GP.HEX).toString());
|
||||||
paths.push(hwWallet.getAddressPath('ledger', isMultisig, account) + txp.inputs[i].path.substring(1));
|
paths.push(hwWallet.getAddressPath(root.description.id, isMultisig, account) + txp.inputs[i].path.substring(1));
|
||||||
}
|
}
|
||||||
var splitTransaction = root._splitTransaction(new ByteString(tx.toString(), GP.HEX));
|
var splitTransaction = root._splitTransaction(new ByteString(tx.toString(), GP.HEX));
|
||||||
var inputs = [];
|
var inputs = [];
|
||||||
|
|
|
@ -38,6 +38,7 @@ angular.module('copayApp.services').factory('platformInfo', function($window) {
|
||||||
ret.isMobile = ret.isAndroid || ret.isIOS || ret.isWP;
|
ret.isMobile = ret.isAndroid || ret.isIOS || ret.isWP;
|
||||||
ret.isChromeApp = $window.chrome && chrome.runtime && chrome.runtime.id && !ret.isNW;
|
ret.isChromeApp = $window.chrome && chrome.runtime && chrome.runtime.id && !ret.isNW;
|
||||||
ret.isDevel = !ret.isMobile && !ret.isChromeApp && !ret.isNW;
|
ret.isDevel = !ret.isMobile && !ret.isChromeApp && !ret.isNW;
|
||||||
|
ret.isIntelTEE = true;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
});
|
});
|
||||||
|
|
|
@ -347,6 +347,7 @@ angular.module('copayApp.services')
|
||||||
account: opts.account || 0,
|
account: opts.account || 0,
|
||||||
derivationStrategy: opts.derivationStrategy || 'BIP44',
|
derivationStrategy: opts.derivationStrategy || 'BIP44',
|
||||||
});
|
});
|
||||||
|
walletClient.credentials.hwInfo = opts.hwInfo;
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
$log.warn("Creating wallet from Extended Public Key Arg:", ex, opts);
|
$log.warn("Creating wallet from Extended Public Key Arg:", ex, opts);
|
||||||
return cb(gettextCatalog.getString('Could not create using the specified extended public key'));
|
return cb(gettextCatalog.getString('Could not create using the specified extended public key'));
|
||||||
|
|
|
@ -7,8 +7,15 @@ angular.module('copayApp.services')
|
||||||
var SETTLE_TIME = 3000;
|
var SETTLE_TIME = 3000;
|
||||||
root.callbacks = {};
|
root.callbacks = {};
|
||||||
|
|
||||||
|
root.description = {
|
||||||
|
id: 'trezor',
|
||||||
|
name: 'Trezor',
|
||||||
|
longName: 'Trezor Hardware Wallet',
|
||||||
|
derivationStrategy: 'BIP48'
|
||||||
|
};
|
||||||
|
|
||||||
root.getEntropySource = function(isMultisig, account, callback) {
|
root.getEntropySource = function(isMultisig, account, callback) {
|
||||||
root.getXPubKey(hwWallet.getEntropyPath('trezor', isMultisig, account), function(data) {
|
root.getXPubKey(hwWallet.getEntropyPath(root.description.id, isMultisig, account), function(data) {
|
||||||
if (!data.success)
|
if (!data.success)
|
||||||
return callback(hwWallet._err(data));
|
return callback(hwWallet._err(data));
|
||||||
|
|
||||||
|
@ -26,22 +33,25 @@ angular.module('copayApp.services')
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
root.initSource = function(opts, callback) {
|
||||||
|
// No initialization for this hardware source.
|
||||||
|
return callback(opts);
|
||||||
|
};
|
||||||
|
|
||||||
root.getInfoForNewWallet = function(isMultisig, account, callback) {
|
root.getInfoForNewWallet = function(opts, callback) {
|
||||||
var opts = {};
|
var isMultisig = opts.n > 1;
|
||||||
root.getEntropySource(isMultisig, account, function(err, data) {
|
root.getEntropySource(isMultisig, opts.account, function(err, data) {
|
||||||
if (err) return callback(err);
|
if (err) return callback(err);
|
||||||
opts.entropySource = data;
|
opts.entropySource = data;
|
||||||
$log.debug('Waiting TREZOR to settle...');
|
$log.debug('Waiting TREZOR to settle...');
|
||||||
$timeout(function() {
|
$timeout(function() {
|
||||||
|
|
||||||
root.getXPubKey(hwWallet.getAddressPath('trezor', isMultisig, account), function(data) {
|
root.getXPubKey(hwWallet.getAddressPath(root.description.id, isMultisig, opts.account), function(data) {
|
||||||
if (!data.success)
|
if (!data.success)
|
||||||
return callback(hwWallet._err(data));
|
return callback(hwWallet._err(data));
|
||||||
|
|
||||||
opts.extendedPublicKey = data.xpubkey;
|
opts.extendedPublicKey = data.xpubkey;
|
||||||
opts.externalSource = 'trezor';
|
opts.externalSource = root.description.id;
|
||||||
opts.account = account;
|
|
||||||
|
|
||||||
if (isMultisig)
|
if (isMultisig)
|
||||||
opts.derivationStrategy = 'BIP48';
|
opts.derivationStrategy = 'BIP48';
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('copayApp.services').factory('walletService', function($log, $timeout, lodash, trezor, ledger, storageService, configService, rateService, uxLanguage, $filter, gettextCatalog, bwcError, $ionicPopup, fingerprintService, ongoingProcess, gettext, $rootScope, txFormatService, $ionicModal, $state, bwcService, bitcore, popupService) {
|
angular.module('copayApp.services').factory('walletService', function($log, $timeout, lodash, trezor, ledger, intelTEE, storageService, configService, rateService, uxLanguage, $filter, gettextCatalog, bwcError, $ionicPopup, fingerprintService, ongoingProcess, gettext, $rootScope, txFormatService, $ionicModal, $state, bwcService, bitcore, popupService) {
|
||||||
// `wallet` is a decorated version of client.
|
// `wallet` is a decorated version of client.
|
||||||
|
|
||||||
var root = {};
|
var root = {};
|
||||||
|
|
||||||
|
root.externalSource = {
|
||||||
|
ledger: ledger.description,
|
||||||
|
trezor: trezor.description,
|
||||||
|
intelTEE: intelTEE.description
|
||||||
|
}
|
||||||
|
|
||||||
root.WALLET_STATUS_MAX_TRIES = 7;
|
root.WALLET_STATUS_MAX_TRIES = 7;
|
||||||
root.WALLET_STATUS_DELAY_BETWEEN_TRIES = 1.4 * 1000;
|
root.WALLET_STATUS_DELAY_BETWEEN_TRIES = 1.4 * 1000;
|
||||||
root.SOFT_CONFIRMATION_LIMIT = 12;
|
root.SOFT_CONFIRMATION_LIMIT = 12;
|
||||||
|
@ -40,6 +46,40 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var _signWithIntelTEE = function(wallet, txp, cb) {
|
||||||
|
$log.info('Requesting Intel TEE to sign the transaction');
|
||||||
|
|
||||||
|
intelTEE.signTx(wallet.credentials.hwInfo.id, txp, function(err, result) {
|
||||||
|
if (err) return cb(err);
|
||||||
|
|
||||||
|
$log.debug('Intel TEE response', result);
|
||||||
|
txp.signatures = result.Signatures;
|
||||||
|
return wallet.signTxProposal(txp, cb);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
root.showMneumonicFromHardware = function(wallet, cb) {
|
||||||
|
switch (wallet.getPrivKeyExternalSourceName()) {
|
||||||
|
case root.externalSource.intelTEE.id:
|
||||||
|
return intelTEE.showMneumonic(wallet.credentials.hwInfo.id, cb);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
cb('Error: unrecognized external source');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
root.showReceiveAddressFromHardware = function(wallet, address, cb) {
|
||||||
|
switch (wallet.getPrivKeyExternalSourceName()) {
|
||||||
|
case root.externalSource.intelTEE.id:
|
||||||
|
return intelTEE.showReceiveAddress(wallet.credentials.hwInfo.id, address, cb);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
cb('Error: unrecognized external source');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
root.invalidateCache = function(wallet) {
|
root.invalidateCache = function(wallet) {
|
||||||
if (wallet.cachedStatus)
|
if (wallet.cachedStatus)
|
||||||
wallet.cachedStatus.isValid = false;
|
wallet.cachedStatus.isValid = false;
|
||||||
|
@ -629,10 +669,12 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
|
||||||
|
|
||||||
if (wallet.isPrivKeyExternal()) {
|
if (wallet.isPrivKeyExternal()) {
|
||||||
switch (wallet.getPrivKeyExternalSourceName()) {
|
switch (wallet.getPrivKeyExternalSourceName()) {
|
||||||
case 'ledger':
|
case root.externalSource.ledger.id:
|
||||||
return _signWithLedger(wallet, txp, cb);
|
return _signWithLedger(wallet, txp, cb);
|
||||||
case 'trezor':
|
case root.externalSource.trezor.id:
|
||||||
return _signWithTrezor(wallet, txp, cb);
|
return _signWithTrezor(wallet, txp, cb);
|
||||||
|
case root.externalSource.intelTEE.id:
|
||||||
|
return _signWithIntelTEE(wallet, txp, cb);
|
||||||
default:
|
default:
|
||||||
var msg = 'Unsupported External Key:' + wallet.getPrivKeyExternalSourceName();
|
var msg = 'Unsupported External Key:' + wallet.getPrivKeyExternalSourceName();
|
||||||
$log.error(msg);
|
$log.error(msg);
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||||
|
<svg version="1.0"
|
||||||
|
id="svg2328" inkscape:output_extension="org.inkscape.output.svg.inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:cc="http://web.resource.org/cc/" sodipodi:version="0.32" sodipodi:docbase="C:\Documents and Settings\Alex Broersma.D2KY7761\Desktop" xmlns:dc="http://purl.org/dc/elements/1.1/" inkscape:version="0.45.1" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" sodipodi:docname="aa.svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="293px" height="194px"
|
||||||
|
viewBox="0 0 293 194" enable-background="new 0 0 293 194" xml:space="preserve">
|
||||||
|
<sodipodi:namedview id="base" inkscape:current-layer="svg2328" inkscape:window-y="-4" inkscape:window-x="-4" inkscape:cy="131.76504" inkscape:cx="219.80302" inkscape:zoom="4.0618557" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" objecttolerance="10.0" gridtolerance="10.0" guidetolerance="10.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:window-width="1440" inkscape:window-height="844">
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<path id="path6" fill="#FFFFFF" d="M291.287,55.059c-13.78-67.174-143.762-71.429-227.549-20.25v5.652
|
||||||
|
C147.415-2.731,266.148-2.456,276.953,59.422c3.641,20.499-7.826,41.818-28.387,54.086v16.053
|
||||||
|
C273.316,120.48,298.616,91.088,291.287,55.059 M138.926,172.67c-57.824,5.353-118.073-3.071-126.508-48.434
|
||||||
|
C8.229,101.9,18.427,78.193,31.878,63.484v-7.875C7.624,76.957-5.551,103.961,2.056,135.844
|
||||||
|
c9.701,40.916,61.407,64.076,140.344,56.364c31.255-3.018,72.157-13.116,100.543-28.782v-22.258
|
||||||
|
C217.146,156.617,174.479,169.381,138.926,172.67z"/>
|
||||||
|
<path id="path8" fill="#FFFFFF" d="M238.312,45.348h-15.158v67.812c0,7.965,3.804,14.891,15.158,15.989"/>
|
||||||
|
<path id="path10" fill="#FFFFFF" d="M57.73,70.13H42.571v44.292c0,7.967,3.804,14.893,15.159,15.99"/>
|
||||||
|
<rect id="rect12" x="42.571" y="47.408" fill="#FFFFFF" width="15.159" height="14.426"/>
|
||||||
|
<path id="path14" fill="#FFFFFF" d="M148.571,129.699c-12.291,0-17.473-8.574-17.473-17.036V53.838h14.993V70.13h11.354V82.33
|
||||||
|
h-11.354v29.426c0,3.461,1.654,5.359,5.235,5.359h6.119v12.584H148.571"/>
|
||||||
|
<path id="path16" fill="#FFFFFF" d="M188.426,81.589c-5.125,0-9.094,2.665-10.748,6.264c-0.992,2.17-1.323,3.819-1.486,6.486h23.206
|
||||||
|
C199.066,87.826,196.143,81.589,188.426,81.589 M176.191,104.613c0,7.722,4.848,13.406,13.337,13.406
|
||||||
|
c6.671,0,9.979-1.868,13.837-5.684l9.262,8.929c-5.954,5.878-12.183,9.45-23.207,9.45c-14.387,0-28.168-7.885-28.168-30.855
|
||||||
|
c0-19.645,12.016-30.744,27.837-30.744c16.04,0,25.245,12.996,25.245,30.059v5.44L176.191,104.613"/>
|
||||||
|
<path id="path18" fill="#FFFFFF" d="M98.576,82.33c4.409,0,6.229,2.171,6.229,5.715v41.735h15.049V87.99
|
||||||
|
c0-8.49-4.521-17.86-17.694-17.86H71.125v59.65h14.993V82.329"/>
|
||||||
|
<text transform="matrix(1.0217 0 0 1 246.8975 55.2607)" fill="#127CC1" font-family="'ArialMT'" font-size="13.8854">®</text>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.2 KiB |
|
@ -4,7 +4,13 @@
|
||||||
<div>
|
<div>
|
||||||
<span ng-show="wallet.status.wallet.singleAddress" class="size-12"><span translate>Auditable</span></span>
|
<span ng-show="wallet.status.wallet.singleAddress" class="size-12"><span translate>Auditable</span></span>
|
||||||
<img style="height:0.6em; margin-right: 1px;" ng-show="wallet.network != 'livenet'" src="img/icon-testnet-white.svg">
|
<img style="height:0.6em; margin-right: 1px;" ng-show="wallet.network != 'livenet'" src="img/icon-testnet-white.svg">
|
||||||
<img style="height:0.6em; margin-right: 1px;" ng-show="!wallet.canSign() && !wallet.isPrivKeyExternal()" src="img/icon-read-only-white.svg">
|
<img style="height:0.6em; margin-right: 1px;" ng-show="wallet.getPrivKeyExternalSourceName() == 'trezor'"
|
||||||
|
src="img/icon-trezor-white.svg">
|
||||||
|
<img style="height:0.6em; margin-right: 1px;" ng-show="wallet.getPrivKeyExternalSourceName() == 'ledger'"
|
||||||
|
src="img/icon-ledger-white.svg">
|
||||||
|
<img style="height:0.6em; margin-right: 1px;" ng-show="wallet.getPrivKeyExternalSourceName() == 'intelTEE'"
|
||||||
|
src="img/icon-inteltee-white.svg">
|
||||||
|
<span class="size-12 dib" style="height:0.6em; margin-right: 1px;" ng-show="wallet.account">#{{wallet.account || 0}} </span>
|
||||||
|
|
||||||
<img style="height:0.6em; margin-right: 1px;" ng-show="wallet.getPrivKeyExternalSourceName() == 'trezor'" src="img/icon-trezor-white.svg">
|
<img style="height:0.6em; margin-right: 1px;" ng-show="wallet.getPrivKeyExternalSourceName() == 'trezor'" src="img/icon-trezor-white.svg">
|
||||||
<img style="height:0.6em; margin-right: 1px;" ng-show="wallet.getPrivKeyExternalSourceName() == 'ledger'" src="img/icon-ledger-white.svg">
|
<img style="height:0.6em; margin-right: 1px;" ng-show="wallet.getPrivKeyExternalSourceName() == 'ledger'" src="img/icon-ledger-white.svg">
|
||||||
|
|
|
@ -18,12 +18,6 @@
|
||||||
</span>
|
</span>
|
||||||
<i class="icon bp-arrow-right"></i>
|
<i class="icon bp-arrow-right"></i>
|
||||||
</a>
|
</a>
|
||||||
<a class="item" ng-show="wallet.isPrivKeyExternal()">
|
|
||||||
<span translate>Hardware Wallet</span>
|
|
||||||
<span class="item-note">
|
|
||||||
{{externalSource}}
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
<a class="item item-icon-right" ui-sref="tabs.preferences.preferencesColor">
|
<a class="item item-icon-right" ui-sref="tabs.preferences.preferencesColor">
|
||||||
<span translate>Color</span>
|
<span translate>Color</span>
|
||||||
<span class="item-note">
|
<span class="item-note">
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
<ion-view>
|
||||||
|
<ion-nav-bar class="bar-royal">
|
||||||
|
<ion-nav-title>{{exteralSource}}</ion-nav-title>
|
||||||
|
<ion-nav-back-button>
|
||||||
|
</ion-nav-back-button>
|
||||||
|
</ion-nav-bar>
|
||||||
|
<ion-content>
|
||||||
|
<div class="padding">
|
||||||
|
<button class="button button-standard button-assertive" ng-click="showMneumonicFromHardwarePopup()">
|
||||||
|
{{'Show Recovery Phrase'|translate}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</ion-content>
|
||||||
|
</ion-view>
|
|
@ -43,6 +43,13 @@
|
||||||
{{derivationStrategy}}
|
{{derivationStrategy}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<a class="item item-icon-right" href ui-sref="tabs.preferences.preferencesExternal" ng-show="wallet.isPrivKeyExternal()">
|
||||||
|
<span translate>Hardware Wallet</span>
|
||||||
|
<span class="item-note">
|
||||||
|
{{externalSource}}
|
||||||
|
</span>
|
||||||
|
<i class="icon bp-arrow-right"></i>
|
||||||
|
</a>
|
||||||
<div class="item" ng-show="wallet.isPrivKeyExternal()">
|
<div class="item" ng-show="wallet.isPrivKeyExternal()">
|
||||||
<span translate>Hardware Wallet</span>
|
<span translate>Hardware Wallet</span>
|
||||||
<span class="item-note">
|
<span class="item-note">
|
||||||
|
|
|
@ -120,7 +120,7 @@
|
||||||
ng-model="formData.derivationPath">
|
ng-model="formData.derivationPath">
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<ion-toggle ng-show="seedSource.id == 'new'" ng-model="formData.testnetEnabled" toggle-class="toggle-positive">
|
<ion-toggle ng-show="seedSource.id == 'new' || seedSource.id == 'intelTEE'" ng-model="formData.testnetEnabled" toggle-class="toggle-positive">
|
||||||
Testnet
|
Testnet
|
||||||
</ion-toggle>
|
</ion-toggle>
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
<select ng-model="formData.seedSource" ng-options="seed as seed.label for seed in seedOptions"></select>
|
<select ng-model="formData.seedSource" ng-options="seed as seed.label for seed in seedOptions"></select>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="item item-input item-stacked-label" ng-show="formData.seedSource.id == 'trezor' || formData.seedSource.id == 'ledger'">
|
<label class="item item-input item-stacked-label" ng-show="formData.seedSource.id == 'trezor' || formData.seedSource.id == 'ledger' || formData.seedSource.id == 'intelTee'">
|
||||||
<span class="input-label" translate>Account Number</span>
|
<span class="input-label" translate>Account Number</span>
|
||||||
<input type="number" ng-model="formData.account" ignore-mouse-wheel>
|
<input type="number" ng-model="formData.account" ignore-mouse-wheel>
|
||||||
</label>
|
</label>
|
||||||
|
|
|
@ -53,6 +53,19 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="row qr">
|
<div class="row qr">
|
||||||
<div class="text-center col center-block" copy-to-clipboard="addr" ng-repeat="wallet in wallets track by $index" ng-class="walletPosition($index)">
|
<div class="text-center col center-block" copy-to-clipboard="addr" ng-repeat="wallet in wallets track by $index" ng-class="walletPosition($index)">
|
||||||
|
|
||||||
|
|
||||||
|
<span style="position: absolute; width: 220px" ng-show="shouldShowReceiveAddressFromHardware()">
|
||||||
|
<div style="width: 100%; height: 226px; background-color: rgba(255,255,255,0.8);">
|
||||||
|
<button class="button light-gray small round m10b"
|
||||||
|
style="background-color: white; border-color: black; color: black; top: 50%; transform: translateY(-50%);"
|
||||||
|
ng-click="showReceiveAddressFromHardware()">
|
||||||
|
<span translate>Show address</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
|
||||||
<qrcode ng-if="walletAddrs[wallet.id]" size="220" data="bitcoin:{{walletAddrs[wallet.id]}}" color="#334"></qrcode>
|
<qrcode ng-if="walletAddrs[wallet.id]" size="220" data="bitcoin:{{walletAddrs[wallet.id]}}" color="#334"></qrcode>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue