bitcore-wallet-service/lib/model/txproposal.js

199 lines
4.6 KiB
JavaScript
Raw Normal View History

2015-01-27 11:40:27 -08:00
'use strict';
2015-01-28 12:06:29 -08:00
var _ = require('lodash');
2015-02-07 07:48:57 -08:00
var Uuid = require('uuid');
2015-02-04 16:38:23 -08:00
var Bitcore = require('bitcore');
2015-02-06 10:15:54 -08:00
var Address = Bitcore.Address;
2015-01-28 12:06:29 -08:00
2015-01-28 11:40:07 -08:00
var TxProposalAction = require('./txproposalaction');
2015-02-02 10:29:14 -08:00
var VERSION = '1.0.0';
2015-01-27 11:40:27 -08:00
function TxProposal(opts) {
2015-02-02 12:07:18 -08:00
opts = opts || {};
2015-01-27 11:40:27 -08:00
2015-02-02 10:29:14 -08:00
this.version = VERSION;
2015-02-12 05:26:13 -08:00
var now = Date.now();
this.createdOn = Math.floor(now / 1000);
this.id = ('00000000000000' + now).slice(-14) + Uuid.v4();
2015-02-02 12:07:18 -08:00
this.creatorId = opts.creatorId;
this.toAddress = opts.toAddress;
this.amount = opts.amount;
this.message = opts.message;
2015-02-10 05:22:23 -08:00
this.proposalSignature = opts.proposalSignature;
2015-02-02 12:07:18 -08:00
this.changeAddress = opts.changeAddress;
this.inputs = opts.inputs;
2015-02-04 06:43:12 -08:00
this.inputPaths = opts.inputPaths;
2015-02-02 12:07:18 -08:00
this.requiredSignatures = opts.requiredSignatures;
2015-02-09 07:27:50 -08:00
this.requiredRejections = opts.requiredRejections;
2015-02-02 12:07:18 -08:00
this.status = 'pending';
2015-02-05 10:50:18 -08:00
this.actions = {};
2015-01-27 11:40:27 -08:00
};
2015-02-05 10:50:18 -08:00
TxProposal.fromObj = function(obj) {
2015-02-02 12:07:18 -08:00
var x = new TxProposal();
2015-01-27 11:40:27 -08:00
2015-02-02 10:29:14 -08:00
x.version = obj.version;
2015-02-02 12:07:18 -08:00
x.createdOn = obj.createdOn;
x.id = obj.id;
x.creatorId = obj.creatorId;
x.toAddress = obj.toAddress;
x.amount = obj.amount;
x.message = obj.message;
2015-02-10 05:22:23 -08:00
x.proposalSignature = obj.proposalSignature;
2015-02-02 12:07:18 -08:00
x.changeAddress = obj.changeAddress;
x.inputs = obj.inputs;
x.requiredSignatures = obj.requiredSignatures;
2015-02-09 07:27:50 -08:00
x.requiredRejections = obj.requiredRejections;
2015-02-02 12:07:18 -08:00
x.status = obj.status;
x.txid = obj.txid;
2015-02-04 06:43:12 -08:00
x.inputPaths = obj.inputPaths;
2015-02-05 10:50:18 -08:00
x.actions = obj.actions;
_.each(x.actions, function(action, copayerId) {
x.actions[copayerId] = new TxProposalAction(action);
2015-02-02 12:07:18 -08:00
});
return x;
2015-01-27 11:40:27 -08:00
};
2015-02-04 16:38:23 -08:00
2015-02-05 10:50:18 -08:00
TxProposal.prototype._updateStatus = function() {
2015-02-02 12:07:18 -08:00
if (this.status != 'pending') return;
2015-01-28 11:40:07 -08:00
2015-02-02 12:07:18 -08:00
if (this.isRejected()) {
this.status = 'rejected';
} else if (this.isAccepted()) {
this.status = 'accepted';
}
2015-01-28 11:40:07 -08:00
};
2015-02-04 16:38:23 -08:00
2015-02-05 12:22:38 -08:00
TxProposal.prototype._getBitcoreTx = function() {
2015-02-05 10:50:18 -08:00
var self = this;
var t = new Bitcore.Transaction();
_.each(this.inputs, function(i) {
t.from(i, i.publicKeys, self.requiredSignatures)
});
t.to(this.toAddress, this.amount)
2015-02-04 16:38:23 -08:00
.change(this.changeAddress);
2015-02-05 10:50:18 -08:00
t._updateChangeOutput();
return t;
2015-02-04 16:38:23 -08:00
};
2015-02-06 10:15:54 -08:00
TxProposal.prototype.getNetworkName = function() {
return Bitcore.Address(this.toAddress).toObject().networkName;
};
2015-02-04 16:38:23 -08:00
2015-02-05 12:22:38 -08:00
TxProposal.prototype.getRawTx = function() {
var t = this._getBitcoreTx();
return t.serialize();
};
2015-02-10 11:55:00 -08:00
/**
* getActors
*
* @return {String[]} copayerIds that performed actions in this proposal (accept / reject)
*/
2015-02-10 11:30:58 -08:00
TxProposal.prototype.getActors = function() {
return _.keys(this.actions);
};
2015-02-10 11:55:00 -08:00
/**
* getActionBy
*
* @param {String} copayerId
* @return {Object} type / createdOn
*/
TxProposal.prototype.getActionBy = function(copayerId) {
var a = this.actions[copayerId];
if (!a) return null;
return {
type: a.type,
createdOn: a.createdOn,
};
};
2015-02-05 10:50:18 -08:00
TxProposal.prototype.addAction = function(copayerId, type, signatures) {
2015-02-02 12:07:18 -08:00
var action = new TxProposalAction({
copayerId: copayerId,
type: type,
2015-02-05 10:50:18 -08:00
signatures: signatures,
2015-02-02 12:07:18 -08:00
});
2015-02-05 10:50:18 -08:00
this.actions[copayerId] = action;
2015-02-02 12:07:18 -08:00
this._updateStatus();
2015-01-28 11:40:07 -08:00
};
2015-02-05 10:50:18 -08:00
// TODO: no sure we should receive xpub or a list of pubkeys (pre derived)
TxProposal.prototype.checkSignatures = function(signatures, xpub) {
var self = this;
2015-02-04 16:38:23 -08:00
var t = this._getBitcoreTx();
2015-02-05 10:50:18 -08:00
if (signatures.length != this.inputs.length)
return false;
var oks = 0,
i = 0,
x = new Bitcore.HDPublicKey(xpub);
_.each(signatures, function(signatureHex) {
var input = self.inputs[i];
try {
var signature = Bitcore.crypto.Signature.fromString(signatureHex);
var pub = x.derive(self.inputPaths[i]).publicKey;
var s = {
inputIndex: i,
signature: signature,
sigtype: Bitcore.crypto.Signature.SIGHASH_ALL,
publicKey: pub,
};
i++;
t.applySignature(s);
oks++;
} catch (e) {
// TODO only for debug now
2015-02-06 10:15:54 -08:00
console.log('DEBUG ONLY:', e.message); //TODO
2015-02-05 10:50:18 -08:00
};
});
return oks === t.inputs.length;
2015-02-04 16:38:23 -08:00
};
2015-02-05 10:50:18 -08:00
TxProposal.prototype.sign = function(copayerId, signatures) {
this.addAction(copayerId, 'accept', signatures);
2015-01-28 11:40:07 -08:00
};
2015-02-05 10:50:18 -08:00
TxProposal.prototype.reject = function(copayerId) {
2015-02-02 12:07:18 -08:00
this.addAction(copayerId, 'reject');
2015-01-28 11:40:07 -08:00
};
2015-02-06 12:04:10 -08:00
TxProposal.prototype.isPending = function() {
2015-02-08 06:20:22 -08:00
return !_.any(['boradcasted', 'rejected'], this.status);
2015-02-06 12:04:10 -08:00
};
2015-02-05 10:50:18 -08:00
TxProposal.prototype.isAccepted = function() {
var votes = _.countBy(_.values(this.actions), 'type');
2015-02-02 12:07:18 -08:00
return votes['accept'] >= this.requiredSignatures;
2015-01-28 11:40:07 -08:00
};
2015-02-05 10:50:18 -08:00
TxProposal.prototype.isRejected = function() {
var votes = _.countBy(_.values(this.actions), 'type');
2015-02-09 07:27:50 -08:00
return votes['reject'] >= this.requiredRejections;
2015-01-28 11:40:07 -08:00
};
2015-02-05 10:50:18 -08:00
TxProposal.prototype.setBroadcasted = function(txid) {
2015-02-02 12:07:18 -08:00
this.txid = txid;
this.status = 'broadcasted';
2015-01-28 11:40:07 -08:00
};
2015-01-27 11:40:27 -08:00
module.exports = TxProposal;