Repairs to getAddressSummary.

This commit is contained in:
Chris Kleeschulte 2017-10-11 16:38:28 -04:00
parent 7da189acf9
commit 93db6790e2
No known key found for this signature in database
GPG Key ID: 33195D27EF6BDB7F
7 changed files with 226 additions and 164 deletions

View File

@ -82,6 +82,7 @@ AddressService.prototype.getAddressHistory = function(addresses, options, callba
};
// this is basically the same as _getAddressHistory apart from the summary
AddressService.prototype.getAddressSummary = function(address, options, callback) {
var self = this;
@ -106,99 +107,62 @@ AddressService.prototype.getAddressSummary = function(address, options, callback
transactions: []
};
self.getAddressHistory(address, options, function(err, results) {
// txid criteria
var start = self._encoding.encodeAddressIndexKey(address, options.from);
var end = self._encoding.encodeAddressIndexKey(address, options.to);
if (err) {
return callback(err);
}
var criteria = {
gte: start,
lte: end
};
var txs = results.items;
for(var i = 0; i < txs.length; i++) {
// txid stream
var txidStream = self._db.createKeyStream(criteria);
var tx = txs[i];
txidStream.on('close', function() {
txidStream.unpipe();
});
for(var j = 0; j < tx.outputs.length; j++) {
// tx stream
var txStream = new Transform({ objectMode: true, highWaterMark: 1000 });
var output = tx.outputs[j];
if (utils.getAddress(output, self._network) !== address) {
continue;
}
result.txApperances++;
result.totalReceivedSat += output.value;
result.balanceSat += output.value;
if (tx.confirmations === 0) {
result.unconfirmedTxApperances++;
result.unconfirmedBalanceSat += output.value;
}
}
for(j = 0; j < tx.inputs.length; j++) {
var input = tx.inputs[j];
if (utils.getAddress(input, self._network) !== address) {
continue;
}
result.totalSentSat += tx.__inputValues[j];
result.balanceSat -= tx.__inputValues[j];
if (tx.confirmations === 0) {
result.unconfirmedBalanceSat -= tx.__inputValues[j];
}
}
result.transactions.push(tx.txid());
}
txStream.on('end', function() {
result.balance = Unit.fromSatoshis(result.balanceSat).toBTC();
assert(result.balance >= 0, 'Balance can\'t be less than zero.');
result.totalReceived = Unit.fromSatoshis(result.totalReceivedSat).toBTC();
result.totalSent = Unit.fromSatoshis(result.totalSentSat).toBTC();
result.unconfirmedBalance = result.unconfirmedBalanceSat;
result.transactions = _.uniq(result.transactions);
result.unconfirmedBalance = Unit.fromSatoshis(result.unconfirmedBalanceSat).toBTC();
callback(null, result);
});
// pipe txids into tx stream for processing
txidStream.pipe(txStream);
txStream._transform = function(chunk, enc, callback) {
var key = self._encoding.decodeAddressIndexKey(chunk);
self._tx.getTransaction(key.txid, options, function(err, tx) {
if(err) {
log.error('Address Service: gettransaction ' + err);
txStream.emit('error', err);
return;
}
if (!tx) {
log.error('Address Service: Could not find tx for txid: ' + key.txid + '. This should not be possible, check indexes.');
txStream.emit('error', new Error('Txid should map to a tx.'));
return;
}
var confirmations = self._header.getBestHeight() - key.height + 1;
result.transactions.push(tx.txid());
result.txApperances++;
// is this an input?
if (key.input) {
result.balanceSat -= tx.__inputValues[key.index];
result.totalSentSat += tx.__inputValues[key.index];
if (confirmations < 1) {
result.unconfirmedBalanceSat -= tx.__inputValues[key.index];
result.unconfirmedTxApperances++;
}
return callback();
}
result.balanceSat += tx.outputs[key.index].value;
result.totalReceivedSat += tx.outputs[key.index].value;
if (confirmations < 1) {
result.unconfirmedBalanceSat += tx.__inputValues[key.index];
result.unconfirmedTxApperances++;
}
callback();
});
};
txStream.on('error', function(err) {
log.error('Address Service: txstream on error ' + err);
txStream.unpipe();
});
txStream._flush = function(callback) {
txStream.emit('end');
callback();
};
};
AddressService.prototype.getAddressUnspentOutputs = function(address, options, callback) {

View File

@ -49,7 +49,6 @@ BlockService.prototype.getAPIMethods = function() {
BlockService.prototype.getInfo = function(callback) {
var self = this;
callback(null, {
blocks: self.getTip().height,
connections: self._p2p.getNumberOfPeers(),
@ -830,12 +829,18 @@ BlockService.prototype._syncBlock = function(block) {
clearTimeout(self._getBlocksTimer);
if (self._lastBlockSaved === block.rhash()) {
return;
}
self._saveBlock(block, function(err) {
if(err) {
return self._handleError(err);
}
self._lastBlockSaved = block.rhash();
if (self._tip.height < self._header.getLastHeader().height) {
return self.emit('next block');
}
@ -928,12 +933,6 @@ BlockService.prototype._sync = function() {
return;
}
if (self._currentQuery === self._tip.hash) {
return;
}
self._currentQuery = self._tip.hash;
log.debug('Block Service: querying header service for next block using tip: ' + self._tip.hash);
self._header.getNextHash(self._tip, function(err, targetHash, nextHash) {
@ -942,13 +941,10 @@ BlockService.prototype._sync = function() {
return self._handleError(err);
}
// this might be because the peer reorg from our tip or the peer went away.
if (self._targetHash === targetHash) {
log.warning('Block Service: we asked for the same hash from the header service more than once.');
if (!targetHash && !nextHash) {
return self.emit('synced');
}
self._targetHash = targetHash;
// to ensure that we can receive blocks that were previously delivered
// this will lead to duplicate transactions being sent
self._p2p.clearInventoryCache();
@ -957,7 +953,6 @@ BlockService.prototype._sync = function() {
// then we must assume we've reorg'ed very shortly after
// we made this call and we should re-compute where we are
self._getBlocksTimer = setTimeout(function() {
self._currentQuery = null;
self.emit('next block');
}, 5000);

View File

@ -878,13 +878,18 @@ HeaderService.prototype._sync = function() {
HeaderService.prototype.getNextHash = function(tip, callback) {
var self = this;
var numResultsNeeded = 2;
// if the tip being passed in is the second to last block, then return 0 because there isn't a block
if (tip.height + 1 >= self._tip.height) {
numResultsNeeded = 1;
var numResultsNeeded = Math.min((self._tip.height - tip.height), 2);
if (numResultsNeeded === 0 && self._tip.hash === tip.hash) {
return callback();
}
if (numResultsNeeded <= 0) {
return callback(new Error('Header Service: block service is mis-aligned '));
}
var start = self._encoding.encodeHeaderHeightKey(tip.height + 1);
var end = self._encoding.encodeHeaderHeightKey(tip.height + 3);
var results = [];

View File

@ -165,4 +165,14 @@ utils.orderByConfirmations = function(list) {
});
};
// items is output or input
utils.getAddress = function(item, network) {
var address = item.getAddress();
if (!address) {
return;
}
address.network = network;
return address.toString();
};
module.exports = utils;

180
package-lock.json generated
View File

@ -833,7 +833,8 @@
"browser-stdout": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz",
"integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8="
"integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=",
"dev": true
},
"browserify-aes": {
"version": "1.0.6",
@ -1224,11 +1225,6 @@
"fs-exists-sync": "0.1.0"
}
},
"diff": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz",
"integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww=="
},
"dom-serializer": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz",
@ -1920,10 +1916,11 @@
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
},
"growl": {
"version": "1.10.3",
"resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz",
"integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q=="
"graceful-readlink": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
"integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=",
"dev": true
},
"handlebars": {
"version": "4.0.10",
@ -2047,11 +2044,6 @@
"sntp": "1.0.9"
}
},
"he": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
"integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0="
},
"hmac-drbg": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
@ -2708,6 +2700,63 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
"integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4="
},
"lodash._baseassign": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz",
"integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=",
"dev": true,
"requires": {
"lodash._basecopy": "3.0.1",
"lodash.keys": "3.1.2"
}
},
"lodash._basecopy": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz",
"integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=",
"dev": true
},
"lodash._basecreate": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz",
"integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=",
"dev": true
},
"lodash._getnative": {
"version": "3.9.1",
"resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz",
"integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=",
"dev": true
},
"lodash._isiterateecall": {
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz",
"integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=",
"dev": true
},
"lodash.create": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz",
"integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=",
"dev": true,
"requires": {
"lodash._baseassign": "3.2.0",
"lodash._basecreate": "3.0.3",
"lodash._isiterateecall": "3.0.9"
}
},
"lodash.isarguments": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
"integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=",
"dev": true
},
"lodash.isarray": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz",
"integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=",
"dev": true
},
"lodash.isplainobject": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
@ -2718,6 +2767,17 @@
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
"integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE="
},
"lodash.keys": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz",
"integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=",
"dev": true,
"requires": {
"lodash._getnative": "3.9.1",
"lodash.isarguments": "3.1.0",
"lodash.isarray": "3.0.4"
}
},
"lodash.mapvalues": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz",
@ -2880,54 +2940,96 @@
}
},
"mocha": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-4.0.1.tgz",
"integrity": "sha512-evDmhkoA+cBNiQQQdSKZa2b9+W2mpLoj50367lhy+Klnx9OV8XlCIhigUnn1gaTFLQCa0kdNhEGDr0hCXOQFDw==",
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-3.2.0.tgz",
"integrity": "sha1-fcT0XlCIB1FxpoiWgU5q6et6heM=",
"dev": true,
"requires": {
"browser-stdout": "1.3.0",
"commander": "2.11.0",
"debug": "3.1.0",
"diff": "3.3.1",
"commander": "2.9.0",
"debug": "2.2.0",
"diff": "1.4.0",
"escape-string-regexp": "1.0.5",
"glob": "7.1.2",
"growl": "1.10.3",
"he": "1.1.1",
"glob": "7.0.5",
"growl": "1.9.2",
"json3": "3.3.2",
"lodash.create": "3.1.1",
"mkdirp": "0.5.1",
"supports-color": "4.4.0"
"supports-color": "3.1.2"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"commander": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz",
"integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=",
"dev": true,
"requires": {
"ms": "2.0.0"
"graceful-readlink": "1.0.1"
}
},
"has-flag": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz",
"integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE="
"debug": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
"integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=",
"dev": true,
"requires": {
"ms": "0.7.1"
}
},
"diff": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz",
"integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8=",
"dev": true
},
"glob": {
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.0.5.tgz",
"integrity": "sha1-tCAqaQmbu00pKnwblbZoK2fr3JU=",
"dev": true,
"requires": {
"fs.realpath": "1.0.0",
"inflight": "1.0.6",
"inherits": "2.0.3",
"minimatch": "3.0.4",
"once": "1.4.0",
"path-is-absolute": "1.0.1"
}
},
"growl": {
"version": "1.9.2",
"resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz",
"integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=",
"dev": true
},
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"dev": true
},
"mkdirp": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"dev": true,
"requires": {
"minimist": "0.0.8"
}
},
"ms": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
"integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=",
"dev": true
},
"supports-color": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz",
"integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==",
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz",
"integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=",
"dev": true,
"requires": {
"has-flag": "2.0.0"
"has-flag": "1.0.0"
}
}
}

View File

@ -56,7 +56,7 @@
"istanbul": "^0.4.3",
"jshint": "^2.9.2",
"jshint-stylish": "^2.1.0",
"mocha": "",
"mocha": "3.2.0",
"proxyquire": "^1.3.1",
"rimraf": "^2.4.2",
"sinon": "^1.15.4"

View File

@ -130,37 +130,22 @@ describe('Address Service', function() {
describe('#AddressSummary', function() {
it('should get the address summary', function(done) {
var encoding = new Encoding(new Buffer('0001', 'hex'));
addressService._encoding = encoding;
var address = 'a';
var txid = tx.txid();
var data = [ null, encoding.encodeAddressIndexKey(address, 123, txid, 1, 0) ];
var inputValues = [120, 0, 120, 120];
tx.__inputValues = inputValues;
var getTransaction = sandbox.stub().callsArgWith(2, null, tx);
addressService._tx = { getTransaction: getTransaction };
addressService._header = { getBestHeight: function() { return 150; } };
it('should get the address summary, incoming', function(done) {
var txidStream = new Readable();
var _tx = tx;
_tx.__inputValues = [ 0, 0, 0, 0 ];
var results = { items: [_tx] };
txidStream._read = function() {
txidStream.push(data.pop());
};
var createReadStream = sandbox.stub().returns(txidStream);
addressService._db = { createKeyStream: createReadStream };
addressService.getAddressSummary(address, {}, function(err, res) {
sandbox.stub(addressService, 'getAddressHistory').callsArgWith(2, null, results);
addressService.getAddressSummary('1JoSiR4dBcSrGs2AZBP2gCHqCCsgzccsGb', {}, function(err, res) {
if (err) {
return done(err);
}
expect(getTransaction.calledOnce).to.be.true;
expect(res).to.deep.equal({ addrStr: 'a',
balance: 0.01139033,
balanceSat: 1139033,
totalReceived: 0.01139033,
totalReceivedSat: 1139033,
expect(res).to.deep.equal({ addrStr: '1JoSiR4dBcSrGs2AZBP2gCHqCCsgzccsGb',
balance: 0.005,
balanceSat: 500000,
totalReceived: 0.005,
totalReceivedSat: 500000,
totalSent: 0,
totalSentSat: 0,
unconfirmedBalance: 0,
@ -175,6 +160,7 @@ describe('Address Service', function() {
});
});
describe('#getAddressUnspentOutputs', function() {
it('should get address utxos', function(done) {