if we're going to have checkpoints, might as well optimize the hell out of them.

This commit is contained in:
Christopher Jeffrey 2016-07-21 12:36:11 -07:00
parent 04565cfadb
commit 27684e9661
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
9 changed files with 73 additions and 45 deletions

View File

@ -12,15 +12,18 @@ process.on('uncaughtException', function(err) {
process.exit(1);
});
var fast = process.argv.indexOf('--fast') !== -1;
var node = new bcoin.fullnode({
logLevel: 'debug',
logFile: true,
db: 'leveldb',
prune: process.argv.indexOf('--prune') !== -1,
compact: process.argv.indexOf('--compact') !== -1,
useCheckpoints: process.argv.indexOf('--checkpoints') !== -1,
useCheckpoints: fast || process.argv.indexOf('--checkpoints') !== -1,
coinCache: fast || process.argv.indexOf('--coin-cache') !== -1,
selfish: process.argv.indexOf('--selfish') !== -1,
headers: process.argv.indexOf('--headers') !== -1,
headers: fast || process.argv.indexOf('--headers') !== -1,
parallel: process.argv.indexOf('--parallel') !== -1
});

View File

@ -112,7 +112,7 @@ Chain.prototype._init = function _init() {
return;
self.logger.info('Block %s (%d) added to chain.',
utils.revHex(entry.hash), entry.height);
entry.rhash, entry.height);
});
this.on('competitor', function(block, entry) {
@ -124,8 +124,8 @@ Chain.prototype._init = function _init() {
entry.height,
self.tip.height,
entry.height,
utils.revHex(self.tip.hash),
utils.revHex(entry.hash),
self.tip.rhash,
entry.rhash,
self.tip.chainwork.toString(),
entry.chainwork.toString(),
self.tip.chainwork.sub(entry.chainwork).toString());
@ -189,6 +189,12 @@ Chain.prototype._open = function open(callback) {
this.logger.info('Chain is loading.');
if (this.options.useCheckpoints)
this.logger.info('Checkpoints are enabled.');
if (this.options.coinCache)
this.logger.info('Coin cache is enabled.');
this.db.open(function(err) {
if (err)
return callback(err);
@ -467,7 +473,7 @@ Chain.prototype.isGenesis = function isGenesis(block) {
Chain.prototype.verify = function verify(block, prev, callback) {
var self = this;
var ret = {};
var height, ts, i, tx, medianTime, commitmentHash;
var i, height, ts, tx, medianTime, commitmentHash;
if (!block.verify(ret)) {
return callback(new VerifyError(block,
@ -488,6 +494,9 @@ Chain.prototype.verify = function verify(block, prev, callback) {
0));
}
if (prev.isHistorical())
return callback(null, this.state);
prev.getRetargetAncestors(function(err, ancestors) {
if (err)
return callback(err);
@ -732,6 +741,9 @@ Chain.prototype.checkDuplicates = function checkDuplicates(block, prev, callback
if (this.isGenesis(block))
return callback();
if (prev.isHistorical())
return callback();
if (this.network.block.bip34height === -1
|| height <= this.network.block.bip34height) {
return this.findDuplicates(block, prev, callback);
@ -809,8 +821,7 @@ Chain.prototype.findDuplicates = function findDuplicates(block, prev, callback)
Chain.prototype.checkInputs = function checkInputs(block, prev, state, callback) {
var self = this;
var height = prev.height + 1;
var scriptCheck = true;
var historical = false;
var historical = prev.isHistorical();
var sigops = 0;
var ret = {};
@ -820,14 +831,6 @@ Chain.prototype.checkInputs = function checkInputs(block, prev, state, callback)
if (this.isGenesis(block))
return callback();
// If we are an ancestor of a checkpoint, we can
// skip the input verification.
if (height <= this.network.checkpoints.lastHeight) {
if (this.options.useCheckpoints)
scriptCheck = false;
historical = true;
}
this.db.getCoinView(block, function(err, view) {
if (err)
return callback(err);
@ -845,6 +848,13 @@ Chain.prototype.checkInputs = function checkInputs(block, prev, state, callback)
}
}
// Skip everything if we're
// using checkpoints.
if (historical) {
view.addTX(tx);
return next();
}
// Verify sequence locks.
self.checkLocks(prev, tx, state.lockFlags, function(err, valid) {
if (err)
@ -886,18 +896,17 @@ Chain.prototype.checkInputs = function checkInputs(block, prev, state, callback)
if (err)
return callback(err);
if (historical)
return callback(null, view);
// Verify all txs in parallel.
utils.every(block.txs, function(tx, next) {
if (!scriptCheck)
return next(null, true);
tx.verifyAsync(state.flags, next);
}, function(err, verified) {
if (err)
return callback(err);
if (!verified) {
assert(!historical, 'BUG: Invalid inputs in historical data!');
return callback(new VerifyError(block,
'invalid',
'mandatory-script-verify-flag-failed',
@ -2067,8 +2076,7 @@ Chain.prototype.findLocator = function findLocator(locator, callback) {
*/
Chain.prototype.isActive = function isActive(prev, id, callback) {
// Optimization for main
if (this.network.type === 'main' && prev.height < 400000)
if (prev.isHistorical())
return callback(null, false);
this.getState(prev, id, function(err, state) {
@ -2346,7 +2354,7 @@ Chain.prototype.getLocks = function getLocks(prev, tx, flags, callback) {
var coinHeight;
if (tx.isCoinbase() || tx.version < 2 || !hasFlag)
return utils.asyncify(callback)(null, minHeight, minTime);
return callback(null, minHeight, minTime);
utils.forEachSerial(tx.inputs, function(input, next) {
if (input.sequence & disableFlag)
@ -2395,10 +2403,10 @@ Chain.prototype.getLocks = function getLocks(prev, tx, flags, callback) {
Chain.prototype.evalLocks = function evalLocks(prev, minHeight, minTime, callback) {
if (minHeight >= prev.height + 1)
return utils.asyncify(callback)(null, false);
return callback(null, false);
if (minTime === -1)
return utils.asyncify(callback)(null, true);
return callback(null, true);
prev.getMedianTimeAsync(function(err, medianTime) {
if (err)

View File

@ -209,9 +209,12 @@ function ChainDB(chain, options) {
// Key size: 66b (* 2)
this.coinWindow = ((165 * 1024 + 2300 * 4) + (2300 * 66 * 2)) * 5;
this.coinCache = new bcoin.lru.nil(this.coinWindow);
this.coinCache = new bcoin.lru.nil();
this.cacheHash = new bcoin.lru(this.cacheWindow, 1);
this.cacheHeight = new bcoin.lru(this.cacheWindow, 1);
if (this.options.coinCache)
this.coinCache = new bcoin.lru(this.coinWindow);
}
utils.inherits(ChainDB, AsyncObject);

View File

@ -446,6 +446,19 @@ ChainEntry.prototype.isSuperMajorityAsync = function isSuperMajorityAsync(versio
});
};
/**
* Test whether the entry is potentially an ancestor of a checkpoint.
* @returns {Boolean}
*/
ChainEntry.prototype.isHistorical = function isHistorical() {
if (this.chain.options.useCheckpoints) {
if (this.height + 1 <= this.network.checkpoints.lastHeight)
return true;
}
return false;
};
ChainEntry.prototype.__defineGetter__('rhash', function() {
return utils.revHex(this.hash);
});

View File

@ -62,7 +62,8 @@ function Fullnode(options) {
preload: false,
spv: false,
prune: this.options.prune,
useCheckpoints: this.options.useCheckpoints
useCheckpoints: this.options.useCheckpoints,
coinCache: this.options.coinCache
});
// Fee estimation.
@ -120,6 +121,7 @@ function Fullnode(options) {
fees: this.fees,
db: this.db,
location: this.location('walletdb'),
useCheckpoints: this.options.useCheckpoints,
verify: false
});

View File

@ -252,6 +252,8 @@ Peer.prototype._onConnect = function _onConnect() {
*/
Peer.prototype._onAck = function _onAck(err) {
var self = this;
if (err) {
this._error(err);
this.destroy();

View File

@ -112,13 +112,16 @@ main.checkpoints = {
225430: '32595730b165f097e7b806a679cf7f3e439040f750433808c101000000000000',
250000: '14d2f24d29bed75354f3f88a5fb50022fc064b02291fdf873800000000000000',
279000: '407ebde958e44190fa9e810ea1fc3a7ef601c3b0a0728cae0100000000000000',
295000: '83a93246c67003105af33ae0b29dd66f689d0f0ff54e9b4d0000000000000000'
295000: '83a93246c67003105af33ae0b29dd66f689d0f0ff54e9b4d0000000000000000',
// Checkpoints from btcd:
300255: 'b2f3a0f0de4120c1089d5f5280a263059f9b6e7c520428160000000000000000',
319400: '3bf115fd057391587ca39a531c5d4989e1adec9b2e05c6210000000000000000',
343185: '548536d48e7678fcfa034202dd45d4a76b1ad061f38b2b070000000000000000',
352940: 'ffc9520143e41c94b6e03c2fa3e62bb76b55ba2df45d75100000000000000000',
382320: 'b28afdde92b0899715e40362f56afdb20e3d135bedc68d0a0000000000000000'
};
main.checkpoints.tsLastCheckpoint = 1397080064;
main.checkpoints.txsLastCheckpoint = 36544669;
main.checkpoints.txsPerDay = 60000.0;
main.checkpoints.lastHeight = 295000;
main.checkpoints.lastHeight = 382320;
/**
* @const {Number}
@ -474,9 +477,6 @@ testnet.checkpoints = {
546: '70cb6af7ebbcb1315d3414029c556c55f3e2fc353c4c9063a76c932a00000000'
};
testnet.checkpoints.tsLastCheckpoint = 1338180505;
testnet.checkpoints.txsLastCheckpoint = 16341;
testnet.checkpoints.txsPerDay = 300;
testnet.checkpoints.lastHeight = 546;
testnet.halvingInterval = 210000;
@ -618,9 +618,6 @@ regtest.alertKey = new Buffer(
'hex');
regtest.checkpoints = {};
regtest.checkpoints.tsLastCheckpoint = 0;
regtest.checkpoints.txsLastCheckpoint = 0;
regtest.checkpoints.txsPerDay = 300;
regtest.checkpoints.lastHeight = 0;
regtest.halvingInterval = 150;
@ -760,9 +757,6 @@ segnet3.alertKey = new Buffer(
'hex');
segnet3.checkpoints = {};
segnet3.checkpoints.tsLastCheckpoint = 0;
segnet3.checkpoints.txsLastCheckpoint = 0;
segnet3.checkpoints.txsPerDay = 300;
segnet3.checkpoints.lastHeight = 0;
segnet3.halvingInterval = 210000;
@ -880,9 +874,6 @@ segnet4.alertKey = new Buffer(
'hex');
segnet4.checkpoints = {};
segnet4.checkpoints.tsLastCheckpoint = 0;
segnet4.checkpoints.txsLastCheckpoint = 0;
segnet4.checkpoints.txsPerDay = 300;
segnet4.checkpoints.lastHeight = 0;
segnet4.halvingInterval = 210000;

View File

@ -349,6 +349,11 @@ TXDB.prototype.addBlock = function addBlock(block, txs, callback, force) {
callback = utils.wrap(callback, unlock);
if (this.options.useCheckpoints) {
if (block.height < this.network.checkpoints.lastHeight)
return this.writeTip(block.hash, callback);
}
if (!Array.isArray(txs))
txs = [txs];

View File

@ -71,7 +71,8 @@ function WalletDB(options) {
this.tx = new bcoin.txdb(this, {
verify: this.options.verify,
useFilter: true
useCheckpoints: this.options.useCheckpoints,
useFilter: true,
});
if (bcoin.useWorkers)