fixes. better reorg.

This commit is contained in:
Christopher Jeffrey 2016-03-05 04:21:19 -08:00
parent 04c5f94997
commit dd54dcad11
6 changed files with 206 additions and 96 deletions

View File

@ -1023,11 +1023,53 @@ BlockDB.prototype.resetHeight = function resetHeight(height, callback, emit) {
});
};
BlockDB.prototype.getTip = function getTip(callback) {
var tip;
assert(this.node);
assert(this.node.chain);
assert(this.node.chain.tip);
tip = this.node.chain.tip.hash;
return callback(null, tip);
};
BlockDB.prototype.resetHash = function resetHash(hash, callback, emit) {
var self = this;
this.getTip(function(err, tip) {
if (err)
return callback(err);
if (!tip)
return callback(new Error('Cannot reset to hash ' + hash));
(function next(tip) {
if (tip === hash)
return callback();
self.removeBlock(tip, function(err, block) {
if (err)
return callback(err);
if (!block)
return callback(new Error('Cannot reset all blocks.'));
// Emit the blocks we removed.
if (emit)
emit(block);
next(block.prevBlock);
});
})(hash);
});
};
BlockDB.prototype._getEntry = function _getEntry(height, callback) {
if (!this.node)
return callback();
return this.node.chain.db.getAsync(height, callback);
return this.node.chain.db.get(height, callback);
};
/**

View File

@ -334,17 +334,20 @@ Chain.prototype._preload = function _preload(callback) {
return;
blocks.forEach(function(data) {
var entry = bcoin.chainblock.fromRaw(self, height, data);
var block = bcoin.headers(entry);
var start;
var entry = bcoin.chainblock.fromRaw(self, data);
var block, start;
entry.height = height;
block = bcoin.headers(entry);
// Do some paranoid checks.
if (lastEntry && entry.prevBlock !== lastEntry.hash) {
start = Math.max(0, height - 2);
stream.destroy();
return self.resetHeight(start, function(e) {
if (e)
throw e;
return self.resetHeight(start, function(err) {
if (err)
throw err;
return callback(new Error('Corrupt headers.'), start + 1);
});
}
@ -354,17 +357,18 @@ Chain.prototype._preload = function _preload(callback) {
if (!block.verifyHeaders()) {
start = Math.max(0, height - 2);
stream.destroy();
return self.resetHeight(start, function(e) {
if (e)
throw e;
return self.resetHeight(start, function(err) {
if (err)
throw err;
return callback(new Error('Bad headers.'), start + 1);
});
}
lastEntry = entry;
// Calculate chainwork.
delete entry.chainwork;
entry.chainwork = entry.getChainwork();
entry.chainwork = entry.getChainwork(lastEntry);
lastEntry = entry;
// Make sure the genesis block is correct.
if (height === 0 && entry.hash !== network.genesis.hash) {
@ -375,8 +379,8 @@ Chain.prototype._preload = function _preload(callback) {
// Filthy hack to avoid writing
// redundant blocks to disk!
if (height <= chainHeight) {
self.db._cache(entry);
self.db._populate(entry);
self.db.addCache(entry);
self.db.bloom(entry.hash, 'hex');
} else {
self.db.save(entry);
}
@ -616,6 +620,7 @@ Chain.prototype._verify2 = function _verify(block, prev, callback) {
}
height = prev.height + 1;
prev.getMedianTime(function(err, medianTime) {
if (err)
return callback(err);
@ -1045,46 +1050,110 @@ Chain.prototype._findFork = function _findFork(fork, longer, callback) {
})();
};
Chain.prototype._reorganize = function _reorganize(entry, block, callback) {
Chain.prototype._reorganize = function _reorganize(entry, callback) {
var self = this;
function done(err) {
// Find the fork and connect/disconnect blocks.
// NOTE: Bitcoind disconnects and reconnects the
// forked block for some reason. We don't do this
// since it was already emitted for the wallet
// and mempool to handle. Technically bitcoind
// shouldn't have done it either.
return this._findFork(this.tip, entry, function(err, fork) {
if (err)
return callback(err);
self.emit('fork', block, {
height: entry.height,
expected: self.tip.hash,
received: entry.hash,
checkpoint: false
});
return callback();
}
return this._findFork(this.tip, entry, function(err, fork) {
if (err)
return done(err);
assert(fork);
self.db.resetHeight(fork.height, function(err) {
if (err)
return done(err);
if (!self.blockdb)
return done();
self.blockdb.resetHeight(fork.height, function(err) {
// Disconnect blocks/txs.
function disconnect(callback) {
self.db.resetHash(fork.hash, function(err) {
if (err)
return done(err);
return callback(err);
return done();
}, function(block) {
self.emit('remove block', block);
if (!self.blockdb)
return callback();
self.blockdb.resetHash(fork.hash, function(err) {
if (err)
return callback(err);
return callback();
}, function(block) {
self.emit('remove block', block);
});
}, function(entry) {
self.emit('remove entry', entry);
});
}
// Connect blocks/txs.
function connect(callback) {
var entries = [];
(function collect(entry) {
if (entry.hash === fork.hash)
return finish();
self.db.get(entry.prevBlock, function(err, entry) {
if (err)
return callback(err);
assert(entry);
entries.push(entry);
collect(entry);
});
})(entry);
function finish() {
entries = entries.slice().reverse();
assert(entries.length > 0);
entries.forEach(function(entry) {
self.emit('add entry', entry);
});
if (!self.blockdb)
return callback();
utils.forEachSerial(entries, function(err, entry) {
return self.blockdb.getBlock(entry.hash, function(err, block) {
if (err)
return callback(err);
assert(block);
self.emit('add block', block);
next();
});
}, function(err) {
if (err)
return callback(err);
return callback();
});
}
}
return disconnect(function(err) {
if (err)
return callback(err);
return connect(function(err) {
if (err)
return callback(err);
self.emit('fork', block, {
height: fork.height,
expected: self.tip.hash,
received: fork.hash,
checkpoint: false
});
return callback();
});
}, function(entry) {
self.emit('remove entry', entry);
});
});
};
@ -1104,7 +1173,7 @@ Chain.prototype._setBestChain = function _setBestChain(entry, block, callback) {
} else if (entry.prevBlock === this.tip.hash) {
done();
} else {
self._reorganize(entry, block, done);
self._reorganize(entry, done);
}
function done(err) {
@ -1218,34 +1287,6 @@ Chain.prototype.revertHeight = function revertHeight(height, callback, force) {
});
};
Chain.prototype._revertLast = function _revertLast(existing, callback, force) {
var self = this;
var unlock = this._lock(_revertLast, [existing, callback], force);
if (!unlock)
return;
function done(err, result) {
unlock();
callback(err, result);
}
this.resetHeight(existing.height - 1, function(err) {
if (err)
return done(err);
self._removeBlock(existing.hash, function(err, existingBlock) {
if (err)
return done(err);
if (existingBlock)
self.emit('remove block', existingBlock);
return done();
});
}, true);
};
Chain.prototype.syncHeight = function syncHeight(callback, force) {
var self = this;
var chainHeight;
@ -1303,13 +1344,6 @@ Chain.prototype.syncHeight = function syncHeight(callback, force) {
});
};
Chain.prototype.resetTime = function resetTime(ts) {
var entry = this.byTime(ts);
if (!entry)
return;
return this.resetHeight(entry.height);
};
Chain.prototype.resetTime = function resetTime(ts, callback, force) {
var self = this;
@ -1996,7 +2030,7 @@ Chain.prototype.getOrphanRoot = function getOrphanRoot(hash) {
Chain.prototype.getCurrentTarget = function getCurrentTarget(callback) {
if (!this.tip)
return callback(null, utils.toCompact(network.powLimit));
return this.getTarget(this.tip, null, callback);
return this.getTargetAsync(this.tip, null, callback);
};
Chain.prototype.getTargetAsync = function getTarget(last, block, callback) {

View File

@ -48,11 +48,6 @@ ChainBlock.prototype.getProof = function getProof() {
};
ChainBlock.prototype.getChainwork = function getChainwork(prev) {
// Workaround for genesis block
// being added _in_ chaindb.
if (!this.chain.db)
return this.getProof();
return (prev ? prev.chainwork : new bn(0)).add(this.getProof());
};
@ -183,10 +178,10 @@ ChainBlock.prototype._alloc = function _alloc(max, callback) {
if (this.previous.length >= max)
return callback();
if (!this.chain.db.cacheHash.has(entry.prevBlock))
if (!this.chain.db.hasCache(entry.prevBlock))
break;
entry = this.chain.db.cacheHash.get(entry.prevBlock);
entry = this.chain.db.getCache(entry.prevBlock);
}
(function next(err, entry) {

View File

@ -170,9 +170,16 @@ ChainDB.prototype.getCache = function getCache(hash) {
};
ChainDB.prototype.getHeight = function getHeight(hash, callback) {
if (hash == null || hash < 0)
return callback(null, -1);
if (typeof hash === 'number')
return callback(null, hash);
// When prevBlock=zero-hash
if (+hash === 0)
return callback(null, -1);
if (this.cacheHash.has(hash))
return callback(null, this.cacheHash.get(hash).height);
@ -191,6 +198,9 @@ ChainDB.prototype.getHeight = function getHeight(hash, callback) {
};
ChainDB.prototype.getHash = function getHash(height, callback) {
if (height == null || height < 0)
return callback(null, null);
if (typeof height === 'string')
return callback(null, height);
@ -202,7 +212,7 @@ ChainDB.prototype.getHash = function getHash(height, callback) {
return callback(err);
if (hash == null)
return callback();
return callback(null, null);
return callback(null, utils.toHex(hash));
});
@ -351,16 +361,20 @@ ChainDB.prototype.getTip = function getTip(callback) {
};
ChainDB.prototype.remove = function remove(block, callback, emit) {
var self = this;
var blocks = [];
var entry;
var entry, hash, height;
this.getBoth(block, function(err, hash, height) {
this.get(block, function(err, data) {
if (err)
return callback(err);
if (hash == null)
if (!data)
return callback();
hash = data.hash;
height = data.height;
(function next(err, nextHash) {
if (err && err.type !== 'NotFoundError')
return callback(err);
@ -425,6 +439,18 @@ ChainDB.prototype.remove = function remove(block, callback, emit) {
}
};
ChainDB.prototype.getNextHash = function getNextHash(hash, callback) {
return this.get('c/n/' + hash, function(err, nextHash) {
if (err && err.type !== 'NotFoundError')
return callback(err);
if (!nextHash)
return callback();
return callback(null, utils.toHex(nextHash));
});
};
ChainDB.prototype.resetHeight = function resetHeight(height, callback, emit) {
var self = this;
@ -433,6 +459,19 @@ ChainDB.prototype.resetHeight = function resetHeight(height, callback, emit) {
return this.removeEntry(height + 1, callback, emit);
};
ChainDB.prototype.resetHash = function resetHash(hash, callback, emit) {
var self = this;
return this.getNextHash(hash, function(err, hash) {
if (err)
return callback(err);
if (!hash)
return callback();
return self.remove(hash, callback, emit);
});
};
ChainDB.prototype.has = function has(height, callback) {
if (height == null || height < 0)
return callback(null, false);

View File

@ -727,7 +727,7 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer, callback) {
self.emit('chain-progress', self.chain.getProgress(), peer);
if (self.chain.height % 20 === 0) {
if (self.chain.total % 20 === 0) {
utils.debug(
'Status: tip=%s ts=%s height=%d blocks=%d orphans=%d active=%d'
+ ' queue=%d target=%s peers=%d pending=%d highest=%d jobs=%d',
@ -738,7 +738,7 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer, callback) {
self.chain.orphan.count,
self.request.activeBlocks,
peer.queue.block.length,
0,
block.bits,
self.peers.all.length,
self.chain.pending.length,
self.chain.bestHeight,

View File

@ -208,7 +208,7 @@ exports.block = {
maxSigops: 1000000 / 50,
maxSigopsCost: 4000000 / 50,
maxOrphanTx: 1000000 / 100,
medianTimeSpan: 11,
medianTimespan: 11,
bip16time: 1333238400
};