diff --git a/lib/model/txproposal.js b/lib/model/txproposal.js index 8f4378d..112a48a 100644 --- a/lib/model/txproposal.js +++ b/lib/model/txproposal.js @@ -13,26 +13,36 @@ function TxProposal() { }; TxProposal.TYPE_SIMPLE = 'simple'; -TxProposal.TYPE_MULTIPLEOUTPUTS = 'multiple-outputs'; +TxProposal.TYPE_MULTIPLEOUTPUTS = 'multiple_outputs'; -TxProposal.prototype._createSimple = function(opts) { - this.toAddress = opts.toAddress; - this.amount = opts.amount; - this.outputOrder = _.shuffle(_.range(2)); - this.network = Bitcore.Address(this.toAddress).toObject().network; +TxProposal.prototype._isTypeSupported = function() { + var supported = [TxProposal.TYPE_SIMPLE, TxProposal.TYPE_MULTIPLEOUTPUTS]; + return !this.type || supported.indexOf(this.type) > -1; }; -TxProposal.prototype._createMultipleOutputs = function(opts) { - this.outputs = opts.outputs; - this.outputOrder = _.shuffle(_.range(this.outputs.length + 1)); - this.network = Bitcore.Address(this.outputs[0].toAddress).toObject().network; +TxProposal._create = {}; + +TxProposal._create.simple = function(txp, opts) { + txp.toAddress = opts.toAddress; + txp.amount = opts.amount; + txp.outputOrder = _.shuffle(_.range(2)); + txp.network = Bitcore.Address(txp.toAddress).toObject().network; +}; + +TxProposal._create.multiple_outputs = function(txp, opts) { + txp.outputs = opts.outputs; + txp.outputOrder = _.shuffle(_.range(txp.outputs.length + 1)); + txp.network = Bitcore.Address(txp.outputs[0].toAddress).toObject().network; }; TxProposal.create = function(opts) { opts = opts || {}; var x = new TxProposal(); - x.type = opts.type; + x.type = opts.type || TxProposal.TYPE_SIMPLE; + if (!x._isTypeSupported()) { + throw new Error('Unsupported transaction proposal type'); + }; var now = Date.now(); x.createdOn = Math.floor(now / 1000); @@ -52,11 +62,7 @@ TxProposal.create = function(opts) { x.fee = null; x.feePerKb = opts.feePerKb; - if (x.type == TxProposal.TYPE_MULTIPLEOUTPUTS) { - x._createMultipleOutputs(opts); - } else { - x._createSimple(opts); - } + TxProposal._create[x.type](x, opts); return x; }; @@ -64,8 +70,12 @@ TxProposal.create = function(opts) { TxProposal.fromObj = function(obj) { var x = new TxProposal(); + x.type = obj.type || TxProposal.TYPE_SIMPLE; + if (!x._isTypeSupported()) { + throw new Error('Unsupported transaction proposal type'); + }; + x.version = obj.version; - x.type = obj.type; x.createdOn = obj.createdOn; x.id = obj.id; x.walletId = obj.walletId; diff --git a/test/models/txproposal.js b/test/models/txproposal.js index 6180486..77d213d 100644 --- a/test/models/txproposal.js +++ b/test/models/txproposal.js @@ -6,19 +6,53 @@ var sinon = require('sinon'); var should = chai.should(); var TXP = require('../../lib/model/txproposal'); var Bitcore = require('bitcore-wallet-utils').Bitcore; -var multiple_outputs = TXP.TYPE_MULTIPLEOUTPUTS; describe('TXProposal', function() { - describe('#fromObj', function() { + describe('#create', function() { it('should create a TXP', function() { - var txp = TXP.fromObj(aTXP()); + var txp = TXP.create(aTxpOpts()); should.exist(txp); + should.exist(txp.toAddress); + should.not.exist(txp.outputs); }); it('should create a multiple-outputs TXP', function() { - var txp = TXP.fromObj(aTXP(multiple_outputs)); + var txp = TXP.create(aTxpOpts(TXP.TYPE_MULTIPLEOUTPUTS)); + should.exist(txp); + should.not.exist(txp.toAddress); + should.exist(txp.outputs); + }); + it('should fail to create a TXP of unknown type', function() { + var txp; + try { + txp = TXP.create(aTxpOpts('bogus')); + } catch(e) { + should.exist(e); + } + should.not.exist(txp); + }); + }); + + describe('#fromObj', function() { + it('should copy a TXP', function() { + var txp = TXP.fromObj(aTXP()); + should.exist(txp); + txp.toAddress.should.equal(aTXP().toAddress); + }); + it('should copy a multiple-outputs TXP', function() { + var txp = TXP.fromObj(aTXP(TXP.TYPE_MULTIPLEOUTPUTS)); should.exist(txp); + txp.outputs.should.deep.equal(aTXP(TXP.TYPE_MULTIPLEOUTPUTS).outputs); + }); + it('should fail to copy a TXP of unknown type', function() { + var txp; + try { + txp = TXP.fromObj(aTxpOpts('bogus')); + } catch(e) { + should.exist(e); + } + should.not.exist(txp); }); }); @@ -40,7 +74,7 @@ describe('TXProposal', function() { t.getChangeOutput().should.deep.equal(t.outputs[0]); }); it('should create a bitcore TX with multiple outputs', function() { - var txp = TXP.fromObj(aTXP(multiple_outputs)); + var txp = TXP.fromObj(aTXP(TXP.TYPE_MULTIPLEOUTPUTS)); txp.outputOrder = [0, 1, 2]; var t = txp.getBitcoreTx(); t.getChangeOutput().should.deep.equal(t.outputs[2]); @@ -98,9 +132,36 @@ var theXPriv = 'xprv9s21ZrQH143K2rMHbXTJmWTuFx6ssqn1vyRoZqPkCXYchBSkp5ey8kMJe84s var theXPub = 'xpub661MyMwAqRbcFLRkhYzK8eQdoywNHJVsJCMQNDoMks5bZymuMcyDgYfnVQYq2Q9npnVmdTAthYGc3N3uxm5sEdnTpSqBc4YYTAhNnoSxCm9'; var theSignatures = ['3045022100896aeb8db75fec22fddb5facf791927a996eb3aee23ee6deaa15471ea46047de02204c0c33f42a9d3ff93d62738712a8c8a5ecd21b45393fdd144e7b01b5a186f1f9']; +var aTxpOpts = function(type) { + var opts = { + type: type, + toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', + amount: 50000000, + message: 'some message' + }; + if (type == TXP.TYPE_MULTIPLEOUTPUTS) { + opts.outputs = [ + { + toAddress: "18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7", + amount: 10000000, + message: "first message" + }, + { + toAddress: "18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7", + amount: 20000000, + message: "second message" + }, + ]; + delete opts.toAddress; + delete opts.amount; + } + return opts; +}; + var aTXP = function(type) { var txp = { "version": "1.0.0", + "type": type, "createdOn": 1423146231, "id": "75c34f49-1ed6-255f-e9fd-0c71ae75ed1e", "walletId": "1", @@ -136,8 +197,7 @@ var aTXP = function(type) { "actions": [], "outputOrder": [0, 1], }; - if (type == 'multiple-outputs') { - txp.type = type; + if (type == TXP.TYPE_MULTIPLEOUTPUTS) { txp.outputs = [ { toAddress: "18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7",