From 32f281fb821d19241485c92601faa35f38d91812 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Fri, 21 Nov 2014 11:06:47 -0300 Subject: [PATCH] add paypro checks --- js/models/TxProposal.js | 53 ++++++++- js/models/Wallet.js | 235 +++++++------------------------------- test/PayPro.js | 126 -------------------- test/TxProposal.js | 128 +++++++++++++++++---- test/mocks/FakeBuilder.js | 30 +++-- 5 files changed, 221 insertions(+), 351 deletions(-) diff --git a/js/models/TxProposal.js b/js/models/TxProposal.js index fba658556..9e6842818 100644 --- a/js/models/TxProposal.js +++ b/js/models/TxProposal.js @@ -41,6 +41,38 @@ function TxProposal(opts) { this._sync(); } +TxProposal.prototype._checkPayPro = function() { + if (!this.merchant) return; + +console.log('[TxProposal.js.46]', + this.paymentProtocolURL , this.merchant.request_url); + + if (this.paymentProtocolURL !== this.merchant.request_url) + throw new Error('PayPro: Mismatch on Payment URLs'); + + 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.) ) + throw new Error('PayPro: Request expired'); + + if (!this.merchant.total || !this.merchant.outs[0].amountSatStr || !this.merchant.outs[0].address) + throw new Error('PayPro: Missing amount'); + + if (this.builder.vanilla.outs.length != 1) + throw new Error('PayPro: Wrong outs in Tx'); + + + var ppOut = this.merchant.outs[0]; + var txOut = this.builder.vanilla.outs[0]; + + if (ppOut.address !== txOut.address) + throw new Error('PayPro: Wrong out address in Tx'); + + if (ppOut.amountSatStr !== txOut.amountSatStr) + throw new Error('PayPro: Wrong amount in Tx'); + +}; TxProposal.prototype._check = function() { @@ -61,6 +93,23 @@ TxProposal.prototype._check = function() { if (hashType && hashType !== Transaction.SIGHASH_ALL) throw new Error('Invalid tx proposal: bad signatures'); }); + this._checkPayPro(); +}; + + +TxProposal.prototype.trimForStorage = function() { + // TODO (remove builder / builderObj. utxos, etc) + // + return this; +}; + +TxProposal.prototype.addMerchantData = function(merchantData) { + var m = _.clone(merchantData); + + // remove unneeded data + m.raw = m.pr.pki_data = m.pr.signature = undefined; + this.merchant = m; + this._checkPayPro(); }; TxProposal.prototype.rejectCount = function() { @@ -101,7 +150,6 @@ TxProposal.prototype._sync = function() { return this; } - TxProposal.prototype.getId = function() { preconditions.checkState(this.builder); return this.builder.build().getNormalizedHash().toString('hex'); @@ -248,11 +296,14 @@ TxProposal.prototype.setRejected = function(copayerId) { if (!this.rejectedBy[copayerId]) this.rejectedBy[copayerId] = Date.now(); + + return this; }; TxProposal.prototype.setSent = function(sentTxid) { this.sentTxid = sentTxid; this.sentTs = Date.now(); + return this; }; TxProposal.prototype.getSent = function() { diff --git a/js/models/Wallet.js b/js/models/Wallet.js index fdc60b08d..4e1aee4c2 100644 --- a/js/models/Wallet.js +++ b/js/models/Wallet.js @@ -474,8 +474,14 @@ Wallet.prototype._processTxProposalPayPro = function(mergeInfo, cb) { log.info('Received a Payment Protocol TX Proposal'); self.fetchPaymentTx(txp.paymentProtocolURL, function(err, merchantData) { if (err) return cb(err); - txp.merchant = merchantData; - return cb(); + + try { + txp.addMerchantData(merchantData); + } catch (e) { + log.error(e); + err = 'BADPAYPRO: ' + e.toString(); + } + return cb(err); }); }; @@ -1435,14 +1441,7 @@ Wallet.prototype.sign = function(ntxid) { var myId = this.getMyCopayerId(); var txp = this.txProposals.get(ntxid); - // If this is a payment protocol request, - // ensure it hasn't been tampered with. - if (!this.verifyPaymentRequest(ntxid)) { - throw new Error('Bad payment request'); - } - var before = txp.countSignatures(); - var keys = this.privateKey.getForPaths(txp.inputChainPaths); txp.builder.sign(keys); @@ -1491,7 +1490,8 @@ Wallet.prototype.sendTx = function(ntxid, cb) { if (txid) { log.debug('Wallet:' + self.getName() + ' Broadcasted TX. BITCOIND txid:', txid); - self.txProposals.get(ntxid).setSent(txid); + var txp = self.txProposals.get(ntxid); + txp.setSent(txid); self.sendTxProposal(ntxid); self.emitAndKeepAlive('txProposalsUpdated'); return cb(txid); @@ -1676,11 +1676,9 @@ Wallet.prototype.receivePaymentRequest = function(options, pr, cb) { untrusted: !trust.caTrusted, selfSigned: trust.selfSigned }, + expires: expires, request_url: options.uri, total: bignum('0', 10).toString(10), - // Expose so other copayers can verify signature - // and identity, not to mention data. - raw: pr.serialize().toString('hex') }; return this.getUnspent(function(err, safeUnspent, unspent) { @@ -1694,9 +1692,7 @@ Wallet.prototype.receivePaymentRequest = function(options, pr, cb) { } catch (e) { var msg = e.message || ''; if (msg.indexOf('not enough unspent tx outputs to fulfill')) { - var sat = /(\d+)/.exec(msg)[1]; e = new Error('No unspent outputs available.'); - e.amount = sat; return cb(e); } } @@ -1815,21 +1811,21 @@ Wallet.prototype.sendPaymentTx = function(ntxid, options, cb) { } var postInfo = { - method: 'POST', - url: txp.merchant.pr.pd.payment_url, - headers: { - // BIP-71 - 'Accept': PayPro.PAYMENT_ACK_CONTENT_TYPE, - 'Content-Type': PayPro.PAYMENT_CONTENT_TYPE - // XHR does not allow these: - // 'Content-Length': (pay.byteLength || pay.length) + '', - // 'Content-Transfer-Encoding': 'binary' - }, - // Technically how this should be done via XHR (used to - // be the ArrayBuffer, now you send the View instead). - data: view, - responseType: 'arraybuffer' - }; + method: 'POST', + url: txp.merchant.pr.pd.payment_url, + headers: { + // BIP-71 + 'Accept': PayPro.PAYMENT_ACK_CONTENT_TYPE, + 'Content-Type': PayPro.PAYMENT_CONTENT_TYPE + // XHR does not allow these: + // 'Content-Length': (pay.byteLength || pay.length) + '', + // 'Content-Transfer-Encoding': 'binary' + }, + // Technically how this should be done via XHR (used to + // be the ArrayBuffer, now you send the View instead). + data: view, + responseType: 'arraybuffer' + }; return Wallet.request(postInfo) .success(function(data, status, headers, config) { @@ -1940,7 +1936,7 @@ Wallet.prototype.createPaymentTxSync = function(options, merchantData, unspent) merchantData.total = bignum(merchantData.total, 10); - var outs = []; + var outs = {}; merchantData.pr.pd.outputs.forEach(function(output) { var amount = output.amount; @@ -1964,13 +1960,11 @@ Wallet.prototype.createPaymentTxSync = function(options, merchantData, unspent) var network = merchantData.pr.pd.network === 'main' ? 'livenet' : 'testnet'; var addr = bitcore.Address.fromScriptPubKey(new bitcore.Script(s), network); - outs.push({ - address: addr[0].toString(), - amountSatStr: bignum.fromBuffer(v, { - endian: 'big', - size: 1 - }).toString(10) - }); + var a = addr[0].toString(); + outs[a] = bignum.fromBuffer(v, { + endian: 'big', + size: 1 + }).add(outs[a] || bignum(0)); merchantData.total = merchantData.total.add(bignum.fromBuffer(v, { endian: 'big', @@ -1978,11 +1972,18 @@ Wallet.prototype.createPaymentTxSync = function(options, merchantData, unspent) })); }); + if (Object.keys(outs) > 1) + throw new Error('PayPro: Unsupported outputs'); + + merchantData.outs = outs; merchantData.total = merchantData.total.toString(10); var b = new Builder(opts) .setUnspent(unspent) - .setOutputs(outs); + .setOutputs({ + address: _.keys(outs)[0], + amountSatStr: _.values(outs)[0].toString(10), + }); merchantData.pr.pd.outputs.forEach(function(output, i) { var script = { @@ -2030,6 +2031,7 @@ Wallet.prototype.createPaymentTxSync = function(options, merchantData, unspent) var meSeen = {}; if (priv) meSeen[myId] = now; + console.log('[Wallet.js.2043]', options, merchantData); //TODO var ntxid = this.txProposals.add(new TxProposal({ inputChainPaths: inputChainPaths, signedBy: me, @@ -2045,159 +2047,6 @@ Wallet.prototype.createPaymentTxSync = function(options, merchantData, unspent) return ntxid; }; -/** - * @desc Verifies a PaymentRequest sent by another peer - * This essentially ensures that a copayer hasn't tampered with a - * PaymentRequest message from a payment server. It verifies the signature - * based on the cert, and checks to ensure the desired outputs are the same as - * the ones on the tx proposal. - * @TODO: Document better - */ -Wallet.prototype.verifyPaymentRequest = function(ntxid) { - if (!ntxid) return false; - - var txp = _.isObject(ntxid) ? ntxid : this.txProposals.get(ntxid); - - // If we're not a payment protocol proposal, ignore. - if (!txp.merchant) return true; - - // The copayer didn't send us the raw payment request, unverifiable. - if (!txp.merchant.raw) return false; - - // var tx = txp.builder.tx; - var tx = txp.builder.build(); - - var data = new Buffer(txp.merchant.raw, 'hex'); - data = PayPro.PaymentRequest.decode(data); - var pr = new PayPro(); - pr = pr.makePaymentRequest(data); - - // Verify the signature so we know this is the real request. - var trust = pr.verify(true); - if (!trust.verified) { - // Signature does not match cert. It may have - // been modified by an untrustworthy person. - // We should not sign this transaction proposal! - return false; - } - - var details = pr.get('serialized_payment_details'); - details = PayPro.PaymentDetails.decode(details); - var pd = new PayPro(); - pd = pd.makePaymentDetails(details); - - var outputs = pd.get('outputs'); - - if (tx.outs.length < outputs.length) { - // Outputs do not and cannot match. - return false; - } - - // Figure out whether the user is supposed - // to decide the value of the outputs. - var undecided = false; - var total = bignum('0', 10); - for (var i = 0; i < outputs.length; i++) { - var output = outputs[i]; - var amount = output.get('amount'); - // big endian - var v = new Buffer(8); - v[0] = (amount.high >> 24) & 0xff; - v[1] = (amount.high >> 16) & 0xff; - v[2] = (amount.high >> 8) & 0xff; - v[3] = (amount.high >> 0) & 0xff; - v[4] = (amount.low >> 24) & 0xff; - v[5] = (amount.low >> 16) & 0xff; - v[6] = (amount.low >> 8) & 0xff; - v[7] = (amount.low >> 0) & 0xff; - total = total.add(bignum.fromBuffer(v, { - endian: 'big', - size: 1 - })); - } - if (+total.toString(10) === 0) { - undecided = true; - } - - for (var i = 0; i < outputs.length; i++) { - var output = outputs[i]; - - var amount = output.get('amount'); - var script = { - offset: output.get('script').offset, - limit: output.get('script').limit, - buffer: new Buffer(new Uint8Array(output.get('script').buffer)) - }; - - // Expected value - // little endian (keep this LE to compare with tx output value) - var ev = new Buffer(8); - ev[0] = (amount.low >> 0) & 0xff; - ev[1] = (amount.low >> 8) & 0xff; - ev[2] = (amount.low >> 16) & 0xff; - ev[3] = (amount.low >> 24) & 0xff; - ev[4] = (amount.high >> 0) & 0xff; - ev[5] = (amount.high >> 8) & 0xff; - ev[6] = (amount.high >> 16) & 0xff; - ev[7] = (amount.high >> 24) & 0xff; - - // Expected script - var es = script.buffer.slice(script.offset, script.limit); - - // Actual value - var av = tx.outs[i].v; - - // Actual script - var as = tx.outs[i].s; - - // XXX allow changing of script as long as address is same - // var as = es; - - // XXX allow changing of script as long as address is same - // var network = pd.get('network') === 'main' ? 'livenet' : 'testnet'; - // var es = bitcore.Address.fromScriptPubKey(new bitcore.Script(es), network)[0]; - // var as = bitcore.Address.fromScriptPubKey(new bitcore.Script(tx.outs[i].s), network)[0]; - - if (undecided) { - av = ev = new Buffer([0]); - } - - // Make sure the tx's output script and values match the payment request's. - if (av.toString('hex') !== ev.toString('hex') || as.toString('hex') !== es.toString('hex')) { - // Verifiable outputs do not match outputs of merchant - // data. We should not sign this transaction proposal! - return false; - } - - // Checking the merchant data itself isn't technically - // necessary as long as we check the transaction, but - // we can do it for good measure. - var ro = txp.merchant.pr.pd.outputs[i]; - - // Actual value - // little endian (keep this LE to compare with the ev above) - var av = new Buffer(8); - av[0] = (ro.amount.low >> 0) & 0xff; - av[1] = (ro.amount.low >> 8) & 0xff; - av[2] = (ro.amount.low >> 16) & 0xff; - av[3] = (ro.amount.low >> 24) & 0xff; - av[4] = (ro.amount.high >> 0) & 0xff; - av[5] = (ro.amount.high >> 8) & 0xff; - av[6] = (ro.amount.high >> 16) & 0xff; - av[7] = (ro.amount.high >> 24) & 0xff; - - // Actual script - var as = new Buffer(ro.script.buffer, 'hex') - .slice(ro.script.offset, ro.script.limit); - - if (av.toString('hex') !== ev.toString('hex') || as.toString('hex') !== es.toString('hex')) { - return false; - } - } - - return true; -}; - /** * @desc Mark that a user has seen a given TxProposal * @return {boolean} true if the internal state has changed @@ -2243,7 +2092,7 @@ Wallet.prototype.subscribeToAddresses = function() { var addrInfo = this.publicKeyRing.getAddressesInfo(); this.blockchain.subscribe(_.pluck(addrInfo, 'addressStr')); - log.debug('Subscribed to ' + addrInfo.length + ' addresses'); + log.debug('Subscribed to ' + addrInfo.length + ' addresses'); }; /** diff --git a/test/PayPro.js b/test/PayPro.js index c8a19bb7d..5ce1d9164 100644 --- a/test/PayPro.js +++ b/test/PayPro.js @@ -788,132 +788,6 @@ describe('PayPro (in Wallet) model', function() { }); }); - it('#try to sign a tampered payment request (raw)', function(done) { - var w = createWallet(); - should.exist(w); - var address = 'bitcoin:2NBzZdFBoQymDgfzH2Pmnthser1E71MmU47?amount=0.00003&r=' + server.uri + '/request'; - var commentText = 'Hello, server. I\'d like to make a payment.'; - w.createPaymentTx({ - uri: address, - memo: commentText - }, function(err, ntxid, merchantData) { - should.equal(err, null); - should.exist(ntxid); - should.exist(merchantData); - - // Tamper with payment request in its raw form: - var data = new Buffer(merchantData.raw, 'hex'); - data = PayPro.PaymentRequest.decode(data); - var pr = new PayPro(); - pr = pr.makePaymentRequest(data); - var details = pr.get('serialized_payment_details'); - details = PayPro.PaymentDetails.decode(details); - var pd = new PayPro(); - pd = pd.makePaymentDetails(details); - var outputs = pd.get('outputs'); - outputs[outputs.length - 1].set('amount', 1000000000); - pd.set('outputs', outputs); - pr.set('serialized_payment_details', pd.serialize()); - merchantData.raw = pr.serialize().toString('hex'); - - var myId = w.getMyCopayerId(); - var txp = w.txProposals.get(ntxid); - should.exist(txp); - should.exist(txp.signedBy[myId]); - should.not.exist(txp.rejectedBy[myId]); - - w.verifyPaymentRequest(ntxid).should.equal(false); - - return done(); - }); - }); - - it('#try to sign a tampered payment request (abstract)', function(done) { - var w = createWallet(); - should.exist(w); - var address = 'bitcoin:2NBzZdFBoQymDgfzH2Pmnthser1E71MmU47?amount=0.00003&r=' + server.uri + '/request'; - var commentText = 'Hello, server. I\'d like to make a payment.'; - w.createPaymentTx({ - uri: address, - memo: commentText - }, function(err, ntxid, merchantData) { - should.equal(err, null); - should.exist(ntxid); - should.exist(merchantData); - - // Tamper with payment request in its abstract form: - var outputs = merchantData.pr.pd.outputs; - var output = outputs[outputs.length - 1]; - var amount = output.amount; - amount.low = 2; - - var myId = w.getMyCopayerId(); - var txp = w.txProposals.get(ntxid); - should.exist(txp); - should.exist(txp.signedBy[myId]); - should.not.exist(txp.rejectedBy[myId]); - - w.verifyPaymentRequest(ntxid).should.equal(false); - - return done(); - }); - }); - - it('#try to sign a tampered txp tx (abstract)', function(done) { - var w = createWallet(); - should.exist(w); - var address = 'bitcoin:2NBzZdFBoQymDgfzH2Pmnthser1E71MmU47?amount=0.00003&r=' + server.uri + '/request'; - var commentText = 'Hello, server. I\'d like to make a payment.'; - w.createPaymentTx({ - uri: address, - memo: commentText - }, function(err, ntxid, merchantData) { - should.equal(err, null); - should.exist(ntxid); - should.exist(merchantData); - - // Tamper with payment request in its abstract form: - var txp = w.txProposals.get(ntxid); - var tx = txp.builder.tx || txp.builder.build(); - tx.outs[0].v = new Buffer([2, 0, 0, 0, 0, 0, 0, 0]); - - var myId = w.getMyCopayerId(); - var txp = w.txProposals.get(ntxid); - should.exist(txp); - should.exist(txp.signedBy[myId]); - should.not.exist(txp.rejectedBy[myId]); - - w.verifyPaymentRequest(ntxid).should.equal(false); - - return done(); - }); - }); - - it('#sign an untampered payment request', function(done) { - var w = createWallet(); - should.exist(w); - var address = 'bitcoin:2NBzZdFBoQymDgfzH2Pmnthser1E71MmU47?amount=0.00003&r=' + server.uri + '/request'; - var commentText = 'Hello, server. I\'d like to make a payment.'; - w.createPaymentTx({ - uri: address, - memo: commentText - }, function(err, ntxid, merchantData) { - should.equal(err, null); - should.exist(ntxid); - should.exist(merchantData); - - var myId = w.getMyCopayerId(); - var txp = w.txProposals.get(ntxid); - should.exist(txp); - should.exist(txp.signedBy[myId]); - should.not.exist(txp.rejectedBy[myId]); - - w.verifyPaymentRequest(ntxid).should.equal(true); - - return done(); - }); - }); - it('#close payment server', function(done) { server.close(function() { return done(); diff --git a/test/TxProposal.js b/test/TxProposal.js index c74f0ffcd..e2a106df3 100644 --- a/test/TxProposal.js +++ b/test/TxProposal.js @@ -11,12 +11,12 @@ var networks = bitcore.networks; var FakeBuilder = requireMock('FakeBuilder'); var TxProposal = copay.TxProposal; -var dummyProposal = new TxProposal({ - creator: 1, +var dummyProposal = function() { return new TxProposal({ + creator: 'creator', createdTs: 1, builder: new FakeBuilder(), inputChainPaths: ['m/1'], -}); +})}; var someKeys = ["03b39d61dc9a504b13ae480049c140dcffa23a6cc9c09d12d6d1f332fee5e18ca5", "022929f515c5cf967474322468c3bd945bb6f281225b2c884b465680ef3052c07e"]; @@ -208,8 +208,8 @@ describe('TxProposal', function() { }); it('#_verifyScriptSig, two signatures', function() { // Data taken from bitcore's TransactionBuilder test - var txp = dummyProposal; - var tx = dummyProposal.builder.build(); + var txp = dummyProposal(); + var tx = dummyProposal().builder.build(); var ret = TxProposal._verifySignatures(pubkeys, validScriptSig, tx.hashForSignature()); ret.should.deep.equal(['03197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d', '03a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e3']); }); @@ -223,13 +223,13 @@ describe('TxProposal', function() { Buffer.isBuffer(info.script.getBuffer()).should.equal(true); }); it('#_updateSignedBy', function() { - var txp = dummyProposal; + var txp = dummyProposal(); txp._inputSigners.should.deep.equal([ ['03197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d', '03a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e3'] ]); }); describe('#_check', function() { - var txp = dummyProposal; + var txp = dummyProposal(); var backup = txp.builder.tx.ins; it('OK', function() { @@ -272,8 +272,96 @@ describe('TxProposal', function() { txp.builder.tx.ins[0].s = backup; }); }); + + describe('#_checkPayPro', function() { + var txp, md; + beforeEach(function() { + txp = dummyProposal(); + txp.paymentProtocolURL = '123'; + md = { + request_url: '123', + pr: { + pd: { + expires: 123, + memo: 'memo', + + }, + }, + total: '1230', + outs: [{ + address: '2NDJbzwzsmRgD2o5HHXPhuq5g6tkKTjYkd6', + amountSatStr: "123" + }], + expires: 92345678900, + }; + }); + + it('OK no merchant data', function() { + txp._checkPayPro(); + }); + it('OK merchant data', function() { + txp.addMerchantData(md); + }); + it('NOK URL', function() { + txp.paymentProtocolURL = '1234'; + (function() { + txp.addMerchantData(md); + }).should.throw('Mismatch'); + }); + it('NOK OUTS', function() { + md.outs = []; + (function() { + txp.addMerchantData(md); + }).should.throw('outputs'); + }); + it('NOK OUTS (case 2)', function() { + md.outs = [{}, {}]; + (function() { + txp.addMerchantData(md); + }).should.throw('outputs'); + }); + it('NOK OUTS (case 3)', function() { + md.outs = [{}, {}]; + (function() { + txp.addMerchantData(md); + }).should.throw('outputs'); + }); + it('NOK Amount', function() { + md.total = undefined; + (function() { + txp.addMerchantData(md); + }).should.throw('amount'); + }); + it('NOK Outs case 4', function() { + md.outs[0].address = 'aaa'; + (function() { + txp.addMerchantData(md); + }).should.throw('address'); + }); + it('NOK Outs case 5', function() { + md.outs[0].amountSatStr = '432'; + (function() { + txp.addMerchantData(md); + }).should.throw('amount'); + }); + + it('NOK Expired', function() { + md.expires = 1; + (function() { + txp.addMerchantData(md); + }).should.throw('expired'); + }); + + it('OK Expired but sent', function() { + md.expires = 2; + txp.sentTs = 1; + txp.addMerchantData(md); + }); + + }); + describe('#merge', function() { - var txp = dummyProposal; + var txp = dummyProposal(); var backup = txp.builder.tx.ins; it('with self', function() { var hasChanged = txp.merge(txp); @@ -307,7 +395,7 @@ describe('TxProposal', function() { }); describe('#setCopayers', function() { it("should fails if Tx has no creator", function() { - var txp = dummyProposal; + var txp = dummyProposal(); txp.signedBy = { 'hugo': 1 }; @@ -319,7 +407,7 @@ describe('TxProposal', function() { }).should.throw('no creator'); }); it("should fails if Tx is not signed by creator", function() { - var txp = dummyProposal; + var txp = dummyProposal(); txp.creator = 'creator'; txp.signedBy = { 'hugo': 1 @@ -336,7 +424,7 @@ describe('TxProposal', function() { it("should fails if Tx has unmapped signatures", function() { - var txp = dummyProposal; + var txp = dummyProposal(); txp.creator = 'creator'; txp.signedBy = { creator: 1 @@ -353,7 +441,7 @@ describe('TxProposal', function() { // This was disabled. Unnecessary to check this. it.skip("should be signed by sender", function() { - var txp = dummyProposal; + var txp = dummyProposal(); var ts = Date.now(); txp._inputSigners = [ ['pk1', 'pk0'] @@ -372,7 +460,7 @@ describe('TxProposal', function() { it("should set signedBy (trivial case)", function() { - var txp = dummyProposal; + var txp = dummyProposal(); var ts = Date.now(); txp._inputSigners = [ ['pk1', 'pk0'] @@ -390,7 +478,7 @@ describe('TxProposal', function() { txp.signedBy['creator'].should.gte(ts); }); it("should assign creator", function() { - var txp = dummyProposal; + var txp = dummyProposal(); var ts = Date.now(); txp._inputSigners = [ ['pk0'] @@ -409,7 +497,7 @@ describe('TxProposal', function() { txp.seenBy['creator'].should.equal(txp.createdTs); }) it("New tx should have only 1 signature", function() { - var txp = dummyProposal; + var txp = dummyProposal(); var ts = Date.now(); txp.signedBy = {}; delete txp['creator']; @@ -431,7 +519,7 @@ describe('TxProposal', function() { }) it("if signed, should not change ts", function() { - var txp = dummyProposal; + var txp = dummyProposal(); var ts = Date.now(); txp._inputSigners = [ ['pk0', 'pk1'] @@ -456,25 +544,25 @@ describe('TxProposal', function() { describe('micelaneous functions', function() { it('should report rejectCount', function() { - var txp = dummyProposal; + var txp = dummyProposal(); txp.rejectCount().should.equal(0); txp.setRejected(['juan']) txp.rejectCount().should.equal(1); }); it('should report isPending 1', function() { - var txp = dummyProposal; + var txp = dummyProposal(); txp.rejectedBy = []; txp.sentTxid = 1; txp.isPending(3).should.equal(false); }); it('should report isPending 2', function() { - var txp = dummyProposal; + var txp = dummyProposal(); txp.rejectedBy = []; txp.sentTxid = null; txp.isPending(3).should.equal(true); }); it('should report isPending 3', function() { - var txp = dummyProposal; + var txp = dummyProposal(); txp.rejectedBy = [1, 2, 3, 4]; txp.sentTxid = null; txp.isPending(3).should.equal(false); diff --git a/test/mocks/FakeBuilder.js b/test/mocks/FakeBuilder.js index 9b3aea95b..ae2d0affe 100644 --- a/test/mocks/FakeBuilder.js +++ b/test/mocks/FakeBuilder.js @@ -2,10 +2,12 @@ var bitcore = bitcore || require('bitcore'); var Script = bitcore.Script; -var VALID_SCRIPTSIG_BUF = new Buffer('0048304502200708a381dde585ef7fdfaeaeb5da9b451d3e22b01eac8a5e3d03b959e24a7478022100c90e76e423523a54a9e9c43858337ebcef1a539a7fc685c2698dd8648fcf1b9101473044022030a77c9613d6ee010717c1abc494668d877e3fa0ae4c520f65cc3b308754c98c02205219d387bcb291bd44805b9468439e4168b02a6a180cdbcc24d84d71d696c1ae014cad532103197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d210380a29968851f93af55e581c43d9ef9294577a439a3ca9fc2bc47d1ca2b3e9127210392dccb2ed470a45984811d6402fdca613c175f8f3e4eb8e2306e8ccd7d0aed032103a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e32103e085eb6fa1f20b2722c16161144314070a2c316a9cae2489fd52ce5f63fff6e455ae','hex'); +var VALID_SCRIPTSIG_BUF = new Buffer('0048304502200708a381dde585ef7fdfaeaeb5da9b451d3e22b01eac8a5e3d03b959e24a7478022100c90e76e423523a54a9e9c43858337ebcef1a539a7fc685c2698dd8648fcf1b9101473044022030a77c9613d6ee010717c1abc494668d877e3fa0ae4c520f65cc3b308754c98c02205219d387bcb291bd44805b9468439e4168b02a6a180cdbcc24d84d71d696c1ae014cad532103197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d210380a29968851f93af55e581c43d9ef9294577a439a3ca9fc2bc47d1ca2b3e9127210392dccb2ed470a45984811d6402fdca613c175f8f3e4eb8e2306e8ccd7d0aed032103a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e32103e085eb6fa1f20b2722c16161144314070a2c316a9cae2489fd52ce5f63fff6e455ae', 'hex'); function Tx() { - this.ins = [{s: VALID_SCRIPTSIG_BUF }]; + this.ins = [{ + s: VALID_SCRIPTSIG_BUF + }]; }; Tx.prototype.getHashType = function() { @@ -23,26 +25,32 @@ function FakeBuilder() { this.test = 1; this.tx = new Tx(); this.signhash = 1; - this.inputMap = [{ address: '2NDJbzwzsmRgD2o5HHXPhuq5g6tkKTjYkd6', + this.inputMap = [{ + address: '2NDJbzwzsmRgD2o5HHXPhuq5g6tkKTjYkd6', scriptPubKey: new Script(new Buffer('a914dc0623476aefb049066b09b0147a022e6eb8429187', 'hex')), scriptType: 4, - i: 0 }]; + i: 0 + }]; - this.vanilla = { - scriptSig: [VALID_SCRIPTSIG_BUF], - } + this.vanilla = { + scriptSig: [VALID_SCRIPTSIG_BUF], + outs: [{ + address: '2NDJbzwzsmRgD2o5HHXPhuq5g6tkKTjYkd6', + amountSatStr: '123', + }] + + } } -FakeBuilder.prototype.merge = function() { -}; +FakeBuilder.prototype.merge = function() {}; -FakeBuilder.prototype.build = function() { +FakeBuilder.prototype.build = function() { return this.tx; }; -FakeBuilder.prototype.toObj = function() { +FakeBuilder.prototype.toObj = function() { return this; }; FakeBuilder.VALID_SCRIPTSIG_BUF = VALID_SCRIPTSIG_BUF;