diff --git a/lib/services/bitcoind.js b/lib/services/bitcoind.js index e882839a..7dcd8dc8 100644 --- a/lib/services/bitcoind.js +++ b/lib/services/bitcoind.js @@ -1338,12 +1338,8 @@ Bitcoin.prototype._paginateTxids = function(fullTxids, fromArg, toArg) { var txids; var from = parseInt(fromArg); var to = parseInt(toArg); - if (from >= 0 && to >= 0) { - $.checkState(from < to, '"from" (' + from + ') is expected to be less than "to" (' + to + ')'); - txids = fullTxids.slice(from, to); - } else { - txids = fullTxids; - } + $.checkState(from < to, '"from" (' + from + ') is expected to be less than "to" (' + to + ')'); + txids = fullTxids.slice(from, to); return txids; }; @@ -1363,7 +1359,10 @@ Bitcoin.prototype.getAddressHistory = function(addressArg, options, callback) { var queryMempool = _.isUndefined(options.queryMempool) ? true : options.queryMempool; var addressStrings = this._getAddressStrings(addresses); - if ((options.to - options.from) > self.maxTransactionHistory) { + var fromArg = parseInt(options.from || 0); + var toArg = parseInt(options.to || self.maxTransactionHistory); + + if ((toArg - fromArg) > self.maxTransactionHistory) { return callback(new Error( '"from" (' + options.from + ') and "to" (' + options.to + ') range should be less than or equal to ' + self.maxTransactionHistory @@ -1377,7 +1376,7 @@ Bitcoin.prototype.getAddressHistory = function(addressArg, options, callback) { var totalCount = txids.length; try { - txids = self._paginateTxids(txids, options.from, options.to); + txids = self._paginateTxids(txids, fromArg, toArg); } catch(e) { return callback(e); } @@ -1424,12 +1423,12 @@ Bitcoin.prototype.getAddressSummary = function(addressArg, options, callback) { var fromArg = parseInt(options.from || 0); var toArg = parseInt(options.to || self.maxTxids); - if ((toArg - fromArg) > self.maxTxids) { - return callback(new Error( - '"from" (' + fromArg + ') and "to" (' + toArg + ') range should be less than or equal to ' + - self.maxTxids - )); - } + if ((toArg - fromArg) > self.maxTxids) { + return callback(new Error( + '"from" (' + fromArg + ') and "to" (' + toArg + ') range should be less than or equal to ' + + self.maxTxids + )); + } var paginatedTxids; try { paginatedTxids = self._paginateTxids(allTxids, fromArg, toArg); @@ -1751,19 +1750,13 @@ Bitcoin.prototype.estimateFee = function(blocks, callback) { Bitcoin.prototype.sendTransaction = function(tx, options, callback) { var self = this; var allowAbsurdFees = false; - var txString; - if (tx instanceof Transaction) { - txString = tx.serialize(); - } else { - txString = tx; - } if (_.isFunction(options) && _.isUndefined(callback)) { callback = options; } else if (_.isObject(options)) { allowAbsurdFees = options.allowAbsurdFees; } - this.client.sendRawTransaction(txString, allowAbsurdFees, function(err, response) { + this.client.sendRawTransaction(tx, allowAbsurdFees, function(err, response) { if (err) { return callback(self._wrapRPCError(err)); } @@ -1880,14 +1873,13 @@ Bitcoin.prototype.getDetailedTransaction = function(txid, callback) { if (!tx.coinbase) { tx.inputSatoshis += input.valueSat; } - var script; - var scriptAsm; + var script = null; + var scriptAsm = null; if (input.scriptSig) { script = input.scriptSig.hex; scriptAsm = input.scriptSig.asm; } else if (input.coinbase) { script = input.coinbase; - scriptAsm = null; } tx.inputs.push({ prevTxId: input.txid || null, @@ -1908,7 +1900,7 @@ Bitcoin.prototype.getDetailedTransaction = function(txid, callback) { var out = result.vout[outputIndex]; tx.outputSatoshis += out.valueSat; var address = null; - if (out.scriptPubKey && out.scriptPubKey.addresses && out.scriptPubKey.addresses.length > 0) { + if (out.scriptPubKey.addresses.length === 1) { address = out.scriptPubKey.addresses[0]; } tx.outputs.push({ diff --git a/test/services/bitcoind.unit.js b/test/services/bitcoind.unit.js index bc8980ad..cde71c63 100644 --- a/test/services/bitcoind.unit.js +++ b/test/services/bitcoind.unit.js @@ -944,6 +944,35 @@ describe('Bitcoin Service', function() { bitcoind._updateTip(node, message); bitcoind._updateTip(node, message); }); + it('will not call syncPercentage if node is stopping', function(done) { + var config = { + node: { + network: bitcore.Networks.testnet + }, + spawn: { + datadir: 'testdir', + exec: 'testpath' + } + }; + var bitcoind = new BitcoinService(config); + bitcoind.syncPercentage = sinon.stub(); + bitcoind._resetCaches = sinon.stub(); + bitcoind.node.stopping = true; + var node = { + client: { + getBlock: sinon.stub().callsArgWith(1, null, { + result: { + height: 10 + } + }) + } + }; + bitcoind.on('tip', function() { + bitcoind.syncPercentage.callCount.should.equal(0); + done(); + }); + bitcoind._updateTip(node, message); + }); }); describe('#_getAddressesFromTransaction', function() { @@ -1268,6 +1297,16 @@ describe('Bitcoin Service', function() { done(); }); }); + it('will call callback if reindex is not enabled', function(done) { + var bitcoind = new BitcoinService(baseConfig); + var node = { + _reindex: false + }; + bitcoind._checkReindex(node, function() { + node._reindex.should.equal(false); + done(); + }); + }); }); describe('#_loadTipFromNode', function() { @@ -2078,7 +2117,7 @@ describe('Bitcoin Service', function() { }); }); - describe('#_getTxidsMempool', function() { + describe('#_getTxidsFromMempool', function() { it('will filter to txids', function() { var bitcoind = new BitcoinService(baseConfig); var deltas = [ @@ -2098,6 +2137,24 @@ describe('Bitcoin Service', function() { txids[1].should.equal('txid1'); txids[2].should.equal('txid2'); }); + it('will not include duplicates', function() { + var bitcoind = new BitcoinService(baseConfig); + var deltas = [ + { + txid: 'txid0', + }, + { + txid: 'txid0', + }, + { + txid: 'txid1', + } + ]; + var txids = bitcoind._getTxidsFromMempool(deltas); + txids.length.should.equal(2); + txids[0].should.equal('txid0'); + txids[1].should.equal('txid1'); + }); }); describe('#_getHeightRangeQuery', function() { @@ -2332,6 +2389,14 @@ describe('Bitcoin Service', function() { afterEach(function() { sandbox.restore(); }); + it('should get 0 confirmation', function() { + var tx = new Transaction(txhex); + tx.height = -1; + var bitcoind = new BitcoinService(baseConfig); + bitcoind.height = 10; + var confirmations = bitcoind._getConfirmationsDetail(tx); + confirmations.should.equal(0); + }); it('should get 1 confirmation', function() { var tx = new Transaction(txhex); tx.height = 10; @@ -3420,6 +3485,21 @@ describe('Bitcoin Service', function() { hash.should.equal(tx.hash); }); }); + it('missing callback will throw error', function() { + var bitcoind = new BitcoinService(baseConfig); + var sendRawTransaction = sinon.stub().callsArgWith(2, null, { + result: tx.hash + }); + bitcoind.nodes.push({ + client: { + sendRawTransaction: sendRawTransaction + } + }); + var transaction = bitcore.Transaction(); + (function() { + bitcoind.sendTransaction(transaction); + }).should.throw(Error); + }); }); describe('#getRawTransaction', function() { @@ -3684,7 +3764,7 @@ describe('Bitcoin Service', function() { }); it('should set coinbase to true', function(done) { var bitcoind = new BitcoinService(baseConfig); - var rawTransaction = _.clone(rpcRawTransaction); + var rawTransaction = JSON.parse((JSON.stringify(rpcRawTransaction))); delete rawTransaction.vin[0]; rawTransaction.vin = [ { @@ -3705,6 +3785,79 @@ describe('Bitcoin Service', function() { done(); }); }); + it('will not include address if address length is zero', function(done) { + var bitcoind = new BitcoinService(baseConfig); + var rawTransaction = JSON.parse((JSON.stringify(rpcRawTransaction))); + rawTransaction.vout[0].scriptPubKey.addresses = []; + bitcoind.nodes.push({ + client: { + getRawTransaction: sinon.stub().callsArgWith(2, null, { + result: rawTransaction + }) + } + }); + var txid = '2d950d00494caf6bfc5fff2a3f839f0eb50f663ae85ce092bc5f9d45296ae91f'; + bitcoind.getDetailedTransaction(txid, function(err, tx) { + should.exist(tx); + should.equal(tx.outputs[0].address, null); + done(); + }); + }); + it('will not include address if address length is greater than 1', function(done) { + var bitcoind = new BitcoinService(baseConfig); + var rawTransaction = JSON.parse((JSON.stringify(rpcRawTransaction))); + rawTransaction.vout[0].scriptPubKey.addresses = ['one', 'two']; + bitcoind.nodes.push({ + client: { + getRawTransaction: sinon.stub().callsArgWith(2, null, { + result: rawTransaction + }) + } + }); + var txid = '2d950d00494caf6bfc5fff2a3f839f0eb50f663ae85ce092bc5f9d45296ae91f'; + bitcoind.getDetailedTransaction(txid, function(err, tx) { + should.exist(tx); + should.equal(tx.outputs[0].address, null); + done(); + }); + }); + it('will not include script if input missing scriptSig or coinbase', function(done) { + var bitcoind = new BitcoinService(baseConfig); + var rawTransaction = JSON.parse((JSON.stringify(rpcRawTransaction))); + delete rawTransaction.vin[0].scriptSig; + delete rawTransaction.vin[0].coinbase; + bitcoind.nodes.push({ + client: { + getRawTransaction: sinon.stub().callsArgWith(2, null, { + result: rawTransaction + }) + } + }); + var txid = '2d950d00494caf6bfc5fff2a3f839f0eb50f663ae85ce092bc5f9d45296ae91f'; + bitcoind.getDetailedTransaction(txid, function(err, tx) { + should.exist(tx); + should.equal(tx.inputs[0].script, null); + done(); + }); + }); + it('will set height to -1 if missing height', function(done) { + var bitcoind = new BitcoinService(baseConfig); + var rawTransaction = JSON.parse((JSON.stringify(rpcRawTransaction))); + delete rawTransaction.height; + bitcoind.nodes.push({ + client: { + getRawTransaction: sinon.stub().callsArgWith(2, null, { + result: rawTransaction + }) + } + }); + var txid = '2d950d00494caf6bfc5fff2a3f839f0eb50f663ae85ce092bc5f9d45296ae91f'; + bitcoind.getDetailedTransaction(txid, function(err, tx) { + should.exist(tx); + should.equal(tx.height, -1); + done(); + }); + }); }); describe('#getBestBlockHash', function() {