From c3654120ee839975f0aab1342079373b9a398151 Mon Sep 17 00:00:00 2001 From: Chris Kleeschulte Date: Thu, 17 Sep 2015 14:26:38 -0400 Subject: [PATCH] MempoolInputIndex and MempoolOutputIndex - Added in memory indexes for mempool input and output by address. --- integration/regtest-node.js | 4 +- lib/services/address/index.js | 47 +++++++++++++++++---- test/data/transaction.json | 3 ++ test/services/address/index.unit.js | 65 +++++++++++++++++++++++------ test/services/bitcoind.unit.js | 2 +- test/services/db.unit.js | 2 + 6 files changed, 99 insertions(+), 24 deletions(-) create mode 100644 test/data/transaction.json diff --git a/integration/regtest-node.js b/integration/regtest-node.js index 5d070b1d..8cd91990 100644 --- a/integration/regtest-node.js +++ b/integration/regtest-node.js @@ -686,9 +686,9 @@ describe('Node Functionality', function() { node.services.bitcoind.sendTransaction(tx.serialize()); setImmediate(function() { - var length = node.services.address.mempoolIndex[address].length; + var length = node.services.address.mempoolOutputIndex[address].length; length.should.equal(1); - should.exist(node.services.address.mempoolIndex[address]); + should.exist(node.services.address.mempoolOutputIndex[address]); done(); }); diff --git a/lib/services/address/index.js b/lib/services/address/index.js index eda7b139..35c9d425 100644 --- a/lib/services/address/index.js +++ b/lib/services/address/index.js @@ -26,7 +26,8 @@ var AddressService = function(options) { this.node.services.bitcoind.on('tx', this.transactionHandler.bind(this)); - this.mempoolIndex = {}; + this.mempoolOutputIndex = {}; + this.mempoolInputIndex = {}; }; @@ -159,11 +160,11 @@ AddressService.prototype.updateMempoolIndex = function(tx) { network: this.node.network }).toString(); - if (!this.mempoolIndex[addressStr]) { - this.mempoolIndex[addressStr] = []; + if (!this.mempoolOutputIndex[addressStr]) { + this.mempoolOutputIndex[addressStr] = []; } - this.mempoolIndex[addressStr].push({ + this.mempoolOutputIndex[addressStr].push({ txid: tx.hash, // TODO use buffer outputIndex: outputIndex, satoshis: output.satoshis, @@ -171,14 +172,31 @@ AddressService.prototype.updateMempoolIndex = function(tx) { }); } + var inputLength = tx.inputs.length; + for (var inputIndex = 0; inputIndex < inputLength; inputIndex++) { + + var input = tx.inputs[inputIndex]; + var address = input.script.toAddress(this.node.network); + if (!address) { + continue; + } + var addressStr = address.toString(); + if (!this.mempoolInputIndex[addressStr]) { + this.mempoolInputIndex[addressStr] = []; + } + this.mempoolInputIndex[addressStr].push({ + txid: tx.hash, // TODO use buffer + inputIndex: inputIndex + }); + } }; AddressService.prototype.resetMempoolIndex = function(callback) { - log.info('Starting reset of mempool index for address service.'); var self = this; var transactionBuffers = self.node.services.bitcoind.getMempoolTransactions(); - this.mempoolIndex = {}; + this.mempoolInputIndex = {}; + this.mempoolOutputIndex = {}; async.each(transactionBuffers, function(txBuffer, next) { var tx = Transaction().fromBuffer(txBuffer); self.updateMempoolIndex(tx); @@ -187,7 +205,6 @@ AddressService.prototype.resetMempoolIndex = function(callback) { if (err) { return callback(err); } - log.info('Mempool index update with address size:', Object.keys(self.mempoolIndex).length); callback(); }); }; @@ -626,7 +643,19 @@ AddressService.prototype.getInputs = function(addressStr, options, callback) { return callback(error); } - // TODO include results from mempool + if(options.queryMempool) { + var mempoolInputs = self.mempoolInputIndex[addressStr]; + if (mempoolInputs) { + for(var i = 0; i < mempoolInputs.length; i++) { + // TODO copy + var newInput = mempooInputs[i]; + newInput.address = addressStr; + newInput.height = -1; + newInput.confirmations = 0; + inputs.push(newInput); + } + } + } callback(null, inputs); @@ -720,7 +749,7 @@ AddressService.prototype.getOutputs = function(addressStr, options, callback) { } if(options.queryMempool) { - var mempoolOutputs = self.mempoolIndex[addressStr]; + var mempoolOutputs = self.mempoolOutputIndex[addressStr]; if (mempoolOutputs) { for(var i = 0; i < mempoolOutputs.length; i++) { // TODO copy diff --git a/test/data/transaction.json b/test/data/transaction.json new file mode 100644 index 00000000..0357cf05 --- /dev/null +++ b/test/data/transaction.json @@ -0,0 +1,3 @@ +[ + "010000000ec9e1d96d51f7d0a5b726184cda744207e51a596f13b564109de6ffc0653055cf000000006a4730440220263d48a2f4c3a2aa6032f96253c853531131171d8ae3d30586a45de7ba7c4006022077db4b39926877939baf59e8d357effe7674e7b12ad986c0a4cd99f2b7acafca012103e9100732bb534bea2f6d3b971914ec8403557306600c01c5ce63ec185737c732ffffffff0c16800642bdf90cbbd340be2e801bee9b907db6d59dc4c7cb269358c1e2593a000000006a4730440220085e39cb3a948559c1b1c88ba635eeef37a767322c1e4d96556e998302acb5dc0220421c03de78121692c538a9bc85a58fbe796384541fe87b6e91c39404318c390d012103e9100732bb534bea2f6d3b971914ec8403557306600c01c5ce63ec185737c732ffffffff559fde3733e950db9e3faea1a27872047a5c8bc14e8e6ac4a7b4f7b5f90c42c2000000006b483045022100a46a109a5acfc34b13c591ab69392e2dc2c3ea12d0900c5f7a539ea888e57ae0022015372bad56d63c6d08a5e66e0c9a63b2fc8dce245aa765e37dac06acb84c18d501210207e2a01d4a334c2d7c9ebacc5ea8a0d4b86fd54599b1aebe125d9e664be012c2ffffffff92760c236f6f18751c4ecab1dfcfebac7358a431b31bcd72b0a09d336233bdce000000006a47304402200857fa82e5b287c6ed5624cbc4fcd068a756a7a9ef785a73ce4e51b15a8aa34b022042cbc6f478b711539c6345d0b05d4bc9a9b5c34b2e4d25f234cf6784ff2eed19012103cab1f64f3d5f20a3f4a070694e6f93f5248b502b3842e369d81f05d07dec01e3ffffffff158669557e8a0b71288df37626601e636c5a8b3797f7f3357590313e8efe790a000000006b48304502210085e62cb95066540730b74aeae133277e511b5bf79de8c0ad60e784638e681ddf022043b456285569e0da133f527412218893f05a781d6f77f8cddd12eb90bfdc5937012103e9100732bb534bea2f6d3b971914ec8403557306600c01c5ce63ec185737c732ffffffffc1f9f57f1eda20f3b45669b3f0d1eae73b410ddf2b4fc1cfe10051a6051eff68000000006a47304402200fbe15c73446309040f4264567d0e8cc46691cf5d0626c443fc2dde716211e5402207f84e68e273755d140f346e029213dbc42b65cfb1e2ac41f72402c7ff45feffc012103e9100732bb534bea2f6d3b971914ec8403557306600c01c5ce63ec185737c732ffffffff39fa27e16c540a9bb796e65d4ac624fc33878e522461d6955764bf7b83c03ce8000000006a4730440220131e6aed76da389f21dfd7cd271fad73c5c533b1c18fbfabd6799a0d0e7dc80602205c38855bea0f1dfbbb608bc1b56c75b91a39980de121ffd9f880b522d412d821012103e9100732bb534bea2f6d3b971914ec8403557306600c01c5ce63ec185737c732ffffffff375fb7ae26eb637ccc007a669b9f50ed10fa53127504b80e0fd2be34c50becd7000000006b483045022100f0a9e585aa3113eae4bfb204f38f96e60dc613c04313aae931b677e4d8d7081d022014664874859f3d47447c6c0f257c28c74e8fdaedd5f781d752f3a4b651d3d179012103e9100732bb534bea2f6d3b971914ec8403557306600c01c5ce63ec185737c732ffffffff6f9d6a3c847e6112bb920424ca0d8d6e0956b128acb206e8fb58b2d2f2d7d46b000000006a4730440220736b198484cf5226616a539146e037a93cc75963885eefe58fc29a7be8123c750220619a456c0fe7437ec67c642d88e890344fc1c51a7b3cfc0ae283d61d0f176c5e012103e9100732bb534bea2f6d3b971914ec8403557306600c01c5ce63ec185737c732ffffffff3cccbd8090d60fcf28064333cf2f51ef0f838ba5e26a5e0f38807ee16d38a649000000006b483045022100e1ed25e9365e596d4fc3cbf278490d8ea765c4266c55f19311cf5da33f8d00750220361888a1738ebba827c0c947690b5f2a5f20e9f1be8956c3a34a4ba03f9e60f5012103e9100732bb534bea2f6d3b971914ec8403557306600c01c5ce63ec185737c732ffffffff7f4d60a2e961490aa465a7df461bf09b151bdc0c162f3bef0c1cbed6160d02c7000000006a47304402204e79b15a1db9a356f00dc9f2d559e31561cad1343ba5809a65b52bd868e3963e022055b9326ed5de9aa9970ec67a2ebf1a9dbf9ee513b64bd13837c87320bb4d6947012103e9100732bb534bea2f6d3b971914ec8403557306600c01c5ce63ec185737c732ffffffffe63b9370ba49a129e071750cbb300128015fdd90d7399f9c4e44934eabbaa2f7000000006b483045022100b9ceb2e376c0334d45bf08bfeb06dc250e7cb01d3a08e2fb3506388683552417022024c7c5bda385b904cca691fb6e1ad8c5eba5858a88a2112cb824dca72793b7a7012103e9100732bb534bea2f6d3b971914ec8403557306600c01c5ce63ec185737c732ffffffffc78b96fddededb6cbc1dff9de51f2743fd42e91de2506794b121928af4729528000000006a47304402201f05caddee5a0ff590b27c4ce25be1cbbeb45dc39679a1b8b0e10b1a378d84bc02203e31b01e14d891e9809c43a4df54494c626c5e47eaeeeb99ab4e02bd73c3d6cd012103e9100732bb534bea2f6d3b971914ec8403557306600c01c5ce63ec185737c732ffffffff30093916240e981a77cb869924fa0c38a894c24b1a6e7d26b117bb9caa7d5bbe000000006a4730440220483f297379daacee14babbf929708a861a991373bca3ed4eef240e2c156a162602205f1e93e375a897c6a9ddc3dc616ccf14137096ebd7888040e1053a769d21b945012103e9100732bb534bea2f6d3b971914ec8403557306600c01c5ce63ec185737c732ffffffff022897d411000000001976a91415354ee1828ed12f243f80dcb92c3a8205285f0188ac3be68c02000000001976a9143ecf8ff79932c3de33829a001236985872d10be188ac00000000" +] diff --git a/test/services/address/index.unit.js b/test/services/address/index.unit.js index 2d220b03..e588e0e5 100644 --- a/test/services/address/index.unit.js +++ b/test/services/address/index.unit.js @@ -10,6 +10,8 @@ var bitcore = require('bitcore'); var Networks = bitcore.Networks; var EventEmitter = require('events').EventEmitter; var errors = bitcorenode.errors; +var Transaction = require('../../../lib/transaction'); +var txData = require('../../data/transaction.json'); var mockdb = { }; @@ -24,7 +26,7 @@ var mocknode = { }; describe('Address Service', function() { - + var txBuf = new Buffer(txData[0], 'hex'); describe('#getAPIMethods', function() { it('should return the correct methods', function() { var am = new AddressService({node: mocknode}); @@ -547,9 +549,6 @@ describe('Address Service', function() { return testStream; } }; - am.node.services.bitcoind = { - getMempoolOutputs: sinon.stub().returns([]) - }; am.getOutputs(address, args, function(err, outputs) { should.not.exist(err); outputs.length.should.equal(1); @@ -575,18 +574,17 @@ describe('Address Service', function() { am.node.services.db.store = { createReadStream: sinon.stub().returns(readStream1) }; - var mempoolOutputs = [ + + am.mempoolOutputIndex = {}; + + am.mempoolOutputIndex[address] = [ { - address: '1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', txid: 'aa2db23f670596e96ed94c405fd11848c8f236d266ee96da37ecd919e53b4371', satoshis: 307627737, script: '76a914f6db95c81dea3d10f0ff8d890927751bf7b203c188ac', - blockHeight: 352532 + outputIndex: 0 } - ]; - am.node.services.bitcoind = { - getMempoolOutputs: sinon.stub().returns(mempoolOutputs) - }; + ] am.getOutputs(address, options, function(err, outputs) { should.not.exist(err); @@ -606,7 +604,8 @@ describe('Address Service', function() { outputs[2].address.should.equal(address); outputs[2].txid.should.equal('aa2db23f670596e96ed94c405fd11848c8f236d266ee96da37ecd919e53b4371'); outputs[2].script.should.equal('76a914f6db95c81dea3d10f0ff8d890927751bf7b203c188ac'); - outputs[2].blockHeight.should.equal(352532); + outputs[2].height.should.equal(-1); + outputs[2].confirmations.should.equal(0); done(); }); @@ -875,5 +874,47 @@ describe('Address Service', function() { }); }); }); + describe('#updateMempoolIndex', function() { + var am; + var db = {}; + var tx = Transaction().fromBuffer(txBuf); + before(function() { + am = new AddressService({node: mocknode}); + }); + + it('will update the input and output indexes', function() { + am.updateMempoolIndex(tx); + am.mempoolInputIndex['18Z29uNgWyUDtNyTKE1PaurbSR131EfANc'][0].txid.should.equal('45202ffdeb8344af4dec07cddf0478485dc65cc7d08303e45959630c89b51ea2'); + am.mempoolOutputIndex['12w93weN8oti3P1e5VYEuygqyujhADF7J5'][0].txid.should.equal('45202ffdeb8344af4dec07cddf0478485dc65cc7d08303e45959630c89b51ea2'); + am.mempoolInputIndex['1JT7KDYwT9JY9o2vyqcKNSJgTWeKfV3ui8'].length.should.equal(12); + am.mempoolOutputIndex['12w93weN8oti3P1e5VYEuygqyujhADF7J5'].length.should.equal(1); + }); + + }); + describe('#resetMempoolIndex', function() { + var am; + var db = {}; + + before(function() { + var testnode = { + db: db, + services: { + bitcoind: { + getMempoolTransactions: sinon.stub().returns([txBuf]), + on: sinon.stub() + } + } + }; + am = new AddressService({node: testnode}); + am.updateMempoolIndex = sinon.stub(); + + }); + it('will reset the input and output indexes', function(done) { + am.resetMempoolIndex(function() { + am.updateMempoolIndex.callCount.should.equal(1); + done(); + }); + }); + }); }); diff --git a/test/services/bitcoind.unit.js b/test/services/bitcoind.unit.js index 87633edc..15d87260 100644 --- a/test/services/bitcoind.unit.js +++ b/test/services/bitcoind.unit.js @@ -406,7 +406,7 @@ describe('Bitcoin Service', function() { ['sendTransaction', 2], ['getTransaction', 3], ['getTransactionWithBlockInfo', 3], - ['getMempoolOutputs', 1], + ['getMempoolTransactions', 0], ['addMempoolUncheckedTransaction', 1], ['getInfo', 0] ]; diff --git a/test/services/db.unit.js b/test/services/db.unit.js index c3106145..10631e9e 100644 --- a/test/services/db.unit.js +++ b/test/services/db.unit.js @@ -935,6 +935,7 @@ describe('DB Service', function() { var blockBuffer = new Buffer(blockData, 'hex'); var block = Block.fromBuffer(blockBuffer); db.node.services = {}; + db.runAllMempoolIndexes = sinon.stub().callsArg(0); db.node.services.bitcoind = { getBlock: sinon.stub().callsArgWith(1, null, blockBuffer), isSynced: sinon.stub().returns(true), @@ -954,6 +955,7 @@ describe('DB Service', function() { callback(); }; db.node.once('synced', function() { + db.runAllMempoolIndexes.callCount.should.equal(1); done(); }); db.sync();