fixes. better reorg.
This commit is contained in:
parent
04c5f94997
commit
dd54dcad11
@ -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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -208,7 +208,7 @@ exports.block = {
|
||||
maxSigops: 1000000 / 50,
|
||||
maxSigopsCost: 4000000 / 50,
|
||||
maxOrphanTx: 1000000 / 100,
|
||||
medianTimeSpan: 11,
|
||||
medianTimespan: 11,
|
||||
bip16time: 1333238400
|
||||
};
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user