From 7e8d17ae13de1452d5a10e90194ca4262a0d1a78 Mon Sep 17 00:00:00 2001 From: Patrick Nagurny Date: Wed, 29 Jul 2015 10:24:29 -0400 Subject: [PATCH] refactor into getAddressHistory --- lib/db.js | 15 +++++++ lib/modules/address.js | 92 ++++++++++++++++++++++++++++++++++-------- lib/transaction.js | 6 ++- 3 files changed, 96 insertions(+), 17 deletions(-) diff --git a/lib/db.js b/lib/db.js index 7d7b6298..621520fe 100644 --- a/lib/db.js +++ b/lib/db.js @@ -80,6 +80,21 @@ DB.prototype.getTransaction = function(txid, queryMempool, callback) { }); }; +DB.prototype.getTransactionWithBlockInfo = function(txid, queryMempool, callback) { + this.getTransaction(txid, queryMempool, callback); + /*this.bitcoind.getTransactionWithBlockInfo(txid, queryMempool, function(err, obj) { + if(err) { + return callback(err); + } + + var tx = Transaction().fromBuffer(obj.buffer); + tx.__blockHeight = obj.blockHeight; + tx.__timestamp = obj.timestamp; + + callback(null, tx); + });*/ +} + DB.prototype.validateBlockData = function(block, callback) { // bitcoind does the validation setImmediate(callback); diff --git a/lib/modules/address.js b/lib/modules/address.js index ef7cce1e..81d5d727 100644 --- a/lib/modules/address.js +++ b/lib/modules/address.js @@ -34,8 +34,7 @@ AddressModule.prototype.getAPIMethods = function() { ['getOutputs', this, this.getOutputs, 2], ['getUnspentOutputs', this, this.getUnspentOutputs, 2], ['isSpent', this, this.isSpent, 2], - ['getTransactionsForAddress', this, this.getTransactionsForAddress, 2], - ['getSpendTxForOutput', this, this.getSpendTxForOutput, 3] + ['getAddressHistory', this, this.getAddressHistory, 2] ]; }; @@ -125,7 +124,7 @@ AddressModule.prototype.blockHandler = function(block, addOutput, callback) { operations.push({ type: action, key: [AddressModule.PREFIXES.SPENTS, input.prevTxId, input.outputIndex].join('-'), - value: txid + value: [txid, j].join(':') }); } } @@ -293,54 +292,115 @@ AddressModule.prototype.isSpent = function(output, queryMempool, callback) { }); }; -AddressModule.prototype.getSpendTxForOutput = function(txid, outputIndex, queryMempool, callback) { +AddressModule.prototype.getSpendInfoForOutput = function(txid, outputIndex, callback) { var self = this; var key = [AddressModule.PREFIXES.SPENTS, txid, outputIndex].join('-'); - this.db.store.get(key, function(err, spendTxId) { + this.db.store.get(key, function(err, value) { if(err) { return callback(err); } - self.db.getTransaction(spendTxId, queryMempool, callback); + value = value.split(':'); + + var info = { + txid: value[0], + inputIndex: value[1] + }; + + callback(null, info); }); }; -AddressModule.prototype.getTransactionsForAddress = function(address, queryMempool, callback) { +AddressModule.prototype.getAddressHistory = function(address, queryMempool, callback) { var self = this; + var txinfos = {}; + + function getTransactionInfo(txid, callback) { + if(txinfos[txid]) { + return callback(null, txinfos[txid]); + } + + self.db.getTransactionWithBlockInfo(txid, queryMempool, function(err, transaction) { + if(err) { + return callback(err); + } + + transaction.populateInputs(self.db, [], function(err) { + if(err) { + return callback(err); + } + + txinfos[transaction.hash] = { + satoshis: 0, + height: transaction.__height, + timestamp: transaction.__timestamp, + outputIndexes: [], + inputIndexes: [], + transaction: transaction + }; + + callback(null, txinfos[transaction.hash]); + }); + }); + } + + this.getOutputs(address, queryMempool, function(err, outputs) { if(err) { return callback(err); } - var transactions = []; - async.eachSeries( outputs, function(output, next) { - self.db.getTransaction(output.txid, queryMempool, function(err, tx) { + getTransactionInfo(output.txid, function(err, txinfo) { if(err) { return next(err); } - transactions.push(tx); + txinfo.outputIndexes.push(output.outputIndex); + txinfo.satoshis += output.satoshis; - self.getSpendTxForOutput(output.txid, output.outputIndex, queryMempool, function(err, tx) { + self.getSpendInfoForOutput(output.txid, output.outputIndex, function(err, spendInfo) { if(err instanceof levelup.errors.NotFoundError) { return next(); } else if(err) { return next(err); } - transactions.push(tx); - next(); + getTransactionInfo(spendInfo.txid, function(err, txinfo) { + if(err) { + return next(err); + } + + txinfo.inputIndexes.push(spendInfo.inputIndex); + txinfo.satoshis -= txinfo.transaction.inputs[spendInfo.inputIndex].output.satoshis; + next(); + }); }); }); }, function(err) { - callback(err, transactions); - }); + if(err) { + return callback(err); + } + + // convert to array + var history = []; + for(var txid in txinfos) { + history.push(txinfos[txid]); + } + + // sort by height + history.sort(function(a, b) { + return a.height < b.height; + }); + + callback(null, history); + } + ); }); }; diff --git a/lib/transaction.js b/lib/transaction.js index 76893db7..f2431409 100644 --- a/lib/transaction.js +++ b/lib/transaction.js @@ -54,6 +54,10 @@ Transaction.prototype._validateInputs = function(db, poolTransactions, callback) Transaction.prototype.populateInputs = function(db, poolTransactions, callback) { var self = this; + if(this.isCoinbase()) { + return setImmediate(callback); + } + async.each( this.inputs, function(input, next) { @@ -68,7 +72,7 @@ Transaction.prototype._populateInput = function(db, input, poolTransactions, cal return callback(new Error('Input is expected to have prevTxId as a buffer')); } var txid = input.prevTxId.toString('hex'); - db.getTransactionFromDB(txid, function(err, prevTx) { + db.getTransaction(txid, false, function(err, prevTx) { if(err instanceof levelup.errors.NotFoundError) { // Check the pool for transaction for(var i = 0; i < poolTransactions.length; i++) {