diff --git a/lib/bcoin/chain.js b/lib/bcoin/chain.js index ccf23a18..e17369e4 100644 --- a/lib/bcoin/chain.js +++ b/lib/bcoin/chain.js @@ -468,42 +468,45 @@ Chain.prototype._verify = function _verify(block, prev, callback) { var ret = {}; var height, ts, i, tx, medianTime, commitmentHash; - function done(err, result) { - prev.free(); - callback(err, result); - } - if (!block.verify(ret)) - return done(new VerifyError(block, 'invalid', ret.reason, ret.score)); + return callback(new VerifyError(block, 'invalid', ret.reason, ret.score)); if (this.options.spv || block.type !== 'block') - return done(null, constants.flags.MANDATORY_VERIFY_FLAGS); + return callback(null, constants.flags.MANDATORY_VERIFY_FLAGS); // Skip the genesis block if (block.isGenesis()) - return done(null, constants.flags.MANDATORY_VERIFY_FLAGS); + return callback(null, constants.flags.MANDATORY_VERIFY_FLAGS); // Ensure it's not an orphan if (!prev) - return done(new VerifyError(block, 'invalid', 'bad-prevblk', 0)); + return callback(new VerifyError(block, 'invalid', 'bad-prevblk', 0)); - prev.ensureAncestors(function(err) { + prev.getRetargetAncestors(function(err, ancestors) { if (err) - return done(err); + return callback(err); height = prev.height + 1; medianTime = prev.getMedianTime(); // Ensure the timestamp is correct - if (block.ts <= medianTime) - return done(new VerifyError(block, 'invalid', 'time-too-old', 0)); + if (block.ts <= medianTime) { + return callback(new VerifyError(block, + 'invalid', + 'time-too-old', + 0)); + } - if (block.bits !== self.getTarget(prev, block)) - return done(new VerifyError(block, 'invalid', 'bad-diffbits', 100)); + if (block.bits !== self.getTarget(prev, block, ancestors)) { + return callback(new VerifyError(block, + 'invalid', + 'bad-diffbits', + 100)); + } - self._checkDeployments(block, prev, function(err, state) { + self._checkDeployments(block, prev, ancestors, function(err, state) { if (err) - return done(err); + return callback(err); // Expose the state of csv and segwit globally. self.csvActive = state.csv; @@ -511,12 +514,16 @@ Chain.prototype._verify = function _verify(block, prev, callback) { // Can't verify any further when merkleblock or headers. if (block.type !== 'block') - return done(null, state.flags); + return callback(null, state.flags); // Make sure the height contained in the coinbase is correct. if (state.coinbaseHeight) { - if (block.getCoinbaseHeight() !== height) - return done(new VerifyError(block, 'invalid', 'bad-cb-height', 100)); + if (block.getCoinbaseHeight() !== height) { + return callback(new VerifyError(block, + 'invalid', + 'bad-cb-height', + 100)); + } } // Check the commitment hash for segwit. @@ -524,13 +531,13 @@ Chain.prototype._verify = function _verify(block, prev, callback) { commitmentHash = block.commitmentHash; if (commitmentHash) { if (!block.witnessNonce) { - return done(new VerifyError(block, + return callback(new VerifyError(block, 'invalid', 'bad-witness-merkle-size', 100)); } if (commitmentHash !== block.getCommitmentHash('hex')) { - return done(new VerifyError(block, + return callback(new VerifyError(block, 'invalid', 'bad-witness-merkle-match', 100)); @@ -542,7 +549,7 @@ Chain.prototype._verify = function _verify(block, prev, callback) { // witness data data cannot contain it. if (!commitmentHash) { if (block.hasWitness()) { - return done(new VerifyError(block, + return callback(new VerifyError(block, 'invalid', 'unexpected-witness', 100)); @@ -552,7 +559,7 @@ Chain.prototype._verify = function _verify(block, prev, callback) { // Check block cost (different from block size // check in non-contextual verification). if (block.getCost() > constants.block.MAX_COST) { - return done(new VerifyError(block, + return callback(new VerifyError(block, 'invalid', 'bad-blk-cost', 100)); @@ -570,14 +577,14 @@ Chain.prototype._verify = function _verify(block, prev, callback) { // Transactions must be finalized with // regards to nSequence and nLockTime. if (!tx.isFinal(height, ts)) { - return done(new VerifyError(block, + return callback(new VerifyError(block, 'invalid', 'bad-txns-nonfinal', 10)); } } - return done(null, state); + return callback(null, state); }); }); }; @@ -587,11 +594,12 @@ Chain.prototype._verify = function _verify(block, prev, callback) { * @private * @param {Block} block * @param {ChainBlock} prev + * @param {ChainBlock[]} ancestors * @param {Function} callback - Returns * [{@link VerifyError}, {@link DeploymentState}]. */ -Chain.prototype._checkDeployments = function _checkDeployments(block, prev, callback) { +Chain.prototype._checkDeployments = function _checkDeployments(block, prev, ancestors, callback) { var self = this; var height = prev.height + 1; var state = { @@ -615,41 +623,41 @@ Chain.prototype._checkDeployments = function _checkDeployments(block, prev, call // Only allow version 2 blocks (coinbase height) // once the majority of blocks are using it. - if (block.version < 2 && prev.isOutdated(2)) + if (block.version < 2 && prev.isOutdated(2, ancestors)) 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)) + if (block.version < 3 && prev.isOutdated(3, ancestors)) 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)) + if (block.version < 4 && prev.isOutdated(4, ancestors)) return callback(new VerifyError(block, 'obsolete', 'bad-version', 0)); // Only allow version 5 blocks (bip141 - segnet3) // once the majority of blocks are using it. if (network.segwitHeight !== -1 && height >= network.segwitHeight) { - if (block.version < 5 && prev.isOutdated(5)) + if (block.version < 5 && prev.isOutdated(5, ancestors)) return callback(new VerifyError(block, 'obsolete', 'bad-version', 0)); } // Make sure the height contained in the coinbase is correct. - if (block.version >= 2 && prev.isUpgraded(2)) + if (block.version >= 2 && prev.isUpgraded(2, ancestors)) state.coinbaseHeight = true; // Signature validation is now enforced (bip66) - if (block.version >= 3 && prev.isUpgraded(3)) + if (block.version >= 3 && prev.isUpgraded(3, ancestors)) state.flags |= constants.flags.VERIFY_DERSIG; // CHECKLOCKTIMEVERIFY is now usable (bip65) - if (block.version >= 4 && prev.isUpgraded(4)) + if (block.version >= 4 && prev.isUpgraded(4, ancestors)) 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)) { + if (block.version >= 5 && prev.isUpgraded(5, ancestors)) { state.flags |= constants.flags.VERIFY_WITNESS; state.segwit = true; } @@ -2046,7 +2054,6 @@ Chain.prototype.getTargetAsync = function getTargetAsync(last, block, callback) /** * Calculate the target synchronously. _Must_ * have ancestors pre-allocated. - * @see ChainBlock#ensureAncestors. * @param {ChainBlock} last - Previous entry. * @param {Block|MerkleBlock|null} - Current block. * @param {Function} callback - returns [Error, Number] @@ -2061,9 +2068,6 @@ Chain.prototype.getTarget = function getTarget(last, block, ancestors) { if (!last) return powLimit; - if (!ancestors) - ancestors = last.ancestors; - // Do not retarget if ((last.height + 1) % network.pow.retargetInterval !== 0) { if (network.pow.allowMinDifficultyBlocks) { @@ -2391,21 +2395,17 @@ Chain.prototype._getInitialState = function _getInitialState(callback) { return callback(); } - prev.ensureAncestors(function(err) { - if (err) { - prev.free(); + prev.getRetargetAncestors(function(err, ancestors) { + if (err) return callback(err); - } - self._checkDeployments(self.tip, prev, function(err, state) { - if (err) { - prev.free(); + + self._checkDeployments(self.tip, prev, ancestors, function(err, state) { + if (err) return callback(err); - } self.csvActive = state.csv; self.segwitActive = state.segwit; - prev.free(); return callback(); }); }); diff --git a/lib/bcoin/chainblock.js b/lib/bcoin/chainblock.js index bb1477bf..1a0d6d36 100644 --- a/lib/bcoin/chainblock.js +++ b/lib/bcoin/chainblock.js @@ -57,7 +57,6 @@ function ChainBlock(chain, data, prev) { this.nonce = data.nonce; this.height = data.height; this.chainwork = data.chainwork || this.getChainwork(prev); - this.ancestors = []; } /** @@ -99,7 +98,7 @@ ChainBlock.prototype.isGenesis = function isGenesis() { * @param {Function} callback */ -ChainBlock.prototype.ensureAncestors = function ensureAncestors(callback) { +ChainBlock.prototype.getRetargetAncestors = function getRetargetAncestors(callback) { var majorityWindow = network.block.majorityWindow; var medianTimespan = constants.block.MEDIAN_TIMESPAN; var powDiffInterval = network.pow.retargetInterval; @@ -107,33 +106,7 @@ ChainBlock.prototype.ensureAncestors = function ensureAncestors(callback) { var max = Math.max(majorityWindow, medianTimespan); if ((this.height + 1) % powDiffInterval === 0 || allowMinDiff) max = Math.max(max, powDiffInterval); - assert(this.ancestors.length === 0); - return this.alloc(max, callback); -}; - -/** - * Allocate ancestors. - * @param {Number} max - Number of ancestors. - * @param {Function} callback - */ - -ChainBlock.prototype.alloc = function alloc(max, callback) { - var self = this; - var i; - - return this.getAncestors(max, function(err, ancestors) { - if (err) - return callback(err); - - assert(ancestors); - - self.ancestors.length = 0; - - for (i = 0; i < ancestors.length; i++) - self.ancestors.push(ancestors[i]); - - return callback(); - }); + return this.getAncestors(max, callback); }; /** @@ -144,14 +117,11 @@ ChainBlock.prototype.alloc = function alloc(max, callback) { ChainBlock.prototype.getAncestors = function getAncestors(max, callback) { var entry = this; - var ancestors = this.ancestors.slice(); + var ancestors = []; if (max === 0) return callback(null, []); - if (ancestors.length) - entry = ancestors.pop(); - assert(utils.isNumber(max)); // Try to do this iteratively and synchronously @@ -186,15 +156,6 @@ ChainBlock.prototype.getAncestors = function getAncestors(max, callback) { })(null, entry); }; -/** - * Free up ancestors. This is very important because - * chain entries are cached in the ChainDB's LRU cache. - */ - -ChainBlock.prototype.free = function free() { - this.ancestors.length = 0; -}; - /** * Test whether the entry is in the main chain. * @param {Function} callback - Return [Error, Boolean]. @@ -291,9 +252,6 @@ ChainBlock.prototype.getMedianTime = function getMedianTime(ancestors) { var timeSpan = constants.block.MEDIAN_TIMESPAN; var i; - if (!ancestors) - ancestors = this.ancestors; - for (i = 0; i < timeSpan && entry; i++, entry = ancestors[i]) median.push(entry.ts); @@ -382,9 +340,6 @@ ChainBlock.prototype.isSuperMajority = function isSuperMajority(version, require var majorityWindow = network.block.majorityWindow; var i; - if (!ancestors) - ancestors = this.ancestors; - for (i = 0; i < majorityWindow && found < required && entry; i++) { if (entry.version >= version) found++; @@ -502,9 +457,7 @@ ChainBlock.fromJSON = function fromJSON(chain, json) { */ ChainBlock.prototype.inspect = function inspect() { - var json = this.toJSON(); - json.ancestors = this.ancestors.length; - return json; + return this.toJSON(); }; /**