Wallet/js/models/core/TxProposals.js

297 lines
6.5 KiB
JavaScript
Raw Normal View History

2014-04-09 17:28:35 -03:00
'use strict';
var imports = require('soop').imports();
var bitcore = require('bitcore');
var util = bitcore.util;
2014-04-09 17:28:35 -03:00
var Transaction = bitcore.Transaction;
var BuilderMockV0 = require('./BuilderMockV0');;
var TransactionBuilder = bitcore.TransactionBuilder;
var Script = bitcore.Script;
2014-07-29 00:05:36 -03:00
var Key = bitcore.Key;
2014-04-09 17:28:35 -03:00
var buffertools = bitcore.buffertools;
var preconditions = require('preconditions').instance();
2014-07-29 00:05:36 -03:00
var COPAY_EPOCH = 1400000000000;
2014-04-09 17:28:35 -03:00
2014-04-09 20:37:14 -03:00
function TxProposal(opts) {
this.creator = opts.creator;
this.createdTs = opts.createdTs;
this.seenBy = opts.seenBy || {};
2014-04-10 02:16:57 -03:00
this.signedBy = opts.signedBy || {};
2014-04-23 02:01:54 -03:00
this.rejectedBy = opts.rejectedBy || {};
this.builder = opts.builder;
2014-04-20 21:53:54 -03:00
this.sentTs = opts.sentTs || null;
2014-04-21 07:27:45 -03:00
this.sentTxid = opts.sentTxid || null;
2014-05-30 15:07:52 -03:00
this.inputChainPaths = opts.inputChainPaths || [];
this.comment = opts.comment || null;
2014-04-18 19:28:28 -03:00
}
2014-06-17 16:42:49 -03:00
TxProposal.prototype.getID = function() {
return this.builder.build().getNormalizedHash().toString('hex');
2014-06-17 16:42:49 -03:00
};
2014-04-18 19:28:28 -03:00
TxProposal.prototype.toObj = function() {
var o = JSON.parse(JSON.stringify(this));
delete o['builder'];
o.builderObj = this.builder.toObj();
return o;
};
2014-04-20 21:23:53 -03:00
2014-04-21 07:27:45 -03:00
TxProposal.prototype.setSent = function(sentTxid) {
2014-04-21 11:28:25 -03:00
this.sentTxid = sentTxid;
this.sentTs = Date.now();
2014-04-20 21:23:53 -03:00
};
TxProposal.fromObj = function(o, forceOpts) {
2014-04-18 19:28:28 -03:00
var t = new TxProposal(o);
try {
2014-07-25 17:48:35 -03:00
// force opts is requested.
for (var k in forceOpts) {
o.builderObj.opts[k] = forceOpts[k];
}
t.builder = TransactionBuilder.fromObj(o.builderObj);
} catch (e) {
if (!o.version) {
t.builder = new BuilderMockV0(o.builderObj);
t.readonly = 1;
};
}
2014-04-18 19:28:28 -03:00
return t;
2014-04-09 20:37:14 -03:00
};
2014-04-18 19:28:28 -03:00
TxProposal.prototype.isValid = function() {
2014-07-25 13:37:12 -03:00
if (this.builder.signhash && this.builder.signhash !== Transaction.SIGHASH_ALL) {
return false;
}
var tx = this.builder.build();
for (var i = 0; i < tx.ins.length; i++) {
var hashType = tx.getHashType(i);
2014-07-25 13:37:12 -03:00
if (hashType && hashType !== Transaction.SIGHASH_ALL) {
return false;
}
}
2014-07-29 00:05:36 -03:00
// Check that has valid signatures
var tx = this.builder.build();
for (var i = 0; i < tx.ins.length; i++) {
var txSigHash = this.tx.hashForSignature(
this.builder.inputMap[i].scriptPubKey, i, Transaction.SIGHASH_ALL);
2014-07-30 09:45:56 -03:00
return false;
2014-07-29 00:05:36 -03:00
}
return true;
};
2014-04-20 20:24:24 -03:00
TxProposal.getSentTs = function() {
return this.sentTs;
};
2014-07-29 00:05:36 -03:00
TxProposal.prototype.merge = function(incoming, author) {
var ret = {};
2014-07-30 09:45:56 -03:00
/* TODO */
2014-06-17 15:30:38 -03:00
2014-07-30 09:45:56 -03:00
/*
2014-07-29 00:05:36 -03:00
events.push({
type: 'seen',
cId: k,
txId: ntxid
});
2014-06-17 15:30:38 -03:00
events.push({
type: 'signed',
cId: k,
txId: ntxid
2014-06-17 15:30:38 -03:00
});
events.push({
type: 'rejected',
cId: k,
txId: ntxid
2014-06-17 15:30:38 -03:00
});
2014-07-30 09:45:56 -03:00
ret.events = this.mergeMetadata(incoming, author);
*/
ret.hasChanged = this.mergeBuilder(other);
return ret;
};
2014-06-17 15:30:38 -03:00
2014-07-30 09:45:56 -03:00
TxProposal.prototype.mergeBuilder = function(other) {
var b0 = this.builder;
var b1 = other.builder;
2014-06-17 15:30:38 -03:00
2014-07-30 09:45:56 -03:00
var before = JSON.stringify(b0.toObj());
b0.merge(b1);
var after = JSON.stringify(b0.toObj());
return after !== before;
2014-06-17 15:30:38 -03:00
};
2014-07-30 09:45:56 -03:00
//This should be on bitcore / Transaction
TxProposal.prototype.countSignatures = function() {
var tx = this.builder.build();
var ret = 0;
for (var i in tx.ins) {
ret += tx.countInputSignatures(i);
}
return ret;
};
2014-04-09 20:37:14 -03:00
module.exports = require('soop')(TxProposal);
2014-04-09 17:28:35 -03:00
function TxProposals(opts) {
opts = opts || {};
this.walletId = opts.walletId;
this.network = opts.networkName === 'livenet' ?
bitcore.networks.livenet : bitcore.networks.testnet;
2014-04-18 19:28:28 -03:00
this.txps = {};
2014-04-09 17:28:35 -03:00
}
TxProposals.fromObj = function(o, forceOpts) {
2014-04-10 02:16:57 -03:00
var ret = new TxProposals({
networkName: o.networkName,
walletId: o.walletId,
2014-04-10 02:16:57 -03:00
});
2014-04-18 19:28:28 -03:00
o.txps.forEach(function(o2) {
var t = TxProposal.fromObj(o2, forceOpts);
2014-07-25 11:05:10 -03:00
if (t.builder) {
var id = t.getID();
ret.txps[id] = t;
}
2014-04-10 02:16:57 -03:00
});
return ret;
};
2014-04-09 17:28:35 -03:00
2014-05-15 17:43:41 -03:00
TxProposals.prototype.getNtxids = function() {
return Object.keys(this.txps);
};
2014-05-15 17:43:41 -03:00
TxProposals.prototype.toObj = function(onlyThisNtxid) {
2014-06-18 10:09:40 -03:00
if (onlyThisNtxid) throw new Error();
2014-04-10 02:16:57 -03:00
var ret = [];
for (var id in this.txps) {
2014-05-15 17:43:41 -03:00
if (onlyThisNtxid && id != onlyThisNtxid)
continue;
2014-04-18 19:28:28 -03:00
var t = this.txps[id];
2014-04-20 20:24:24 -03:00
if (!t.sent)
ret.push(t.toObj());
2014-04-18 19:28:28 -03:00
}
return {
txps: ret,
walletId: this.walletId,
2014-04-10 02:16:57 -03:00
networkName: this.network.name,
};
2014-04-09 17:28:35 -03:00
};
TxProposals.prototype.merge = function(inTxp, author) {
2014-06-17 15:30:38 -03:00
var myTxps = this.txps;
var ntxid = inTxp.getID();
2014-06-18 10:09:40 -03:00
var ret = {};
ret.events = [];
ret.events.hasChanged = false;
if (myTxps[ntxid]) {
var v0 = myTxps[ntxid];
var v1 = inTxp;
ret = v0.merge(v1, author);
} else {
2014-06-18 10:21:26 -03:00
this.txps[ntxid] = inTxp;
ret.hasChanged = true;
ret.events.push({
type: 'new',
cid: inTxp.creator,
tx: ntxid
});
}
return ret;
2014-04-11 01:09:42 -03:00
};
2014-04-15 18:23:35 -03:00
TxProposals.prototype.add = function(data) {
preconditions.checkArgument(data.inputChainPaths);
preconditions.checkArgument(data.signedBy);
preconditions.checkArgument(data.creator);
preconditions.checkArgument(data.createdTs);
preconditions.checkArgument(data.builder);
2014-06-17 16:42:49 -03:00
var txp = new TxProposal(data);
var ntxid = txp.getID();
this.txps[ntxid] = txp;
2014-05-15 16:39:22 -03:00
return ntxid;
2014-04-18 19:28:28 -03:00
};
TxProposals.prototype.setSent = function(ntxid, txid) {
2014-04-20 20:24:24 -03:00
//sent TxProposals are local an not broadcasted.
2014-04-21 07:30:46 -03:00
this.txps[ntxid].setSent(txid);
2014-04-15 18:23:35 -03:00
};
2014-04-23 02:01:54 -03:00
TxProposals.prototype.getTxProposal = function(ntxid, copayers) {
2014-04-23 02:01:54 -03:00
var txp = this.txps[ntxid];
var i = JSON.parse(JSON.stringify(txp));
i.builder = txp.builder;
i.ntxid = ntxid;
i.peerActions = {};
if (copayers) {
for (var j = 0; j < copayers.length; j++) {
var p = copayers[j];
i.peerActions[p] = {};
}
}
for (var p in txp.seenBy) {
i.peerActions[p] = {
seen: txp.seenBy[p]
};
2014-04-23 02:01:54 -03:00
}
for (var p in txp.signedBy) {
i.peerActions[p] = i.peerActions[p] || {};
2014-04-23 02:01:54 -03:00
i.peerActions[p].sign = txp.signedBy[p];
}
var r = 0;
for (var p in txp.rejectedBy) {
i.peerActions[p] = i.peerActions[p] || {};
2014-04-23 02:01:54 -03:00
i.peerActions[p].rejected = txp.rejectedBy[p];
r++;
}
i.rejectCount = r;
2014-04-23 02:01:54 -03:00
var c = txp.creator;
i.peerActions[c] = i.peerActions[c] || {};
i.peerActions[c].create = txp.createdTs;
return i;
};
2014-06-05 14:55:19 -03:00
//returns the unspent txid-vout used in PENDING Txs
2014-04-23 02:01:54 -03:00
TxProposals.prototype.getUsedUnspent = function(maxRejectCount) {
2014-06-05 14:55:19 -03:00
var ret = {};
for (var i in this.txps) {
2014-04-21 12:00:14 -03:00
var u = this.txps[i].builder.getSelectedUnspent();
2014-06-05 14:55:19 -03:00
var p = this.getTxProposal(i);
if (p.rejectCount > maxRejectCount || p.sentTxid)
2014-04-23 02:01:54 -03:00
continue;
2014-06-05 14:55:19 -03:00
for (var j in u) {
ret[u[j].txid + ',' + u[j].vout] = 1;
2014-04-21 12:00:14 -03:00
}
}
return ret;
};
2014-04-18 19:28:28 -03:00
2014-06-18 09:07:32 -03:00
TxProposals.TxProposal = TxProposal;
2014-04-09 17:28:35 -03:00
module.exports = require('soop')(TxProposals);