Fixed off by one error when gathering blocks.

This commit is contained in:
Chris Kleeschulte 2017-08-08 17:53:07 -04:00
parent a8d709caf6
commit 7809476147
8 changed files with 61 additions and 49 deletions

View File

@ -33,22 +33,20 @@ inherits(BlockService, BaseService);
BlockService.dependencies = [ 'p2p', 'db', 'header' ]; BlockService.dependencies = [ 'p2p', 'db', 'header' ];
BlockService.MAX_BLOCKS = 1;
// --- public prototype functions // --- public prototype functions
BlockService.prototype.getAPIMethods = function() { BlockService.prototype.getAPIMethods = function() {
var methods = [ var methods = [
['getBlock', this, this.getBlock, 1], ['getBlock', this, this.getBlock, 1],
['getRawBlock', this, this.getRawBlock, 1], ['getRawBlock', this, this.getRawBlock, 1],
['getBlockOverview', this, this.getBlockOverview, 1], ['getBlockOverview', this, this.getBlockOverview, 1],
['getBestBlockHash', this, this.getBestBlockHash, 0] ['getBestBlockHash', this, this.getBestBlockHash, 0],
['syncPercentage', this, this.syncPercentage, 0]
]; ];
return methods; return methods;
}; };
BlockService.prototype.getBestBlockHash = function() { BlockService.prototype.getBestBlockHash = function(callback) {
var headers = this._header.getAllHeaders(); callback(this._header.getAllHeaders().getLastIndex().hash);
return headers[headers.length - 1].hash;
}; };
BlockService.prototype.getBlock = function(arg, callback) { BlockService.prototype.getBlock = function(arg, callback) {
@ -165,10 +163,13 @@ BlockService.prototype.subscribe = function(name, emitter) {
}; };
BlockService.prototype._syncPercentage = function() {
var ratio = this._tip.height/this._header.getAllHeaders().size;
return (ratio*100).toFixed(2);
};
BlockService.prototype.syncPercentage = function(callback) { BlockService.prototype.syncPercentage = function(callback) {
var p2pHeight = this._p2p.getBestHeight(); callback(null, this._syncPercentage());
var percentage = ((p2pHeight / (this._tip.height || p2pHeight)) * 100).toFixed(2);
callback(null, percentage);
}; };
BlockService.prototype.unsubscribe = function(name, emitter) { BlockService.prototype.unsubscribe = function(name, emitter) {
@ -460,7 +461,7 @@ BlockService.prototype.onBlock = function(block, callback) {
}); });
}; };
BlockService.prototype._onBlock = function(block) { BlockService.prototype._onBlock = function(block, header) {
if (this.node.stopping || this._reorging) { if (this.node.stopping || this._reorging) {
return; return;
@ -468,6 +469,12 @@ BlockService.prototype._onBlock = function(block) {
log.debug('Block Service: new block: ' + block.rhash()); log.debug('Block Service: new block: ' + block.rhash());
// if this block's height is not what we expect, do not process.
if (header.height !== this._tip.height + 1) {
log.debug('Block Service: New block appears to be a newer block than we can handle, skipping.');
return;
}
var reorg = this._detectReorg(block); var reorg = this._detectReorg(block);
if (reorg) { if (reorg) {
this._handleReorg(block, this._header.getAllHeaders()); this._handleReorg(block, this._header.getAllHeaders());
@ -494,7 +501,7 @@ BlockService.prototype._setTip = function(tip) {
BlockService.prototype._startSync = function() { BlockService.prototype._startSync = function() {
this._numNeeded = this._bestHeight - this._tip.height; this._numNeeded = this._header.getAllHeaders().size - this._tip.height;
if (this._numNeeded <= 0) { if (this._numNeeded <= 0) {
return; return;
} }
@ -525,22 +532,33 @@ BlockService.prototype._sync = function() {
} }
var headers = this._header.getAllHeaders(); var headers = this._header.getAllHeaders();
var size = headers.size - 1; var lastHeaderIndex = headers.size - 1;
if (this._tip.height < size) { if (this._tip.height < lastHeaderIndex) {
if (this._tip.height % 100 === 0) { if (this._tip.height % 144 === 0) {
log.info('Block Service: Blocks download progress: ' + this._tip.height + '/' + log.info('Block Service: Blocks download progress: ' +
this._bestHeight + ' (' + (this._tip.height / this._bestHeight*100).toFixed(2) + '%)'); this._tip.height + '/' + headers.size +
' (' + this._syncPercentage() + '%)');
} }
var end = headers.getIndex(Math.min(this._tip.height + BlockService.MAX_BLOCKS + 1, size)); // the end should be our current tip height + 2 blocks, but if we only
var endHash = end ? end.hash : null; // have one block to go, then the end should be 0, so we just get the last block
var endHash;
if (this._tip.height === lastHeaderIndex - 1) {
endHash = 0;
} else {
var end = headers.getIndex(this._tip.height + 2);
endHash = end ? end.hash : null;
}
this._p2p.getBlocks({ startHash: this._tip.hash, endHash: endHash }); this._p2p.getBlocks({ startHash: this._tip.hash, endHash: endHash });
return; return;
} }
log.info('Block Service: The best block hash is: ' + this._tip.hash +
' at height: ' + this._tip.height);
}; };
module.exports = BlockService; module.exports = BlockService;

View File

@ -181,23 +181,17 @@ HeaderService.prototype._onBlock = function(block) {
log.debug('Header Service: new block: ' + hash); log.debug('Header Service: new block: ' + hash);
// this is case where a block was requested by referencing a var header = this._headers.get(hash);
// hash from our own headers set. if (!header) {
if (this._headers.get(hash)) { header = block.toHeaders().toJSON();
header.timestamp = header.ts;
for (var i = 0; i < this.subscriptions.block.length; i++) { header.prevHash = header.prevBlock;
this.subscriptions.block[i].emit('header/block', block); this._onHeaders([header]);
}
return;
} }
var header = block.toHeaders().toJSON(); for (var i = 0; i < this.subscriptions.block.length; i++) {
header.timestamp = header.ts; this.subscriptions.block[i].emit('header/block', block);
header.prevHash = header.prevBlock; }
this._onHeaders([header]);
}; };
HeaderService.prototype._onHeaders = function(headers) { HeaderService.prototype._onHeaders = function(headers) {
@ -240,7 +234,8 @@ HeaderService.prototype._onHeaders = function(headers) {
return; return;
} }
log.debug('Header Service: download complete.'); log.info('Header Service: ' + self._headers.size - 1 + ' headers downloaded and persisted.');
log.info('Header Service: ' + self._headers.getLastIndex().hash + ' is the best block hash.');
// at this point, we can check our header list to see if our starting tip diverged from the tip // at this point, we can check our header list to see if our starting tip diverged from the tip
// that we have now // that we have now
@ -300,7 +295,7 @@ HeaderService.prototype._startSync = function() {
HeaderService.prototype._sync = function() { HeaderService.prototype._sync = function() {
log.debug('Header Service: download progress: ' + this._tip.height + '/' + log.debug('Header Service: download progress: ' + this._tip.height + '/' +
this._bestHeight + ' (' + (this._tip.height / this._bestHeight*100).toFixed(2) + '%)'); this._bestHeight + ' (' + (this._tip.height / this._bestHeight*100.00).toFixed(2) + '%)');
this._p2p.getHeaders({ startHash: this._tip.hash }); this._p2p.getHeaders({ startHash: this._tip.hash });
@ -323,7 +318,7 @@ HeaderService.prototype._getPersistedHeaders = function(callback) {
var start = self._encoding.encodeHeaderKey(0); var start = self._encoding.encodeHeaderKey(0);
var end = self._encoding.encodeHeaderKey(startingHeight + 1); var end = self._encoding.encodeHeaderKey(startingHeight + 1);
log.debug('Getting persisted headers from genesis block to block ' + (self._tip.height + 1)); log.debug('Getting persisted headers from genesis block to block ' + self._tip.height);
var criteria = { var criteria = {
gte: start, gte: start,

View File

@ -176,6 +176,7 @@ P2P.prototype._connect = function() {
}; };
P2P.prototype._getBestHeight = function() { P2P.prototype._getBestHeight = function() {
if (this._peers === 0) { if (this._peers === 0) {
return 0; return 0;
} }

View File

@ -29,10 +29,6 @@ TimestampService.prototype.getAPIMethods = function() {
]; ];
}; };
TimestampService.prototype.syncPercentage = function(callback) {
return callback(null, ((this._tip.height / this._block.getBestBlockHeight()) * 100).toFixed(2) + '%');
};
TimestampService.prototype.getBlockHashesByTimestamp = function(low, high, callback) { TimestampService.prototype.getBlockHashesByTimestamp = function(low, high, callback) {
assert(_.isNumber(low) && _.isNumber(high) && low < high, assert(_.isNumber(low) && _.isNumber(high) && low < high,

View File

@ -37,7 +37,6 @@ TransactionService.prototype.getAPIMethods = function() {
['getRawTransaction', this, this.getRawTransaction, 1], ['getRawTransaction', this, this.getRawTransaction, 1],
['getTransaction', this, this.getTransaction, 1], ['getTransaction', this, this.getTransaction, 1],
['getDetailedTransaction', this, this.getDetailedTransaction, 1], ['getDetailedTransaction', this, this.getDetailedTransaction, 1],
['syncPercentage', this, this.syncPercentage, 0],
['getInputValues', this, this._getInputValues, 1] ['getInputValues', this, this._getInputValues, 1]
]; ];
}; };
@ -90,13 +89,13 @@ TransactionService.prototype.getTransaction = function(txid, options, callback)
} }
// TODO: this could cause crazy amounts of recursion if input values are missing from the entire chain of txs // TODO: this could cause crazy amounts of recursion if input values are missing from the entire chain of txs
self._addMissingInputValues(_tx, callback); self._addMissingInputValues(_tx, options, callback);
}); });
}; };
TransactionService.prototype._addMissingInputValues = function(tx, callback) { TransactionService.prototype._addMissingInputValues = function(tx, options, callback) {
// if we have cache misses from when we populated input values, // if we have cache misses from when we populated input values,
// then we must go and find them after the fact (lazy-load). // then we must go and find them after the fact (lazy-load).
@ -110,7 +109,7 @@ TransactionService.prototype._addMissingInputValues = function(tx, callback) {
} }
var outputIndex = input.prevout.index; var outputIndex = input.prevout.index;
self.getTransaction(input.prevout.txid(), function(err, _tx) { self.getTransaction(input.prevout.txid(), options, function(err, _tx) {
if (err || !_tx) { if (err || !_tx) {
return next(err || new Error('tx not found for tx id: ' + input.prevout.txid())); return next(err || new Error('tx not found for tx id: ' + input.prevout.txid()));

View File

@ -101,7 +101,8 @@ describe('Block Service', function() {
var processBlock = sandbox.stub(blockService, '_processBlock'); var processBlock = sandbox.stub(blockService, '_processBlock');
blockService._unprocessedBlocks = []; blockService._unprocessedBlocks = [];
blockService._header = { getAllHeaders: getAllHeaders }; blockService._header = { getAllHeaders: getAllHeaders };
blockService._onBlock(block); blockService._tip = { height: 123 };
blockService._onBlock(block, { height: 124 });
expect(detectReorg.callCount).to.equal(1); expect(detectReorg.callCount).to.equal(1);
expect(processBlock.callCount).to.equal(1); expect(processBlock.callCount).to.equal(1);
}); });
@ -115,7 +116,8 @@ describe('Block Service', function() {
var handleReorg = sandbox.stub(blockService, '_handleReorg'); var handleReorg = sandbox.stub(blockService, '_handleReorg');
blockService._unprocessedBlocks = []; blockService._unprocessedBlocks = [];
blockService._header = { getAllHeaders: getAllHeaders }; blockService._header = { getAllHeaders: getAllHeaders };
blockService._onBlock(block); blockService._tip = { height: 123 };
blockService._onBlock(block, { height: 124 });
expect(handleReorg.callCount).to.equal(1); expect(handleReorg.callCount).to.equal(1);
expect(detectReorg.callCount).to.equal(1); expect(detectReorg.callCount).to.equal(1);
expect(processBlock.callCount).to.equal(0); expect(processBlock.callCount).to.equal(0);
@ -166,6 +168,7 @@ describe('Block Service', function() {
var sync = sandbox.stub(blockService, '_sync'); var sync = sandbox.stub(blockService, '_sync');
blockService._bestHeight = 100; blockService._bestHeight = 100;
blockService._tip = { height: 99 }; blockService._tip = { height: 99 };
blockService._header = { getAllHeaders: sinon.stub().returns({ size: 100 }) };
blockService._startSync(); blockService._startSync();
expect(sync.calledOnce).to.be.true; expect(sync.calledOnce).to.be.true;
expect(blockService._numNeeded).to.equal(1); expect(blockService._numNeeded).to.equal(1);

View File

@ -111,7 +111,7 @@ describe('Header Service', function() {
headerService._bestHeight = { height: 1 }; headerService._bestHeight = { height: 1 };
headerService._headers = { headerService._headers = {
getIndex: function() { return { hash: header.hash }; }, getIndex: function() { return { hash: header.hash }; },
getLastIndex: sinon.stub(), getLastIndex: sinon.stub().returns({ hash: 'aa' }),
set: sinon.stub() set: sinon.stub()
}; };
var getChainwork = sandbox.stub(headerService, '_getChainwork').returns(new BN(1)); var getChainwork = sandbox.stub(headerService, '_getChainwork').returns(new BN(1));

View File

@ -124,9 +124,9 @@ describe('Transaction Service', function() {
describe('#_addMissingInputValues', function() { describe('#_addMissingInputValues', function() {
it('should add missing input values on a tx', function(done) { it('should add missing input values on a tx', function(done) {
sandbox.stub(txService, 'getTransaction').callsArgWith(1, null, tx); sandbox.stub(txService, 'getTransaction').callsArgWith(2, null, tx);
tx.__inputValues = []; tx.__inputValues = [];
txService._addMissingInputValues(tx, function(err, tx) { txService._addMissingInputValues(tx, {}, function(err, tx) {
if (err) { if (err) {
return done(err); return done(err);
} }