2016-05-09 15:56:44 -03:00
|
|
|
|
'use strict';
|
|
|
|
|
|
|
2016-12-05 17:33:46 -05:00
|
|
|
|
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) {
|
2016-08-12 11:10:26 -03:00
|
|
|
|
|
2017-08-24 17:02:49 -03:00
|
|
|
|
// Ratio low amount warning (fee/amount) in incoming TX
|
|
|
|
|
|
var LOW_AMOUNT_RATIO = 0.15;
|
2017-06-22 17:00:20 -03:00
|
|
|
|
|
|
|
|
|
|
// Ratio of "many utxos" warning in total balance (fee/amount)
|
|
|
|
|
|
var TOTAL_LOW_WARNING_RATIO = .3;
|
2017-06-22 13:44:49 -03:00
|
|
|
|
|
2016-05-09 15:56:44 -03:00
|
|
|
|
var root = {};
|
|
|
|
|
|
|
2016-12-05 17:33:46 -05:00
|
|
|
|
root.externalSource = {
|
|
|
|
|
|
ledger: ledger.description,
|
|
|
|
|
|
trezor: trezor.description,
|
|
|
|
|
|
intelTEE: intelTEE.description
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-08-17 18:02:47 -03:00
|
|
|
|
root.WALLET_STATUS_MAX_TRIES = 7;
|
|
|
|
|
|
root.WALLET_STATUS_DELAY_BETWEEN_TRIES = 1.4 * 1000;
|
2016-08-17 17:26:13 -03:00
|
|
|
|
root.SOFT_CONFIRMATION_LIMIT = 12;
|
2016-08-18 10:37:08 -03:00
|
|
|
|
root.SAFE_CONFIRMATIONS = 6;
|
2016-08-15 10:25:43 -03:00
|
|
|
|
|
2016-09-08 12:13:37 -03:00
|
|
|
|
var errors = bwcService.getErrors();
|
|
|
|
|
|
|
2016-08-15 10:25:43 -03:00
|
|
|
|
var _signWithLedger = function(wallet, txp, cb) {
|
2016-05-09 15:56:44 -03:00
|
|
|
|
$log.info('Requesting Ledger Chrome app to sign the transaction');
|
|
|
|
|
|
|
2016-08-15 10:25:43 -03:00
|
|
|
|
ledger.signTx(txp, wallet.credentials.account, function(result) {
|
2016-05-09 15:56:44 -03:00
|
|
|
|
$log.debug('Ledger response', result);
|
|
|
|
|
|
if (!result.success)
|
|
|
|
|
|
return cb(result.message || result.error);
|
|
|
|
|
|
|
|
|
|
|
|
txp.signatures = lodash.map(result.signatures, function(s) {
|
|
|
|
|
|
return s.substring(0, s.length - 2);
|
|
|
|
|
|
});
|
2016-08-15 10:25:43 -03:00
|
|
|
|
return wallet.signTxProposal(txp, cb);
|
2016-05-09 15:56:44 -03:00
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-08-15 10:25:43 -03:00
|
|
|
|
var _signWithTrezor = function(wallet, txp, cb) {
|
2016-05-09 15:56:44 -03:00
|
|
|
|
$log.info('Requesting Trezor to sign the transaction');
|
|
|
|
|
|
|
2016-08-15 10:25:43 -03:00
|
|
|
|
var xPubKeys = lodash.pluck(wallet.credentials.publicKeyRing, 'xPubKey');
|
|
|
|
|
|
trezor.signTx(xPubKeys, txp, wallet.credentials.account, function(err, result) {
|
2016-05-09 15:56:44 -03:00
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
|
|
|
|
|
|
|
$log.debug('Trezor response', result);
|
|
|
|
|
|
txp.signatures = result.signatures;
|
2016-08-15 10:25:43 -03:00
|
|
|
|
return wallet.signTxProposal(txp, cb);
|
2016-05-09 15:56:44 -03:00
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-12-05 17:33:46 -05:00
|
|
|
|
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:
|
2017-03-17 17:00:26 -04:00
|
|
|
|
root.getAddressObj(wallet, address, function(err, addrObj) {
|
|
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
|
return intelTEE.showReceiveAddress(wallet.credentials.hwInfo.id, addrObj, cb);
|
|
|
|
|
|
});
|
2016-12-05 17:33:46 -05:00
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
cb('Error: unrecognized external source');
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-08-22 22:10:46 -03:00
|
|
|
|
root.invalidateCache = function(wallet) {
|
2016-09-02 14:55:18 -03:00
|
|
|
|
if (wallet.cachedStatus)
|
2016-08-23 10:26:47 -03:00
|
|
|
|
wallet.cachedStatus.isValid = false;
|
2016-08-22 22:10:46 -03:00
|
|
|
|
|
2016-09-02 14:55:18 -03:00
|
|
|
|
if (wallet.completeHistory)
|
2016-08-23 10:26:47 -03:00
|
|
|
|
wallet.completeHistory.isValid = false;
|
2016-09-02 14:55:18 -03:00
|
|
|
|
|
|
|
|
|
|
if (wallet.cachedActivity)
|
|
|
|
|
|
wallet.cachedActivity.isValid = false;
|
|
|
|
|
|
|
|
|
|
|
|
if (wallet.cachedTxps)
|
|
|
|
|
|
wallet.cachedTxps.isValid = false;
|
2016-08-22 22:10:46 -03:00
|
|
|
|
};
|
|
|
|
|
|
|
2016-08-17 18:02:47 -03:00
|
|
|
|
root.getStatus = function(wallet, opts, cb) {
|
|
|
|
|
|
opts = opts || {};
|
2016-08-15 10:25:43 -03:00
|
|
|
|
|
2016-08-23 16:53:26 -03:00
|
|
|
|
|
|
|
|
|
|
function processPendingTxps(status) {
|
|
|
|
|
|
var txps = status.pendingTxps;
|
|
|
|
|
|
var now = Math.floor(Date.now() / 1000);
|
|
|
|
|
|
|
|
|
|
|
|
/* To test multiple outputs...
|
|
|
|
|
|
var txp = {
|
|
|
|
|
|
message: 'test multi-output',
|
|
|
|
|
|
fee: 1000,
|
|
|
|
|
|
createdOn: new Date() / 1000,
|
|
|
|
|
|
outputs: []
|
|
|
|
|
|
};
|
|
|
|
|
|
function addOutput(n) {
|
|
|
|
|
|
txp.outputs.push({
|
|
|
|
|
|
amount: 600,
|
|
|
|
|
|
toAddress: '2N8bhEwbKtMvR2jqMRcTCQqzHP6zXGToXcK',
|
|
|
|
|
|
message: 'output #' + (Number(n) + 1)
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
lodash.times(150, addOutput);
|
|
|
|
|
|
txps.push(txp);
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
lodash.each(txps, function(tx) {
|
|
|
|
|
|
|
2017-08-28 15:51:13 -03:00
|
|
|
|
tx = txFormatService.processTx(wallet.coin, tx);
|
2016-08-23 16:53:26 -03:00
|
|
|
|
|
|
|
|
|
|
// no future transactions...
|
|
|
|
|
|
if (tx.createdOn > now)
|
|
|
|
|
|
tx.createdOn = now;
|
|
|
|
|
|
|
|
|
|
|
|
tx.wallet = wallet;
|
|
|
|
|
|
|
|
|
|
|
|
if (!tx.wallet) {
|
|
|
|
|
|
$log.error("no wallet at txp?");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var action = lodash.find(tx.actions, {
|
|
|
|
|
|
copayerId: tx.wallet.copayerId
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (!action && tx.status == 'pending') {
|
|
|
|
|
|
tx.pendingForUs = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (action && action.type == 'accept') {
|
|
|
|
|
|
tx.statusForUs = 'accepted';
|
|
|
|
|
|
} else if (action && action.type == 'reject') {
|
|
|
|
|
|
tx.statusForUs = 'rejected';
|
|
|
|
|
|
} else {
|
|
|
|
|
|
tx.statusForUs = 'pending';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!tx.deleteLockTime)
|
|
|
|
|
|
tx.canBeRemoved = true;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
wallet.pendingTxps = txps;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-08-17 18:02:47 -03:00
|
|
|
|
function get(cb) {
|
|
|
|
|
|
wallet.getStatus({
|
|
|
|
|
|
twoStep: true
|
|
|
|
|
|
}, function(err, ret) {
|
2016-12-19 17:07:58 -03:00
|
|
|
|
if (err) {
|
|
|
|
|
|
if (err instanceof errors.NOT_AUTHORIZED) {
|
|
|
|
|
|
return cb('WALLET_NOT_REGISTERED');
|
|
|
|
|
|
}
|
|
|
|
|
|
return cb(err);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-08-17 18:02:47 -03:00
|
|
|
|
return cb(null, ret);
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
2016-08-15 10:25:43 -03:00
|
|
|
|
|
2016-08-17 18:02:47 -03:00
|
|
|
|
function cacheBalance(wallet, balance) {
|
|
|
|
|
|
if (!balance) return;
|
2016-08-15 10:25:43 -03:00
|
|
|
|
|
2016-09-23 11:14:50 -03:00
|
|
|
|
var config = configService.getSync().wallet;
|
2016-08-15 10:25:43 -03:00
|
|
|
|
|
2016-08-17 18:48:30 -03:00
|
|
|
|
var cache = wallet.cachedStatus;
|
|
|
|
|
|
|
2016-08-17 18:02:47 -03:00
|
|
|
|
// Address with Balance
|
2016-08-17 18:48:30 -03:00
|
|
|
|
cache.balanceByAddress = balance.byAddress;
|
2016-08-15 10:25:43 -03:00
|
|
|
|
|
2016-12-14 12:29:09 -05:00
|
|
|
|
// Total wallet balance is same regardless of 'spend unconfirmed funds' setting.
|
|
|
|
|
|
cache.totalBalanceSat = balance.totalAmount;
|
|
|
|
|
|
|
2016-08-17 18:02:47 -03:00
|
|
|
|
// Spend unconfirmed funds
|
2016-09-23 11:14:50 -03:00
|
|
|
|
if (config.spendUnconfirmed) {
|
2016-08-17 18:48:30 -03:00
|
|
|
|
cache.lockedBalanceSat = balance.lockedAmount;
|
|
|
|
|
|
cache.availableBalanceSat = balance.availableAmount;
|
|
|
|
|
|
cache.totalBytesToSendMax = balance.totalBytesToSendMax;
|
2016-12-09 16:42:11 -05:00
|
|
|
|
cache.pendingAmount = 0;
|
|
|
|
|
|
cache.spendableAmount = balance.totalAmount - balance.lockedAmount;
|
2016-08-17 18:02:47 -03:00
|
|
|
|
} else {
|
2016-08-17 18:48:30 -03:00
|
|
|
|
cache.lockedBalanceSat = balance.lockedConfirmedAmount;
|
|
|
|
|
|
cache.availableBalanceSat = balance.availableConfirmedAmount;
|
|
|
|
|
|
cache.totalBytesToSendMax = balance.totalBytesToSendConfirmedMax;
|
|
|
|
|
|
cache.pendingAmount = balance.totalAmount - balance.totalConfirmedAmount;
|
2016-12-09 16:42:11 -05:00
|
|
|
|
cache.spendableAmount = balance.totalConfirmedAmount - balance.lockedAmount;
|
2016-08-17 18:02:47 -03:00
|
|
|
|
}
|
2016-08-15 10:25:43 -03:00
|
|
|
|
|
2016-08-17 18:02:47 -03:00
|
|
|
|
// Selected unit
|
2016-09-23 11:14:50 -03:00
|
|
|
|
cache.unitToSatoshi = config.settings.unitToSatoshi;
|
2016-08-17 18:48:30 -03:00
|
|
|
|
cache.satToUnit = 1 / cache.unitToSatoshi;
|
2016-08-15 10:25:43 -03:00
|
|
|
|
|
2016-08-17 18:02:47 -03:00
|
|
|
|
//STR
|
2017-08-30 19:40:35 -03:00
|
|
|
|
cache.totalBalanceStr = txFormatService.formatAmount(cache.totalBalanceSat) + ' ' + (wallet.coin).toUpperCase();
|
|
|
|
|
|
cache.lockedBalanceStr = txFormatService.formatAmount(cache.lockedBalanceSat) + ' ' + (wallet.coin).toUpperCase();
|
|
|
|
|
|
cache.availableBalanceStr = txFormatService.formatAmount(cache.availableBalanceSat) + ' ' + (wallet.coin).toUpperCase();
|
|
|
|
|
|
cache.spendableBalanceStr = txFormatService.formatAmount(cache.spendableAmount) + ' ' + (wallet.coin).toUpperCase();
|
|
|
|
|
|
cache.pendingBalanceStr = txFormatService.formatAmount(cache.pendingAmount) + ' ' + (wallet.coin).toUpperCase();
|
2016-08-15 10:25:43 -03:00
|
|
|
|
|
2016-09-23 11:14:50 -03:00
|
|
|
|
cache.alternativeName = config.settings.alternativeName;
|
|
|
|
|
|
cache.alternativeIsoCode = config.settings.alternativeIsoCode;
|
2016-08-15 10:25:43 -03:00
|
|
|
|
|
2016-11-14 10:09:11 -03:00
|
|
|
|
// Check address
|
|
|
|
|
|
root.isAddressUsed(wallet, balance.byAddress, function(err, used) {
|
|
|
|
|
|
if (used) {
|
|
|
|
|
|
$log.debug('Address used. Creating new');
|
|
|
|
|
|
// Force new address
|
|
|
|
|
|
root.getAddress(wallet, true, function(err, addr) {
|
|
|
|
|
|
$log.debug('New address: ', addr);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2016-08-17 18:02:47 -03:00
|
|
|
|
rateService.whenAvailable(function() {
|
2016-08-15 10:25:43 -03:00
|
|
|
|
|
2017-08-28 15:51:13 -03:00
|
|
|
|
var totalBalanceAlternative = rateService.toFiat(cache.totalBalanceSat, cache.alternativeIsoCode, wallet.coin);
|
|
|
|
|
|
var pendingBalanceAlternative = rateService.toFiat(cache.pendingAmount, cache.alternativeIsoCode, wallet.coin);
|
|
|
|
|
|
var lockedBalanceAlternative = rateService.toFiat(cache.lockedBalanceSat, cache.alternativeIsoCode, wallet.coin);
|
|
|
|
|
|
var spendableBalanceAlternative = rateService.toFiat(cache.spendableAmount, cache.alternativeIsoCode, wallet.coin);
|
|
|
|
|
|
var alternativeConversionRate = rateService.toFiat(100000000, cache.alternativeIsoCode, wallet.coin);
|
2016-08-15 10:25:43 -03:00
|
|
|
|
|
2016-08-17 18:48:30 -03:00
|
|
|
|
cache.totalBalanceAlternative = $filter('formatFiatAmount')(totalBalanceAlternative);
|
2016-12-09 16:42:11 -05:00
|
|
|
|
cache.pendingBalanceAlternative = $filter('formatFiatAmount')(pendingBalanceAlternative);
|
2016-08-17 18:48:30 -03:00
|
|
|
|
cache.lockedBalanceAlternative = $filter('formatFiatAmount')(lockedBalanceAlternative);
|
2016-12-09 16:42:11 -05:00
|
|
|
|
cache.spendableBalanceAlternative = $filter('formatFiatAmount')(spendableBalanceAlternative);
|
2016-08-17 18:48:30 -03:00
|
|
|
|
cache.alternativeConversionRate = $filter('formatFiatAmount')(alternativeConversionRate);
|
2016-08-12 11:10:26 -03:00
|
|
|
|
|
2016-08-17 18:48:30 -03:00
|
|
|
|
cache.alternativeBalanceAvailable = true;
|
|
|
|
|
|
cache.isRateAvailable = true;
|
2016-08-17 17:26:13 -03:00
|
|
|
|
});
|
|
|
|
|
|
};
|
2016-08-12 11:10:26 -03:00
|
|
|
|
|
2016-08-17 18:48:30 -03:00
|
|
|
|
function isStatusCached() {
|
|
|
|
|
|
return wallet.cachedStatus && wallet.cachedStatus.isValid;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
function cacheStatus(status) {
|
|
|
|
|
|
wallet.cachedStatus = status || {};
|
|
|
|
|
|
var cache = wallet.cachedStatus;
|
|
|
|
|
|
cache.statusUpdatedOn = Date.now();
|
|
|
|
|
|
cache.isValid = true;
|
|
|
|
|
|
cache.email = status.preferences ? status.preferences.email : null;
|
2016-08-17 18:02:47 -03:00
|
|
|
|
cacheBalance(wallet, status.balance);
|
2016-08-17 17:26:13 -03:00
|
|
|
|
};
|
2016-08-12 11:10:26 -03:00
|
|
|
|
|
2016-08-17 18:02:47 -03:00
|
|
|
|
function walletStatusHash(status) {
|
|
|
|
|
|
return status ? status.balance.totalAmount : wallet.totalBalanceSat;
|
2016-08-12 11:10:26 -03:00
|
|
|
|
};
|
|
|
|
|
|
|
2016-08-17 18:02:47 -03:00
|
|
|
|
function _getStatus(initStatusHash, tries, cb) {
|
2016-08-23 10:26:47 -03:00
|
|
|
|
if (isStatusCached() && !opts.force) {
|
|
|
|
|
|
$log.debug('Wallet status cache hit:' + wallet.id);
|
2016-09-23 16:09:51 -03:00
|
|
|
|
cacheStatus(wallet.cachedStatus);
|
|
|
|
|
|
processPendingTxps(wallet.cachedStatus);
|
2016-08-23 10:26:47 -03:00
|
|
|
|
return cb(null, wallet.cachedStatus);
|
|
|
|
|
|
};
|
2016-08-17 17:26:13 -03:00
|
|
|
|
|
|
|
|
|
|
tries = tries || 0;
|
|
|
|
|
|
|
2016-08-17 18:02:47 -03:00
|
|
|
|
$log.debug('Updating Status:', wallet.credentials.walletName, tries);
|
|
|
|
|
|
get(function(err, status) {
|
|
|
|
|
|
if (err) return cb(err);
|
2016-08-12 11:10:26 -03:00
|
|
|
|
|
2016-08-17 18:02:47 -03:00
|
|
|
|
var currentStatusHash = walletStatusHash(status);
|
|
|
|
|
|
$log.debug('Status update. hash:' + currentStatusHash + ' Try:' + tries);
|
|
|
|
|
|
if (opts.untilItChanges &&
|
|
|
|
|
|
initStatusHash == currentStatusHash &&
|
|
|
|
|
|
tries < root.WALLET_STATUS_MAX_TRIES &&
|
|
|
|
|
|
walletId == wallet.credentials.walletId) {
|
|
|
|
|
|
return $timeout(function() {
|
|
|
|
|
|
$log.debug('Retrying update... ' + walletId + ' Try:' + tries)
|
|
|
|
|
|
return _getStatus(initStatusHash, ++tries, cb);
|
|
|
|
|
|
}, root.WALLET_STATUS_DELAY_BETWEEN_TRIES * tries);
|
|
|
|
|
|
}
|
2016-08-12 11:10:26 -03:00
|
|
|
|
|
2016-08-23 16:53:26 -03:00
|
|
|
|
processPendingTxps(status);
|
|
|
|
|
|
|
2016-08-17 18:02:47 -03:00
|
|
|
|
$log.debug('Got Wallet Status for:' + wallet.credentials.walletName);
|
2016-08-12 11:10:26 -03:00
|
|
|
|
|
2016-08-17 18:48:30 -03:00
|
|
|
|
cacheStatus(status);
|
2016-08-12 11:10:26 -03:00
|
|
|
|
|
2016-08-17 18:02:47 -03:00
|
|
|
|
return cb(null, status);
|
2016-08-12 11:10:26 -03:00
|
|
|
|
});
|
2016-08-17 17:26:13 -03:00
|
|
|
|
};
|
|
|
|
|
|
|
2016-08-18 10:37:08 -03:00
|
|
|
|
_getStatus(walletStatusHash(), 0, cb);
|
2016-08-12 11:10:26 -03:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
var getSavedTxs = function(walletId, cb) {
|
|
|
|
|
|
storageService.getTxHistory(walletId, function(err, txs) {
|
|
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
|
|
|
|
|
|
|
var localTxs = [];
|
|
|
|
|
|
|
|
|
|
|
|
if (!txs) {
|
|
|
|
|
|
return cb(null, localTxs);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
localTxs = JSON.parse(txs);
|
|
|
|
|
|
} catch (ex) {
|
|
|
|
|
|
$log.warn(ex);
|
|
|
|
|
|
}
|
|
|
|
|
|
return cb(null, lodash.compact(localTxs));
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-08-15 10:25:43 -03:00
|
|
|
|
var getTxsFromServer = function(wallet, skip, endingTxid, limit, cb) {
|
2016-08-12 11:10:26 -03:00
|
|
|
|
var res = [];
|
|
|
|
|
|
|
2016-08-15 10:25:43 -03:00
|
|
|
|
wallet.getTxHistory({
|
2016-08-12 11:10:26 -03:00
|
|
|
|
skip: skip,
|
|
|
|
|
|
limit: limit
|
|
|
|
|
|
}, function(err, txsFromServer) {
|
|
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
|
|
|
|
|
|
|
if (!txsFromServer.length)
|
|
|
|
|
|
return cb();
|
|
|
|
|
|
|
|
|
|
|
|
var res = lodash.takeWhile(txsFromServer, function(tx) {
|
|
|
|
|
|
return tx.txid != endingTxid;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2016-12-01 16:05:56 -03:00
|
|
|
|
return cb(null, res, res.length >= limit);
|
2016-08-12 11:10:26 -03:00
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-08-17 17:26:13 -03:00
|
|
|
|
var removeAndMarkSoftConfirmedTx = function(txs) {
|
|
|
|
|
|
return lodash.filter(txs, function(tx) {
|
|
|
|
|
|
if (tx.confirmations >= root.SOFT_CONFIRMATION_LIMIT)
|
|
|
|
|
|
return tx;
|
|
|
|
|
|
tx.recent = true;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var processNewTxs = function(wallet, txs) {
|
|
|
|
|
|
var config = configService.getSync().wallet.settings;
|
|
|
|
|
|
var now = Math.floor(Date.now() / 1000);
|
|
|
|
|
|
var txHistoryUnique = {};
|
|
|
|
|
|
var ret = [];
|
|
|
|
|
|
wallet.hasUnsafeConfirmed = false;
|
|
|
|
|
|
|
|
|
|
|
|
lodash.each(txs, function(tx) {
|
2017-08-28 15:51:13 -03:00
|
|
|
|
tx = txFormatService.processTx(wallet.coin, tx);
|
2016-08-17 17:26:13 -03:00
|
|
|
|
|
|
|
|
|
|
// no future transactions...
|
|
|
|
|
|
if (tx.time > now)
|
|
|
|
|
|
tx.time = now;
|
|
|
|
|
|
|
2016-08-18 10:37:08 -03:00
|
|
|
|
if (tx.confirmations >= root.SAFE_CONFIRMATIONS) {
|
|
|
|
|
|
tx.safeConfirmed = root.SAFE_CONFIRMATIONS + '+';
|
2016-08-17 17:26:13 -03:00
|
|
|
|
} else {
|
|
|
|
|
|
tx.safeConfirmed = false;
|
|
|
|
|
|
wallet.hasUnsafeConfirmed = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (tx.note) {
|
|
|
|
|
|
delete tx.note.encryptedEditedByName;
|
|
|
|
|
|
delete tx.note.encryptedBody;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!txHistoryUnique[tx.txid]) {
|
|
|
|
|
|
ret.push(tx);
|
|
|
|
|
|
txHistoryUnique[tx.txid] = true;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
$log.debug('Ignoring duplicate TX in history: ' + tx.txid)
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
};
|
2016-08-12 11:10:26 -03:00
|
|
|
|
|
2017-01-18 11:48:08 -03:00
|
|
|
|
var updateLocalTxHistory = function(wallet, opts, cb) {
|
2016-08-12 11:10:26 -03:00
|
|
|
|
var FIRST_LIMIT = 5;
|
|
|
|
|
|
var LIMIT = 50;
|
|
|
|
|
|
var requestLimit = FIRST_LIMIT;
|
2016-08-15 10:25:43 -03:00
|
|
|
|
var walletId = wallet.credentials.walletId;
|
2016-08-12 11:10:26 -03:00
|
|
|
|
|
2017-01-18 11:48:08 -03:00
|
|
|
|
var opts = opts || {};
|
|
|
|
|
|
var progressFn = opts.progressFn || function() {};
|
|
|
|
|
|
var foundLimitTx = false;
|
2016-08-18 10:37:08 -03:00
|
|
|
|
|
2017-06-22 13:44:49 -03:00
|
|
|
|
|
|
|
|
|
|
if (opts.feeLevels) {
|
|
|
|
|
|
opts.lowAmount = root.getLowAmount(wallet, opts.feeLevels);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-08-12 11:10:26 -03:00
|
|
|
|
var fixTxsUnit = function(txs) {
|
|
|
|
|
|
if (!txs || !txs[0] || !txs[0].amountStr) return;
|
|
|
|
|
|
|
2017-08-27 23:50:27 -03:00
|
|
|
|
var cacheCoin = txs[0].amountStr.split(' ')[1];
|
2016-08-12 11:10:26 -03:00
|
|
|
|
|
2017-08-30 18:25:12 -03:00
|
|
|
|
if (cacheCoin == 'bits') {
|
2016-08-12 11:10:26 -03:00
|
|
|
|
|
2017-08-30 18:25:12 -03:00
|
|
|
|
$log.debug('Fixing Tx Cache Unit to: ' + 'btc')
|
|
|
|
|
|
lodash.each(txs, function(tx) {
|
|
|
|
|
|
tx.amountStr = txFormatService.formatAmountStr('BTC', tx.amount);
|
|
|
|
|
|
tx.feeStr = txFormatService.formatAmountStr('BTC', tx.fees);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2016-08-12 11:10:26 -03:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
getSavedTxs(walletId, function(err, txsFromLocal) {
|
|
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
|
|
|
|
|
|
|
fixTxsUnit(txsFromLocal);
|
|
|
|
|
|
|
2016-08-17 17:26:13 -03:00
|
|
|
|
var confirmedTxs = removeAndMarkSoftConfirmedTx(txsFromLocal);
|
2016-08-12 11:10:26 -03:00
|
|
|
|
var endingTxid = confirmedTxs[0] ? confirmedTxs[0].txid : null;
|
|
|
|
|
|
var endingTs = confirmedTxs[0] ? confirmedTxs[0].time : null;
|
|
|
|
|
|
|
|
|
|
|
|
// First update
|
2016-09-30 15:58:56 -03:00
|
|
|
|
progressFn(txsFromLocal, 0);
|
2016-08-18 10:37:08 -03:00
|
|
|
|
wallet.completeHistory = txsFromLocal;
|
2016-08-12 11:10:26 -03:00
|
|
|
|
|
2017-01-18 11:48:08 -03:00
|
|
|
|
function getNewTxs(newTxs, skip, next) {
|
2016-08-18 10:37:08 -03:00
|
|
|
|
getTxsFromServer(wallet, skip, endingTxid, requestLimit, function(err, res, shouldContinue) {
|
2016-12-01 16:54:12 -03:00
|
|
|
|
if (err) {
|
2017-01-16 16:00:09 -03:00
|
|
|
|
$log.warn(bwcError.msg(err, 'Server Error')); //TODO
|
2016-12-01 16:54:12 -03:00
|
|
|
|
if (err instanceof errors.CONNECTION_ERROR || (err.message && err.message.match(/5../))) {
|
2017-01-04 12:24:00 -03:00
|
|
|
|
$log.info('Retrying history download in 5 secs...');
|
2016-12-01 16:54:12 -03:00
|
|
|
|
return $timeout(function() {
|
2017-01-18 11:48:08 -03:00
|
|
|
|
return getNewTxs(newTxs, skip, next);
|
2016-12-01 16:54:12 -03:00
|
|
|
|
}, 5000);
|
|
|
|
|
|
};
|
2017-01-18 11:48:08 -03:00
|
|
|
|
return next(err);
|
2016-12-01 16:54:12 -03:00
|
|
|
|
}
|
2016-08-12 11:10:26 -03:00
|
|
|
|
|
2016-08-18 10:37:08 -03:00
|
|
|
|
newTxs = newTxs.concat(processNewTxs(wallet, lodash.compact(res)));
|
2016-08-12 11:10:26 -03:00
|
|
|
|
|
2016-09-30 15:58:56 -03:00
|
|
|
|
progressFn(newTxs.concat(txsFromLocal), newTxs.length);
|
2016-08-12 11:10:26 -03:00
|
|
|
|
|
|
|
|
|
|
skip = skip + requestLimit;
|
|
|
|
|
|
|
|
|
|
|
|
$log.debug('Syncing TXs. Got:' + newTxs.length + ' Skip:' + skip, ' EndingTxid:', endingTxid, ' Continue:', shouldContinue);
|
|
|
|
|
|
|
2017-01-18 11:48:08 -03:00
|
|
|
|
// TODO Dirty <HACK>
|
|
|
|
|
|
// do not sync all history, just looking for a single TX.
|
|
|
|
|
|
if (opts.limitTx) {
|
|
|
|
|
|
|
2017-06-06 15:38:26 -03:00
|
|
|
|
foundLimitTx = lodash.find(newTxs, {
|
2017-01-18 11:48:08 -03:00
|
|
|
|
txid: opts.limitTx,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (foundLimitTx) {
|
|
|
|
|
|
$log.debug('Found limitTX: ' + opts.limitTx);
|
2017-05-18 18:51:49 -03:00
|
|
|
|
return next(null, [foundLimitTx]);
|
2017-01-18 11:48:08 -03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// </HACK>
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-08-12 11:10:26 -03:00
|
|
|
|
if (!shouldContinue) {
|
|
|
|
|
|
$log.debug('Finished Sync: New / soft confirmed Txs: ' + newTxs.length);
|
2017-01-18 11:48:08 -03:00
|
|
|
|
return next(null, newTxs);
|
2016-08-12 11:10:26 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
requestLimit = LIMIT;
|
2017-01-18 11:48:08 -03:00
|
|
|
|
getNewTxs(newTxs, skip, next);
|
2016-08-12 11:10:26 -03:00
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
getNewTxs([], 0, function(err, txs) {
|
|
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
|
|
|
|
|
|
|
var newHistory = lodash.uniq(lodash.compact(txs.concat(confirmedTxs)), function(x) {
|
|
|
|
|
|
return x.txid;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function updateNotes(cb2) {
|
|
|
|
|
|
if (!endingTs) return cb2();
|
|
|
|
|
|
|
|
|
|
|
|
$log.debug('Syncing notes from: ' + endingTs);
|
2016-08-15 10:25:43 -03:00
|
|
|
|
wallet.getTxNotes({
|
2016-08-12 11:10:26 -03:00
|
|
|
|
minTs: endingTs
|
|
|
|
|
|
}, function(err, notes) {
|
|
|
|
|
|
if (err) {
|
|
|
|
|
|
$log.warn(err);
|
|
|
|
|
|
return cb2();
|
|
|
|
|
|
};
|
|
|
|
|
|
lodash.each(notes, function(note) {
|
|
|
|
|
|
$log.debug('Note for ' + note.txid);
|
|
|
|
|
|
lodash.each(newHistory, function(tx) {
|
|
|
|
|
|
if (tx.txid == note.txid) {
|
|
|
|
|
|
$log.debug('...updating note for ' + note.txid);
|
|
|
|
|
|
tx.note = note;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
return cb2();
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-06-22 11:38:13 -03:00
|
|
|
|
function updateLowAmount(txs) {
|
|
|
|
|
|
if (!opts.lowAmount) return;
|
2017-06-22 13:44:49 -03:00
|
|
|
|
|
2017-06-22 11:38:13 -03:00
|
|
|
|
lodash.each(txs, function(tx) {
|
|
|
|
|
|
tx.lowAmount = tx.amount < opts.lowAmount;
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
updateLowAmount(txs);
|
|
|
|
|
|
|
2016-08-12 11:10:26 -03:00
|
|
|
|
updateNotes(function() {
|
2017-01-18 11:48:08 -03:00
|
|
|
|
|
|
|
|
|
|
// <HACK>
|
|
|
|
|
|
if (foundLimitTx) {
|
|
|
|
|
|
$log.debug('Tx history read until limitTx: ' + opts.limitTx);
|
|
|
|
|
|
return cb(null, newHistory);
|
|
|
|
|
|
}
|
|
|
|
|
|
// </HACK>
|
|
|
|
|
|
|
2016-08-12 11:10:26 -03:00
|
|
|
|
var historyToSave = JSON.stringify(newHistory);
|
|
|
|
|
|
|
|
|
|
|
|
lodash.each(txs, function(tx) {
|
|
|
|
|
|
tx.recent = true;
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
$log.debug('Tx History synced. Total Txs: ' + newHistory.length);
|
|
|
|
|
|
|
|
|
|
|
|
// Final update
|
2016-08-17 17:26:13 -03:00
|
|
|
|
if (walletId == wallet.credentials.walletId) {
|
|
|
|
|
|
wallet.completeHistory = newHistory;
|
2016-08-12 11:10:26 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return storageService.setTxHistory(historyToSave, walletId, function() {
|
|
|
|
|
|
$log.debug('Tx History saved.');
|
|
|
|
|
|
|
|
|
|
|
|
return cb();
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-09-21 17:28:59 -03:00
|
|
|
|
root.getTxNote = function(wallet, txid, cb) {
|
|
|
|
|
|
wallet.getTxNote({
|
|
|
|
|
|
txid: txid
|
|
|
|
|
|
}, function(err, note) {
|
2016-10-17 11:12:17 -03:00
|
|
|
|
if (err) return cb(err);
|
2016-09-21 17:28:59 -03:00
|
|
|
|
return cb(null, note);
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-10-11 16:46:06 -03:00
|
|
|
|
root.editTxNote = function(wallet, args, cb) {
|
|
|
|
|
|
wallet.editTxNote(args, function(err, res) {
|
|
|
|
|
|
return cb(err, res);
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-09-21 16:04:25 -03:00
|
|
|
|
root.getTxp = function(wallet, txpid, cb) {
|
|
|
|
|
|
wallet.getTx(txpid, function(err, txp) {
|
|
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
|
return cb(null, txp);
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-09-20 16:21:05 -03:00
|
|
|
|
root.getTx = function(wallet, txid, cb) {
|
2016-09-21 10:49:56 -03:00
|
|
|
|
|
2017-06-22 11:38:13 -03:00
|
|
|
|
function finish(list) {
|
2017-03-08 15:55:30 -03:00
|
|
|
|
var tx = lodash.find(list, {
|
2017-06-22 11:38:13 -03:00
|
|
|
|
txid: txid
|
2016-09-20 16:21:05 -03:00
|
|
|
|
});
|
|
|
|
|
|
|
2017-03-08 15:55:30 -03:00
|
|
|
|
if (!tx) return cb('Could not get transaction');
|
|
|
|
|
|
return cb(null, tx);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
if (wallet.completeHistory && wallet.completeHistory.isValid) {
|
|
|
|
|
|
finish(wallet.completeHistory);
|
2016-09-20 16:45:43 -03:00
|
|
|
|
} else {
|
2017-01-18 11:48:08 -03:00
|
|
|
|
root.getTxHistory(wallet, {
|
|
|
|
|
|
limitTx: txid
|
|
|
|
|
|
}, function(err, txHistory) {
|
2016-09-20 16:45:43 -03:00
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
|
|
2017-03-08 15:55:30 -03:00
|
|
|
|
finish(txHistory);
|
2016-09-20 16:45:43 -03:00
|
|
|
|
});
|
|
|
|
|
|
}
|
2016-09-20 16:21:05 -03:00
|
|
|
|
};
|
2016-08-12 11:10:26 -03:00
|
|
|
|
|
2017-01-18 12:09:17 -03:00
|
|
|
|
|
|
|
|
|
|
root.clearTxHistory = function(wallet, cb) {
|
|
|
|
|
|
root.invalidateCache(wallet);
|
|
|
|
|
|
|
|
|
|
|
|
storageService.removeTxHistory(wallet.id, function(err) {
|
|
|
|
|
|
if (err) {
|
|
|
|
|
|
$log.error(err);
|
2017-01-18 14:25:39 -03:00
|
|
|
|
return cb(err);
|
2017-01-18 12:09:17 -03:00
|
|
|
|
}
|
|
|
|
|
|
return cb();
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2017-06-22 11:38:13 -03:00
|
|
|
|
|
2017-01-18 12:09:17 -03:00
|
|
|
|
|
2016-08-18 10:37:08 -03:00
|
|
|
|
root.getTxHistory = function(wallet, opts, cb) {
|
2016-08-17 18:48:30 -03:00
|
|
|
|
opts = opts || {};
|
|
|
|
|
|
|
2016-08-15 10:25:43 -03:00
|
|
|
|
var walletId = wallet.credentials.walletId;
|
2016-08-12 11:10:26 -03:00
|
|
|
|
|
2016-08-17 18:02:47 -03:00
|
|
|
|
if (!wallet.isComplete()) return cb();
|
2016-08-12 11:10:26 -03:00
|
|
|
|
|
2016-08-22 22:10:46 -03:00
|
|
|
|
function isHistoryCached() {
|
|
|
|
|
|
return wallet.completeHistory && wallet.completeHistory.isValid;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
if (isHistoryCached() && !opts.force) return cb(null, wallet.completeHistory);
|
|
|
|
|
|
|
2016-08-12 11:10:26 -03:00
|
|
|
|
$log.debug('Updating Transaction History');
|
|
|
|
|
|
|
2017-01-18 11:48:08 -03:00
|
|
|
|
updateLocalTxHistory(wallet, opts, function(err, txs) {
|
2016-08-17 18:48:30 -03:00
|
|
|
|
if (err) return cb(err);
|
2016-08-22 22:10:46 -03:00
|
|
|
|
|
2017-01-18 11:48:08 -03:00
|
|
|
|
if (opts.limitTx) {
|
|
|
|
|
|
return cb(err, txs);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-08-22 22:10:46 -03:00
|
|
|
|
wallet.completeHistory.isValid = true;
|
2016-08-18 10:37:08 -03:00
|
|
|
|
return cb(err, wallet.completeHistory);
|
2016-08-12 11:10:26 -03:00
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-08-15 10:25:43 -03:00
|
|
|
|
root.isEncrypted = function(wallet) {
|
|
|
|
|
|
if (lodash.isEmpty(wallet)) return;
|
|
|
|
|
|
var isEncrypted = wallet.isPrivKeyEncrypted();
|
2016-05-09 15:56:44 -03:00
|
|
|
|
if (isEncrypted) $log.debug('Wallet is encrypted');
|
|
|
|
|
|
return isEncrypted;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-08-15 10:25:43 -03:00
|
|
|
|
root.createTx = function(wallet, txp, cb) {
|
|
|
|
|
|
if (lodash.isEmpty(txp) || lodash.isEmpty(wallet))
|
2016-05-09 15:56:44 -03:00
|
|
|
|
return cb('MISSING_PARAMETER');
|
|
|
|
|
|
|
2016-11-23 11:23:19 -03:00
|
|
|
|
wallet.createTxProposal(txp, function(err, createdTxp) {
|
|
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
|
else {
|
|
|
|
|
|
$log.debug('Transaction created');
|
|
|
|
|
|
return cb(null, createdTxp);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2016-05-09 15:56:44 -03:00
|
|
|
|
};
|
|
|
|
|
|
|
2016-08-15 10:25:43 -03:00
|
|
|
|
root.publishTx = function(wallet, txp, cb) {
|
|
|
|
|
|
if (lodash.isEmpty(txp) || lodash.isEmpty(wallet))
|
2016-05-09 15:56:44 -03:00
|
|
|
|
return cb('MISSING_PARAMETER');
|
|
|
|
|
|
|
2016-08-15 10:25:43 -03:00
|
|
|
|
wallet.publishTxProposal({
|
2016-06-06 18:26:45 -03:00
|
|
|
|
txp: txp
|
|
|
|
|
|
}, function(err, publishedTx) {
|
2016-05-09 15:56:44 -03:00
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
|
else {
|
|
|
|
|
|
$log.debug('Transaction published');
|
|
|
|
|
|
return cb(null, publishedTx);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-08-29 11:58:23 -03:00
|
|
|
|
root.signTx = function(wallet, txp, password, cb) {
|
2016-08-29 15:17:41 -03:00
|
|
|
|
if (!wallet || !txp || !cb)
|
2016-05-09 15:56:44 -03:00
|
|
|
|
return cb('MISSING_PARAMETER');
|
2016-06-06 18:26:45 -03:00
|
|
|
|
|
2016-08-15 10:25:43 -03:00
|
|
|
|
if (wallet.isPrivKeyExternal()) {
|
|
|
|
|
|
switch (wallet.getPrivKeyExternalSourceName()) {
|
2016-12-05 17:33:46 -05:00
|
|
|
|
case root.externalSource.ledger.id:
|
2016-08-15 10:25:43 -03:00
|
|
|
|
return _signWithLedger(wallet, txp, cb);
|
2016-12-05 17:33:46 -05:00
|
|
|
|
case root.externalSource.trezor.id:
|
2016-08-15 10:25:43 -03:00
|
|
|
|
return _signWithTrezor(wallet, txp, cb);
|
2016-12-05 17:33:46 -05:00
|
|
|
|
case root.externalSource.intelTEE.id:
|
|
|
|
|
|
return _signWithIntelTEE(wallet, txp, cb);
|
2016-05-09 15:56:44 -03:00
|
|
|
|
default:
|
2016-08-15 10:25:43 -03:00
|
|
|
|
var msg = 'Unsupported External Key:' + wallet.getPrivKeyExternalSourceName();
|
2016-05-09 15:56:44 -03:00
|
|
|
|
$log.error(msg);
|
|
|
|
|
|
return cb(msg);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
2016-08-29 11:58:23 -03:00
|
|
|
|
wallet.signTxProposal(txp, password, function(err, signedTxp) {
|
|
|
|
|
|
$log.debug('Transaction signed err:' + err);
|
2016-05-09 15:56:44 -03:00
|
|
|
|
return cb(err, signedTxp);
|
|
|
|
|
|
});
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
$log.warn('Error at signTxProposal:', e);
|
|
|
|
|
|
return cb(e);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-08-15 10:25:43 -03:00
|
|
|
|
root.broadcastTx = function(wallet, txp, cb) {
|
|
|
|
|
|
if (lodash.isEmpty(txp) || lodash.isEmpty(wallet))
|
2016-05-09 15:56:44 -03:00
|
|
|
|
return cb('MISSING_PARAMETER');
|
2016-06-06 18:26:45 -03:00
|
|
|
|
|
2016-05-09 15:56:44 -03:00
|
|
|
|
if (txp.status != 'accepted')
|
|
|
|
|
|
return cb('TX_NOT_ACCEPTED');
|
|
|
|
|
|
|
2016-08-15 10:25:43 -03:00
|
|
|
|
wallet.broadcastTxProposal(txp, function(err, broadcastedTxp, memo) {
|
2016-06-06 18:26:45 -03:00
|
|
|
|
if (err)
|
2016-05-09 15:56:44 -03:00
|
|
|
|
return cb(err);
|
|
|
|
|
|
|
|
|
|
|
|
$log.debug('Transaction broadcasted');
|
|
|
|
|
|
if (memo) $log.info(memo);
|
|
|
|
|
|
|
|
|
|
|
|
return cb(null, broadcastedTxp);
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-08-15 10:25:43 -03:00
|
|
|
|
root.rejectTx = function(wallet, txp, cb) {
|
|
|
|
|
|
if (lodash.isEmpty(txp) || lodash.isEmpty(wallet))
|
2016-05-09 15:56:44 -03:00
|
|
|
|
return cb('MISSING_PARAMETER');
|
2016-06-06 18:26:45 -03:00
|
|
|
|
|
2016-08-15 10:25:43 -03:00
|
|
|
|
wallet.rejectTxProposal(txp, null, function(err, rejectedTxp) {
|
2016-05-09 15:56:44 -03:00
|
|
|
|
$log.debug('Transaction rejected');
|
|
|
|
|
|
return cb(err, rejectedTxp);
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-08-15 10:25:43 -03:00
|
|
|
|
root.removeTx = function(wallet, txp, cb) {
|
|
|
|
|
|
if (lodash.isEmpty(txp) || lodash.isEmpty(wallet))
|
2016-05-09 15:56:44 -03:00
|
|
|
|
return cb('MISSING_PARAMETER');
|
2016-06-06 18:26:45 -03:00
|
|
|
|
|
2016-08-15 10:25:43 -03:00
|
|
|
|
wallet.removeTxProposal(txp, function(err) {
|
2016-05-09 15:56:44 -03:00
|
|
|
|
$log.debug('Transaction removed');
|
2016-09-02 14:55:18 -03:00
|
|
|
|
|
|
|
|
|
|
root.invalidateCache(wallet);
|
|
|
|
|
|
$rootScope.$emit('Local/TxAction', wallet.id);
|
|
|
|
|
|
|
2016-05-09 15:56:44 -03:00
|
|
|
|
return cb(err);
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-06-06 18:26:45 -03:00
|
|
|
|
root.updateRemotePreferences = function(clients, prefs, cb) {
|
|
|
|
|
|
prefs = prefs || {};
|
2017-01-16 16:00:09 -03:00
|
|
|
|
cb = cb || function() {};
|
2016-06-06 18:26:45 -03:00
|
|
|
|
|
|
|
|
|
|
if (!lodash.isArray(clients))
|
|
|
|
|
|
clients = [clients];
|
|
|
|
|
|
|
2017-01-16 16:00:09 -03:00
|
|
|
|
function updateRemotePreferencesFor(clients, prefs, next) {
|
2016-08-15 10:25:43 -03:00
|
|
|
|
var wallet = clients.shift();
|
2017-01-16 16:00:09 -03:00
|
|
|
|
if (!wallet) return next();
|
2016-08-15 10:25:43 -03:00
|
|
|
|
$log.debug('Saving remote preferences', wallet.credentials.walletName, prefs);
|
2016-06-06 18:26:45 -03:00
|
|
|
|
|
2016-08-15 10:25:43 -03:00
|
|
|
|
wallet.savePreferences(prefs, function(err) {
|
2016-06-06 18:26:45 -03:00
|
|
|
|
|
2017-01-16 16:00:09 -03:00
|
|
|
|
if (err) {
|
|
|
|
|
|
popupService.showAlert(bwcError.msg(err, gettextCatalog.getString('Could not save preferences on the server')));
|
|
|
|
|
|
return next(err);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
updateRemotePreferencesFor(clients, prefs, next);
|
2016-06-06 18:26:45 -03:00
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Update this JIC.
|
2017-05-14 19:21:12 -03:00
|
|
|
|
var config = configService.getSync();
|
|
|
|
|
|
var walletSettings = config.wallet.settings;
|
2016-06-06 18:26:45 -03:00
|
|
|
|
|
|
|
|
|
|
//prefs.email (may come from arguments)
|
2017-05-14 19:21:12 -03:00
|
|
|
|
prefs.email = config.emailNotifications.email;
|
2016-06-07 10:37:39 -03:00
|
|
|
|
prefs.language = uxLanguage.getCurrentLanguage();
|
2017-08-24 17:02:49 -03:00
|
|
|
|
// prefs.unit = walletSettings.unitCode; // TODO: remove, not used
|
2016-06-06 18:26:45 -03:00
|
|
|
|
|
2017-01-16 16:00:09 -03:00
|
|
|
|
updateRemotePreferencesFor(lodash.clone(clients), prefs, function(err) {
|
2016-06-06 18:26:45 -03:00
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
|
|
2017-01-16 16:00:09 -03:00
|
|
|
|
$log.debug('Remote preferences saved for' + lodash.map(clients, function(x) {
|
|
|
|
|
|
return x.credentials.walletId;
|
|
|
|
|
|
}).join(','));
|
|
|
|
|
|
|
2016-06-06 18:26:45 -03:00
|
|
|
|
lodash.each(clients, function(c) {
|
|
|
|
|
|
c.preferences = lodash.assign(prefs, c.preferences);
|
|
|
|
|
|
});
|
|
|
|
|
|
return cb();
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-08-15 10:25:43 -03:00
|
|
|
|
root.recreate = function(wallet, cb) {
|
2016-09-08 12:13:37 -03:00
|
|
|
|
$log.debug('Recreating wallet:', wallet.id);
|
2016-08-12 11:10:26 -03:00
|
|
|
|
ongoingProcess.set('recreating', true);
|
2016-08-15 10:25:43 -03:00
|
|
|
|
wallet.recreateWallet(function(err) {
|
2016-08-17 17:26:13 -03:00
|
|
|
|
wallet.notAuthorized = false;
|
2016-08-12 11:10:26 -03:00
|
|
|
|
ongoingProcess.set('recreating', false);
|
2016-09-08 12:13:37 -03:00
|
|
|
|
return cb(err);
|
2016-08-12 11:10:26 -03:00
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-09-08 12:13:37 -03:00
|
|
|
|
root.startScan = function(wallet, cb) {
|
|
|
|
|
|
cb = cb || function() {};
|
2016-08-12 11:10:26 -03:00
|
|
|
|
|
2016-09-08 12:13:37 -03:00
|
|
|
|
$log.debug('Scanning wallet ' + wallet.id);
|
|
|
|
|
|
if (!wallet.isComplete()) return;
|
2016-08-12 11:10:26 -03:00
|
|
|
|
|
2016-09-08 12:13:37 -03:00
|
|
|
|
wallet.updating = true;
|
|
|
|
|
|
ongoingProcess.set('scanning', true);
|
2016-08-15 10:25:43 -03:00
|
|
|
|
wallet.startScan({
|
2016-08-12 11:10:26 -03:00
|
|
|
|
includeCopayerBranches: true,
|
|
|
|
|
|
}, function(err) {
|
2016-09-08 12:13:37 -03:00
|
|
|
|
wallet.updating = false;
|
|
|
|
|
|
ongoingProcess.set('scanning', false);
|
|
|
|
|
|
return cb(err);
|
2016-08-12 11:10:26 -03:00
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-08-15 10:25:43 -03:00
|
|
|
|
root.expireAddress = function(wallet, cb) {
|
|
|
|
|
|
$log.debug('Cleaning Address ' + wallet.id);
|
|
|
|
|
|
storageService.clearLastAddress(wallet.id, function(err) {
|
|
|
|
|
|
return cb(err);
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-11-14 10:09:11 -03:00
|
|
|
|
// Check address
|
|
|
|
|
|
root.isAddressUsed = function(wallet, byAddress, cb) {
|
2016-08-15 10:25:43 -03:00
|
|
|
|
storageService.getLastAddress(wallet.id, function(err, addr) {
|
|
|
|
|
|
var used = lodash.find(byAddress, {
|
|
|
|
|
|
address: addr
|
|
|
|
|
|
});
|
2016-11-14 10:09:11 -03:00
|
|
|
|
return cb(err, used);
|
2016-08-15 10:25:43 -03:00
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-08-15 11:56:59 -03:00
|
|
|
|
var createAddress = function(wallet, cb) {
|
2016-08-15 10:25:43 -03:00
|
|
|
|
$log.debug('Creating address for wallet:', wallet.id);
|
|
|
|
|
|
|
|
|
|
|
|
wallet.createAddress({}, function(err, addr) {
|
|
|
|
|
|
if (err) {
|
|
|
|
|
|
var prefix = gettextCatalog.getString('Could not create address');
|
2017-05-29 17:09:21 -03:00
|
|
|
|
if (err instanceof errors.CONNECTION_ERROR || (err.message && err.message.match(/5../))) {
|
|
|
|
|
|
$log.warn(err);
|
2016-08-15 10:25:43 -03:00
|
|
|
|
return $timeout(function() {
|
2016-08-15 11:56:59 -03:00
|
|
|
|
createAddress(wallet, cb);
|
2016-08-15 10:25:43 -03:00
|
|
|
|
}, 5000);
|
2017-05-29 17:09:21 -03:00
|
|
|
|
} else if (err instanceof errors.MAIN_ADDRESS_GAP_REACHED || (err.message && err.message == 'MAIN_ADDRESS_GAP_REACHED')) {
|
|
|
|
|
|
$log.warn(err);
|
2016-08-15 10:25:43 -03:00
|
|
|
|
prefix = null;
|
|
|
|
|
|
wallet.getMainAddresses({
|
|
|
|
|
|
reverse: true,
|
|
|
|
|
|
limit: 1
|
|
|
|
|
|
}, function(err, addr) {
|
|
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
|
return cb(null, addr[0].address);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
return bwcError.cb(err, prefix, cb);
|
|
|
|
|
|
}
|
|
|
|
|
|
return cb(null, addr.address);
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-11-16 15:23:26 -03:00
|
|
|
|
root.getMainAddresses = function(wallet, opts, cb) {
|
|
|
|
|
|
opts = opts || {};
|
2016-11-14 16:51:11 -03:00
|
|
|
|
opts.reverse = true;
|
|
|
|
|
|
wallet.getMainAddresses(opts, function(err, addresses) {
|
2016-11-17 13:30:52 -03:00
|
|
|
|
return cb(err, addresses);
|
2016-11-14 16:51:11 -03:00
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-11-15 17:40:44 -03:00
|
|
|
|
root.getBalance = function(wallet, opts, cb) {
|
|
|
|
|
|
opts = opts || {};
|
|
|
|
|
|
wallet.getBalance(opts, function(err, resp) {
|
|
|
|
|
|
return cb(err, resp);
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2017-06-22 13:44:49 -03:00
|
|
|
|
|
|
|
|
|
|
// These 2 functions were taken from
|
|
|
|
|
|
// https://github.com/bitpay/bitcore-wallet-service/blob/master/lib/model/txproposal.js#L243
|
|
|
|
|
|
|
|
|
|
|
|
function getEstimatedSizeForSingleInput(wallet) {
|
|
|
|
|
|
switch (wallet.credentials.addressType) {
|
|
|
|
|
|
case 'P2PKH':
|
|
|
|
|
|
return 147;
|
|
|
|
|
|
default:
|
|
|
|
|
|
case 'P2SH':
|
|
|
|
|
|
return wallet.m * 72 + wallet.n * 36 + 44;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
root.getEstimatedTxSize = function(wallet, nbOutputs) {
|
|
|
|
|
|
// Note: found empirically based on all multisig P2SH inputs and within m & n allowed limits.
|
|
|
|
|
|
var safetyMargin = 0.02;
|
|
|
|
|
|
|
|
|
|
|
|
var overhead = 4 + 4 + 9 + 9;
|
|
|
|
|
|
var inputSize = getEstimatedSizeForSingleInput(wallet);
|
|
|
|
|
|
var outputSize = 34;
|
|
|
|
|
|
var nbInputs = 1; //Assume 1 input
|
|
|
|
|
|
var nbOutputs = nbOutputs || 2; // Assume 2 outputs
|
|
|
|
|
|
|
|
|
|
|
|
var size = overhead + inputSize * nbInputs + outputSize * nbOutputs;
|
|
|
|
|
|
return parseInt((size * (1 + safetyMargin)).toFixed(0));
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-08-24 17:02:49 -03:00
|
|
|
|
// Approx utxo amount, from which the uxto is economically redeemable
|
2017-06-22 17:00:20 -03:00
|
|
|
|
root.getMinFee = function(wallet, feeLevels, nbOutputs) {
|
2017-06-22 13:44:49 -03:00
|
|
|
|
var lowLevelRate = (lodash.find(feeLevels[wallet.network], {
|
|
|
|
|
|
level: 'normal',
|
2017-08-28 17:17:32 -03:00
|
|
|
|
}).feePerKb / 1000).toFixed(0);
|
2017-06-22 13:44:49 -03:00
|
|
|
|
|
|
|
|
|
|
var size = root.getEstimatedTxSize(wallet, nbOutputs);
|
2017-06-22 17:00:20 -03:00
|
|
|
|
return size * lowLevelRate;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2017-06-22 13:44:49 -03:00
|
|
|
|
|
2017-08-24 17:02:49 -03:00
|
|
|
|
// Approx utxo amount, from which the uxto is economically redeemable
|
2017-06-22 17:00:20 -03:00
|
|
|
|
root.getLowAmount = function(wallet, feeLevels, nbOutputs) {
|
|
|
|
|
|
var minFee = root.getMinFee(wallet,feeLevels, nbOutputs);
|
|
|
|
|
|
return parseInt( minFee / LOW_AMOUNT_RATIO);
|
2017-06-22 13:44:49 -03:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
root.getLowUtxos = function(wallet, levels, cb) {
|
|
|
|
|
|
|
2017-08-30 15:54:30 -03:00
|
|
|
|
wallet.getUtxos({coin: wallet.coin}, function(err, resp) {
|
2017-06-22 13:44:49 -03:00
|
|
|
|
if (err || !resp || !resp.length) return cb();
|
|
|
|
|
|
|
2017-06-23 13:06:41 -03:00
|
|
|
|
var minFee = root.getMinFee(wallet, levels, resp.length);
|
2017-06-22 13:44:49 -03:00
|
|
|
|
|
2017-06-23 13:06:41 -03:00
|
|
|
|
var balance = lodash.sum(resp, 'satoshis');
|
|
|
|
|
|
|
|
|
|
|
|
// for 2 outputs
|
|
|
|
|
|
var lowAmount = root.getLowAmount(wallet, levels);
|
2017-06-22 13:44:49 -03:00
|
|
|
|
var lowUtxos = lodash.filter(resp, function(x) {
|
2017-06-23 13:06:41 -03:00
|
|
|
|
return x.satoshis < lowAmount;
|
2017-06-22 13:44:49 -03:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
var totalLow = lodash.sum(lowUtxos, 'satoshis');
|
|
|
|
|
|
|
|
|
|
|
|
return cb(err, {
|
2017-06-22 17:00:20 -03:00
|
|
|
|
allUtxos: resp || [],
|
|
|
|
|
|
lowUtxos: lowUtxos || [],
|
2017-06-23 13:06:41 -03:00
|
|
|
|
warning: minFee / balance > TOTAL_LOW_WARNING_RATIO,
|
|
|
|
|
|
minFee: minFee,
|
2017-06-22 13:44:49 -03:00
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-08-15 10:25:43 -03:00
|
|
|
|
root.getAddress = function(wallet, forceNew, cb) {
|
2016-09-22 18:24:46 -03:00
|
|
|
|
storageService.getLastAddress(wallet.id, function(err, addr) {
|
2016-08-15 10:25:43 -03:00
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
|
|
2016-09-22 18:24:46 -03:00
|
|
|
|
if (!forceNew && addr) return cb(null, addr);
|
2016-08-15 10:25:43 -03:00
|
|
|
|
|
2016-11-14 17:51:15 -03:00
|
|
|
|
if (!wallet.isComplete())
|
|
|
|
|
|
return cb('WALLET_NOT_COMPLETE');
|
2016-11-14 12:31:38 -03:00
|
|
|
|
|
2016-11-14 17:51:15 -03:00
|
|
|
|
createAddress(wallet, function(err, _addr) {
|
|
|
|
|
|
if (err) return cb(err, addr);
|
|
|
|
|
|
storageService.storeLastAddress(wallet.id, _addr, function() {
|
|
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
|
return cb(null, _addr);
|
2016-08-15 10:25:43 -03:00
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2017-03-17 17:00:26 -04:00
|
|
|
|
root.getAddressObj = function(wallet, address, cb) {
|
|
|
|
|
|
wallet.getMainAddresses({
|
|
|
|
|
|
reverse: true
|
|
|
|
|
|
}, function(err, addr) {
|
|
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
|
var addrObj = lodash.find(addr, function(a) {
|
|
|
|
|
|
return a.address == address;
|
|
|
|
|
|
});
|
|
|
|
|
|
var err = null;
|
|
|
|
|
|
if (!addrObj) {
|
|
|
|
|
|
err = 'Error: specified address not in wallet';
|
|
|
|
|
|
}
|
|
|
|
|
|
return cb(err, addrObj);
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
2016-08-12 11:10:26 -03:00
|
|
|
|
|
2016-08-15 16:07:30 -03:00
|
|
|
|
root.isReady = function(wallet, cb) {
|
|
|
|
|
|
if (!wallet.isComplete())
|
|
|
|
|
|
return cb('WALLET_NOT_COMPLETE');
|
|
|
|
|
|
|
2016-08-30 17:07:49 -03:00
|
|
|
|
if (wallet.needsBackup)
|
|
|
|
|
|
return cb('WALLET_NEEDS_BACKUP');
|
|
|
|
|
|
return cb();
|
2016-08-15 16:07:30 -03:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-08-18 10:08:23 -03:00
|
|
|
|
// An alert dialog
|
2016-08-29 15:17:41 -03:00
|
|
|
|
var askPassword = function(name, title, cb) {
|
2016-09-27 16:01:15 -03:00
|
|
|
|
var opts = {
|
2016-10-11 17:10:00 -03:00
|
|
|
|
inputType: 'password',
|
2016-12-20 12:04:29 -03:00
|
|
|
|
forceHTMLPrompt: true,
|
|
|
|
|
|
class: 'text-warn'
|
2016-09-27 16:01:15 -03:00
|
|
|
|
};
|
|
|
|
|
|
popupService.showPrompt(title, name, opts, function(res) {
|
|
|
|
|
|
if (!res) return cb();
|
|
|
|
|
|
if (res) return cb(res)
|
2016-08-18 10:08:23 -03:00
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-08-29 15:17:41 -03:00
|
|
|
|
|
|
|
|
|
|
root.encrypt = function(wallet, cb) {
|
2016-12-13 14:21:57 -03:00
|
|
|
|
var title = gettextCatalog.getString('Enter new spending password');
|
2016-12-20 12:04:29 -03:00
|
|
|
|
var warnMsg = gettextCatalog.getString('Your wallet key will be encrypted. The Spending Password cannot be recovered. Be sure to write it down.');
|
|
|
|
|
|
askPassword(warnMsg, title, function(password) {
|
2016-08-29 15:17:41 -03:00
|
|
|
|
if (!password) return cb('no password');
|
2017-03-11 19:27:31 -03:00
|
|
|
|
title = gettextCatalog.getString('Confirm your new spending password');
|
|
|
|
|
|
askPassword(warnMsg, title, function(password2) {
|
2016-08-30 17:07:49 -03:00
|
|
|
|
if (!password2 || password != password2)
|
2016-08-29 15:17:41 -03:00
|
|
|
|
return cb('password mismatch');
|
|
|
|
|
|
|
|
|
|
|
|
wallet.encryptPrivateKey(password);
|
|
|
|
|
|
return cb();
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
root.decrypt = function(wallet, cb) {
|
|
|
|
|
|
$log.debug('Disabling private key encryption for' + wallet.name);
|
2016-12-20 12:04:29 -03:00
|
|
|
|
askPassword(null, gettextCatalog.getString('Enter Spending Password'), function(password) {
|
2016-08-29 15:17:41 -03:00
|
|
|
|
if (!password) return cb('no password');
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
wallet.decryptPrivateKey(password);
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
return cb(e);
|
|
|
|
|
|
}
|
|
|
|
|
|
return cb();
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-08-18 10:08:23 -03:00
|
|
|
|
root.handleEncryptedWallet = function(wallet, cb) {
|
|
|
|
|
|
if (!root.isEncrypted(wallet)) return cb();
|
|
|
|
|
|
|
2016-12-13 14:21:57 -03:00
|
|
|
|
askPassword(wallet.name, gettextCatalog.getString('Enter Spending Password'), function(password) {
|
2016-10-12 18:11:02 -03:00
|
|
|
|
if (!password) return cb('No password');
|
|
|
|
|
|
if (!wallet.checkPassword(password)) return cb('Wrong password');
|
2016-09-08 12:13:37 -03:00
|
|
|
|
|
2016-08-29 11:58:23 -03:00
|
|
|
|
return cb(null, password);
|
2016-08-18 10:08:23 -03:00
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-08-23 13:20:57 -03:00
|
|
|
|
|
|
|
|
|
|
root.reject = function(wallet, txp, cb) {
|
|
|
|
|
|
ongoingProcess.set('rejectTx', true);
|
|
|
|
|
|
root.rejectTx(wallet, txp, function(err, txpr) {
|
|
|
|
|
|
root.invalidateCache(wallet);
|
|
|
|
|
|
ongoingProcess.set('rejectTx', false);
|
2016-09-02 14:55:18 -03:00
|
|
|
|
|
2016-08-23 13:20:57 -03:00
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
|
|
|
|
|
|
|
$rootScope.$emit('Local/TxAction', wallet.id);
|
|
|
|
|
|
return cb(null, txpr);
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-12-14 15:26:56 -03:00
|
|
|
|
root.onlyPublish = function(wallet, txp, cb, customStatusHandler) {
|
|
|
|
|
|
ongoingProcess.set('sendingTx', true, customStatusHandler);
|
2016-08-18 10:08:23 -03:00
|
|
|
|
root.publishTx(wallet, txp, function(err, publishedTxp) {
|
2016-08-23 13:20:57 -03:00
|
|
|
|
root.invalidateCache(wallet);
|
2016-12-14 15:26:56 -03:00
|
|
|
|
ongoingProcess.set('sendingTx', false, customStatusHandler);
|
|
|
|
|
|
if (err) return cb(bwcError.msg(err));
|
|
|
|
|
|
$rootScope.$emit('Local/TxAction', wallet.id);
|
|
|
|
|
|
return cb();
|
2016-08-18 10:08:23 -03:00
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-08-29 11:58:23 -03:00
|
|
|
|
|
|
|
|
|
|
root.prepare = function(wallet, cb) {
|
|
|
|
|
|
fingerprintService.check(wallet, function(err) {
|
|
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
|
|
|
|
|
|
|
root.handleEncryptedWallet(wallet, function(err, password) {
|
|
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
|
|
|
|
|
|
|
return cb(null, password);
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-10-07 20:03:51 -04:00
|
|
|
|
root.publishAndSign = function(wallet, txp, cb, customStatusHandler) {
|
2016-08-18 10:08:23 -03:00
|
|
|
|
|
|
|
|
|
|
var publishFn = root.publishTx;
|
|
|
|
|
|
|
|
|
|
|
|
// Already published?
|
|
|
|
|
|
if (txp.status == 'pending') {
|
|
|
|
|
|
publishFn = function(wallet, txp, cb) {
|
|
|
|
|
|
return cb(null, txp);
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-08-29 11:58:23 -03:00
|
|
|
|
root.prepare(wallet, function(err, password) {
|
2016-12-13 14:21:57 -03:00
|
|
|
|
if (err) return cb(bwcError.msg(err));
|
2016-08-18 10:08:23 -03:00
|
|
|
|
|
2016-10-07 20:03:51 -04:00
|
|
|
|
ongoingProcess.set('sendingTx', true, customStatusHandler);
|
|
|
|
|
|
|
2016-08-29 11:58:23 -03:00
|
|
|
|
publishFn(wallet, txp, function(err, publishedTxp) {
|
2016-10-07 20:03:51 -04:00
|
|
|
|
ongoingProcess.set('sendingTx', false, customStatusHandler);
|
2016-12-13 14:21:57 -03:00
|
|
|
|
if (err) return cb(bwcError.msg(err));
|
2016-08-18 10:08:23 -03:00
|
|
|
|
|
2016-10-07 20:03:51 -04:00
|
|
|
|
ongoingProcess.set('signingTx', true, customStatusHandler);
|
2016-08-29 11:58:23 -03:00
|
|
|
|
root.signTx(wallet, publishedTxp, password, function(err, signedTxp) {
|
2016-10-10 16:04:54 -04:00
|
|
|
|
ongoingProcess.set('signingTx', false, customStatusHandler);
|
2016-08-29 11:58:23 -03:00
|
|
|
|
root.invalidateCache(wallet);
|
2016-08-18 10:08:23 -03:00
|
|
|
|
|
|
|
|
|
|
|
2016-08-29 11:58:23 -03:00
|
|
|
|
if (err) {
|
2016-08-29 15:17:41 -03:00
|
|
|
|
$log.warn('sign error:' + err);
|
2016-12-13 14:21:57 -03:00
|
|
|
|
var msg = err && err.message ?
|
2016-08-29 11:58:23 -03:00
|
|
|
|
err.message :
|
2016-12-13 14:21:57 -03:00
|
|
|
|
gettextCatalog.getString('The payment was created but could not be completed. Please try again from home screen');
|
2016-09-02 14:55:18 -03:00
|
|
|
|
|
2016-08-29 11:58:23 -03:00
|
|
|
|
$rootScope.$emit('Local/TxAction', wallet.id);
|
2016-08-29 15:17:41 -03:00
|
|
|
|
return cb(msg);
|
2016-08-29 11:58:23 -03:00
|
|
|
|
}
|
2016-08-18 10:08:23 -03:00
|
|
|
|
|
2016-08-29 11:58:23 -03:00
|
|
|
|
if (signedTxp.status == 'accepted') {
|
2016-10-07 20:03:51 -04:00
|
|
|
|
ongoingProcess.set('broadcastingTx', true, customStatusHandler);
|
2016-08-29 11:58:23 -03:00
|
|
|
|
root.broadcastTx(wallet, signedTxp, function(err, broadcastedTxp) {
|
2016-10-07 20:03:51 -04:00
|
|
|
|
ongoingProcess.set('broadcastingTx', false, customStatusHandler);
|
2016-12-13 14:21:57 -03:00
|
|
|
|
if (err) return cb(bwcError.msg(err));
|
2016-08-18 10:08:23 -03:00
|
|
|
|
|
2016-09-02 14:55:18 -03:00
|
|
|
|
$rootScope.$emit('Local/TxAction', wallet.id);
|
2016-10-07 20:03:51 -04:00
|
|
|
|
return cb(null, broadcastedTxp);
|
2016-08-29 11:58:23 -03:00
|
|
|
|
});
|
|
|
|
|
|
} else {
|
2016-09-02 14:55:18 -03:00
|
|
|
|
$rootScope.$emit('Local/TxAction', wallet.id);
|
2016-08-29 11:58:23 -03:00
|
|
|
|
return cb(null, signedTxp);
|
|
|
|
|
|
}
|
2016-08-18 10:08:23 -03:00
|
|
|
|
});
|
|
|
|
|
|
});
|
2016-08-17 17:31:45 -03:00
|
|
|
|
});
|
|
|
|
|
|
};
|
2016-08-18 19:23:58 -03:00
|
|
|
|
|
2016-10-12 18:11:02 -03:00
|
|
|
|
root.getEncodedWalletInfo = function(wallet, password, cb) {
|
2016-08-29 16:13:18 -03:00
|
|
|
|
var derivationPath = wallet.credentials.getBaseAddressDerivationPath();
|
|
|
|
|
|
var encodingType = {
|
|
|
|
|
|
mnemonic: 1,
|
|
|
|
|
|
xpriv: 2,
|
|
|
|
|
|
xpub: 3
|
|
|
|
|
|
};
|
|
|
|
|
|
var info;
|
|
|
|
|
|
|
|
|
|
|
|
// not supported yet
|
|
|
|
|
|
if (wallet.credentials.derivationStrategy != 'BIP44' || !wallet.canSign())
|
2016-10-13 10:16:10 -03:00
|
|
|
|
return cb(gettextCatalog.getString('Exporting via QR not supported for this wallet'));
|
2016-08-24 17:54:01 -03:00
|
|
|
|
|
2016-10-12 18:11:02 -03:00
|
|
|
|
var keys = root.getKeysWithPassword(wallet, password);
|
2016-08-24 17:54:01 -03:00
|
|
|
|
|
2016-10-12 18:11:02 -03:00
|
|
|
|
if (keys.mnemonic) {
|
|
|
|
|
|
info = {
|
|
|
|
|
|
type: encodingType.mnemonic,
|
|
|
|
|
|
data: keys.mnemonic,
|
2016-08-24 17:54:01 -03:00
|
|
|
|
}
|
2016-10-12 18:11:02 -03:00
|
|
|
|
} else {
|
|
|
|
|
|
info = {
|
|
|
|
|
|
type: encodingType.xpriv,
|
|
|
|
|
|
data: keys.xPrivKey
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2016-08-17 17:31:45 -03:00
|
|
|
|
|
2016-10-12 18:11:02 -03:00
|
|
|
|
return cb(null, info.type + '|' + info.data + '|' + wallet.credentials.network.toLowerCase() + '|' + derivationPath + '|' + (wallet.credentials.mnemonicHasPassphrase));
|
2016-08-18 19:23:58 -03:00
|
|
|
|
};
|
2016-08-17 17:31:45 -03:00
|
|
|
|
|
2016-08-29 11:58:23 -03:00
|
|
|
|
root.setTouchId = function(wallet, enabled, cb) {
|
2016-09-01 16:23:27 -03:00
|
|
|
|
|
|
|
|
|
|
var opts = {
|
|
|
|
|
|
touchIdFor: {}
|
|
|
|
|
|
};
|
|
|
|
|
|
opts.touchIdFor[wallet.id] = enabled;
|
|
|
|
|
|
|
2016-08-29 11:58:23 -03:00
|
|
|
|
fingerprintService.check(wallet, function(err) {
|
2016-09-01 16:23:27 -03:00
|
|
|
|
if (err) {
|
|
|
|
|
|
opts.touchIdFor[wallet.id] = !enabled;
|
|
|
|
|
|
$log.debug('Error with fingerprint:' + err);
|
|
|
|
|
|
return cb(err);
|
2016-08-29 11:58:23 -03:00
|
|
|
|
}
|
|
|
|
|
|
configService.set(opts, cb);
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-08-29 16:13:18 -03:00
|
|
|
|
root.getKeys = function(wallet, cb) {
|
2016-08-29 11:58:23 -03:00
|
|
|
|
root.prepare(wallet, function(err, password) {
|
|
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
|
var keys;
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
keys = wallet.getKeys(password);
|
|
|
|
|
|
} catch (e) {
|
2016-09-08 16:19:45 -03:00
|
|
|
|
return cb(e);
|
2016-08-29 11:58:23 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return cb(null, keys);
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-10-12 18:11:02 -03:00
|
|
|
|
root.getKeysWithPassword = function(wallet, password) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
return wallet.getKeys(password);
|
|
|
|
|
|
} catch (e) {}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-11-03 17:53:32 -03:00
|
|
|
|
root.getSendMaxInfo = function(wallet, opts, cb) {
|
|
|
|
|
|
opts = opts || {};
|
|
|
|
|
|
wallet.getSendMaxInfo(opts, function(err, res) {
|
2016-11-23 11:23:19 -03:00
|
|
|
|
return cb(err, res);
|
2016-11-03 17:53:32 -03:00
|
|
|
|
});
|
|
|
|
|
|
};
|
2016-08-24 19:28:20 -03:00
|
|
|
|
|
2016-05-09 15:56:44 -03:00
|
|
|
|
return root;
|
|
|
|
|
|
});
|