From f3c2134dfa2c3562248b81ae5ae08b3df2b7e570 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Sun, 17 Apr 2016 12:09:30 -0700 Subject: [PATCH] deployments. --- lib/bcoin/bloom.js | 19 +- lib/bcoin/chain.js | 334 +++++++++++++++++--------------- lib/bcoin/chainblock.js | 57 +++++- lib/bcoin/fullnode.js | 6 +- lib/bcoin/mempool.js | 96 +++------ lib/bcoin/node.js | 2 - lib/bcoin/peer.js | 10 +- lib/bcoin/protocol/constants.js | 36 +++- lib/bcoin/spvnode.js | 6 +- 9 files changed, 315 insertions(+), 251 deletions(-) diff --git a/lib/bcoin/bloom.js b/lib/bcoin/bloom.js index 79d796d5..e00b655a 100644 --- a/lib/bcoin/bloom.js +++ b/lib/bcoin/bloom.js @@ -11,24 +11,31 @@ var utils = require('./utils'); * Bloom Filter * @exports Bloom * @constructor - * @param {Number} size - Filter size in bytes. + * @param {Number|Bufer} size - Filter size in bytes, or filter itself. * @param {Number} n - Number of hash functions. * @param {Number} tweak - Seed value. * @property {Buffer} filter * @property {Number} size * @property {Number} n * @property {Number} tweak + * @property {Number} update - Update flag (see {@link constants.filterFlags}). */ -function Bloom(size, n, tweak) { +function Bloom(size, n, tweak, update) { if (!(this instanceof Bloom)) - return new Bloom(size, n, tweak); + return new Bloom(size, n, tweak, update); + + if (Buffer.isBuffer(size)) { + this.filter = size; + this.size = this.filter.length * 8; + } else { + this.filter = new Buffer(Math.ceil(size / 8)); + this.size = size; + } - this.filter = new Buffer(Math.ceil(size / 8)); - this.size = size; this.n = n; this.tweak = tweak; - this.update = null; + this.update = update; this.reset(); } diff --git a/lib/bcoin/chain.js b/lib/bcoin/chain.js index 74fc2ffc..bb6e1163 100644 --- a/lib/bcoin/chain.js +++ b/lib/bcoin/chain.js @@ -38,6 +38,7 @@ var VerifyError = utils.VerifyError; * @property {ChainBlock?} tip * @property {Number} height * @property {Boolean} segwitActive + * @property {Boolean} csvActive * @property {Object} orphan - Orphan map. * @emits Chain#open * @emits Chain#error @@ -78,6 +79,7 @@ function Chain(options) { this.tip = null; this.height = -1; this.segwitActive = null; + this.csvActive = null; this.orphan = { map: {}, @@ -218,11 +220,14 @@ Chain.prototype._init = function _init() { if (self.bestHeight === -1) network.height = tip.height; - self.isSegwitActive(function(err, result) { + self._getInitialState(function(err) { if (err) return self.emit('error', err); - if (result) + if (self.csvActive) + bcoin.debug('CSV is active.'); + + if (self.segwitActive) bcoin.debug('Segwit is active.'); self.loaded = true; @@ -463,11 +468,8 @@ Chain.prototype._verifyContext = function _verifyContext(block, prev, callback) Chain.prototype._verify = function _verify(block, prev, callback) { var self = this; - var flags = constants.flags.MANDATORY_VERIFY_FLAGS; - var lockFlags = constants.flags.MANDATORY_LOCKTIME_FLAGS; - var height, ts, i, tx, coinbaseHeight; - var medianTime, segwit, commitmentHash; var ret = {}; + var height, ts, i, tx, medianTime, commitmentHash; function done(err, result) { prev.free(); @@ -478,11 +480,11 @@ Chain.prototype._verify = function _verify(block, prev, callback) { return done(new VerifyError(block, 'invalid', ret.reason, ret.score)); if (this.options.spv || block.type !== 'block') - return done(); + return done(null, constants.flags.MANDATORY_VERIFY_FLAGS); // Skip the genesis block if (block.isGenesis()) - return done(null, flags); + return done(null, constants.flags.MANDATORY_VERIFY_FLAGS); // Ensure it's not an orphan if (!prev) @@ -502,103 +504,26 @@ Chain.prototype._verify = function _verify(block, prev, callback) { if (block.bits !== self.getTarget(prev, block)) return done(new VerifyError(block, 'invalid', 'bad-diffbits', 100)); - // For some reason bitcoind has p2sh in the - // mandatory flags by default, when in reality - // it wasn't activated until march 30th 2012. - // The first p2sh output and redeem script - // appeared on march 7th 2012, only it did - // not have a signature. See: - // 6a26d2ecb67f27d1fa5524763b49029d7106e91e3cc05743073461a719776192 - // 9c08a4d78931342b37fd5f72900fb9983087e6f46c4a097d8a1f52c74e28eaf6 - if (block.ts < constants.block.BIP16_TIME) - flags &= ~constants.flags.VERIFY_P2SH; + self._checkDeployments(block, prev, function(err, state) { + if (err) + return done(err); - // Only allow version 2 blocks (coinbase height) - // once the majority of blocks are using it. - if (block.version < 2 && prev.isOutdated(2)) - return done(new VerifyError(block, 'obsolete', 'bad-version', 0)); + // Expose the state of csv and segwit globally. + self.csvActive = state.csv; + self.segwitActive = state.segwit; - // Only allow version 3 blocks (sig validation) - // once the majority of blocks are using it. - if (block.version < 3 && prev.isOutdated(3)) - return done(new VerifyError(block, 'obsolete', 'bad-version', 0)); - - // Only allow version 4 blocks (checklocktimeverify) - // once the majority of blocks are using it. - if (block.version < 4 && prev.isOutdated(4)) - return done(new VerifyError(block, 'obsolete', 'bad-version', 0)); - - // Only allow version 5 blocks (segwit) - // once the majority of blocks are using it. - if (network.segwitHeight !== -1 && height >= network.segwitHeight) { - if (block.version < 5 && prev.isOutdated(5)) - return done(new VerifyError(block, 'obsolete', 'bad-version', 0)); - } - - // Only allow version 8 blocks (locktime median past) - // once the majority of blocks are using it. - // if (block.version < 8 && prev.isOutdated(8)) - // return done(new VerifyError(block, 'obsolete', 'bad-version', 0)); - - // Make sure the height contained in the coinbase is correct. - if (network.block.bip34height !== -1 && height >= network.block.bip34height) { - if (block.version >= 2 && prev.isUpgraded(2)) - coinbaseHeight = true; - } - - // Signature validation is now enforced (bip66) - if (block.version >= 3 && prev.isUpgraded(3)) - flags |= constants.flags.VERIFY_DERSIG; - - // CHECKLOCKTIMEVERIFY is now usable (bip65) - if (block.version >= 4 && prev.isUpgraded(4)) - flags |= constants.flags.VERIFY_CHECKLOCKTIMEVERIFY; - - // Segregrated witness is now usable (bip141 - segnet3) - if (network.segwitHeight !== -1 && height >= network.segwitHeight) { - if (block.version >= 5) { - if (prev.isUpgraded(5)) { - flags |= constants.flags.VERIFY_WITNESS; - segwit = true; - self.segwitActive = true; - } else { - self.segwitActive = false; - } - } - } - - // Segregrated witness is now usable (bip141 - segnet4) - if (network.deployments.witness) { - self.getState(prev, 'witness', function(err, state) { - if (err) - return callback(err); - - if (state === constants.thresholdStates.ACTIVE) { - flags |= constants.flags.VERIFY_WITNESS; - segwit = true; - self.segwitActive = true; - } else { - self.segwitActive = false; - } - - return finish(); - }); - } else { - finish(); - } - - function finish() { // Can't verify any further when merkleblock or headers. if (block.type !== 'block') - return done(null, flags); + return done(null, state.flags); // Make sure the height contained in the coinbase is correct. - if (coinbaseHeight) { + if (state.coinbaseHeight) { if (block.getCoinbaseHeight() !== height) return done(new VerifyError(block, 'invalid', 'bad-cb-height', 100)); } - if (segwit) { + // Check the commitment hash for segwit. + if (state.segwit) { commitmentHash = block.commitmentHash; if (commitmentHash) { if (!block.witnessNonce) { @@ -616,6 +541,8 @@ Chain.prototype._verify = function _verify(block, prev, callback) { } } + // Blocks that do not commit to + // witness data data cannot contain it. if (!commitmentHash) { if (block.hasWitness()) { return done(new VerifyError(block, @@ -626,7 +553,7 @@ Chain.prototype._verify = function _verify(block, prev, callback) { } // Get timestamp for tx.isFinal(). - ts = (lockFlags & constants.flags.MEDIAN_TIME_PAST) !== 0 + ts = (state.lockFlags & constants.flags.MEDIAN_TIME_PAST) !== 0 ? medianTime : block.ts; @@ -644,8 +571,122 @@ Chain.prototype._verify = function _verify(block, prev, callback) { } } - return done(null, flags); + return done(null, state.flags); + }); + }); +}; + +/** + * Check all deployments on a chain, ranging from p2sh to segwit. + * @private + * @param {Block} block + * @param {ChainBlock} prev + * @param {Function} callback - Returns [{@link VerifyError}, Object]. + */ + +Chain.prototype._checkDeployments = function _checkDeployments(block, prev, callback) { + var self = this; + var height = prev.height + 1; + var state = { + flags: constants.flags.MANDATORY_VERIFY_FLAGS, + lockFlags: constants.flags.MANDATORY_LOCKTIME_FLAGS, + coinbaseHeight: false, + segwit: false, + csv: false + }; + + // For some reason bitcoind has p2sh in the + // mandatory flags by default, when in reality + // it wasn't activated until march 30th 2012. + // The first p2sh output and redeem script + // appeared on march 7th 2012, only it did + // not have a signature. See: + // 6a26d2ecb67f27d1fa5524763b49029d7106e91e3cc05743073461a719776192 + // 9c08a4d78931342b37fd5f72900fb9983087e6f46c4a097d8a1f52c74e28eaf6 + if (block.ts < constants.block.BIP16_TIME) + state.flags &= ~constants.flags.VERIFY_P2SH; + + // Only allow version 2 blocks (coinbase height) + // once the majority of blocks are using it. + if (block.version < 2 && prev.isOutdated(2)) + return callback(new VerifyError(block, 'obsolete', 'bad-version', 0)); + + // Only allow version 3 blocks (sig validation) + // once the majority of blocks are using it. + if (block.version < 3 && prev.isOutdated(3)) + return callback(new VerifyError(block, 'obsolete', 'bad-version', 0)); + + // Only allow version 4 blocks (checklocktimeverify) + // once the majority of blocks are using it. + if (block.version < 4 && prev.isOutdated(4)) + return callback(new VerifyError(block, 'obsolete', 'bad-version', 0)); + + // Only allow version 5 blocks (segwit) + // once the majority of blocks are using it. + if (network.segwitHeight !== -1 && height >= network.segwitHeight) { + if (block.version < 5 && prev.isOutdated(5)) + return callback(new VerifyError(block, 'obsolete', 'bad-version', 0)); + } + + // Make sure the height contained in the coinbase is correct. + if (network.block.bip34height !== -1 && height >= network.block.bip34height) { + if (block.version >= 2 && prev.isUpgraded(2)) + state.coinbaseHeight = true; + } + + // Signature validation is now enforced (bip66) + if (block.version >= 3 && prev.isUpgraded(3)) + state.flags |= constants.flags.VERIFY_DERSIG; + + // CHECKLOCKTIMEVERIFY is now usable (bip65) + if (block.version >= 4 && prev.isUpgraded(4)) + state.flags |= constants.flags.VERIFY_CHECKLOCKTIMEVERIFY; + + // Segregrated witness is now usable (bip141 - segnet3) + if (network.segwitHeight !== -1 && height >= network.segwitHeight) { + if (block.version >= 5 && prev.isUpgraded(5)) { + state.flags |= constants.flags.VERIFY_WITNESS; + state.segwit = true; } + } + + utils.serial([ + function(next) { + // CHECKSEQUENCEVERIFY and median time + // past locktimes are now usable (bip9 & bip113). + self.isActive(prev, 'csv', function(err, active) { + if (err) + return next(err); + + if (active) { + state.flags |= constants.flags.VERIFY_CHECKSEQUENCEVERIFY; + state.lockFlags |= constants.flags.VERIFY_SEQUENCE; + state.lockFlags |= constants.flags.MEDIAN_TIME_PAST; + state.csv = true; + } + + return next(); + }); + }, + function(next) { + // Segregrated witness is now usable (bip141 - segnet4) + self.isActive(prev, 'witness', function(err, active) { + if (err) + return next(err); + + if (active) { + state.flags |= constants.flags.VERIFY_WITNESS; + state.segwit = true; + } + + return next(); + }); + } + ], function(err) { + if (err) + return callback(err); + + return callback(null, state); }); }; @@ -2111,6 +2152,29 @@ Chain.prototype.findLocator = function findLocator(locator, callback) { }, callback); }; +/** + * Check whether a versionbits deployment is active (BIP9: versionbits). + * @example + * chain.isActive(entry, 'witness', callback); + * @see https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki + * @param {ChainBlock} prev - Previous chain entry. + * @param {String} id - Deployment id. + * @param {Function} callback - Returns [Error, Number]. + */ + +Chain.prototype.isActive = function isActive(prev, id, callback) { + // Optimization for main + if (network.type === 'main' && prev.height < 400000) + return callback(null, false); + + this.getState(prev, id, function(err, state) { + if (err) + return callback(err); + + return callback(null, state === constants.thresholdStates.ACTIVE); + }); +}; + /** * Get chain entry state for a deployment (BIP9: versionbits). * @example @@ -2292,70 +2356,46 @@ Chain.prototype.computeBlockVersion = function computeBlockVersion(prev, callbac * A helper function to test whether segwit is active at any * given time. Since segwit affects almost all of bitcoin, it * is one deployment that needs to be checked frequently. + * @private * @param {Function} callback - Returns [Error, Boolean]. */ -Chain.prototype.isSegwitActive = function isSegwitActive(callback, force) { +Chain.prototype._getInitialState = function _getInitialState(callback) { var self = this; - var unlock; if (this.segwitActive != null) - return utils.asyncify(callback)(null, this.segwitActive); - - if (!network.witness) { - this.segwitActive = false; - return utils.asyncify(callback)(null, false); - } + return utils.nextTick(callback); if (!this.tip) - return utils.asyncify(callback)(null, false); - - // unlock = this._lock(isSegwitActive, [callback], force); - // if (!unlock) - // return; - // callback = utils.wrap(callback, unlock); - - if (network.type === 'segnet4') { - return this.tip.getPrevious(function(err, prev) { - if (err) - return callback(err); - - return self.getState(prev, 'witness', function(err, state) { - if (err) - return callback(err); - self.segwitActive = state === constants.thresholdStates.ACTIVE; - return callback(null, self.segwitActive); - }); - }); - } - - assert(network.type === 'segnet3'); - - if (!(network.segwitHeight !== -1 && this.tip.height >= network.segwitHeight)) - return utils.asyncify(callback)(null, false); + return utils.nextTick(callback); return this.tip.getPrevious(function(err, prev) { if (err) return callback(err); if (!prev) { + self.csvActive = false; self.segwitActive = false; - return callback(null, false); + return callback(); } prev.ensureAncestors(function(err) { - if (err) - return callback(err); - - if (self.tip.version >= 5 && prev.isUpgraded(5)) { + if (err) { prev.free(); - self.segwitActive = true; - return callback(null, true); + return callback(err); } + self._checkDeployments(self.tip, prev, function(err, state) { + if (err) { + prev.free(); + return callback(err); + } - prev.free(); - self.segwitActive = false; - return callback(null, false); + self.csvActive = state.csv; + self.segwitActive = state.segwit; + + prev.free(); + return callback(); + }); }); }); }; @@ -2375,14 +2415,9 @@ Chain.prototype.isSegwitActive = function isSegwitActive(callback, force) { * @param {Function} callback - Returns [Error, Boolean]. */ -Chain.prototype.checkFinal = function checkFinal(prev, tx, flags, callback, force) { +Chain.prototype.checkFinal = function checkFinal(prev, tx, flags, callback) { var height = prev.height + 1; - // var unlock = this._lock(checkFinal, [prev, tx, flags, callback], force); - // if (!unlock) - // return; - // callback = utils.wrap(callback, unlock); - function check(err, ts) { if (err) return callback(err); @@ -2490,14 +2525,9 @@ Chain.prototype.evalLocks = function evalLocks(entry, minHeight, minTime, callba * @param {Function} callback - Returns [Error, Boolean]. */ -Chain.prototype.checkLocks = function checkLocks(tx, flags, entry, callback, force) { +Chain.prototype.checkLocks = function checkLocks(tx, flags, entry, callback) { var self = this; - // var unlock = this._lock(checkLocks, [tx, flags, entry, callback], force); - // if (!unlock) - // return; - // callback = utils.wrap(callback, unlock); - this.getLocks(tx, flags, entry, function(err, minHeight, minTime) { if (err) return callback(err); diff --git a/lib/bcoin/chainblock.js b/lib/bcoin/chainblock.js index c73f44ae..0d993f3d 100644 --- a/lib/bcoin/chainblock.js +++ b/lib/bcoin/chainblock.js @@ -302,6 +302,22 @@ ChainBlock.prototype.getMedianTime = function getMedianTime(ancestors) { return median[median.length / 2 | 0]; }; +/** + * Get median time past asynchronously (see {@link ChainBlock#getMedianTime}). + * @param {Function} callback - Returns [Error, Number]. + */ + +ChainBlock.prototype.getMedianTimeAsync = function getMedianTimeAsync(callback) { + var self = this; + + return this.getAncestors(constants.block.MEDIAN_TIMESPAN, function(err, ancestors) { + if (err) + return callback(err); + + return callback(null, self.getMedianTime(ancestors)); + }); +}; + /** * Check isSuperMajority against majorityRejectOutdated. * @param {Number} version @@ -313,6 +329,20 @@ ChainBlock.prototype.isOutdated = function isOutdated(version, ancestors) { return this.isSuperMajority(version, network.block.majorityRejectOutdated, ancestors); }; +/** + * Check {@link ChainBlock#isUpgraded asynchronously}. + * @param {Number} version + * @param {Function} callback - Returns [Error, Boolean]. + * @returns {Boolean} + */ + +ChainBlock.prototype.isOutdatedAsync = function isOutdatedAsync(version, callback) { + return this.isSuperMajorityAsync( + version, + network.block.majorityRejectOutdated, + callback); +}; + /** * Check isSuperMajority against majorityEnforceUpgrade. * @param {Number} version @@ -324,6 +354,20 @@ ChainBlock.prototype.isUpgraded = function isUpgraded(version, ancestors) { return this.isSuperMajority(version, network.block.majorityEnforceUpgrade, ancestors); }; +/** + * Check {@link ChainBlock#isUpgraded} asynchronously. + * @param {Number} version + * @param {Function} callback + * @returns {Boolean} + */ + +ChainBlock.prototype.isUpgradedAsync = function isUpgradedAsync(version, callback) { + return this.isSuperMajorityAsync( + version, + network.block.majorityEnforceUpgrade, + callback); +}; + /** * Calculate found number of block versions within the majority window. * @param {Number} version @@ -351,18 +395,21 @@ ChainBlock.prototype.isSuperMajority = function isSuperMajority(version, require }; /** - * Get median time past asynchronously. - * @param {Function} callback - Returns [Error, Number]. + * Calculate {@link ChainBlock#isSuperMajority asynchronously}. + * @param {Number} version + * @param {Number} required + * @param {Function} callback - Returns [Error, Boolean]. + * @returns {Boolean} */ -ChainBlock.prototype.getMedianTimeAsync = function getMedianTimeAsync(callback) { +ChainBlock.prototype.isSuperMajorityAsync = function isSuperMajorityAsync(version, required, callback) { var self = this; - return this.getAncestors(constants.block.MEDIAN_TIMESPAN, function(err, ancestors) { + return this.getAncestors(network.block.majorityWindow, function(err, ancestors) { if (err) return callback(err); - return callback(null, self.getMedianTime(ancestors)); + return callback(null, self.isSuperMajority(version, required, ancestors)); }); }; diff --git a/lib/bcoin/fullnode.js b/lib/bcoin/fullnode.js index 96b89859..d795061e 100644 --- a/lib/bcoin/fullnode.js +++ b/lib/bcoin/fullnode.js @@ -9,6 +9,7 @@ module.exports = function(bcoin) { var utils = require('./utils'); var assert = utils.assert; +var network = bcoin.protocol.network; /** * Create a fullnode complete with a chain, @@ -47,9 +48,6 @@ function Fullnode(options) { if (!(this instanceof Fullnode)) return new Fullnode(options); - if (!options) - options = {}; - bcoin.node.call(this, options); this.loaded = false; @@ -86,7 +84,7 @@ Fullnode.prototype._init = function _init() { this.pool = new bcoin.pool({ chain: this.chain, mempool: this.mempool, - witness: this.network.witness, + witness: network.witness, listen: this.options.listen, selfish: this.options.selfish, spv: false diff --git a/lib/bcoin/mempool.js b/lib/bcoin/mempool.js index 1d49ad95..a8fe922d 100644 --- a/lib/bcoin/mempool.js +++ b/lib/bcoin/mempool.js @@ -89,63 +89,6 @@ function Mempool(options) { utils.inherits(Mempool, EventEmitter); -/** - * Standard verify flags. - * @const {VerifyFlags} - * @default - */ - -Mempool.flags = constants.flags.STANDARD_VERIFY_FLAGS; - -/** - * Mandatory verify flags. - * @const {VerifyFlags} - * @default - */ - -Mempool.mandatory = constants.flags.MANDATORY_VERIFY_FLAGS; - -/** - * Locktime flags. - * @const {LockFlags} - * @default - */ - -Mempool.lockFlags = constants.flags.STANDARD_LOCKTIME_FLAGS; - -/** - * Ancestor limit. - * @const {Number} - * @default - */ - -Mempool.ANCESTOR_LIMIT = 25; - -/** - * Maximum mempool size in bytes. - * @const {Number} - * @default - */ - -Mempool.MAX_MEMPOOL_SIZE = 300 << 20; - -/** - * The time at which transactions - * fall out of the mempool. - * @const {Number} - * @default - */ - -Mempool.MEMPOOL_EXPIRY = 72 * 60 * 60; - -/** - * Maximum number of orphan transactions. - * @const {Number} - * @default - */ - -Mempool.MAX_ORPHAN_TX = 100; - Mempool.prototype._lock = function _lock(func, args, force) { return this.locker.lock(func, args, force); }; @@ -340,12 +283,12 @@ Mempool.prototype.removeBlock = function removeBlock(block, callback, force) { Mempool.prototype.limitMempoolSize = function limitMempoolSize(callback) { var self = this; - if (this.size <= Mempool.MAX_MEMPOOL_SIZE) + if (this.size <= constants.mempool.MAX_MEMPOOL_SIZE) return callback(null, true); this.tx.getRange({ start: 0, - end: utils.now() - Mempool.MEMPOOL_EXPIRY + end: utils.now() - constants.mempool.MEMPOOL_EXPIRY }, function(err, txs) { if (err) return callback(err); @@ -360,7 +303,7 @@ Mempool.prototype.limitMempoolSize = function limitMempoolSize(callback) { if (err) return callback(err); - return callback(self.size <= Mempool.MAX_MEMPOOL_SIZE); + return callback(self.size <= constants.mempool.MAX_MEMPOOL_SIZE); }); }); }); @@ -526,8 +469,8 @@ Mempool.prototype.hasTX = function hasTX(hash, callback) { Mempool.prototype.addTX = function addTX(tx, callback, force) { var self = this; - var flags = Mempool.flags; - var lockFlags = Mempool.lockFlags; + var flags = constants.flags.STANDARD_VERIFY_FLAGS; + var lockFlags = constants.flags.STANDARD_LOCKTIME_FLAGS; var ret = {}; var now; @@ -561,6 +504,18 @@ Mempool.prototype.addTX = function addTX(tx, callback, force) { if (tx.isCoinbase()) return callback(new VerifyError(tx, 'invalid', 'coinbase', 100)); + if (this.requireStandard) { + if (!tx.isStandard(flags, ret)) + return callback(new VerifyError(tx, ret.reason, 0)); + + if (!this.chain.csvActive && tx.version >= 2) { + return callback(new VerifyError(tx, + 'nonstandard', + 'premature-version2-tx', + 0)); + } + } + this.chain.checkFinal(this.chain.tip, tx, lockFlags, function(err, isFinal) { if (err) return callback(err); @@ -568,11 +523,6 @@ Mempool.prototype.addTX = function addTX(tx, callback, force) { if (!isFinal) return callback(new VerifyError(tx, 'nonstandard', 'non-final', 0)); - if (self.requireStandard) { - if (!tx.isStandard(flags, ret)) - return callback(new VerifyError(tx, ret.reason, 0)); - } - self.seenTX(tx, function(err, exists) { if (err) return callback(err); @@ -600,7 +550,7 @@ Mempool.prototype.addTX = function addTX(tx, callback, force) { return callback(err); if (!tx.hasCoins()) { - if (self.totalSize > Mempool.MAX_MEMPOOL_SIZE) { + if (self.totalSize > constants.mempool.MAX_MEMPOOL_SIZE) { return callback(new VerifyError(tx, 'insufficientfee', 'mempool full', @@ -723,9 +673,9 @@ Mempool.prototype.removeUnchecked = function removeUnchecked(tx, callback) { Mempool.prototype.verify = function verify(tx, callback) { var self = this; var height = this.chain.height + 1; - var lockFlags = Mempool.lockFlags; - var flags = Mempool.flags; - var mandatory = Mempool.mandatory; + var lockFlags = constants.flags.STANDARD_LOCKTIME_FLAGS; + var flags = constants.flags.STANDARD_VERIFY_FLAGS; + var mandatory = constants.flags.MANDATORY_VERIFY_FLAGS; var ret = {}; var fee, now, free, minFee; @@ -808,7 +758,7 @@ Mempool.prototype.verify = function verify(tx, callback) { if (err) return callback(err); - if (count > Mempool.ANCESTOR_LIMIT) { + if (count > constants.mempool.ANCESTOR_LIMIT) { return callback(new VerifyError(tx, 'nonstandard', 'too-long-mempool-chain', @@ -934,7 +884,7 @@ Mempool.prototype.storeOrphan = function storeOrphan(tx, callback, force) { batch.put('m/D/' + hash, tx.toExtended(true)); - if (self.orphans > Mempool.MAX_ORPHAN_TX) { + if (self.orphans > constants.mempool.MAX_ORPHAN_TX) { return self.purgeOrphans(function(err) { if (err) return callback(err); diff --git a/lib/bcoin/node.js b/lib/bcoin/node.js index 44ab739e..46769abe 100644 --- a/lib/bcoin/node.js +++ b/lib/bcoin/node.js @@ -8,7 +8,6 @@ module.exports = function(bcoin) { var EventEmitter = require('events').EventEmitter; -var network = bcoin.protocol.network; var utils = require('./utils'); /** @@ -31,7 +30,6 @@ function Node(options) { this.options = options; - this.network = network; this.mempool = null; this.pool = null; this.chain = null; diff --git a/lib/bcoin/peer.js b/lib/bcoin/peer.js index 2104f0ac..d36da4e3 100644 --- a/lib/bcoin/peer.js +++ b/lib/bcoin/peer.js @@ -645,10 +645,12 @@ Peer.prototype._emitMerkle = function _emitMerkle() { }; Peer.prototype._handleFilterLoad = function _handleFilterLoad(payload) { - var size = payload.filter.length * 8; - this.filter = new bcoin.bloom(size, payload.n, payload.tweak); - this.filter.filter = payload.filter; - this.filter.update = payload.update; + this.filter = new bcoin.bloom( + payload.filter, + payload.n, + payload.tweak, + payload.update + ); this.relay = true; }; diff --git a/lib/bcoin/protocol/constants.js b/lib/bcoin/protocol/constants.js index fb38d16c..922b40b3 100644 --- a/lib/bcoin/protocol/constants.js +++ b/lib/bcoin/protocol/constants.js @@ -314,7 +314,6 @@ exports.hashTypeByVal = utils.revMap(exports.hashType); exports.block = { MAX_SIZE: 1000000, MAX_SIGOPS: 1000000 / 50, - MAX_ORPHAN_TX: 1000000 / 100, MEDIAN_TIMESPAN: 11, BIP16_TIME: 1333238400, SIGHASH_LIMIT: 1300000000 @@ -355,6 +354,41 @@ exports.script = { MAX_OP_RETURN: 80 }; +exports.mempool = {}; + +/** + * Ancestor limit. + * @const {Number} + * @default + */ + +exports.mempool.ANCESTOR_LIMIT = 25; + +/** + * Maximum mempool size in bytes. + * @const {Number} + * @default + */ + +exports.mempool.MAX_MEMPOOL_SIZE = 300 << 20; + +/** + * The time at which transactions + * fall out of the mempool. + * @const {Number} + * @default + */ + +exports.mempool.MEMPOOL_EXPIRY = 72 * 60 * 60; + +/** + * Maximum number of orphan transactions. + * @const {Number} + * @default + */ + +exports.mempool.MAX_ORPHAN_TX = 100; + /** * Reject codes. Note that `internal` and higher * are not meant for use on the p2p network. diff --git a/lib/bcoin/spvnode.js b/lib/bcoin/spvnode.js index ff24f9ae..db5bc169 100644 --- a/lib/bcoin/spvnode.js +++ b/lib/bcoin/spvnode.js @@ -9,6 +9,7 @@ module.exports = function(bcoin) { var utils = require('./utils'); var assert = utils.assert; +var network = bcoin.protocol.network; /** * Create an spv node which only maintains @@ -36,9 +37,6 @@ function SPVNode(options) { if (!(this instanceof SPVNode)) return new SPVNode(options); - if (!options) - options = {}; - bcoin.node.call(this, options); this.loaded = false; @@ -62,7 +60,7 @@ SPVNode.prototype._init = function _init() { this.pool = new bcoin.pool({ chain: this.chain, - witness: this.network.witness, + witness: network.witness, selfish: true, listen: false, spv: true