|
|
@ -222,18 +222,57 @@ CopayServer.prototype._getUtxos = function (opts, cb) { |
|
|
|
bc.getUnspentUtxos(addresses, function (err, utxos) { |
|
|
|
if (err) return cb(err); |
|
|
|
|
|
|
|
// TODO: filter 'locked' utxos
|
|
|
|
self.getPendingTxs({ walletId: opts.walletId }, function (err, txps) { |
|
|
|
if (err) return cb(err); |
|
|
|
|
|
|
|
var inputs = _.chain(txps) |
|
|
|
.pluck('input') |
|
|
|
.flatten() |
|
|
|
.map(function (utxo) { return utxo.txid + '|' + utxo.vout }); |
|
|
|
|
|
|
|
return cb(null, utxos); |
|
|
|
var dictionary = _.groupBy(utxos, function (utxo) { |
|
|
|
return utxo.txid + '|' + utxo.vout; |
|
|
|
}); |
|
|
|
|
|
|
|
_.each(inputs, function (input) { |
|
|
|
if (dictionary[input]) { |
|
|
|
dictionary[input].locked = true; |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
return cb(null, utxos); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
CopayServer.prototype._createRawTx = function (tx) { |
|
|
|
|
|
|
|
/** |
|
|
|
* Creates a new transaction proposal. |
|
|
|
* @param {Object} opts |
|
|
|
* @param {string} opts.walletId - The wallet id. |
|
|
|
* @returns {Object} balance - Total amount & locked amount. |
|
|
|
*/ |
|
|
|
CopayServer.prototype.getBalance = function (opts, cb) { |
|
|
|
var self = this; |
|
|
|
|
|
|
|
self._getUtxos({ walletId: opts.walletId }, function (err, utxos) { |
|
|
|
if (err) return cb(err); |
|
|
|
|
|
|
|
var balance = {}; |
|
|
|
balance.totalAmount = _.reduce(utxos, function(sum, utxo) { return sum + utxo.amount; }); |
|
|
|
balance.lockedAmount = _.reduce(_.without(utxos, { locked: true }), function(sum, utxo) { return sum + utxo.amount; }); |
|
|
|
|
|
|
|
return cb(null, balance); |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
CopayServer.prototype._createRawTx = function (txp, utxos) { |
|
|
|
var rawTx = new Bitcore.Transaction() |
|
|
|
.from(tx.inputs) |
|
|
|
.to(tx.toAddress, tx.amount) |
|
|
|
.change(tx.changeAddress); |
|
|
|
.from(utxos) |
|
|
|
.to(txp.toAddress, txp.amount) |
|
|
|
.change(txp.changeAddress); |
|
|
|
|
|
|
|
return rawTx; |
|
|
|
}; |
|
|
@ -258,16 +297,19 @@ CopayServer.prototype.createTx = function (opts, cb) { |
|
|
|
self._getUtxos({ walletId: wallet.id }, function (err, utxos) { |
|
|
|
if (err) return cb(err); |
|
|
|
|
|
|
|
utxos = _.without(utxos, { locked: true }); |
|
|
|
|
|
|
|
var txp = new TxProposal({ |
|
|
|
creatorId: opts.copayerId, |
|
|
|
toAddress: opts.toAddress, |
|
|
|
amount: opts.amount, |
|
|
|
changeAddress: opts.changeAddress, |
|
|
|
inputs: utxos, |
|
|
|
requiredSignatures: wallet.m, |
|
|
|
maxRejections: wallet.n - wallet.m, |
|
|
|
}); |
|
|
|
txp.rawTx = self._createRawTx(txp); |
|
|
|
txp.rawTx = self._createRawTx(txp, utxos); |
|
|
|
|
|
|
|
// TODO: assign used inputs
|
|
|
|
|
|
|
|
self.storage.storeTx(wallet.id, txp, function (err) { |
|
|
|
if (err) return cb(err); |
|
|
@ -345,17 +387,7 @@ CopayServer.prototype.rejectTx = function (opts, cb) { |
|
|
|
self.storage.storeTx(opts.walletId, txp, function (err) { |
|
|
|
if (err) return cb(err); |
|
|
|
|
|
|
|
if (txp.status == 'accepted'); |
|
|
|
self._broadcastTx(txp.rawTx, function (err, txid) { |
|
|
|
if (err) return cb(err); |
|
|
|
|
|
|
|
tx.setBroadcasted(txid); |
|
|
|
self.storage.storeTx(opts.walletId, txp, function (err) { |
|
|
|
if (err) return cb(err); |
|
|
|
|
|
|
|
return cb(); |
|
|
|
}); |
|
|
|
}); |
|
|
|
return cb(); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}; |
|
|
@ -364,7 +396,6 @@ CopayServer.prototype.rejectTx = function (opts, cb) { |
|
|
|
* Retrieves all pending transaction proposals. |
|
|
|
* @param {Object} opts |
|
|
|
* @param {string} opts.walletId - The wallet id. |
|
|
|
* @param {string} opts.copayerId - The wallet id. |
|
|
|
* @returns {TxProposal[]} Transaction proposal. |
|
|
|
*/ |
|
|
|
CopayServer.prototype.getPendingTxs = function (opts, cb) { |
|
|
|