Wallet/src/js/services/profileService.js

987 lines
30 KiB
JavaScript
Raw Normal View History

2015-03-06 12:00:10 -03:00
'use strict';
angular.module('copayApp.services')
2016-12-16 12:06:30 -03:00
.factory('profileService', function profileServiceFactory($rootScope, $timeout, $filter, $log, sjcl, lodash, storageService, bwcService, configService, pushNotificationsService, gettextCatalog, bwcError, uxLanguage, platformInfo, txFormatService, $state) {
var isChromeApp = platformInfo.isChromeApp;
var isCordova = platformInfo.isCordova;
var isWP = platformInfo.isWP;
2016-06-17 09:33:31 -03:00
var isIOS = platformInfo.isIOS;
2015-03-06 12:00:10 -03:00
var root = {};
2016-01-22 18:16:50 -03:00
var errors = bwcService.getErrors();
var usePushNotifications = isCordova && !isWP;
2015-03-06 12:00:10 -03:00
2016-08-22 15:11:58 -03:00
var UPDATE_PERIOD = 15;
2015-10-23 12:16:41 -03:00
2015-03-06 12:00:10 -03:00
root.profile = null;
2016-08-29 11:56:31 -03:00
Object.defineProperty(root, "focusedClient", {
get: function() {
throw "focusedClient is not used any more"
},
set: function() {
throw "focusedClient is not used any more"
}
2016-08-22 14:42:43 -03:00
});
2015-03-06 12:00:10 -03:00
2016-08-29 11:56:31 -03:00
root.wallet = {}; // decorated version of client
2015-03-06 12:00:10 -03:00
2016-08-22 14:42:43 -03:00
root.updateWalletSettings = function(wallet) {
2016-08-15 10:25:43 -03:00
var defaults = configService.getDefaults();
configService.whenAvailable(function(config) {
wallet.usingCustomBWS = config.bwsFor && config.bwsFor[wallet.id] && (config.bwsFor[wallet.id] != defaults.bws.url);
wallet.name = (config.aliasFor && config.aliasFor[wallet.id]) || wallet.credentials.walletName;
wallet.color = (config.colorFor && config.colorFor[wallet.id]) || '#4A90E2';
2016-09-02 16:36:18 -03:00
wallet.email = config.emailFor && config.emailFor[wallet.id];
});
2016-08-22 14:42:43 -03:00
}
2016-08-15 10:25:43 -03:00
2016-08-30 17:07:49 -03:00
root.setBackupFlag = function(walletId) {
storageService.setBackupFlag(walletId, function(err) {
if (err) $log.error(err);
$log.debug('Backup flag stored');
2016-08-30 17:07:49 -03:00
root.wallet[walletId].needsBackup = false;
});
};
function _requiresBackup(wallet) {
if (wallet.isPrivKeyExternal()) return false;
if (!wallet.credentials.mnemonic) return false;
if (wallet.credentials.network == 'testnet') return false;
return true;
};
function _needsBackup(wallet, cb) {
if (!_requiresBackup(wallet))
return cb(false);
storageService.getBackupFlag(wallet.credentials.walletId, function(err, val) {
if (err) $log.error(err);
if (val) return cb(false);
return cb(true);
});
};
function _balanceIsHidden(wallet, cb) {
storageService.getHideBalanceFlag(wallet.credentials.walletId, function(err, shouldHideBalance) {
if (err) $log.error(err);
var hideBalance = (shouldHideBalance == 'true') ? true : false;
return cb(hideBalance);
});
};
2016-06-06 12:21:15 -03:00
// Adds a wallet client to profileService
2016-08-22 14:42:43 -03:00
root.bindWalletClient = function(wallet, opts) {
2016-06-06 12:21:15 -03:00
var opts = opts || {};
2016-08-22 14:42:43 -03:00
var walletId = wallet.credentials.walletId;
2016-08-15 10:25:43 -03:00
if ((root.wallet[walletId] && root.wallet[walletId].started) && !opts.force) {
2016-06-06 12:21:15 -03:00
return false;
2015-10-19 11:19:28 -03:00
}
2015-03-06 12:00:10 -03:00
// INIT WALLET VIEWMODEL
wallet.id = walletId;
wallet.started = true;
wallet.doNotVerifyPayPro = isChromeApp;
wallet.network = wallet.credentials.network;
wallet.copayerId = wallet.credentials.copayerId;
wallet.m = wallet.credentials.m;
wallet.n = wallet.credentials.n;
root.updateWalletSettings(wallet);
root.wallet[walletId] = wallet;
2016-08-30 17:07:49 -03:00
_needsBackup(wallet, function(val) {
wallet.needsBackup = val;
});
2016-08-22 14:42:43 -03:00
_balanceIsHidden(wallet, function(val) {
wallet.balanceHidden = val;
});
2016-08-22 14:42:43 -03:00
wallet.removeAllListeners();
2016-08-22 14:42:43 -03:00
wallet.on('report', function(n) {
$log.info('BWC Report:' + n);
});
2015-04-13 14:58:07 -03:00
2016-08-22 14:42:43 -03:00
wallet.on('notification', function(n) {
2016-09-01 17:41:00 -03:00
$log.debug('BWC Notification:', n);
2016-09-01 17:41:00 -03:00
if (n.type == "NewBlock" && n.data.network == "testnet") {
throttledBwsEvent(n, wallet);
2016-10-12 18:42:30 -03:00
} else newBwsEvent(n, wallet);
2015-10-19 11:19:28 -03:00
});
2015-03-06 12:00:10 -03:00
2016-08-22 14:42:43 -03:00
wallet.on('walletCompleted', function() {
2015-10-19 11:19:28 -03:00
$log.debug('Wallet completed');
2016-08-22 14:42:43 -03:00
root.updateCredentials(JSON.parse(wallet.export()), function() {
2016-06-06 12:21:15 -03:00
$rootScope.$emit('Local/WalletCompleted', walletId);
2015-03-06 12:00:10 -03:00
});
2015-10-19 11:19:28 -03:00
});
2015-03-06 12:00:10 -03:00
2016-08-31 15:16:00 -03:00
wallet.initialize({
notificationIncludeOwn: true,
}, function(err) {
2015-10-19 11:19:28 -03:00
if (err) {
$log.error('Could not init notifications err:', err);
return;
}
2016-08-22 15:11:58 -03:00
wallet.setNotificationsInterval(UPDATE_PERIOD);
2016-08-23 09:35:32 -03:00
wallet.openWallet(function(err) {
2016-08-29 11:56:31 -03:00
if (wallet.status !== true)
2016-08-23 09:35:32 -03:00
$log.log('Wallet + ' + walletId + ' status:' + wallet.status)
});
2016-08-22 14:42:43 -03:00
});
$rootScope.$on('Local/SettingsUpdated', function(e, walletId) {
if (!walletId || walletId == wallet.id) {
2016-08-29 11:56:31 -03:00
$log.debug('Updating settings for wallet:' + wallet.id);
2016-08-22 14:42:43 -03:00
root.updateWalletSettings(wallet);
}
2015-10-19 11:19:28 -03:00
});
2015-04-16 19:44:12 -03:00
2016-06-06 12:21:15 -03:00
return true;
};
var throttledBwsEvent = lodash.throttle(function(n, wallet) {
newBwsEvent(n, wallet);
}, 10000);
var newBwsEvent = function(n, wallet) {
if (wallet.cachedStatus)
wallet.cachedStatus.isValid = false;
if (wallet.completeHistory)
wallet.completeHistory.isValid = false;
if (wallet.cachedActivity)
wallet.cachedActivity.isValid = false;
if (wallet.cachedTxps)
wallet.cachedTxps.isValid = false;
$rootScope.$emit('bwsEvent', wallet.id, n.type, n);
};
2016-06-21 11:17:42 -03:00
var validationLock = false;
root.runValidation = function(client, delay, retryDelay) {
2016-07-28 15:04:09 -03:00
2016-06-21 11:17:42 -03:00
delay = delay || 500;
retryDelay = retryDelay || 50;
if (validationLock) {
return $timeout(function() {
$log.debug('ValidatingWallet Locked: Retrying in: ' + retryDelay);
return root.runValidation(client, delay, retryDelay);
}, retryDelay);
}
validationLock = true;
2016-06-17 09:33:31 -03:00
// IOS devices are already checked
var skipDeviceValidation = isIOS || root.profile.isDeviceChecked(platformInfo.ua);
2016-06-14 12:28:21 -03:00
var walletId = client.credentials.walletId;
2016-06-21 11:17:42 -03:00
$log.debug('ValidatingWallet: ' + walletId + ' skip Device:' + skipDeviceValidation);
2016-06-14 12:28:21 -03:00
$timeout(function() {
client.validateKeyDerivation({
skipDeviceValidation: skipDeviceValidation,
}, function(err, isOK) {
2016-06-21 11:17:42 -03:00
validationLock = false;
$log.debug('ValidatingWallet End: ' + walletId + ' isOK:' + isOK);
2016-06-14 12:28:21 -03:00
if (isOK) {
root.profile.setChecked(platformInfo.ua, walletId);
} else {
$log.warn('Key Derivation failed for wallet:' + walletId);
storageService.clearLastAddress(walletId, function() {});
}
2016-07-28 15:04:09 -03:00
2016-06-21 11:17:42 -03:00
root.storeProfileIfDirty();
2016-06-14 12:28:21 -03:00
});
2016-06-21 11:17:42 -03:00
}, delay);
2016-06-14 12:28:21 -03:00
};
2016-06-06 12:21:15 -03:00
// Used when reading wallets from the profile
2016-06-13 09:50:37 -03:00
root.bindWallet = function(credentials, cb) {
2016-08-15 10:25:43 -03:00
if (!credentials.walletId || !credentials.m)
2016-06-13 09:50:37 -03:00
return cb('bindWallet should receive credentials JSON');
2016-06-06 12:21:15 -03:00
2016-08-29 11:56:31 -03:00
// Create the client
2016-06-07 12:03:00 -03:00
var getBWSURL = function(walletId) {
2016-06-06 12:21:15 -03:00
var config = configService.getSync();
var defaults = configService.getDefaults();
return ((config.bwsFor && config.bwsFor[walletId]) || defaults.bws.url);
};
2016-06-14 12:28:21 -03:00
var client = bwcService.getClient(JSON.stringify(credentials), {
bwsurl: getBWSURL(credentials.walletId),
});
2016-06-06 12:21:15 -03:00
var skipKeyValidation = root.profile.isChecked(platformInfo.ua, credentials.walletId);
2016-06-15 12:15:06 -03:00
if (!skipKeyValidation)
2016-06-21 11:17:42 -03:00
root.runValidation(client, 500);
2016-06-06 12:21:15 -03:00
2016-06-14 12:28:21 -03:00
$log.info('Binding wallet:' + credentials.walletId + ' Validating?:' + !skipKeyValidation);
return cb(null, root.bindWalletClient(client));
2015-03-06 12:00:10 -03:00
};
root.bindProfile = function(profile, cb) {
root.profile = profile;
2016-01-04 08:55:28 -03:00
2015-03-06 12:00:10 -03:00
configService.get(function(err) {
2015-04-25 12:37:04 -03:00
$log.debug('Preferences read');
2015-03-06 12:00:10 -03:00
if (err) return cb(err);
2016-06-06 12:21:15 -03:00
2016-06-13 09:50:37 -03:00
function bindWallets(cb) {
var l = root.profile.credentials.length;
2016-06-15 12:15:06 -03:00
var i = 0,
totalBound = 0;
2016-06-13 09:50:37 -03:00
2016-06-13 10:39:28 -03:00
if (!l) return cb();
2016-06-13 09:50:37 -03:00
lodash.each(root.profile.credentials, function(credentials) {
root.bindWallet(credentials, function(err, bound) {
i++;
totalBound += bound;
if (i == l) {
$log.info('Bound ' + totalBound + ' out of ' + l + ' wallets');
return cb();
2016-05-29 21:20:40 -03:00
}
2016-06-13 09:50:37 -03:00
});
});
}
bindWallets(function() {
2016-08-22 14:42:43 -03:00
root.isBound = true;
2016-08-22 15:11:58 -03:00
lodash.each(root._queue, function(x) {
$timeout(function() {
return x();
}, 1);
});
root._queue = [];
2016-08-22 14:42:43 -03:00
root.isDisclaimerAccepted(function(val) {
if (!val) {
return cb(new Error('NONAGREEDDISCLAIMER: Non agreed disclaimer'));
}
var config = configService.getSync();
if (config.pushNotifications.enabled && usePushNotifications)
root.pushNotificationsInit();
2016-08-22 14:42:43 -03:00
return cb();
});
2015-03-06 12:00:10 -03:00
});
});
};
2016-08-22 15:11:58 -03:00
root._queue = [];
root.whenAvailable = function(cb) {
if (!root.isBound) {
root._queue.push(cb);
return;
}
return cb();
};
root.pushNotificationsInit = function() {
var defaults = configService.getDefaults();
2016-08-15 10:25:43 -03:00
var push = pushNotificationsService.init(root.wallet);
2016-01-04 08:55:28 -03:00
2016-10-10 13:45:43 -03:00
if (!push) return;
push.on('notification', function(data) {
if (!data.additionalData.foreground) {
$log.debug('Push notification event: ', data.message);
$timeout(function() {
var wallets = root.getWallets();
var walletToFind = data.additionalData.walletId;
2016-01-04 08:55:28 -03:00
var walletFound = lodash.find(wallets, function(w) {
return (lodash.isEqual(walletToFind, sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash(w.id))));
});
if (!walletFound) return $log.debug('Wallet not found');
}, 100);
}
});
2016-10-06 12:17:32 -03:00
push.on('error', function(e) {
2016-10-07 14:57:36 -03:00
$log.warn('Error with push notifications:' + e.message);
2016-10-06 12:17:32 -03:00
});
2015-03-06 12:00:10 -03:00
};
2016-01-04 08:55:28 -03:00
root.loadAndBindProfile = function(cb) {
storageService.getProfile(function(err, profile) {
if (err) {
$rootScope.$emit('Local/DeviceError', err);
return cb(err);
}
if (!profile) {
// Migration??
storageService.tryToMigrate(function(err, migratedProfile) {
if (err) return cb(err);
if (!migratedProfile)
return cb(new Error('NOPROFILE: No profile'));
profile = migratedProfile;
return root.bindProfile(profile, cb);
})
} else {
2015-12-04 18:45:35 -03:00
$log.debug('Profile read');
return root.bindProfile(profile, cb);
}
});
2015-03-06 12:00:10 -03:00
};
2016-06-07 10:39:53 -03:00
var seedWallet = function(opts, cb) {
2015-09-02 15:56:00 -03:00
opts = opts || {};
2016-06-01 15:49:20 -03:00
var walletClient = bwcService.getClient(null, opts);
2015-09-02 15:56:00 -03:00
var network = opts.networkName || 'livenet';
2015-08-27 12:07:13 -03:00
2015-09-02 15:56:00 -03:00
if (opts.mnemonic) {
try {
opts.mnemonic = root._normalizeMnemonic(opts.mnemonic);
walletClient.seedFromMnemonic(opts.mnemonic, {
network: network,
passphrase: opts.passphrase,
2015-11-04 01:54:54 -03:00
account: opts.account || 0,
2015-11-10 20:05:05 -03:00
derivationStrategy: opts.derivationStrategy || 'BIP44',
});
2015-11-04 17:50:05 -03:00
2015-09-02 15:56:00 -03:00
} catch (ex) {
$log.info(ex);
2016-12-16 12:06:30 -03:00
return cb(gettextCatalog.getString('Could not create: Invalid wallet recovery phrase'));
2015-09-02 15:56:00 -03:00
}
2015-09-05 00:11:14 -03:00
} else if (opts.extendedPrivateKey) {
try {
walletClient.seedFromExtendedPrivateKey(opts.extendedPrivateKey);
} catch (ex) {
$log.warn(ex);
2016-12-16 12:06:30 -03:00
return cb(gettextCatalog.getString('Could not create using the specified extended private key'));
2015-09-05 00:11:14 -03:00
}
2015-09-02 15:56:00 -03:00
} else if (opts.extendedPublicKey) {
try {
walletClient.seedFromExtendedPublicKey(opts.extendedPublicKey, opts.externalSource, opts.entropySource, {
2015-11-05 16:00:38 -03:00
account: opts.account || 0,
2015-11-10 20:05:05 -03:00
derivationStrategy: opts.derivationStrategy || 'BIP44',
});
2015-09-02 15:56:00 -03:00
} catch (ex) {
$log.warn("Creating wallet from Extended Public Key Arg:", ex, opts);
2016-12-16 12:06:30 -03:00
return cb(gettextCatalog.getString('Could not create using the specified extended public key'));
2015-09-02 15:56:00 -03:00
}
} else {
var lang = uxLanguage.getCurrentLanguage();
try {
walletClient.seedFromRandomWithMnemonic({
network: network,
passphrase: opts.passphrase,
language: lang,
account: 0,
});
2015-09-02 15:56:00 -03:00
} catch (e) {
$log.info('Error creating recovery phrase: ' + e.message);
2015-09-02 15:56:00 -03:00
if (e.message.indexOf('language') > 0) {
$log.info('Using default language for recovery phrase');
walletClient.seedFromRandomWithMnemonic({
network: network,
passphrase: opts.passphrase,
account: 0,
});
2015-09-02 15:56:00 -03:00
} else {
return cb(e);
}
2015-08-27 12:07:13 -03:00
}
}
2015-09-02 15:56:00 -03:00
return cb(null, walletClient);
};
2016-06-06 12:21:15 -03:00
// Creates a wallet on BWC/BWS
2016-06-07 10:39:53 -03:00
var doCreateWallet = function(opts, cb) {
2016-06-06 12:21:15 -03:00
$log.debug('Creating Wallet:', opts);
2016-06-13 10:39:28 -03:00
$timeout(function() {
seedWallet(opts, function(err, walletClient) {
if (err) return cb(err);
2016-06-13 10:39:28 -03:00
var name = opts.name || gettextCatalog.getString('Personal Wallet');
var myName = opts.myName || gettextCatalog.getString('me');
2016-06-05 16:27:49 -03:00
2016-06-13 10:39:28 -03:00
walletClient.createWallet(name, myName, opts.m, opts.n, {
network: opts.networkName,
singleAddress: opts.singleAddress,
walletPrivKey: opts.walletPrivKey,
}, function(err, secret) {
2016-12-16 12:06:30 -03:00
if (err) return bwcError.cb(err, gettextCatalog.getString('Error creating wallet'), cb);
2016-06-13 10:39:28 -03:00
return cb(null, walletClient, secret);
});
2015-03-06 12:00:10 -03:00
});
2016-06-13 11:44:58 -03:00
}, 50);
2015-03-06 12:00:10 -03:00
};
2016-06-06 12:21:15 -03:00
// create and store a wallet
root.createWallet = function(opts, cb) {
2016-06-07 10:39:53 -03:00
doCreateWallet(opts, function(err, walletClient, secret) {
2016-06-07 12:34:16 -03:00
if (err) return cb(err);
2016-08-15 16:07:30 -03:00
addAndBindWalletClient(walletClient, {
2016-06-06 12:21:15 -03:00
bwsurl: opts.bwsurl
}, cb);
2015-09-02 15:56:00 -03:00
});
2015-03-06 12:00:10 -03:00
};
2016-06-06 12:21:15 -03:00
// joins and stores a wallet
2015-10-19 17:58:12 -03:00
root.joinWallet = function(opts, cb) {
2015-03-06 12:00:10 -03:00
var walletClient = bwcService.getClient();
$log.debug('Joining Wallet:', opts);
try {
2015-11-11 10:53:51 -03:00
var walletData = bwcService.parseSecret(opts.secret);
2015-08-12 11:16:34 -03:00
// check if exist
if (lodash.find(root.profile.credentials, {
2016-09-01 16:48:46 -03:00
'walletId': walletData.walletId
})) {
2016-12-16 12:06:30 -03:00
return cb(gettextCatalog.getString('Cannot join the same wallet more that once'));
2015-08-12 11:16:34 -03:00
}
} catch (ex) {
$log.debug(ex);
2016-12-16 12:06:30 -03:00
return cb(gettextCatalog.getString('Bad wallet invitation'));
}
2015-09-02 15:56:00 -03:00
opts.networkName = walletData.network;
$log.debug('Joining Wallet:', opts);
2016-06-07 10:39:53 -03:00
seedWallet(opts, function(err, walletClient) {
2015-09-02 15:56:00 -03:00
if (err) return cb(err);
2015-09-28 10:26:57 -03:00
walletClient.joinWallet(opts.secret, opts.myName || 'me', {}, function(err) {
2016-12-16 12:06:30 -03:00
if (err) return bwcError.cb(err, gettextCatalog.getString('Could not join wallet'), cb);
2016-08-15 16:07:30 -03:00
addAndBindWalletClient(walletClient, {
2016-06-06 12:21:15 -03:00
bwsurl: opts.bwsurl
}, cb);
2015-10-19 17:26:15 -03:00
});
});
2015-03-06 12:00:10 -03:00
};
2016-08-15 10:25:43 -03:00
root.getWallet = function(walletId) {
return root.wallet[walletId];
2015-06-27 13:22:56 -03:00
};
2016-08-15 10:25:43 -03:00
2016-06-06 12:21:15 -03:00
root.deleteWalletClient = function(client, cb) {
var walletId = client.credentials.walletId;
var config = configService.getSync();
if (config.pushNotifications.enabled)
pushNotificationsService.unsubscribe(root.getWallet(walletId), function(err) {
if (err) $log.warn('Unsubscription error: ' + err.message);
else $log.debug('Unsubscribed from push notifications service');
});
2015-03-06 12:00:10 -03:00
2016-06-06 19:09:57 -03:00
$log.debug('Deleting Wallet:', client.credentials.walletName);
client.removeAllListeners();
2016-06-06 12:21:15 -03:00
root.profile.deleteWallet(walletId);
2015-03-06 12:00:10 -03:00
2016-08-15 10:25:43 -03:00
delete root.wallet[walletId];
2015-11-06 16:54:25 -03:00
2016-06-06 12:21:15 -03:00
storageService.removeAllWalletData(walletId, function(err) {
2016-05-29 21:20:40 -03:00
if (err) $log.warn(err);
});
2015-11-06 16:54:25 -03:00
2016-08-22 15:11:58 -03:00
storageService.storeProfile(root.profile, function(err) {
if (err) return cb(err);
return cb();
2015-03-06 12:00:10 -03:00
});
};
2016-06-17 11:46:40 -03:00
root.setMetaData = function(walletClient, addressBook, cb) {
2015-11-09 10:58:10 -03:00
storageService.getAddressbook(walletClient.credentials.network, function(err, localAddressBook) {
2015-11-11 10:37:07 -03:00
var localAddressBook1 = {};
try {
localAddressBook1 = JSON.parse(localAddressBook);
} catch (ex) {
$log.warn(ex);
}
var mergeAddressBook = lodash.merge(addressBook, localAddressBook1);
2015-11-09 10:58:10 -03:00
storageService.setAddressbook(walletClient.credentials.network, JSON.stringify(addressBook), function(err) {
if (err) return cb(err);
2016-06-17 11:46:40 -03:00
return cb(null);
});
});
}
2016-06-06 18:26:45 -03:00
// Adds and bind a new client to the profile
2016-08-15 16:07:30 -03:00
var addAndBindWalletClient = function(client, opts, cb) {
2016-06-07 12:34:16 -03:00
if (!client || !client.credentials)
2016-12-16 12:06:30 -03:00
return cb(gettextCatalog.getString('Could not access wallet'));
2016-06-07 12:34:16 -03:00
2016-06-06 12:21:15 -03:00
var walletId = client.credentials.walletId
2015-03-06 12:00:10 -03:00
2016-06-06 12:21:15 -03:00
if (!root.profile.addWallet(JSON.parse(client.export())))
2016-12-16 12:06:30 -03:00
return cb(gettextCatalog.getString('Wallet already in Copay'));
2015-10-25 23:25:44 -03:00
var skipKeyValidation = root.profile.isChecked(platformInfo.ua, walletId);
2016-06-15 12:15:06 -03:00
if (!skipKeyValidation)
root.runValidation(client);
2016-06-06 12:21:15 -03:00
root.bindWalletClient(client);
2015-10-25 23:25:44 -03:00
2016-06-06 12:21:15 -03:00
var saveBwsUrl = function(cb) {
var defaults = configService.getDefaults();
var bwsFor = {};
bwsFor[walletId] = opts.bwsurl || defaults.bws.url;
2015-11-14 17:29:45 -03:00
2016-06-06 18:26:45 -03:00
// Dont save the default
if (bwsFor[walletId] == defaults.bws.url)
return cb();
2016-06-06 12:21:15 -03:00
configService.set({
bwsFor: bwsFor,
}, function(err) {
if (err) $log.warn(err);
return cb();
});
};
2015-11-14 17:29:45 -03:00
2016-06-06 12:21:15 -03:00
saveBwsUrl(function() {
2016-08-22 14:42:43 -03:00
storageService.storeProfile(root.profile, function(err) {
var config = configService.getSync();
if (config.pushNotifications.enabled)
pushNotificationsService.enableNotifications(root.wallet);
return cb(err, client);
2015-10-25 23:25:44 -03:00
});
2015-10-25 23:06:29 -03:00
});
2015-03-06 12:00:10 -03:00
};
2016-06-06 18:26:45 -03:00
root.storeProfileIfDirty = function(cb) {
if (root.profile.dirty) {
storageService.storeProfile(root.profile, function(err) {
$log.debug('Saved modified Profile');
if (cb) return cb(err);
});
} else {
if (cb) return cb();
};
};
2015-08-24 17:09:59 -03:00
root.importWallet = function(str, opts, cb) {
2015-11-06 16:04:35 -03:00
2016-06-01 15:49:20 -03:00
var walletClient = bwcService.getClient(null, opts);
2015-10-19 17:26:15 -03:00
2015-08-24 17:09:59 -03:00
$log.debug('Importing Wallet:', opts);
2016-10-12 18:42:30 -03:00
2015-08-24 17:09:59 -03:00
try {
2016-10-19 13:36:22 -03:00
var c = JSON.parse(str);
if (c.xPrivKey && c.xPrivKeyEncrypted) {
$log.warn('Found both encrypted and decrypted key. Deleting the encrypted version');
delete c.xPrivKeyEncrypted;
delete c.mnemonicEncrypted;
}
str = JSON.stringify(c);
2015-08-24 17:09:59 -03:00
walletClient.import(str, {
compressed: opts.compressed,
password: opts.password
});
} catch (err) {
2016-12-16 12:06:30 -03:00
return cb(gettextCatalog.getString('Could not import. Check input file and spending password'));
2015-08-24 17:09:59 -03:00
}
2015-11-06 17:45:23 -03:00
str = JSON.parse(str);
2016-09-03 22:25:51 -03:00
if (!str.n) {
return cb("Backup format not recognized. If you are using a Copay Beta backup and version is older than 0.10, please see: https://github.com/bitpay/copay/issues/4730#issuecomment-244522614");
}
2015-11-09 10:58:10 -03:00
var addressBook = str.addressBook || {};
2015-11-06 16:04:35 -03:00
2016-08-15 16:07:30 -03:00
addAndBindWalletClient(walletClient, {
2016-06-15 12:15:06 -03:00
bwsurl: opts.bwsurl
2016-06-06 12:21:15 -03:00
}, function(err, walletId) {
if (err) return cb(err);
2016-06-17 11:46:40 -03:00
root.setMetaData(walletClient, addressBook, function(error) {
2016-02-22 19:32:24 -03:00
if (error) $log.warn(error);
2016-08-15 16:07:30 -03:00
return cb(err, walletClient);
});
2015-11-06 11:26:50 -03:00
});
2015-08-24 17:09:59 -03:00
};
2015-10-20 12:03:08 -03:00
root.importExtendedPrivateKey = function(xPrivKey, opts, cb) {
2016-06-01 16:44:07 -03:00
var walletClient = bwcService.getClient(null, opts);
2015-09-05 00:11:14 -03:00
$log.debug('Importing Wallet xPrivKey');
2016-05-09 12:03:25 -03:00
walletClient.importFromExtendedPrivateKey(xPrivKey, opts, function(err) {
2016-06-07 17:11:57 -03:00
if (err) {
if (err instanceof errors.NOT_AUTHORIZED)
return cb(err);
2016-12-16 12:06:30 -03:00
return bwcError.cb(err, gettextCatalog.getString('Could not import'), cb);
2016-06-07 17:11:57 -03:00
}
2015-09-05 00:11:14 -03:00
2016-08-15 16:07:30 -03:00
addAndBindWalletClient(walletClient, {
2016-06-15 12:15:06 -03:00
bwsurl: opts.bwsurl
2016-06-06 12:21:15 -03:00
}, cb);
2015-09-05 00:11:14 -03:00
});
};
root._normalizeMnemonic = function(words) {
2016-12-28 16:49:42 -03:00
if (!words || !words.indexOf) return words;
2015-10-19 11:19:28 -03:00
var isJA = words.indexOf('\u3000') > -1;
var wordList = words.split(/[\u3000\s]+/);
return wordList.join(isJA ? '\u3000' : ' ');
2015-09-05 01:02:01 -03:00
};
2015-09-05 00:11:14 -03:00
root.importMnemonic = function(words, opts, cb) {
2016-06-01 16:44:07 -03:00
var walletClient = bwcService.getClient(null, opts);
2015-09-05 01:02:01 -03:00
2015-08-24 17:09:59 -03:00
$log.debug('Importing Wallet Mnemonic');
words = root._normalizeMnemonic(words);
2015-08-24 17:09:59 -03:00
walletClient.importFromMnemonic(words, {
2015-09-02 15:56:00 -03:00
network: opts.networkName,
2015-08-24 17:09:59 -03:00
passphrase: opts.passphrase,
2015-11-04 17:50:05 -03:00
account: opts.account || 0,
2015-08-24 17:09:59 -03:00
}, function(err) {
2016-06-07 17:11:57 -03:00
if (err) {
if (err instanceof errors.NOT_AUTHORIZED)
return cb(err);
2016-12-16 12:06:30 -03:00
return bwcError.cb(err, gettextCatalog.getString('Could not import'), cb);
2016-06-07 17:11:57 -03:00
}
2015-08-24 17:09:59 -03:00
2016-08-15 16:07:30 -03:00
addAndBindWalletClient(walletClient, {
2016-06-15 12:15:06 -03:00
bwsurl: opts.bwsurl
2016-06-06 12:21:15 -03:00
}, cb);
2015-08-24 17:09:59 -03:00
});
};
root.importExtendedPublicKey = function(opts, cb) {
2016-06-01 16:44:07 -03:00
var walletClient = bwcService.getClient(null, opts);
$log.debug('Importing Wallet XPubKey');
walletClient.importFromExtendedPublicKey(opts.extendedPublicKey, opts.externalSource, opts.entropySource, {
2015-11-04 01:54:54 -03:00
account: opts.account || 0,
2015-11-10 20:05:05 -03:00
derivationStrategy: opts.derivationStrategy || 'BIP44',
}, function(err) {
if (err) {
// in HW wallets, req key is always the same. They can't addAccess.
2016-01-22 18:16:50 -03:00
if (err instanceof errors.NOT_AUTHORIZED)
err.name = 'WALLET_DOES_NOT_EXIST';
2016-12-16 12:06:30 -03:00
return bwcError.cb(err, gettextCatalog.getString('Could not import'), cb);
}
2016-08-15 16:07:30 -03:00
addAndBindWalletClient(walletClient, {
2016-06-15 12:15:06 -03:00
bwsurl: opts.bwsurl
2016-06-06 12:21:15 -03:00
}, cb);
});
};
root.createProfile = function(cb) {
$log.info('Creating profile');
2015-10-20 12:18:44 -03:00
var defaults = configService.getDefaults();
configService.get(function(err) {
if (err) $log.debug(err);
var p = Profile.create();
storageService.storeNewProfile(p, function(err) {
if (err) return cb(err);
root.bindProfile(p, function(err) {
// ignore NONAGREEDDISCLAIMER
if (err && err.toString().match('NONAGREEDDISCLAIMER')) return cb();
return cb(err);
2015-03-06 12:00:10 -03:00
});
});
});
};
root.createDefaultWallet = function(cb) {
var opts = {};
opts.m = 1;
opts.n = 1;
opts.networkName = 'livenet';
root.createWallet(opts, cb);
};
2015-12-07 12:52:42 -03:00
root.setDisclaimerAccepted = function(cb) {
2016-02-22 19:32:24 -03:00
root.profile.disclaimerAccepted = true;
2016-06-14 17:48:00 -03:00
storageService.storeProfile(root.profile, function(err) {
2016-02-22 19:32:24 -03:00
return cb(err);
2015-11-30 13:32:54 -03:00
});
2015-12-04 18:45:35 -03:00
};
2015-12-07 12:52:42 -03:00
root.isDisclaimerAccepted = function(cb) {
2016-02-22 19:32:24 -03:00
var disclaimerAccepted = root.profile && root.profile.disclaimerAccepted;
if (disclaimerAccepted)
return cb(true);
// OLD flag
storageService.getCopayDisclaimerFlag(function(err, val) {
if (val) {
root.profile.disclaimerAccepted = true;
2015-12-04 18:45:35 -03:00
return cb(true);
2016-01-04 08:55:28 -03:00
} else {
2015-12-04 18:45:35 -03:00
return cb();
}
2016-01-04 08:55:28 -03:00
});
2015-12-04 18:45:35 -03:00
};
2015-11-30 13:32:54 -03:00
2016-06-06 18:26:45 -03:00
root.updateCredentials = function(credentials, cb) {
2016-06-14 17:59:23 -03:00
root.profile.updateWallet(credentials);
2016-06-14 17:48:00 -03:00
storageService.storeProfile(root.profile, cb);
2015-03-06 12:00:10 -03:00
};
2017-01-31 14:24:13 -03:00
root.getLastKnownBalance = function(wid, cb) {
storageService.getBalanceCache(wid, cb);
};
root.addLastKnownBalance = function(wallet, cb) {
var now = Math.floor(Date.now() / 1000);
var showRange = 600; // 10min;
root.getLastKnownBalance(wallet.id, function(err, data) {
if (data) {
data = JSON.parse(data);
wallet.cachedBalance = data.balance;
wallet.cachedBalanceUpdatedOn = (data.updatedOn < now - showRange) ? data.updatedOn : null;
}
return cb();
});
};
root.setLastKnownBalance = function(wid, balance, cb) {
storageService.setBalanceCache(wid, {
balance: balance,
updatedOn: Math.floor(Date.now() / 1000),
}, cb);
};
2016-08-15 11:56:59 -03:00
root.getWallets = function(opts) {
if (opts && !lodash.isObject(opts))
throw "bad argument";
opts = opts || {};
2016-08-15 10:25:43 -03:00
var ret = lodash.values(root.wallet);
2016-08-15 11:56:59 -03:00
if (opts.network) {
2016-08-15 10:25:43 -03:00
ret = lodash.filter(ret, function(x) {
2016-08-15 11:56:59 -03:00
return (x.credentials.network == opts.network);
});
}
if (opts.n) {
ret = lodash.filter(ret, function(w) {
return (w.credentials.n == opts.n);
2016-08-15 10:25:43 -03:00
});
}
if (opts.m) {
ret = lodash.filter(ret, function(w) {
return (w.credentials.m == opts.m);
});
}
2016-08-15 11:56:59 -03:00
if (opts.onlyComplete) {
2016-08-15 10:25:43 -03:00
ret = lodash.filter(ret, function(w) {
2016-08-15 11:56:59 -03:00
return w.isComplete();
2016-08-15 10:25:43 -03:00
});
2016-08-29 11:56:31 -03:00
} else {}
2016-08-15 10:25:43 -03:00
2017-01-31 14:24:13 -03:00
// Add cached balance async
lodash.each(ret, function(x) {
root.addLastKnownBalance(x, function() {});
});
2016-08-31 17:12:36 -03:00
return lodash.sortBy(ret, [
2016-08-31 18:12:28 -03:00
2016-08-31 17:12:36 -03:00
function(x) {
return x.isComplete();
}, 'createdOn'
]);
2016-06-06 18:26:45 -03:00
};
2015-03-06 12:00:10 -03:00
2016-08-31 12:18:04 -03:00
root.toggleHideBalanceFlag = function(walletId, cb) {
2016-08-31 11:49:28 -03:00
root.wallet[walletId].balanceHidden = !root.wallet[walletId].balanceHidden;
storageService.setHideBalanceFlag(walletId, root.wallet[walletId].balanceHidden.toString(), cb);
2016-08-31 17:12:36 -03:00
};
2016-08-31 18:12:28 -03:00
root.getNotifications = function(opts, cb) {
opts = opts || {};
2016-08-31 17:12:36 -03:00
2017-01-31 14:24:13 -03:00
var TIME_STAMP = 60 * 60 * 6;
2017-01-18 11:00:51 -03:00
var MAX = 30;
2016-08-31 17:12:36 -03:00
2016-09-27 15:57:39 -03:00
var typeFilter = {
'NewOutgoingTx': 1,
'NewIncomingTx': 1
2016-08-31 17:12:36 -03:00
};
var w = root.getWallets();
if (lodash.isEmpty(w)) return cb();
var l = w.length,
j = 0,
notifications = [];
2016-08-31 18:12:28 -03:00
function isActivityCached(wallet) {
return wallet.cachedActivity && wallet.cachedActivity.isValid;
};
2016-09-01 17:41:00 -03:00
function updateNotifications(wallet, cb2) {
2016-08-31 18:12:28 -03:00
if (isActivityCached(wallet) && !opts.force) return cb2();
wallet.getNotifications({
timeSpan: TIME_STAMP,
includeOwn: true,
}, function(err, n) {
if (err) return cb2(err);
wallet.cachedActivity = {
n: n.slice(-MAX),
isValid: true,
};
return cb2();
});
};
function process(notifications) {
if (!notifications) return [];
var shown = lodash.sortBy(notifications, 'createdOn').reverse();
shown = shown.splice(0, opts.limit || MAX);
lodash.each(shown, function(x) {
x.txpId = x.data ? x.data.txProposalId : null;
x.txid = x.data ? x.data.txid : null;
x.types = [x.type];
if (x.data && x.data.amount)
x.amountStr = txFormatService.formatAmountStr(x.data.amount);
x.action = function() {
// TODO?
2016-09-28 11:08:08 -03:00
// $state.go('tabs.wallet', {
2016-09-20 12:02:45 -03:00
// walletId: x.walletId,
// txpId: x.txpId,
// txid: x.txid,
// });
2016-08-31 18:12:28 -03:00
};
});
2016-09-01 16:50:13 -03:00
var finale = shown; // GROUPING DISABLED!
var finale = [],
2016-11-17 16:07:06 -03:00
prev;
// Item grouping... DISABLED.
// REMOVE (if we want 1-to-1 notification) ????
lodash.each(shown, function(x) {
if (prev && prev.walletId === x.walletId && prev.txpId && prev.txpId === x.txpId && prev.creatorId && prev.creatorId === x.creatorId) {
prev.types.push(x.type);
prev.data = lodash.assign(prev.data, x.data);
prev.txid = prev.txid || x.txid;
prev.amountStr = prev.amountStr || x.amountStr;
prev.creatorName = prev.creatorName || x.creatorName;
} else {
finale.push(x);
prev = x;
}
});
2016-08-31 18:12:28 -03:00
var u = bwcService.getUtils();
lodash.each(finale, function(x) {
if (x.data && x.data.message && x.wallet && x.wallet.credentials.sharedEncryptingKey) {
// TODO TODO TODO => BWC
x.message = u.decryptMessage(x.data.message, x.wallet.credentials.sharedEncryptingKey);
}
});
return finale;
};
2016-08-31 17:12:36 -03:00
lodash.each(w, function(wallet) {
2016-09-01 17:41:00 -03:00
updateNotifications(wallet, function(err) {
2016-08-31 17:12:36 -03:00
j++;
if (err) {
2016-08-31 18:12:28 -03:00
$log.warn('Error updating notifications:' + err);
} else {
2016-09-14 11:08:47 -03:00
var n;
n = lodash.filter(wallet.cachedActivity.n, function(x) {
2016-09-27 15:57:39 -03:00
return typeFilter[x.type];
2016-08-31 18:12:28 -03:00
});
2016-08-31 17:12:36 -03:00
2016-08-31 18:12:28 -03:00
var idToName = {};
if (wallet.cachedStatus) {
lodash.each(wallet.cachedStatus.wallet.copayers, function(c) {
idToName[c.id] = c.name;
});
}
2016-08-31 17:12:36 -03:00
2016-08-31 18:12:28 -03:00
lodash.each(n, function(x) {
x.wallet = wallet;
if (x.creatorId && wallet.cachedStatus) {
x.creatorName = idToName[x.creatorId];
};
2016-08-31 17:12:36 -03:00
});
2016-08-31 18:12:28 -03:00
notifications.push(n);
}
if (j == l) {
notifications = lodash.sortBy(notifications, 'createdOn');
2016-09-01 16:48:46 -03:00
notifications = lodash.compact(lodash.flatten(notifications)).slice(0, MAX);
2017-01-18 11:00:51 -03:00
var total = notifications.length;
return cb(null, process(notifications), total);
2016-08-31 17:12:36 -03:00
};
});
});
};
2016-08-31 11:49:28 -03:00
2016-09-01 17:41:00 -03:00
root.getTxps = function(opts, cb) {
2016-09-01 19:14:18 -03:00
var MAX = 100;
2016-09-01 17:41:00 -03:00
opts = opts || {};
var w = root.getWallets();
if (lodash.isEmpty(w)) return cb();
var txps = [];
lodash.each(w, function(x) {
if (x.pendingTxps)
txps = txps.concat(x.pendingTxps);
});
2016-09-01 19:14:18 -03:00
var n = txps.length;
txps = lodash.sortBy(txps, 'pendingForUs', 'createdOn');
txps = lodash.compact(lodash.flatten(txps)).slice(0, opts.limit || MAX);
2016-09-02 14:33:18 -03:00
return cb(null, txps, n);
2016-09-01 17:41:00 -03:00
};
2015-03-06 12:00:10 -03:00
return root;
});