Wallet/js/models/core/TxProposals.js

272 lines
6.2 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');
2014-04-11 01:09:42 -03:00
var util = bitcore.util;
2014-04-09 17:28:35 -03:00
var Transaction = bitcore.Transaction;
2014-04-11 01:09:42 -03:00
var Builder = bitcore.TransactionBuilder;
var Script = bitcore.Script;
2014-04-09 17:28:35 -03:00
var buffertools = bitcore.buffertools;
2014-04-09 20:37:14 -03:00
function TxProposal(opts) {
2014-04-18 19:28:28 -03:00
this.creator = opts.creator;
this.createdTs = opts.createdTs;
2014-04-10 02:16:57 -03:00
this.seenBy = opts.seenBy || {};
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 || [];
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
};
2014-04-18 19:28:28 -03:00
TxProposal.fromObj = function(o) {
var t = new TxProposal(o);
var b = new Builder.fromObj(o.builderObj);
t.builder = b;
return t;
2014-04-09 20:37:14 -03:00
};
2014-04-18 19:28:28 -03:00
2014-04-20 20:24:24 -03:00
TxProposal.getSentTs = function() {
return this.sentTs;
};
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;
2014-04-09 17:28:35 -03:00
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
}
2014-04-10 02:16:57 -03:00
TxProposals.fromObj = function(o) {
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);
var id = t.builder.build().getNormalizedHash().toString('hex');
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-04-10 02:16:57 -03:00
var ret = [];
2014-04-18 19:28:28 -03:00
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
}
2014-04-10 02:16:57 -03:00
return {
2014-04-11 01:09:42 -03:00
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
};
2014-04-11 01:09:42 -03:00
TxProposals.prototype._startMerge = function(myTxps, theirTxps) {
var fromUs=0, fromTheirs=0, merged =0;
2014-04-18 19:28:28 -03:00
var toMerge = {}, ready={};
2014-04-11 01:09:42 -03:00
2014-04-18 19:28:28 -03:00
for(var hash in theirTxps){
2014-04-11 01:09:42 -03:00
if (!myTxps[hash]) {
2014-04-18 19:28:28 -03:00
ready[hash]=theirTxps[hash]; // only in theirs;
2014-04-11 01:09:42 -03:00
fromTheirs++;
}
else {
toMerge[hash]=theirTxps[hash]; // need Merging
merged++;
}
2014-04-18 19:28:28 -03:00
}
2014-04-11 01:09:42 -03:00
2014-04-18 19:28:28 -03:00
for(var hash in myTxps){
2014-04-11 01:09:42 -03:00
if(!toMerge[hash]) {
2014-04-18 19:28:28 -03:00
ready[hash]=myTxps[hash]; // only in myTxps;
2014-04-11 01:09:42 -03:00
fromUs++;
}
2014-04-18 19:28:28 -03:00
}
2014-04-11 01:09:42 -03:00
return {
stats: {
fromUs: fromUs,
fromTheirs: fromTheirs,
merged: merged,
},
ready: ready,
toMerge: toMerge,
};
};
2014-04-23 02:01:54 -03:00
// TODO add signatures.
2014-04-11 01:09:42 -03:00
TxProposals.prototype._mergeMetadata = function(myTxps, theirTxps, mergeInfo) {
var toMerge = mergeInfo.toMerge;
2014-04-20 20:24:24 -03:00
var hasChanged =0;
2014-04-11 01:09:42 -03:00
Object.keys(toMerge).forEach(function(hash) {
var v0 = myTxps[hash];
var v1 = toMerge[hash];
Object.keys(v1.seenBy).forEach(function(k) {
2014-04-23 02:01:54 -03:00
if (!v0.seenBy[k]) {
2014-04-20 20:24:24 -03:00
v0.seenBy[k] = v1.seenBy[k];
hasChanged++;
}
2014-04-11 01:09:42 -03:00
});
Object.keys(v1.signedBy).forEach(function(k) {
2014-04-20 20:24:24 -03:00
if (!v0.signedBy[k]) {
v0.signedBy[k] = v1.signedBy[k];
hasChanged++;
}
2014-04-11 01:09:42 -03:00
});
2014-04-20 20:24:24 -03:00
2014-04-23 02:01:54 -03:00
Object.keys(v1.rejectedBy).forEach(function(k) {
if (!v0.rejectedBy[k]) {
v0.rejectedBy[k] = v1.rejectedBy[k];
hasChanged++;
}
});
2014-04-21 07:27:45 -03:00
if (!v0.sentTxid && v1.sentTxid) {
v0.sentTs = v1.sentTs;
v0.sentTxid = v1.sentTxid;
2014-04-20 20:24:24 -03:00
hasChanged++;
}
2014-04-11 01:09:42 -03:00
});
2014-04-20 20:24:24 -03:00
return hasChanged;
2014-04-11 01:09:42 -03:00
};
TxProposals.prototype._mergeBuilder = function(myTxps, theirTxps, mergeInfo) {
2014-04-11 01:09:42 -03:00
var toMerge = mergeInfo.toMerge;
2014-04-20 20:24:24 -03:00
var hasChanged=0;
2014-04-11 01:09:42 -03:00
2014-04-18 19:28:28 -03:00
for(var hash in toMerge){
var v0 = myTxps[hash].builder;
var v1 = toMerge[hash].builder;
2014-04-11 01:09:42 -03:00
2014-04-20 20:24:24 -03:00
// TODO: enhance this
var before = JSON.stringify(v0.toObj());
v0.merge(v1);
2014-04-20 20:24:24 -03:00
var after = JSON.stringify(v0.toObj());
if (after !== before) hasChanged ++;
}
2014-04-11 01:09:42 -03:00
};
2014-04-15 18:23:35 -03:00
TxProposals.prototype.add = function(data) {
var ntxid = data.builder.build().getNormalizedHash().toString('hex');
this.txps[ntxid] = new TxProposal(data);
2014-05-15 16:39:22 -03:00
return ntxid;
2014-04-18 19:28:28 -03:00
};
2014-04-21 07:30:46 -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] = {};
}
}
2014-04-23 02:01:54 -03:00
for(var p in txp.seenBy){
i.peerActions[p]={seen: txp.seenBy[p]};
}
for(var p in txp.signedBy){
i.peerActions[p]= i.peerActions[p] || {};
i.peerActions[p].sign = txp.signedBy[p];
}
var r=0;
for(var p in txp.rejectedBy){
i.peerActions[p]= i.peerActions[p] || {};
i.peerActions[p].rejected = txp.rejectedBy[p];
r++;
}
i.rejectCount=r;
var c = txp.creator;
i.peerActions[c] = i.peerActions[c] || {};
i.peerActions[c].create = txp.createdTs;
return i;
};
TxProposals.prototype.getUsedUnspent = function(maxRejectCount) {
2014-04-21 12:00:14 -03:00
var ret = [];
for(var i in this.txps) {
var u = this.txps[i].builder.getSelectedUnspent();
2014-04-23 02:01:54 -03:00
if (this.getTxProposal(i).rejectCount>maxRejectCount)
continue;
2014-04-21 12:00:14 -03:00
for (var j in u){
ret.push(u[j].txid);
}
}
return ret;
};
2014-04-18 19:28:28 -03:00
2014-04-11 01:09:42 -03:00
TxProposals.prototype.merge = function(t) {
if (this.network.name !== t.network.name)
throw new Error('network mismatch in:', t);
2014-04-11 01:09:42 -03:00
var res = [];
2014-04-20 20:24:24 -03:00
var hasChanged = 0;
2014-04-11 01:09:42 -03:00
2014-04-18 19:28:28 -03:00
var myTxps = this.txps;
var theirTxps = t.txps;
2014-04-11 01:09:42 -03:00
var mergeInfo = this._startMerge(myTxps, theirTxps);
2014-04-20 20:24:24 -03:00
hasChanged += this._mergeMetadata(myTxps, theirTxps, mergeInfo);
hasChanged += this._mergeBuilder(myTxps, theirTxps, mergeInfo);
2014-04-11 01:09:42 -03:00
Object.keys(mergeInfo.toMerge).forEach(function(hash) {
2014-04-18 19:28:28 -03:00
mergeInfo.ready[hash] = myTxps[hash];
2014-04-11 01:09:42 -03:00
});
2014-04-20 20:24:24 -03:00
mergeInfo.stats.hasChanged = hasChanged;
2014-04-11 01:09:42 -03:00
this.txps=mergeInfo.ready;
return mergeInfo.stats;
};
2014-04-09 17:28:35 -03:00
module.exports = require('soop')(TxProposals);