|
|
@ -15,39 +15,39 @@ var Defaults = Common.Defaults; |
|
|
|
|
|
|
|
var TxProposalAction = require('./txproposalaction'); |
|
|
|
|
|
|
|
function TxProposalv3() {}; |
|
|
|
function TxProposal() {}; |
|
|
|
|
|
|
|
TxProposalv3.Types = { |
|
|
|
TxProposal.Types = { |
|
|
|
STANDARD: 'standard', |
|
|
|
EXTERNAL: 'external' |
|
|
|
}; |
|
|
|
|
|
|
|
TxProposalv3.isTypeSupported = function(type) { |
|
|
|
return _.contains(_.values(TxProposalv3.Types), type); |
|
|
|
TxProposal.isTypeSupported = function(type) { |
|
|
|
return _.contains(_.values(TxProposal.Types), type); |
|
|
|
}; |
|
|
|
|
|
|
|
TxProposalv3._create = {}; |
|
|
|
TxProposal._create = {}; |
|
|
|
|
|
|
|
TxProposalv3._create.standard = function(txp, opts) { |
|
|
|
TxProposal._create.standard = function(txp, opts) { |
|
|
|
txp.outputs = _.map(opts.outputs, function(output) { |
|
|
|
return _.pick(output, ['amount', 'toAddress', 'message']); |
|
|
|
}); |
|
|
|
txp.outputOrder = _.shuffle(_.range(txp.outputs.length + 1)); |
|
|
|
}; |
|
|
|
|
|
|
|
TxProposalv3._create.external = function(txp, opts) { |
|
|
|
TxProposal._create.external = function(txp, opts) { |
|
|
|
txp.setInputs(opts.inputs || []); |
|
|
|
txp.outputs = opts.outputs; |
|
|
|
txp.outputOrder = _.range(txp.outputs.length + 1); |
|
|
|
}; |
|
|
|
|
|
|
|
TxProposalv3.create = function(opts) { |
|
|
|
TxProposal.create = function(opts) { |
|
|
|
opts = opts || {}; |
|
|
|
|
|
|
|
var x = new TxProposalv3(); |
|
|
|
var x = new TxProposal(); |
|
|
|
|
|
|
|
x.version = 3; |
|
|
|
x.type = opts.type || TxProposalv3.Types.STANDARD; |
|
|
|
x.type = opts.type || TxProposal.Types.STANDARD; |
|
|
|
|
|
|
|
var now = Date.now(); |
|
|
|
x.createdOn = Math.floor(now / 1000); |
|
|
@ -73,8 +73,8 @@ TxProposalv3.create = function(opts) { |
|
|
|
|
|
|
|
x.customData = opts.customData; |
|
|
|
|
|
|
|
if (_.isFunction(TxProposalv3._create[x.type])) { |
|
|
|
TxProposalv3._create[x.type](x, opts); |
|
|
|
if (_.isFunction(TxProposal._create[x.type])) { |
|
|
|
TxProposal._create[x.type](x, opts); |
|
|
|
} |
|
|
|
|
|
|
|
x.amount = x.getTotalAmount(); |
|
|
@ -86,8 +86,8 @@ TxProposalv3.create = function(opts) { |
|
|
|
return x; |
|
|
|
}; |
|
|
|
|
|
|
|
TxProposalv3.fromObj = function(obj) { |
|
|
|
var x = new TxProposalv3(); |
|
|
|
TxProposal.fromObj = function(obj) { |
|
|
|
var x = new TxProposal(); |
|
|
|
|
|
|
|
x.version = obj.version; |
|
|
|
x.type = obj.type; |
|
|
@ -126,19 +126,19 @@ TxProposalv3.fromObj = function(obj) { |
|
|
|
return x; |
|
|
|
}; |
|
|
|
|
|
|
|
TxProposalv3.prototype.toObject = function() { |
|
|
|
TxProposal.prototype.toObject = function() { |
|
|
|
var x = _.cloneDeep(this); |
|
|
|
x.isPending = this.isPending(); |
|
|
|
x.isTemporary = this.isTemporary(); |
|
|
|
return x; |
|
|
|
}; |
|
|
|
|
|
|
|
TxProposalv3.prototype.setInputs = function(inputs) { |
|
|
|
TxProposal.prototype.setInputs = function(inputs) { |
|
|
|
this.inputs = inputs; |
|
|
|
this.inputPaths = _.pluck(inputs, 'path'); |
|
|
|
}; |
|
|
|
|
|
|
|
TxProposalv3.prototype._updateStatus = function() { |
|
|
|
TxProposal.prototype._updateStatus = function() { |
|
|
|
if (this.status != 'pending') return; |
|
|
|
|
|
|
|
if (this.isRejected()) { |
|
|
@ -148,7 +148,7 @@ TxProposalv3.prototype._updateStatus = function() { |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
TxProposalv3.prototype._buildTx = function() { |
|
|
|
TxProposal.prototype._buildTx = function() { |
|
|
|
var self = this; |
|
|
|
|
|
|
|
var t = new Bitcore.Transaction(); |
|
|
@ -208,7 +208,7 @@ TxProposalv3.prototype._buildTx = function() { |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
TxProposalv3.prototype._getCurrentSignatures = function() { |
|
|
|
TxProposal.prototype._getCurrentSignatures = function() { |
|
|
|
var acceptedActions = _.filter(this.actions, { |
|
|
|
type: 'accept' |
|
|
|
}); |
|
|
@ -221,7 +221,7 @@ TxProposalv3.prototype._getCurrentSignatures = function() { |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
TxProposalv3.prototype.getBitcoreTx = function() { |
|
|
|
TxProposal.prototype.getBitcoreTx = function() { |
|
|
|
var self = this; |
|
|
|
|
|
|
|
var t = this._buildTx(); |
|
|
@ -234,13 +234,13 @@ TxProposalv3.prototype.getBitcoreTx = function() { |
|
|
|
return t; |
|
|
|
}; |
|
|
|
|
|
|
|
TxProposalv3.prototype.getRawTx = function() { |
|
|
|
TxProposal.prototype.getRawTx = function() { |
|
|
|
var t = this.getBitcoreTx(); |
|
|
|
|
|
|
|
return t.uncheckedSerialize(); |
|
|
|
}; |
|
|
|
|
|
|
|
TxProposalv3.prototype.getEstimatedSize = function() { |
|
|
|
TxProposal.prototype.getEstimatedSize = function() { |
|
|
|
// Note: found empirically based on all multisig P2SH inputs and within m & n allowed limits.
|
|
|
|
var safetyMargin = 0.05; |
|
|
|
var walletM = this.requiredSignatures; |
|
|
@ -256,7 +256,7 @@ TxProposalv3.prototype.getEstimatedSize = function() { |
|
|
|
return parseInt((size * (1 + safetyMargin)).toFixed(0)); |
|
|
|
}; |
|
|
|
|
|
|
|
TxProposalv3.prototype.estimateFee = function() { |
|
|
|
TxProposal.prototype.estimateFee = function() { |
|
|
|
var fee = this.feePerKb * this.getEstimatedSize() / 1000; |
|
|
|
this.fee = parseInt(fee.toFixed(0)); |
|
|
|
}; |
|
|
@ -266,7 +266,7 @@ TxProposalv3.prototype.estimateFee = function() { |
|
|
|
* |
|
|
|
* @return {Number} total amount of all outputs excluding change output |
|
|
|
*/ |
|
|
|
TxProposalv3.prototype.getTotalAmount = function() { |
|
|
|
TxProposal.prototype.getTotalAmount = function() { |
|
|
|
return _.sum(this.outputs, 'amount'); |
|
|
|
}; |
|
|
|
|
|
|
@ -275,7 +275,7 @@ TxProposalv3.prototype.getTotalAmount = function() { |
|
|
|
* |
|
|
|
* @return {String[]} copayerIds that performed actions in this proposal (accept / reject) |
|
|
|
*/ |
|
|
|
TxProposalv3.prototype.getActors = function() { |
|
|
|
TxProposal.prototype.getActors = function() { |
|
|
|
return _.pluck(this.actions, 'copayerId'); |
|
|
|
}; |
|
|
|
|
|
|
@ -285,7 +285,7 @@ TxProposalv3.prototype.getActors = function() { |
|
|
|
* |
|
|
|
* @return {String[]} copayerIds that approved the tx proposal (accept) |
|
|
|
*/ |
|
|
|
TxProposalv3.prototype.getApprovers = function() { |
|
|
|
TxProposal.prototype.getApprovers = function() { |
|
|
|
return _.pluck( |
|
|
|
_.filter(this.actions, { |
|
|
|
type: 'accept' |
|
|
@ -298,13 +298,13 @@ TxProposalv3.prototype.getApprovers = function() { |
|
|
|
* @param {String} copayerId |
|
|
|
* @return {Object} type / createdOn |
|
|
|
*/ |
|
|
|
TxProposalv3.prototype.getActionBy = function(copayerId) { |
|
|
|
TxProposal.prototype.getActionBy = function(copayerId) { |
|
|
|
return _.find(this.actions, { |
|
|
|
copayerId: copayerId |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
TxProposalv3.prototype.addAction = function(copayerId, type, comment, signatures, xpub) { |
|
|
|
TxProposal.prototype.addAction = function(copayerId, type, comment, signatures, xpub) { |
|
|
|
var action = TxProposalAction.create({ |
|
|
|
copayerId: copayerId, |
|
|
|
type: type, |
|
|
@ -316,7 +316,7 @@ TxProposalv3.prototype.addAction = function(copayerId, type, comment, signatures |
|
|
|
this._updateStatus(); |
|
|
|
}; |
|
|
|
|
|
|
|
TxProposalv3.prototype._addSignaturesToBitcoreTx = function(tx, signatures, xpub) { |
|
|
|
TxProposal.prototype._addSignaturesToBitcoreTx = function(tx, signatures, xpub) { |
|
|
|
var self = this; |
|
|
|
|
|
|
|
if (signatures.length != this.inputs.length) |
|
|
@ -346,7 +346,7 @@ TxProposalv3.prototype._addSignaturesToBitcoreTx = function(tx, signatures, xpub |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
TxProposalv3.prototype.sign = function(copayerId, signatures, xpub) { |
|
|
|
TxProposal.prototype.sign = function(copayerId, signatures, xpub) { |
|
|
|
try { |
|
|
|
// Tests signatures are OK
|
|
|
|
var tx = this.getBitcoreTx(); |
|
|
@ -366,36 +366,36 @@ TxProposalv3.prototype.sign = function(copayerId, signatures, xpub) { |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
TxProposalv3.prototype.reject = function(copayerId, reason) { |
|
|
|
TxProposal.prototype.reject = function(copayerId, reason) { |
|
|
|
this.addAction(copayerId, 'reject', reason); |
|
|
|
}; |
|
|
|
|
|
|
|
TxProposalv3.prototype.isTemporary = function() { |
|
|
|
TxProposal.prototype.isTemporary = function() { |
|
|
|
return this.status == 'temporary'; |
|
|
|
}; |
|
|
|
|
|
|
|
TxProposalv3.prototype.isPending = function() { |
|
|
|
TxProposal.prototype.isPending = function() { |
|
|
|
return !_.contains(['broadcasted', 'rejected'], this.status); |
|
|
|
}; |
|
|
|
|
|
|
|
TxProposalv3.prototype.isAccepted = function() { |
|
|
|
TxProposal.prototype.isAccepted = function() { |
|
|
|
var votes = _.countBy(this.actions, 'type'); |
|
|
|
return votes['accept'] >= this.requiredSignatures; |
|
|
|
}; |
|
|
|
|
|
|
|
TxProposalv3.prototype.isRejected = function() { |
|
|
|
TxProposal.prototype.isRejected = function() { |
|
|
|
var votes = _.countBy(this.actions, 'type'); |
|
|
|
return votes['reject'] >= this.requiredRejections; |
|
|
|
}; |
|
|
|
|
|
|
|
TxProposalv3.prototype.isBroadcasted = function() { |
|
|
|
TxProposal.prototype.isBroadcasted = function() { |
|
|
|
return this.status == 'broadcasted'; |
|
|
|
}; |
|
|
|
|
|
|
|
TxProposalv3.prototype.setBroadcasted = function() { |
|
|
|
TxProposal.prototype.setBroadcasted = function() { |
|
|
|
$.checkState(this.txid); |
|
|
|
this.status = 'broadcasted'; |
|
|
|
this.broadcastedOn = Math.floor(Date.now() / 1000); |
|
|
|
}; |
|
|
|
|
|
|
|
module.exports = TxProposalv3; |
|
|
|
module.exports = TxProposal; |