|
|
@ -264,6 +264,7 @@ WalletService.prototype.getWallet = function(opts, cb) { |
|
|
|
/** |
|
|
|
* Retrieves wallet status. |
|
|
|
* @param {Object} opts |
|
|
|
* @param {Object} opts.twoStep[=false] - Optional: use 2-step balance computation for improved performance |
|
|
|
* @param {Object} opts.includeExtendedInfo - Include PKR info & address managers for wallet & copayers |
|
|
|
* @returns {Object} status |
|
|
|
*/ |
|
|
@ -297,7 +298,7 @@ WalletService.prototype.getStatus = function(opts, cb) { |
|
|
|
}); |
|
|
|
}, |
|
|
|
function(next) { |
|
|
|
self.getBalance({}, function(err, balance) { |
|
|
|
self.getBalance(opts, function(err, balance) { |
|
|
|
if (err) return next(err); |
|
|
|
status.balance = balance; |
|
|
|
next(); |
|
|
@ -833,7 +834,7 @@ WalletService.prototype._getBlockchainExplorer = function(network) { |
|
|
|
return this.blockchainExplorer; |
|
|
|
}; |
|
|
|
|
|
|
|
WalletService.prototype._getUtxosForAddresses = function(addresses, cb) { |
|
|
|
WalletService.prototype._getUtxos = function(addresses, cb) { |
|
|
|
var self = this; |
|
|
|
|
|
|
|
if (addresses.length == 0) return cb(null, []); |
|
|
@ -856,24 +857,36 @@ WalletService.prototype._getUtxosForAddresses = function(addresses, cb) { |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
WalletService.prototype._getUtxosForCurrentWallet = function(cb) { |
|
|
|
WalletService.prototype._getUtxosForCurrentWallet = function(addresses, cb) { |
|
|
|
var self = this; |
|
|
|
|
|
|
|
function utxoKey(utxo) { |
|
|
|
return utxo.txid + '|' + utxo.vout |
|
|
|
}; |
|
|
|
|
|
|
|
// Get addresses for this wallet
|
|
|
|
self.storage.fetchAddresses(self.walletId, function(err, addresses) { |
|
|
|
if (err) return cb(err); |
|
|
|
async.waterfall([ |
|
|
|
|
|
|
|
function(next) { |
|
|
|
if (_.isArray(addresses)) { |
|
|
|
if (!_.isEmpty(addresses)) { |
|
|
|
next(null, addresses); |
|
|
|
} else { |
|
|
|
next(null, []); |
|
|
|
} |
|
|
|
} else { |
|
|
|
self.storage.fetchAddresses(self.walletId, next); |
|
|
|
} |
|
|
|
}, |
|
|
|
function(addresses, next) { |
|
|
|
if (addresses.length == 0) return next(null, []); |
|
|
|
|
|
|
|
var addressStrs = _.pluck(addresses, 'address'); |
|
|
|
self._getUtxosForAddresses(addressStrs, function(err, utxos) { |
|
|
|
if (err) return cb(err); |
|
|
|
if (utxos.length == 0) return cb(null, []); |
|
|
|
self._getUtxos(addressStrs, function(err, utxos) { |
|
|
|
if (err) return next(err); |
|
|
|
if (utxos.length == 0) return next(null, []); |
|
|
|
|
|
|
|
self.getPendingTxs({}, function(err, txps) { |
|
|
|
if (err) return cb(err); |
|
|
|
if (err) return next(err); |
|
|
|
|
|
|
|
var lockedInputs = _.map(_.flatten(_.pluck(txps, 'inputs')), utxoKey); |
|
|
|
var utxoIndex = _.indexBy(utxos, utxoKey); |
|
|
@ -890,10 +903,11 @@ WalletService.prototype._getUtxosForCurrentWallet = function(cb) { |
|
|
|
utxo.publicKeys = addressToPath[utxo.address].publicKeys; |
|
|
|
}); |
|
|
|
|
|
|
|
return cb(null, utxos); |
|
|
|
}); |
|
|
|
return next(null, utxos); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}, |
|
|
|
], cb); |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
@ -908,9 +922,9 @@ WalletService.prototype.getUtxos = function(opts, cb) { |
|
|
|
opts = opts || {}; |
|
|
|
|
|
|
|
if (_.isUndefined(opts.addresses)) { |
|
|
|
self._getUtxosForCurrentWallet(cb); |
|
|
|
self._getUtxosForCurrentWallet(null, cb); |
|
|
|
} else { |
|
|
|
self._getUtxosForAddresses(opts.addresses, cb); |
|
|
|
self._getUtxos(opts.addresses, cb); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
@ -950,16 +964,12 @@ WalletService.prototype._computeBytesToSendMax = function(utxos, cb) { |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
|
* Creates a new transaction proposal. |
|
|
|
* @param {Object} opts |
|
|
|
* @returns {Object} balance - Total amount & locked amount. |
|
|
|
*/ |
|
|
|
WalletService.prototype.getBalance = function(opts, cb) { |
|
|
|
WalletService.prototype._getBalanceFromAddresses = function(addresses, cb) { |
|
|
|
var self = this; |
|
|
|
|
|
|
|
self.getUtxos({}, function(err, utxos) { |
|
|
|
self._getUtxosForCurrentWallet(addresses, function(err, utxos) { |
|
|
|
if (err) return cb(err); |
|
|
|
|
|
|
|
var balance = self._totalizeUtxos(utxos); |
|
|
|
|
|
|
|
// Compute balance by address
|
|
|
@ -980,14 +990,118 @@ WalletService.prototype.getBalance = function(opts, cb) { |
|
|
|
|
|
|
|
self._computeBytesToSendMax(utxos, function(err, size) { |
|
|
|
if (err) { |
|
|
|
log.error('Could not compute fees needed to transfer max amount', err); |
|
|
|
log.error('Could not compute size of send max transaction', err); |
|
|
|
} |
|
|
|
balance.totalBytesToSendMax = size || 0; |
|
|
|
balance.totalBytesToSendMax = _.isNumber(size) ? size : null; |
|
|
|
return cb(null, balance); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
WalletService.prototype._getBalanceOneStep = function(opts, cb) { |
|
|
|
var self = this; |
|
|
|
|
|
|
|
self.storage.fetchAddresses(self.walletId, function(err, addresses) { |
|
|
|
if (err) return cb(err); |
|
|
|
self._getBalanceFromAddresses(addresses, function(err, balance) { |
|
|
|
if (err) return cb(err); |
|
|
|
|
|
|
|
// Update cache
|
|
|
|
async.series([ |
|
|
|
|
|
|
|
function(next) { |
|
|
|
self.storage.cleanActiveAddresses(self.walletId, next); |
|
|
|
}, |
|
|
|
function(next) { |
|
|
|
var active = _.pluck(balance.byAddress, 'address') |
|
|
|
self.storage.storeActiveAddresses(self.walletId, active, next); |
|
|
|
}, |
|
|
|
], function(err) { |
|
|
|
if (err) { |
|
|
|
log.warn('Could not update wallet cache', err); |
|
|
|
} |
|
|
|
return cb(null, balance); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
WalletService.prototype._getActiveAddresses = function(cb) { |
|
|
|
var self = this; |
|
|
|
|
|
|
|
self.storage.fetchActiveAddresses(self.walletId, function(err, active) { |
|
|
|
if (err) { |
|
|
|
log.warn('Could not fetch active addresses from cache', err); |
|
|
|
return cb(); |
|
|
|
} |
|
|
|
|
|
|
|
if (!_.isArray(active)) return cb(); |
|
|
|
|
|
|
|
self.storage.fetchAddresses(self.walletId, function(err, allAddresses) { |
|
|
|
if (err) return cb(err); |
|
|
|
|
|
|
|
var now = Math.floor(Date.now() / 1000); |
|
|
|
var recent = _.pluck(_.filter(allAddresses, function(address) { |
|
|
|
return address.createdOn > (now - 24 * 3600); |
|
|
|
}), 'address'); |
|
|
|
|
|
|
|
var result = _.union(active, recent); |
|
|
|
|
|
|
|
var index = _.indexBy(allAddresses, 'address'); |
|
|
|
result = _.map(result, function(r) { |
|
|
|
return index[r]; |
|
|
|
}); |
|
|
|
return cb(null, result); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
|
* Get wallet balance. |
|
|
|
* @param {Object} opts |
|
|
|
* @param {Boolean} opts.twoStep[=false] - Optional - Use 2 step balance computation for improved performance |
|
|
|
* @returns {Object} balance - Total amount & locked amount. |
|
|
|
*/ |
|
|
|
WalletService.prototype.getBalance = function(opts, cb) { |
|
|
|
var self = this; |
|
|
|
|
|
|
|
opts = opts || {}; |
|
|
|
|
|
|
|
if (!opts.twoStep) |
|
|
|
return self._getBalanceOneStep(opts, cb); |
|
|
|
|
|
|
|
self.storage.countAddresses(self.walletId, function(err, nbAddresses) { |
|
|
|
if (err) return cb(err); |
|
|
|
if (nbAddresses < Defaults.TWO_STEP_BALANCE_THRESHOLD) { |
|
|
|
return self._getBalanceOneStep(opts, cb); |
|
|
|
} |
|
|
|
|
|
|
|
self._getActiveAddresses(function(err, activeAddresses) { |
|
|
|
if (err) return cb(err); |
|
|
|
if (!_.isArray(activeAddresses)) { |
|
|
|
return self._getBalanceOneStep(opts, cb); |
|
|
|
} else { |
|
|
|
log.debug('Requesting partial balance for ' + activeAddresses.length + ' out of ' + nbAddresses + ' addresses'); |
|
|
|
self._getBalanceFromAddresses(activeAddresses, function(err, partialBalance) { |
|
|
|
if (err) return cb(err); |
|
|
|
cb(null, partialBalance); |
|
|
|
setTimeout(function() { |
|
|
|
self._getBalanceOneStep(opts, function(err, fullBalance) { |
|
|
|
if (err) return; |
|
|
|
if (!_.isEqual(partialBalance, fullBalance)) { |
|
|
|
log.debug('Cache miss: balance in active addresses differs from final balance'); |
|
|
|
self._notify('BalanceUpdated', fullBalance); |
|
|
|
} |
|
|
|
}); |
|
|
|
}, 1); |
|
|
|
return; |
|
|
|
}); |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
WalletService.prototype._sampleFeeLevels = function(network, points, cb) { |
|
|
|
var self = this; |
|
|
|
|
|
|
@ -1102,7 +1216,7 @@ WalletService.prototype._selectTxInputs = function(txp, utxosToExclude, cb) { |
|
|
|
return _.pluck(_.sortBy(list, 'order'), 'utxo'); |
|
|
|
}; |
|
|
|
|
|
|
|
self.getUtxos({}, function(err, utxos) { |
|
|
|
self._getUtxosForCurrentWallet(null, function(err, utxos) { |
|
|
|
if (err) return cb(err); |
|
|
|
|
|
|
|
var excludeIndex = _.reduce(utxosToExclude, function(res, val) { |
|
|
@ -1601,7 +1715,7 @@ WalletService.prototype._broadcastRawTx = function(network, raw, cb) { |
|
|
|
bc.broadcast(raw, function(err, txid) { |
|
|
|
if (err) return cb(err); |
|
|
|
return cb(null, txid); |
|
|
|
}) |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
|