From 27684e96611eaf33f1e014bad9e1e42e07662fe8 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Thu, 21 Jul 2016 12:36:11 -0700 Subject: [PATCH] if we're going to have checkpoints, might as well optimize the hell out of them. --- bin/node | 7 +++-- lib/bcoin/chain.js | 54 ++++++++++++++++++++--------------- lib/bcoin/chaindb.js | 5 +++- lib/bcoin/chainentry.js | 13 +++++++++ lib/bcoin/fullnode.js | 4 ++- lib/bcoin/peer.js | 2 ++ lib/bcoin/protocol/network.js | 25 ++++++---------- lib/bcoin/txdb.js | 5 ++++ lib/bcoin/walletdb.js | 3 +- 9 files changed, 73 insertions(+), 45 deletions(-) diff --git a/bin/node b/bin/node index 6caa9647..f8683219 100755 --- a/bin/node +++ b/bin/node @@ -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 }); diff --git a/lib/bcoin/chain.js b/lib/bcoin/chain.js index c46bef08..1b124d1a 100644 --- a/lib/bcoin/chain.js +++ b/lib/bcoin/chain.js @@ -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) diff --git a/lib/bcoin/chaindb.js b/lib/bcoin/chaindb.js index caeea2fb..6d35bb9f 100644 --- a/lib/bcoin/chaindb.js +++ b/lib/bcoin/chaindb.js @@ -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); diff --git a/lib/bcoin/chainentry.js b/lib/bcoin/chainentry.js index 9a8193b0..2d6c9a7a 100644 --- a/lib/bcoin/chainentry.js +++ b/lib/bcoin/chainentry.js @@ -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); }); diff --git a/lib/bcoin/fullnode.js b/lib/bcoin/fullnode.js index df15283b..b65c38cd 100644 --- a/lib/bcoin/fullnode.js +++ b/lib/bcoin/fullnode.js @@ -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 }); diff --git a/lib/bcoin/peer.js b/lib/bcoin/peer.js index ac440527..752dfd0f 100644 --- a/lib/bcoin/peer.js +++ b/lib/bcoin/peer.js @@ -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(); diff --git a/lib/bcoin/protocol/network.js b/lib/bcoin/protocol/network.js index 843a86f0..53e42613 100644 --- a/lib/bcoin/protocol/network.js +++ b/lib/bcoin/protocol/network.js @@ -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; diff --git a/lib/bcoin/txdb.js b/lib/bcoin/txdb.js index a85e9a46..88162cf6 100644 --- a/lib/bcoin/txdb.js +++ b/lib/bcoin/txdb.js @@ -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]; diff --git a/lib/bcoin/walletdb.js b/lib/bcoin/walletdb.js index faccc532..72511ef9 100644 --- a/lib/bcoin/walletdb.js +++ b/lib/bcoin/walletdb.js @@ -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)