|
|
@ -227,21 +227,13 @@ CopayServer.prototype._getUtxos = function (opts, cb) { |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
CopayServer.prototype._doCreateTx = function (copayerId, toAddress, amount, changeAddress, utxos, cb) { |
|
|
|
var tx = new TxProposal({ |
|
|
|
creatorId: copayerId, |
|
|
|
toAddress: toAddress, |
|
|
|
amount: amount, |
|
|
|
changeAddress: changeAddress, |
|
|
|
inputs: utxos, |
|
|
|
}); |
|
|
|
|
|
|
|
tx.raw = new Bitcore.Transaction() |
|
|
|
CopayServer.prototype._createRawTx = function (tx) { |
|
|
|
var rawTx = new Bitcore.Transaction() |
|
|
|
.from(tx.inputs) |
|
|
|
.to(tx.toAddress, tx.amount) |
|
|
|
.change(tx.changeAddress); |
|
|
|
|
|
|
|
return tx; |
|
|
|
return rawTx; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
@ -264,18 +256,28 @@ CopayServer.prototype.createTx = function (opts, cb) { |
|
|
|
self._getUtxos({ walletId: wallet.id }, function (err, utxos) { |
|
|
|
if (err) return cb(err); |
|
|
|
|
|
|
|
var tx = self._doCreateTx(opts.copayerId, opts.toAddress, opts.amount, opts.changeAddress, utxos); |
|
|
|
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); |
|
|
|
|
|
|
|
self.storage.storeTx(tx, function (err) { |
|
|
|
self.storage.storeTx(txp, function (err) { |
|
|
|
if (err) return cb(err); |
|
|
|
|
|
|
|
return cb(null, tx); |
|
|
|
return cb(null, txp); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
CopayServer.prototype._broadcastTx = function (tx, cb) { |
|
|
|
CopayServer.prototype._broadcastTx = function (rawTx, cb) { |
|
|
|
// TODO: this should attempt to broadcast _all_ accepted txps?
|
|
|
|
cb = cb || function () {}; |
|
|
|
|
|
|
|
throw 'not implemented'; |
|
|
@ -295,26 +297,29 @@ CopayServer.prototype.signTx = function (opts, cb) { |
|
|
|
self.getWallet({ id: opts.walletId }, function (err, wallet) { |
|
|
|
if (err || !wallet) return cb(err); |
|
|
|
|
|
|
|
self.fetchTx(wallet.id, opts.txProposalId, function (err, tx) { |
|
|
|
self.fetchTx(wallet.id, opts.txProposalId, function (err, txp) { |
|
|
|
if (err) return cb(err); |
|
|
|
if (!tx) return cb('Transaction proposal not found'); |
|
|
|
var action = _.find(tx.actions, { copayerId: opts.copayerId }); |
|
|
|
if (!txp) return cb('Transaction proposal not found'); |
|
|
|
var action = _.find(txp.actions, { copayerId: opts.copayerId }); |
|
|
|
if (action) return cb('Copayer already acted upon this transaction proposal'); |
|
|
|
if (tx.status != 'pending') return cb('The transaction proposal is not pending'); |
|
|
|
if (txp.status != 'pending') return cb('The transaction proposal is not pending'); |
|
|
|
|
|
|
|
tx.sign(opts.copayerId, opts.signature); |
|
|
|
if (tx.isRejected()) { |
|
|
|
tx.status = 'rejected'; |
|
|
|
} else if (tx.isAccepted()) { |
|
|
|
tx.status = 'accepted'; |
|
|
|
} |
|
|
|
self.storage.storeTx(wallet.id, tx, function (err) { |
|
|
|
txp.sign(opts.copayerId, opts.signature); |
|
|
|
|
|
|
|
self.storage.storeTx(wallet.id, txp, function (err) { |
|
|
|
if (err) return cb(err); |
|
|
|
|
|
|
|
if (tx.status == 'accepted'); |
|
|
|
self._broadcastTx(tx); |
|
|
|
if (txp.status == 'accepted'); |
|
|
|
self._broadcastTx(txp.rawTx, function (err, txid) { |
|
|
|
if (err) return cb(err); |
|
|
|
|
|
|
|
return cb(); |
|
|
|
tx.setBroadcasted(txid); |
|
|
|
self.storage.storeTx(wallet.id, txp, function (err) { |
|
|
|
if (err) return cb(err); |
|
|
|
|
|
|
|
return cb(); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
|