Fixed tests and repaired reorg logic.

This commit is contained in:
Chris Kleeschulte 2017-10-26 15:35:01 -04:00
parent 299b905d5b
commit 0a4e0dd9fd
No known key found for this signature in database
GPG Key ID: 33195D27EF6BDB7F
6 changed files with 77 additions and 30 deletions

View File

@ -1,5 +1,5 @@
{ {
"network": "livenet", "network": "testnet",
"port": 3001, "port": 3001,
"datadir": "/tmp", "datadir": "/tmp",
"services": [ "services": [
@ -7,16 +7,28 @@
"db", "db",
"header", "header",
"block", "block",
"mempool",
"address",
"transaction", "transaction",
"timestamp", "timestamp",
"mempool", "fee",
"address" "insight-api",
"web"
], ],
"servicesConfig": { "servicesConfig": {
"p2p": { "insight-api": {
"peers": [ "routePrefix": "api",
{ "ip": { "v4": "<some trusted full node>" } } "disableRateLimiter": true,
] "enableCache": true
},
"fee": {
"rpc": {
"user": "local",
"pass": "local",
"host": "localhost",
"protocol": "http",
"port": 18332
}
} }
} }
} }

View File

@ -421,6 +421,9 @@ BlockService.prototype.syncPercentage = function(callback) {
}; };
BlockService.prototype._detectReorg = function(block) { BlockService.prototype._detectReorg = function(block) {
// a block that is regarded as a "reorging block" could be one that was
// mined using a previously-orphaned block as its previous block.
// in this case, we want to completely ignore this block and move on
return bcoin.util.revHex(block.prevBlock) !== this._tip.hash; return bcoin.util.revHex(block.prevBlock) !== this._tip.hash;
}; };
@ -617,7 +620,7 @@ BlockService.prototype._findLatestValidBlockHeader = function(callback) {
async.until(function() { async.until(function() {
return iterCount++ >= self._recentBlockHashes.length || header; return iterCount++ > self._recentBlockHashes.length || header;
}, function(next) { }, function(next) {
@ -627,15 +630,24 @@ BlockService.prototype._findLatestValidBlockHeader = function(callback) {
return next(err); return next(err);
} }
var hash = blockServiceHash;
var height = blockServiceHeight;
blockServiceHeight--;
blockServiceHash = self._recentBlockHashes.get(hash);
if (!_header) { if (!_header) {
// try again with the previous hash of the current hash
return next(); return next();
} }
if (_header.hash === blockServiceHash && _header.height === blockServiceHeight--) { // if there was no reorg (the header service just received an orphan block, we should
// get the header of our tip here.
if (_header.hash === hash && _header.height === height) {
header = _header; header = _header;
return next();
} }
blockServiceHash = self._recentBlockHashes.get(blockServiceHash);
next(); next();
}); });
@ -648,9 +660,8 @@ BlockService.prototype._findLatestValidBlockHeader = function(callback) {
// the header could be undefined // the header could be undefined
// this means that the header service has no record of // this means that the header service has no record of
// any of our recent block hashes in its indexes. // any of our recent block hashes in its indexes.
// this means we've reorged deeper than recentBlockHashesCount number // if some joker mines a block using an orphan block as its prev block, then the effect of this will be
// of blocks or the header service connected to the wrong chain. // us detecting a reorg, but not actually reorging anything
// either way, we can't continue
assert(header, 'Block Service: we could not locate any of our recent block hashes in the header service ' + assert(header, 'Block Service: we could not locate any of our recent block hashes in the header service ' +
'index. Perhaps our header service sync\'ed to the wrong chain?'); 'index. Perhaps our header service sync\'ed to the wrong chain?');
@ -718,7 +729,7 @@ BlockService.prototype._handleReorg = function(callback) {
var self = this; var self = this;
// we want to ensure that we can reask for previously delievered inventory // we want to ensure that we can re-ask for previously delievered inventory
self._p2p.clearInventoryCache(); self._p2p.clearInventoryCache();
var commonAncestorHeader; var commonAncestorHeader;
@ -734,6 +745,11 @@ BlockService.prototype._handleReorg = function(callback) {
return next(err); return next(err);
} }
// nothing to do, skip and proceed
if (_commonAncestorHeader.hash === self._tip.hash) {
return callback();
}
commonAncestorHeader = _commonAncestorHeader; commonAncestorHeader = _commonAncestorHeader;
next(); next();

View File

@ -195,13 +195,33 @@ HeaderService.prototype.start = function(callback) {
self._adjustTipBackToCheckpoint(); self._adjustTipBackToCheckpoint();
if (self._tip.height === 0) { async.waterfall([
return self._setGenesisBlock(next);
}
self._adjustHeadersForCheckPointTip(next); function(next) {
if (self._tip.height === 0) {
return self._setGenesisBlock(next);
}
next();
},
function(next) {
self._adjustHeadersForCheckPointTip(next);
}
], function(err) {
if (err) {
return next(err);
}
next();
});
}
},
], function(err) { ], function(err) {
if (err) { if (err) {

View File

@ -44,22 +44,20 @@ describe('Block Service', function() {
}); });
}); });
describe('#_findCommonAncestorAndBlockHashesToRemove', function() { describe('#_findLatestValidBlockHeader', function() {
it('should find the common ancestor and hashes between the current chain and the new chain', function(done) { it('should find the latest valid block header whose hash is also in our block index', function(done) {
sandbox.stub(blockService, '_getBlock').callsArgWith(1, null, block2); blockService._tip = { hash: 'aa', height: 2 };
blockService._tip = { hash: 'aa' };
var headers = new utils.SimpleMap();
blockService._header = { getBlockHeader: sandbox.stub().callsArgWith(1, null, { hash: 'bb' }) }; blockService._header = { getBlockHeader: sandbox.stub().callsArgWith(1, null, { hash: 'aa', height: 2 }) };
blockService._findCommonAncestorAndBlockHashesToRemove(function(err, header, hashes) { blockService._findLatestValidBlockHeader(function(err, header) {
if(err) { if(err) {
return done(err); return done(err);
} }
expect(header).to.deep.equal({ hash: 'bb' }); expect(header).to.deep.equal({ hash: 'aa', height: 2 });
done(); done();
}); });

View File

@ -52,7 +52,7 @@ describe('Header Service', function() {
headerService.start(function() { headerService.start(function() {
expect(setGenesisBlock.calledOnce).to.be.true; expect(setGenesisBlock.calledOnce).to.be.true;
expect(adjustHeadersForCheckPointTip.calledOnce).to.be.false; expect(adjustHeadersForCheckPointTip.calledOnce).to.be.true;
expect(setListeners.calledOnce).to.be.true; expect(setListeners.calledOnce).to.be.true;
expect(headerService._tip).to.be.deep.equal({ height: 0, hash: '00' }); expect(headerService._tip).to.be.deep.equal({ height: 0, hash: '00' });
expect(headerService._encoding).to.be.instanceOf(Encoding); expect(headerService._encoding).to.be.instanceOf(Encoding);

View File

@ -32,12 +32,10 @@ describe('Mempool Service', function() {
it('should get the db prefix', function(done) { it('should get the db prefix', function(done) {
var getPrefix = sandbox.stub().callsArgWith(1, null, new Buffer('0001', 'hex')); var getPrefix = sandbox.stub().callsArgWith(1, null, new Buffer('0001', 'hex'));
var startSubs = sandbox.stub(mempoolService, '_startSubscriptions');
mempoolService._db = { getPrefix: getPrefix }; mempoolService._db = { getPrefix: getPrefix };
mempoolService.start(function() { mempoolService.start(function() {
expect(getPrefix.calledOnce).to.be.true; expect(getPrefix.calledOnce).to.be.true;
expect(startSubs.calledOnce).to.be.true;
done(); done();
}); });
}); });
@ -82,6 +80,9 @@ describe('Mempool Service', function() {
describe('#_onBlock', function() { describe('#_onBlock', function() {
it('should remove block\'s txs from database', function(done) { it('should remove block\'s txs from database', function(done) {
mempoolService.node = { openBus: sinon.stub() };
mempoolService._p2p = { getMempool: sinon.stub() };
sandbox.stub(mempoolService, '_startSubscriptions');
mempoolService.enable(); mempoolService.enable();
mempoolService.onBlock(block, function(err, ops) { mempoolService.onBlock(block, function(err, ops) {
expect(ops[0].type).to.deep.equal('del'); expect(ops[0].type).to.deep.equal('del');