Fixed off by one error when gathering blocks.
This commit is contained in:
parent
a8d709caf6
commit
7809476147
@ -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;
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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()));
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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));
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user