Ivan Socolsky
9 years ago
11 changed files with 722 additions and 1827 deletions
@ -1,441 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
var _ = require('lodash'); |
|||
var $ = require('preconditions').singleton(); |
|||
var Uuid = require('uuid'); |
|||
var log = require('npmlog'); |
|||
log.debug = log.verbose; |
|||
log.disableColor(); |
|||
|
|||
var Bitcore = require('bitcore-lib'); |
|||
|
|||
var Common = require('../common'); |
|||
var Constants = Common.Constants; |
|||
var Defaults = Common.Defaults; |
|||
|
|||
var TxProposalAction = require('./txproposalaction'); |
|||
|
|||
function TxProposal() {}; |
|||
|
|||
TxProposal.Types = { |
|||
SIMPLE: 'simple', |
|||
MULTIPLEOUTPUTS: 'multiple_outputs', |
|||
EXTERNAL: 'external' |
|||
}; |
|||
|
|||
TxProposal.isTypeSupported = function(type) { |
|||
return _.contains(_.values(TxProposal.Types), type); |
|||
}; |
|||
|
|||
TxProposal._create = {}; |
|||
|
|||
TxProposal._create.simple = function(txp, opts) { |
|||
txp.toAddress = opts.toAddress; |
|||
txp.amount = opts.amount; |
|||
txp.outputOrder = _.shuffle(_.range(2)); |
|||
try { |
|||
txp.network = Bitcore.Address(txp.toAddress).toObject().network; |
|||
} catch (ex) {} |
|||
}; |
|||
|
|||
TxProposal._create.undefined = TxProposal._create.simple; |
|||
|
|||
TxProposal._create.multiple_outputs = function(txp, opts) { |
|||
txp.outputs = _.map(opts.outputs, function(output) { |
|||
return _.pick(output, ['amount', 'toAddress', 'message']); |
|||
}); |
|||
txp.outputOrder = _.shuffle(_.range(txp.outputs.length + 1)); |
|||
txp.amount = txp.getTotalAmount(); |
|||
try { |
|||
txp.network = Bitcore.Address(txp.outputs[0].toAddress).toObject().network; |
|||
} catch (ex) {} |
|||
}; |
|||
|
|||
TxProposal._create.external = function(txp, opts) { |
|||
txp.setInputs(opts.inputs || []); |
|||
txp.outputs = opts.outputs; |
|||
txp.outputOrder = _.range(txp.outputs.length + 1); |
|||
txp.amount = txp.getTotalAmount(); |
|||
try { |
|||
txp.network = Bitcore.Address(txp.outputs[0].toAddress).toObject().network; |
|||
} catch (ex) {} |
|||
}; |
|||
|
|||
TxProposal.create = function(opts) { |
|||
opts = opts || {}; |
|||
|
|||
var x = new TxProposal(); |
|||
|
|||
x.version = '2.0.0'; |
|||
x.type = opts.type || TxProposal.Types.SIMPLE; |
|||
|
|||
var now = Date.now(); |
|||
x.createdOn = Math.floor(now / 1000); |
|||
x.id = _.padLeft(now, 14, '0') + Uuid.v4(); |
|||
x.walletId = opts.walletId; |
|||
x.creatorId = opts.creatorId; |
|||
x.message = opts.message; |
|||
x.payProUrl = opts.payProUrl; |
|||
x.proposalSignature = opts.proposalSignature; |
|||
x.changeAddress = opts.changeAddress; |
|||
x.inputs = []; |
|||
x.inputPaths = []; |
|||
x.requiredSignatures = opts.requiredSignatures; |
|||
x.requiredRejections = opts.requiredRejections; |
|||
x.walletN = opts.walletN; |
|||
x.status = 'pending'; |
|||
x.actions = []; |
|||
x.fee = null; |
|||
x.feePerKb = opts.feePerKb; |
|||
x.excludeUnconfirmedUtxos = opts.excludeUnconfirmedUtxos; |
|||
x.proposalSignaturePubKey = opts.proposalSignaturePubKey; |
|||
x.proposalSignaturePubKeySig = opts.proposalSignaturePubKeySig; |
|||
x.addressType = opts.addressType || Constants.SCRIPT_TYPES.P2SH; |
|||
x.derivationStrategy = opts.derivationStrategy || Constants.DERIVATION_STRATEGIES.BIP45; |
|||
x.customData = opts.customData; |
|||
|
|||
if (_.isFunction(TxProposal._create[x.type])) { |
|||
TxProposal._create[x.type](x, opts); |
|||
} |
|||
|
|||
return x; |
|||
}; |
|||
|
|||
TxProposal.fromObj = function(obj) { |
|||
var x = new TxProposal(); |
|||
|
|||
x.version = obj.version; |
|||
if (obj.version === '1.0.0') { |
|||
x.type = TxProposal.Types.SIMPLE; |
|||
} else { |
|||
x.type = obj.type; |
|||
} |
|||
x.createdOn = obj.createdOn; |
|||
x.id = obj.id; |
|||
x.walletId = obj.walletId; |
|||
x.creatorId = obj.creatorId; |
|||
x.outputs = obj.outputs; |
|||
x.toAddress = obj.toAddress; |
|||
x.amount = obj.amount; |
|||
x.message = obj.message; |
|||
x.payProUrl = obj.payProUrl; |
|||
x.proposalSignature = obj.proposalSignature; |
|||
x.changeAddress = obj.changeAddress; |
|||
x.inputs = obj.inputs; |
|||
x.requiredSignatures = obj.requiredSignatures; |
|||
x.requiredRejections = obj.requiredRejections; |
|||
x.walletN = obj.walletN; |
|||
x.status = obj.status; |
|||
x.txid = obj.txid; |
|||
x.broadcastedOn = obj.broadcastedOn; |
|||
x.inputPaths = obj.inputPaths; |
|||
x.actions = _.map(obj.actions, function(action) { |
|||
return TxProposalAction.fromObj(action); |
|||
}); |
|||
x.outputOrder = obj.outputOrder; |
|||
x.network = obj.network; |
|||
x.fee = obj.fee; |
|||
x.feePerKb = obj.feePerKb; |
|||
x.excludeUnconfirmedUtxos = obj.excludeUnconfirmedUtxos; |
|||
x.proposalSignaturePubKey = obj.proposalSignaturePubKey; |
|||
x.proposalSignaturePubKeySig = obj.proposalSignaturePubKeySig; |
|||
x.addressType = obj.addressType || Constants.SCRIPT_TYPES.P2SH; |
|||
x.derivationStrategy = obj.derivationStrategy || Constants.DERIVATION_STRATEGIES.BIP45; |
|||
x.customData = obj.customData; |
|||
|
|||
return x; |
|||
}; |
|||
|
|||
TxProposal.prototype.toObject = function() { |
|||
var x = _.cloneDeep(this); |
|||
x.isPending = this.isPending(); |
|||
return x; |
|||
}; |
|||
|
|||
TxProposal.prototype.setInputs = function(inputs) { |
|||
this.inputs = inputs; |
|||
this.inputPaths = _.pluck(inputs, 'path'); |
|||
}; |
|||
|
|||
TxProposal.prototype._updateStatus = function() { |
|||
if (this.status != 'pending') return; |
|||
|
|||
if (this.isRejected()) { |
|||
this.status = 'rejected'; |
|||
} else if (this.isAccepted()) { |
|||
this.status = 'accepted'; |
|||
} |
|||
}; |
|||
|
|||
TxProposal.prototype._buildTx = function() { |
|||
var self = this; |
|||
|
|||
var t = new Bitcore.Transaction(); |
|||
|
|||
$.checkState(_.contains(_.values(Constants.SCRIPT_TYPES), self.addressType)); |
|||
|
|||
switch (self.addressType) { |
|||
case Constants.SCRIPT_TYPES.P2SH: |
|||
_.each(self.inputs, function(i) { |
|||
t.from(i, i.publicKeys, self.requiredSignatures); |
|||
}); |
|||
break; |
|||
case Constants.SCRIPT_TYPES.P2PKH: |
|||
t.from(self.inputs); |
|||
break; |
|||
} |
|||
|
|||
if (self.toAddress && self.amount && !self.outputs) { |
|||
t.to(self.toAddress, self.amount); |
|||
} else if (self.outputs) { |
|||
_.each(self.outputs, function(o) { |
|||
$.checkState(o.script || o.toAddress, 'Output should have either toAddress or script specified'); |
|||
if (o.script) { |
|||
t.addOutput(new Bitcore.Transaction.Output({ |
|||
script: o.script, |
|||
satoshis: o.amount |
|||
})); |
|||
} else { |
|||
t.to(o.toAddress, o.amount); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
if (_.startsWith(self.version, '1.')) { |
|||
Bitcore.Transaction.FEE_SECURITY_MARGIN = 1; |
|||
t.feePerKb(self.feePerKb); |
|||
} else { |
|||
t.fee(self.fee); |
|||
} |
|||
|
|||
t.change(self.changeAddress.address); |
|||
|
|||
// Shuffle outputs for improved privacy
|
|||
if (t.outputs.length > 1) { |
|||
var outputOrder = _.reject(self.outputOrder, function(order) { |
|||
return order >= t.outputs.length; |
|||
}); |
|||
$.checkState(t.outputs.length == outputOrder.length); |
|||
t.sortOutputs(function(outputs) { |
|||
return _.map(outputOrder, function(i) { |
|||
return outputs[i]; |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
// Validate inputs vs outputs independently of Bitcore
|
|||
var totalInputs = _.reduce(self.inputs, function(memo, i) { |
|||
return +i.satoshis + memo; |
|||
}, 0); |
|||
var totalOutputs = _.reduce(t.outputs, function(memo, o) { |
|||
return +o.satoshis + memo; |
|||
}, 0); |
|||
|
|||
$.checkState(totalInputs - totalOutputs <= Defaults.MAX_TX_FEE); |
|||
|
|||
return t; |
|||
}; |
|||
|
|||
|
|||
TxProposal.prototype._getCurrentSignatures = function() { |
|||
var acceptedActions = _.filter(this.actions, { |
|||
type: 'accept' |
|||
}); |
|||
|
|||
return _.map(acceptedActions, function(x) { |
|||
return { |
|||
signatures: x.signatures, |
|||
xpub: x.xpub, |
|||
}; |
|||
}); |
|||
}; |
|||
|
|||
TxProposal.prototype.getBitcoreTx = function() { |
|||
var self = this; |
|||
|
|||
var t = this._buildTx(); |
|||
|
|||
var sigs = this._getCurrentSignatures(); |
|||
_.each(sigs, function(x) { |
|||
self._addSignaturesToBitcoreTx(t, x.signatures, x.xpub); |
|||
}); |
|||
|
|||
return t; |
|||
}; |
|||
|
|||
TxProposal.prototype.getNetworkName = function() { |
|||
return Bitcore.Address(this.changeAddress.address).toObject().network; |
|||
}; |
|||
|
|||
TxProposal.prototype.getRawTx = function() { |
|||
var t = this.getBitcoreTx(); |
|||
|
|||
return t.uncheckedSerialize(); |
|||
}; |
|||
|
|||
TxProposal.prototype.getEstimatedSizeForSingleInput = function() { |
|||
return this.requiredSignatures * 72 + this.walletN * 36 + 44; |
|||
}; |
|||
|
|||
TxProposal.prototype.getEstimatedSize = function() { |
|||
// Note: found empirically based on all multisig P2SH inputs and within m & n allowed limits.
|
|||
var safetyMargin = 0.05; |
|||
|
|||
var overhead = 4 + 4 + 9 + 9; |
|||
var inputSize = this.getEstimatedSizeForSingleInput(); |
|||
var outputSize = 34; |
|||
var nbInputs = this.inputs.length; |
|||
var nbOutputs = (_.isArray(this.outputs) ? Math.max(1, this.outputs.length) : 1) + 1; |
|||
|
|||
var size = overhead + inputSize * nbInputs + outputSize * nbOutputs; |
|||
|
|||
return parseInt((size * (1 + safetyMargin)).toFixed(0)); |
|||
}; |
|||
|
|||
TxProposal.prototype.estimateFee = function() { |
|||
var fee = this.feePerKb * this.getEstimatedSize() / 1000; |
|||
|
|||
this.fee = parseInt(fee.toFixed(0)); |
|||
}; |
|||
|
|||
/** |
|||
* getTotalAmount |
|||
* |
|||
* @return {Number} total amount of all outputs excluding change output |
|||
*/ |
|||
TxProposal.prototype.getTotalAmount = function() { |
|||
if (this.type == TxProposal.Types.MULTIPLEOUTPUTS || this.type == TxProposal.Types.EXTERNAL) { |
|||
return _.pluck(this.outputs, 'amount') |
|||
.reduce(function(total, n) { |
|||
return total + n; |
|||
}, 0); |
|||
} else { |
|||
return this.amount; |
|||
} |
|||
}; |
|||
|
|||
/** |
|||
* getActors |
|||
* |
|||
* @return {String[]} copayerIds that performed actions in this proposal (accept / reject) |
|||
*/ |
|||
TxProposal.prototype.getActors = function() { |
|||
return _.pluck(this.actions, 'copayerId'); |
|||
}; |
|||
|
|||
|
|||
/** |
|||
* getApprovers |
|||
* |
|||
* @return {String[]} copayerIds that approved the tx proposal (accept) |
|||
*/ |
|||
TxProposal.prototype.getApprovers = function() { |
|||
return _.pluck( |
|||
_.filter(this.actions, { |
|||
type: 'accept' |
|||
}), 'copayerId'); |
|||
}; |
|||
|
|||
/** |
|||
* getActionBy |
|||
* |
|||
* @param {String} copayerId |
|||
* @return {Object} type / createdOn |
|||
*/ |
|||
TxProposal.prototype.getActionBy = function(copayerId) { |
|||
return _.find(this.actions, { |
|||
copayerId: copayerId |
|||
}); |
|||
}; |
|||
|
|||
TxProposal.prototype.addAction = function(copayerId, type, comment, signatures, xpub) { |
|||
var action = TxProposalAction.create({ |
|||
copayerId: copayerId, |
|||
type: type, |
|||
signatures: signatures, |
|||
xpub: xpub, |
|||
comment: comment, |
|||
}); |
|||
this.actions.push(action); |
|||
this._updateStatus(); |
|||
}; |
|||
|
|||
TxProposal.prototype._addSignaturesToBitcoreTx = function(tx, signatures, xpub) { |
|||
var self = this; |
|||
|
|||
if (signatures.length != this.inputs.length) |
|||
throw new Error('Number of signatures does not match number of inputs'); |
|||
|
|||
var 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, |
|||
}; |
|||
tx.inputs[i].addSignature(tx, s); |
|||
i++; |
|||
} catch (e) {}; |
|||
}); |
|||
|
|||
if (i != tx.inputs.length) |
|||
throw new Error('Wrong signatures'); |
|||
}; |
|||
|
|||
|
|||
TxProposal.prototype.sign = function(copayerId, signatures, xpub) { |
|||
try { |
|||
// Tests signatures are OK
|
|||
var tx = this.getBitcoreTx(); |
|||
this._addSignaturesToBitcoreTx(tx, signatures, xpub); |
|||
|
|||
this.addAction(copayerId, 'accept', null, signatures, xpub); |
|||
|
|||
if (this.status == 'accepted') { |
|||
this.raw = tx.uncheckedSerialize(); |
|||
this.txid = tx.id; |
|||
} |
|||
|
|||
return true; |
|||
} catch (e) { |
|||
log.debug(e); |
|||
return false; |
|||
} |
|||
}; |
|||
|
|||
TxProposal.prototype.reject = function(copayerId, reason) { |
|||
this.addAction(copayerId, 'reject', reason); |
|||
}; |
|||
|
|||
TxProposal.prototype.isPending = function() { |
|||
return !_.contains(['broadcasted', 'rejected'], this.status); |
|||
}; |
|||
|
|||
TxProposal.prototype.isAccepted = function() { |
|||
var votes = _.countBy(this.actions, 'type'); |
|||
return votes['accept'] >= this.requiredSignatures; |
|||
}; |
|||
|
|||
TxProposal.prototype.isRejected = function() { |
|||
var votes = _.countBy(this.actions, 'type'); |
|||
return votes['reject'] >= this.requiredRejections; |
|||
}; |
|||
|
|||
TxProposal.prototype.isBroadcasted = function() { |
|||
return this.status == 'broadcasted'; |
|||
}; |
|||
|
|||
TxProposal.prototype.setBroadcasted = function() { |
|||
$.checkState(this.txid); |
|||
this.status = 'broadcasted'; |
|||
this.broadcastedOn = Math.floor(Date.now() / 1000); |
|||
}; |
|||
|
|||
module.exports = TxProposal; |
File diff suppressed because it is too large
@ -1,237 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
var _ = require('lodash'); |
|||
var chai = require('chai'); |
|||
var sinon = require('sinon'); |
|||
var should = chai.should(); |
|||
var TxProposal = require('../../lib/model/txproposal_legacy'); |
|||
var Bitcore = require('bitcore-lib'); |
|||
|
|||
describe('TXProposal legacy', function() { |
|||
|
|||
describe('#create', function() { |
|||
it('should create a TxProposal', function() { |
|||
var txp = TxProposal.create(aTxpOpts()); |
|||
should.exist(txp); |
|||
should.exist(txp.toAddress); |
|||
should.not.exist(txp.outputs); |
|||
}); |
|||
it('should create a multiple-outputs TxProposal', function() { |
|||
var txp = TxProposal.create(aTxpOpts(TxProposal.Types.MULTIPLEOUTPUTS)); |
|||
should.exist(txp); |
|||
should.not.exist(txp.toAddress); |
|||
should.exist(txp.outputs); |
|||
}); |
|||
it('should create an external TxProposal', function() { |
|||
var txp = TxProposal.create(aTxpOpts(TxProposal.Types.EXTERNAL)); |
|||
should.exist(txp); |
|||
should.not.exist(txp.toAddress); |
|||
should.exist(txp.outputs); |
|||
should.exist(txp.inputs); |
|||
}); |
|||
}); |
|||
|
|||
describe('#fromObj', function() { |
|||
it('should copy a TxProposal', function() { |
|||
var txp = TxProposal.fromObj(aTXP()); |
|||
should.exist(txp); |
|||
txp.toAddress.should.equal(aTXP().toAddress); |
|||
}); |
|||
it('should copy a multiple-outputs TxProposal', function() { |
|||
var txp = TxProposal.fromObj(aTXP(TxProposal.Types.MULTIPLEOUTPUTS)); |
|||
should.exist(txp); |
|||
txp.outputs.should.deep.equal(aTXP(TxProposal.Types.MULTIPLEOUTPUTS).outputs); |
|||
}); |
|||
}); |
|||
|
|||
describe('#getBitcoreTx', function() { |
|||
it('should create a valid bitcore TX', function() { |
|||
var txp = TxProposal.fromObj(aTXP()); |
|||
var t = txp.getBitcoreTx(); |
|||
should.exist(t); |
|||
}); |
|||
it('should order outputs as specified by outputOrder', function() { |
|||
var txp = TxProposal.fromObj(aTXP()); |
|||
|
|||
txp.outputOrder = [0, 1]; |
|||
var t = txp.getBitcoreTx(); |
|||
t.getChangeOutput().should.deep.equal(t.outputs[1]); |
|||
|
|||
txp.outputOrder = [1, 0]; |
|||
var t = txp.getBitcoreTx(); |
|||
t.getChangeOutput().should.deep.equal(t.outputs[0]); |
|||
}); |
|||
it('should create a bitcore TX with multiple outputs', function() { |
|||
var txp = TxProposal.fromObj(aTXP(TxProposal.Types.MULTIPLEOUTPUTS)); |
|||
txp.outputOrder = [0, 1, 2]; |
|||
var t = txp.getBitcoreTx(); |
|||
t.getChangeOutput().should.deep.equal(t.outputs[2]); |
|||
}); |
|||
}); |
|||
|
|||
describe('#getTotalAmount', function() { |
|||
it('should be compatible with simple proposal legacy amount', function() { |
|||
var x = TxProposal.fromObj(aTXP()); |
|||
var total = x.getTotalAmount(); |
|||
total.should.equal(x.amount); |
|||
}); |
|||
it('should handle multiple-outputs', function() { |
|||
var x = TxProposal.fromObj(aTXP(TxProposal.Types.MULTIPLEOUTPUTS)); |
|||
var totalOutput = 0; |
|||
_.each(x.outputs, function(o) { |
|||
totalOutput += o.amount |
|||
}); |
|||
x.getTotalAmount().should.equal(totalOutput); |
|||
}); |
|||
it('should handle external', function() { |
|||
var x = TxProposal.fromObj(aTXP(TxProposal.Types.EXTERNAL)); |
|||
var totalOutput = 0; |
|||
_.each(x.outputs, function(o) { |
|||
totalOutput += o.amount |
|||
}); |
|||
x.getTotalAmount().should.equal(totalOutput); |
|||
}); |
|||
|
|||
}); |
|||
|
|||
describe('#sign', function() { |
|||
it('should sign 2-2', function() { |
|||
var txp = TxProposal.fromObj(aTXP()); |
|||
txp.sign('1', theSignatures, theXPub); |
|||
txp.isAccepted().should.equal(false); |
|||
txp.isRejected().should.equal(false); |
|||
txp.sign('2', theSignatures, theXPub); |
|||
txp.isAccepted().should.equal(true); |
|||
txp.isRejected().should.equal(false); |
|||
}); |
|||
}); |
|||
|
|||
describe('#getRawTx', function() { |
|||
it('should generate correct raw transaction for signed 2-2', function() { |
|||
var txp = TxProposal.fromObj(aTXP()); |
|||
txp.sign('1', theSignatures, theXPub); |
|||
txp.getRawTx().should.equal('0100000001ab069f7073be9b491bb1ad4233a45d2e383082ccc7206df905662d6d8499e66e080000009200483045022100896aeb8db75fec22fddb5facf791927a996eb3aee23ee6deaa15471ea46047de02204c0c33f42a9d3ff93d62738712a8c8a5ecd21b45393fdd144e7b01b5a186f1f9014752210319008ffe1b3e208f5ebed8f46495c056763f87b07930a7027a92ee477fb0cb0f2103b5f035af8be40d0db5abb306b7754949ab39032cf99ad177691753b37d10130152aeffffffff0280f0fa02000000001976a91451224bca38efcaa31d5340917c3f3f713b8b20e488ac70c9fa020000000017a914778192003f0e9e1d865c082179cc3dae5464b03d8700000000'); |
|||
}); |
|||
}); |
|||
|
|||
|
|||
|
|||
describe('#reject', function() { |
|||
it('should reject 2-2', function() { |
|||
var txp = TxProposal.fromObj(aTXP()); |
|||
txp.reject('1'); |
|||
txp.isAccepted().should.equal(false); |
|||
txp.isRejected().should.equal(true); |
|||
}); |
|||
}); |
|||
|
|||
|
|||
describe('#reject & #sign', function() { |
|||
it('should finally reject', function() { |
|||
var txp = TxProposal.fromObj(aTXP()); |
|||
txp.sign('1', theSignatures); |
|||
txp.isAccepted().should.equal(false); |
|||
txp.isRejected().should.equal(false); |
|||
txp.reject('2'); |
|||
txp.isAccepted().should.equal(false); |
|||
txp.isRejected().should.equal(true); |
|||
}); |
|||
}); |
|||
|
|||
}); |
|||
|
|||
var theXPriv = 'xprv9s21ZrQH143K2rMHbXTJmWTuFx6ssqn1vyRoZqPkCXYchBSkp5ey8kMJe84sxfXq5uChWH4gk94rWbXZt2opN9kg4ufKGvUM7HQSLjnoh7e'; |
|||
var theXPub = 'xpub661MyMwAqRbcFLRkhYzK8eQdoywNHJVsJCMQNDoMks5bZymuMcyDgYfnVQYq2Q9npnVmdTAthYGc3N3uxm5sEdnTpSqBc4YYTAhNnoSxCm9'; |
|||
var theSignatures = ['3045022100896aeb8db75fec22fddb5facf791927a996eb3aee23ee6deaa15471ea46047de02204c0c33f42a9d3ff93d62738712a8c8a5ecd21b45393fdd144e7b01b5a186f1f9']; |
|||
|
|||
var aTxpOpts = function(type) { |
|||
var opts = { |
|||
type: type, |
|||
toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', |
|||
amount: 50000000, |
|||
message: 'some message' |
|||
}; |
|||
if (type == TxProposal.Types.MULTIPLEOUTPUTS || type == TxProposal.Types.EXTERNAL) { |
|||
opts.outputs = [{ |
|||
toAddress: "18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7", |
|||
amount: 10000000, |
|||
message: "first message" |
|||
}, { |
|||
toAddress: "18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7", |
|||
amount: 20000000, |
|||
message: "second message" |
|||
}, ]; |
|||
delete opts.toAddress; |
|||
delete opts.amount; |
|||
} |
|||
if (type == TxProposal.Types.EXTERNAL) { |
|||
opts.inputs = [{ |
|||
"txid": "6ee699846d2d6605f96d20c7cc8230382e5da43342adb11b499bbe73709f06ab", |
|||
"vout": 8, |
|||
"satoshis": 100000000, |
|||
"scriptPubKey": "a914a8a9648754fbda1b6c208ac9d4e252075447f36887", |
|||
"address": "3H4pNP6J4PW4NnvdrTg37VvZ7h2QWuAwtA", |
|||
"path": "m/2147483647/0/1", |
|||
"publicKeys": ["0319008ffe1b3e208f5ebed8f46495c056763f87b07930a7027a92ee477fb0cb0f", "03b5f035af8be40d0db5abb306b7754949ab39032cf99ad177691753b37d101301"] |
|||
}]; |
|||
} |
|||
return opts; |
|||
}; |
|||
|
|||
var aTXP = function(type) { |
|||
var txp = { |
|||
"version": '2.0.0', |
|||
"type": type, |
|||
"createdOn": 1423146231, |
|||
"id": "75c34f49-1ed6-255f-e9fd-0c71ae75ed1e", |
|||
"walletId": "1", |
|||
"creatorId": "1", |
|||
"toAddress": "18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7", |
|||
"network": "livenet", |
|||
"amount": 50000000, |
|||
"message": 'some message', |
|||
"proposalSignature": '7035022100896aeb8db75fec22fddb5facf791927a996eb3aee23ee6deaa15471ea46047de02204c0c33f42a9d3ff93d62738712a8c8a5ecd21b45393fdd144e7b01b5a186f1f9', |
|||
"changeAddress": { |
|||
"version": '1.0.0', |
|||
"createdOn": 1424372337, |
|||
"address": '3CauZ5JUFfmSAx2yANvCRoNXccZ3YSUjXH', |
|||
"path": 'm/2147483647/1/0', |
|||
"publicKeys": ['030562cb099e6043dc499eb359dd97c9d500a3586498e4bcf0228a178cc20e6f16', |
|||
'0367027d17dbdfc27b5e31f8ed70e14d47949f0fa392261e977db0851c8b0d6fac', |
|||
'0315ae1e8aa866794ae603389fb2b8549153ebf04e7cdf74501dadde5c75ddad11' |
|||
] |
|||
}, |
|||
"inputs": [{ |
|||
"txid": "6ee699846d2d6605f96d20c7cc8230382e5da43342adb11b499bbe73709f06ab", |
|||
"vout": 8, |
|||
"satoshis": 100000000, |
|||
"scriptPubKey": "a914a8a9648754fbda1b6c208ac9d4e252075447f36887", |
|||
"address": "3H4pNP6J4PW4NnvdrTg37VvZ7h2QWuAwtA", |
|||
"path": "m/2147483647/0/1", |
|||
"publicKeys": ["0319008ffe1b3e208f5ebed8f46495c056763f87b07930a7027a92ee477fb0cb0f", "03b5f035af8be40d0db5abb306b7754949ab39032cf99ad177691753b37d101301"] |
|||
}], |
|||
"inputPaths": ["m/2147483647/0/1"], |
|||
"requiredSignatures": 2, |
|||
"requiredRejections": 1, |
|||
"walletN": 2, |
|||
"status": "pending", |
|||
"actions": [], |
|||
"outputOrder": [0, 1], |
|||
"fee": 10000, |
|||
}; |
|||
if (type == TxProposal.Types.MULTIPLEOUTPUTS) { |
|||
txp.outputs = [{ |
|||
toAddress: "18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7", |
|||
amount: 10000000, |
|||
message: "first message" |
|||
}, { |
|||
toAddress: "18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7", |
|||
amount: 20000000, |
|||
message: "second message" |
|||
}, ]; |
|||
txp.outputOrder = [0, 1, 2]; |
|||
delete txp.toAddress; |
|||
delete txp.amount; |
|||
} |
|||
return txp; |
|||
}; |
Loading…
Reference in new issue