diff --git a/js/models/TxProposal.js b/js/models/TxProposal.js index 1bfbf6c23..bb36052c3 100644 --- a/js/models/TxProposal.js +++ b/js/models/TxProposal.js @@ -38,6 +38,17 @@ function TxProposal(opts) { this.readonly = opts.readonly || null; this.merchant = opts.merchant || null; this.paymentProtocolURL = opts.paymentProtocolURL || null; + + if (opts.creator) { + var now = Date.now(); + var me = {}; + me[opts.creator] = now; + + this.signedBy = this.seenBy = me; + this.creator = opts.creator; + this.createdTs = now; + } + this._sync(); } @@ -50,7 +61,7 @@ TxProposal.prototype._checkPayPro = function() { if (!this.merchant.outs || this.merchant.outs.length !== 1) throw new Error('PayPro: Unsopported number of outputs'); - if (this.merchant.expires < (this.getSent() || Date.now()/1000.) ) + if (this.merchant.expires < (this.getSent() || Date.now() / 1000.)) throw new Error('PayPro: Request expired'); if (!this.merchant.total || !this.merchant.outs[0].amountSatStr || !this.merchant.outs[0].address) diff --git a/js/models/Wallet.js b/js/models/Wallet.js index 1d382b068..5ce44a90a 100644 --- a/js/models/Wallet.js +++ b/js/models/Wallet.js @@ -1541,7 +1541,9 @@ Wallet.prototype.fetchPaymentRequest = function(options, cb) { var merchantData, err; try { merchantData = self.parsePaymentRequest(options, pr); - } catch (e) { err = e}; + } catch (e) { + err = e + }; log.debug('PayPro request data', merchantData); return cb(err, merchantData); @@ -1831,7 +1833,7 @@ Wallet.prototype.sendPaymentTx = function(ntxid, options, cb) { ack = paypro.makePaymentACK(data); return self.receivePaymentRequestACK(ntxid, tx, txp, ack, cb); }) - .error(function(data, status ) { + .error(function(data, status) { log.debug('Sending to server was not met with a returned tx.'); log.debug('XHR status: ' + status); self._processTxProposalSent(ntxid, function(err, txid) { @@ -2136,7 +2138,7 @@ Wallet.prototype.createTx = function(opts, cb) { var self = this; var toAddress = opts.toAddress; var amountSat = opts.amountSat; - preconditions.checkArgument(!opts.comment || opts.comment.length <= 100); + var comment = opts.comment; var url = opts.url; if (url && !opts.merchantData) { @@ -2148,7 +2150,7 @@ Wallet.prototype.createTx = function(opts, cb) { if (err) return cb(err); opts.merchantData = merchantData; opts.amountSat = merchantData.outs[0].address; - opts.toAddress = merchantData.outs[0].amount; + opts.toAddress = merchantData.outs[0].amount; self.createTx(opts, cb); }); }; @@ -2158,19 +2160,21 @@ Wallet.prototype.createTx = function(opts, cb) { this.getUnspent(function(err, safeUnspent) { if (err) return cb(new Error('Could not get list of UTXOs')); - var ntxid; + var ntxid, txp; try { - var txp = self.createTxProposal(toAddress, amountSat, safeUnspent, opts.builderOpts); - - if (opts.merchantData) - txp.addMerchantData(opts.merchantData); - - var ntxid = self.addNewTxProposal(txp); - log.debug('TXP Added: ', ntxid); + txp = self.createTxProposal(toAddress, amountSat, comment, safeUnspent, opts.builderOpts); } catch (e) { + log.error(e); return cb(e); } + if (opts.merchantData) + txp.addMerchantData(opts.merchantData); + + var ntxid = self.txProposals.add(txp); + log.debug('TXP Added: ', ntxid); + + if (!ntxid) { return cb(new Error('Error creating the transaction')); } @@ -2194,10 +2198,11 @@ var sanitize = function(address) { * @desc Create a transaction proposal * @TODO: Document more */ -Wallet.prototype.createTxProposal = function(toAddress, amountSat, utxos, builderOpts) { +Wallet.prototype.createTxProposal = function(toAddress, amountSat, comment, utxos, builderOpts) { preconditions.checkArgument(toAddress); preconditions.checkArgument(amountSat); preconditions.checkArgument(_.isArray(utxos)); + preconditions.checkArgument(!comment || comment.length <= 100, 'Comment too long'); builderOpts = builderOpts || {}; var pkr = this.publicKeyRing; @@ -2257,32 +2262,11 @@ Wallet.prototype.createTxProposal = function(toAddress, amountSat, utxos, builde inputChainPaths: inputChainPaths, comment: comment, builder: b, + creator: this.getMyCopayerId(), }); }; -/* addNewTxProposal - * adds a transaction proposal to the list. Sets current copayer and creation metadata. - * - * @param {txp} Transaction Proposal Object - * @desc returns normalized transaction ID - * @param {ntxid} - */ -Wallet.prototype.addNewTxProposal = function(txp) { - var myId = this.getMyCopayerId(); - var now = Date.now(); - var me = {}; - me[myId] = now; - - // Add metadata to TxP - txp.signedBy = txp.seenBy = me; - txp.creator = myId; - txp.createdTs = now; - - var ntxid = this.txProposals.add(txp); - return ntxid; -}; - /** * @desc Updates all the indexes for the current publicKeyRing. This scans * the blockchain looking for transactions on derived addresses. diff --git a/test/Wallet.js b/test/Wallet.js index 63665173c..aa034fa20 100644 --- a/test/Wallet.js +++ b/test/Wallet.js @@ -222,7 +222,7 @@ describe('Wallet model', function() { unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(1, true); var f = function() { - var ntxid = w.createTxSync( + var ntxid = w.createTxProposal( '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', '123456789', null, @@ -233,18 +233,18 @@ describe('Wallet model', function() { }); + it('#create, check builder opts', function() { var w = cachedCreateW2(); unspentTest[0].address = w.publicKeyRing.getAddress(1, true, w.publicKey).toString(); unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(1, true, w.publicKey); - var ntxid = w.createTxSync( + var txp = w.createTxProposal( 'mgGJEugdPnvhmRuFdbdQcFfoFLc1XXeB79', '123456789', null, unspentTest ); - var t = w.txProposals; - var opts = JSON.parse(t.txps[ntxid].builder.vanilla.opts); + var opts = JSON.parse(txp.builder.vanilla.opts); opts.signhash.should.equal(1); (opts.lockTime === null).should.be.true; should.not.exist(opts.fee); @@ -257,15 +257,13 @@ describe('Wallet model', function() { unspentTest[0].address = w.publicKeyRing.getAddress(1, true, w.publicKey).toString(); unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(1, true, w.publicKey); - var ntxid = w.createTxSync( + var txp = w.createTxProposal( 'mgGJEugdPnvhmRuFdbdQcFfoFLc1XXeB79', '123456789', null, unspentTest ); - var t = w.txProposals; - var txp = t.txps[ntxid]; Object.keys(txp._inputSigners).length.should.equal(1); var tx = txp.builder.build(); should.exist(tx); @@ -282,15 +280,13 @@ describe('Wallet model', function() { unspentTest[0].address = w.publicKeyRing.getAddress(1, true, w.publicKey).toString(); unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(1, true, w.publicKey); - var ntxid = w.createTxSync( + var txp = w.createTxProposal( 'mgGJEugdPnvhmRuFdbdQcFfoFLc1XXeB79', '123456789', comment, unspentTest ); - var t = w.txProposals; - var txp = t.txps[ntxid]; var tx = txp.builder.build(); should.exist(tx); txp.comment.should.equal(comment); @@ -304,16 +300,14 @@ describe('Wallet model', function() { unspentTest[0].address = w.publicKeyRing.getAddress(1, true, w.publicKey).toString(); unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(1, true, w.publicKey); - var badCreate = function() { - w.createTxSync( + (function() { + w.createTxProposal( 'mgGJEugdPnvhmRuFdbdQcFfoFLc1XXeB79', '123456789', comment, unspentTest ); - } - - chai.expect(badCreate).to.throw(Error); + }).should.throw('Comment'); }); it('#addressIsOwn', function() { @@ -336,21 +330,19 @@ describe('Wallet model', function() { for (var index = 0; index < 3; index++) { unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange, w.publicKey).toString(); unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange, w.publicKey); - w.createTxSync( + var txp = w.createTxProposal( 'mgGJEugdPnvhmRuFdbdQcFfoFLc1XXeB79', '123456789', null, unspentTest ); - var t = w.txProposals; - var k = Object.keys(t.txps)[0]; - var tx = t.txps[k].builder.build(); + var tx = txp.builder.build(); should.exist(tx); tx.isComplete().should.equal(false); tx.countInputMissingSignatures(0).should.equal(2); - (t.txps[k].signedBy[w.privateKey.getId()] - ts > 0).should.equal(true); - (t.txps[k].seenBy[w.privateKey.getId()] - ts > 0).should.equal(true); + (txp.signedBy[w.privateKey.getId()] - ts > 0).should.equal(true); + (txp.seenBy[w.privateKey.getId()] - ts > 0).should.equal(true); } } }); @@ -774,7 +766,10 @@ describe('Wallet model', function() { var w = cachedCreateW2(); var utxo = createUTXO(w); w.blockchain.fixUnspent(utxo); - w.createTx(toAddress, amountSatStr, null, function(err, ntxid) { + w.createTx({ + toAddress: toAddress, + amountSat: amountSatStr, + }, function(err, ntxid) { ntxid.length.should.equal(64); done(); }); @@ -788,7 +783,10 @@ describe('Wallet model', function() { var w = createW2([k2]); var utxo = createUTXO(w); w.blockchain.fixUnspent(utxo); - w.createTx(toAddress, amountSatStr, null, function(err, ntxid) { + w.createTx({ + toAddress: toAddress, + amountSat: amountSatStr, + }, function(err, ntxid) { w.on('txProposalsUpdated', function() { w.getTxProposals()[0].signedByUs.should.equal(true); w.getTxProposals()[0].rejectedByUs.should.equal(false); @@ -802,7 +800,10 @@ describe('Wallet model', function() { var w = cachedCreateW2(); var utxo = createUTXO(w); w.blockchain.fixUnspent(utxo); - w.createTx(toAddress, amountSatStr, null, function(err, ntxid) { + w.createTx({ + toAddress: toAddress, + amountSat: amountSatStr, + }, function(err, ntxid) { (function() { w.reject(ntxid); }).should.throw('reject a signed'); @@ -814,7 +815,10 @@ describe('Wallet model', function() { var oldK = w.privateKey; var utxo = createUTXO(w); w.blockchain.fixUnspent(utxo); - w.createTx(toAddress, amountSatStr, null, function(err, ntxid) { + w.createTx({ + toAddress: toAddress, + amountSat: amountSatStr, + }, function(err, ntxid) { var s = sinon.stub(w, 'getMyCopayerId').returns('213'); Object.keys(w.txProposals.get(ntxid).rejectedBy).length.should.equal(0); w.reject(ntxid); @@ -828,7 +832,10 @@ describe('Wallet model', function() { var w = createW2(null, 1); var utxo = createUTXO(w); w.blockchain.fixUnspent(utxo); - w.createTx(toAddress, amountSatStr, null, function(err, ntxid) { + w.createTx({ + toAddress: toAddress, + amountSat: amountSatStr, + }, function(err, ntxid) { w.sendTx(ntxid, function(txid) { txid.length.should.equal(64); done(); @@ -839,7 +846,10 @@ describe('Wallet model', function() { var w = createW2(null, 1); var utxo = createUTXO(w); w.blockchain.fixUnspent(utxo); - w.createTx(toAddress, amountSatStr, null, function(err, ntxid) { + w.createTx({ + toAddress: toAddress, + amountSat: amountSatStr, + }, function(err, ntxid) { var txp = w.txProposals.get(ntxid); // Assign fake builder txp.builder = new Builder(); @@ -858,7 +868,10 @@ describe('Wallet model', function() { var w = createW2(null, 1); var utxo = createUTXO(w); w.blockchain.fixUnspent(utxo); - w.createTx(toAddress, amountSatStr, null, function(err, ntxid) { + w.createTx({ + toAddress: toAddress, + amountSat: amountSatStr, + }, function(err, ntxid) { sinon.stub(w.blockchain, 'broadcast').yields({ statusCode: 303 }); @@ -872,7 +885,10 @@ describe('Wallet model', function() { var w = cachedCreateW2(); var utxo = createUTXO(w); w.blockchain.fixUnspent(utxo); - w.createTx(toAddress, amountSatStr, null, function(err, ntxid) { + w.createTx({ + toAddress: toAddress, + amountSat: amountSatStr, + }, function(err, ntxid) { w.sendTxProposal.bind(w).should.throw('Illegal Argument.'); (function() { w.sendTxProposal(ntxid); @@ -885,7 +901,10 @@ describe('Wallet model', function() { var w = cachedCreateW2(); var utxo = createUTXO(w); w.blockchain.fixUnspent(utxo); - w.createTx(toAddress, amountSatStr, null, function(err, ntxid) { + w.createTx({ + toAddress: toAddress, + amountSat: amountSatStr, + }, function(err, ntxid) { w.sendAllTxProposals.bind(w).should.not.throw(); (function() { w.sendAllTxProposals(); @@ -900,7 +919,10 @@ describe('Wallet model', function() { var utxo = createUTXO(w); w.blockchain.fixUnspent(utxo); sinon.stub(w, 'getUnspent').yields('error', null); - w.createTx(toAddress, amountSatStr, null, function(err, ntxid) { + w.createTx({ + toAddress: toAddress, + amountSat: amountSatStr, + }, function(err, ntxid) { chai.expect(err.message).to.equal('Could not get list of UTXOs'); done(); }); @@ -2049,7 +2071,7 @@ describe('Wallet model', function() { }]; w.blockchain.getTransactions = sinon.stub().yields(null, { - items: txs.slice(2,3), + items: txs.slice(2, 3), totalItems: txs.length, }); w.getAddressesInfo = sinon.stub().returns([{