From 3c27f072918685e7b04582b2e19dffa2be5bb94a Mon Sep 17 00:00:00 2001 From: Patrick Nagurny Date: Mon, 23 Jan 2017 16:59:26 -0500 Subject: [PATCH] getAddressHistory --- lib/services/address/encoding.js | 61 ++++++++++++++++-------- lib/services/address/index.js | 81 +++++++++++++++++++++++++++++--- 2 files changed, 117 insertions(+), 25 deletions(-) diff --git a/lib/services/address/encoding.js b/lib/services/address/encoding.js index c8abcce9..62a114df 100644 --- a/lib/services/address/encoding.js +++ b/lib/services/address/encoding.js @@ -13,30 +13,53 @@ function Encoding(prefix) { this.prefix = prefix; } +Encoding.prototype.getTerminalKey = function(startKey) { + var endKey = Buffer.from(startKey); + endKey.writeUInt8(startKey.readUInt8(startKey.length - 1) + 1, startKey.length - 1); + return endKey; +}; + Encoding.prototype.encodeAddressIndexKey = function(address, isSpent, height, txid, index, spending) { + // TODO if later params are given but not earlier ones, throw an error + var buffers = [this.prefix]; + var addressSizeBuffer = new Buffer(1); addressSizeBuffer.writeUInt8(address.length); var addressBuffer = new Buffer(address, 'utf8'); - var heightBuffer = new Buffer(4); - heightBuffer.writeUInt32BE(height); - var txidBuffer = new Buffer(txid, 'hex'); - var indexBuffer = new Buffer(4); - indexBuffer.writeUInt32BE(index); - var spendingBuffer = new Buffer(1); - spendingBuffer.writeUInt8(spending); - var isSpentBuffer = new Buffer(1); - isSpentBuffer.writeUInt8(isSpent); - return Buffer.concat([ - this.prefix, - addressSizeBuffer, - addressBuffer, - isSpentBuffer, - heightBuffer, - txidBuffer, - indexBuffer, - spendingBuffer - ]); + buffers.push(addressSizeBuffer); + buffers.push(addressBuffer); + + if(isSpent !== undefined) { + var isSpentBuffer = new Buffer(1); + isSpentBuffer.writeUInt8(isSpent); + buffers.push(isSpentBuffer); + } + + if(height !== undefined) { + var heightBuffer = new Buffer(4); + heightBuffer.writeUInt32BE(height); + buffers.push(heightBuffer); + } + + if(txid) { + var txidBuffer = new Buffer(txid, 'hex'); + buffers.push(txidBuffer); + } + + if(index !== undefined) { + var indexBuffer = new Buffer(4); + indexBuffer.writeUInt32BE(index); + buffers.push(indexBuffer); + } + + if(spending !== undefined) { + var spendingBuffer = new Buffer(1); + spendingBuffer.writeUInt8(spending); + buffers.push(spendingBuffer); + } + + return Buffer.concat(buffers); }; Encoding.prototype.decodeAddressIndexKey = function(buffer) { diff --git a/lib/services/address/index.js b/lib/services/address/index.js index 2094e079..3ba26e61 100644 --- a/lib/services/address/index.js +++ b/lib/services/address/index.js @@ -1058,13 +1058,82 @@ AddressService.prototype.isSpent = function(output, options, callback) { * @param {Function} callback */ AddressService.prototype.getAddressHistory = function(addresses, options, callback) { + var self = this; - //var history = new AddressHistory({ - // node: this.node, - // options: options, - // addresses: addresses - //}); - //history.get(callback); + var txids = []; + + async.eachLimit(addresses, self.concurrency, function(address, next) { + self.getAddressTxids(address, options, function(err, tmpTxids) { + if(err) { + return next(err); + } + + txids = _.union(txids, tmpTxids); + return next(); + }); + }, function(err) { + async.mapLimit(txids, self.concurrency, function(txid, next) { + self.node.services.transaction.getTransaction(txid, function(err, tx) { + if(err) { + return next(err); + } + + var txObj = tx.toObject(); + for(var i = 0; i < txObj.inputs.length; i++) { + txObj.inputs[i].satoshis = tx.__inputValues[i]; + } + + next(null, txObj); + }); + }, callback); + }); +}; + +AddressService.prototype.getAddressTxids = function(address, options, callback) { + var self = this; + + self._getAddressTxidsByIndex(address, false, options.start, options.end, function(err, txids1) { + if(err) { + return callback(err); + } + + self._getAddressTxidsByIndex(address, true, options.start, options.end, function(err, txids2) { + if(err) { + return callback(err); + } + + callback(null, _.union(txids1, txids2)); + }); + }); +}; + +AddressService.prototype._getAddressTxidsByIndex = function(address, isSpent, startHeight, endHeight, callback) { + var self = this; + + var txids = {}; + + var start = self.encoding.encodeAddressIndexKey(address, isSpent, startHeight); + var end = self.encoding.encodeAddressIndexKey(address, isSpent, endHeight); + + var stream = self.store.createKeyStream({ + gte: start, + lt: end + }); + + var streamErr = null; + + stream.on('data', function(buffer) { + var key = self.encoding.decodeAddressIndexKey(buffer); + txids[key.txid] = true; + }); + + stream.on('end', function() { + callback(streamErr, Object.keys(txids)); + }); + + stream.on('error', function(err) { + streamErr = err; + }); }; /**