|
|
@ -62,10 +62,10 @@ var VerifyError = utils.VerifyError; |
|
|
|
* @property {Locker} locker |
|
|
|
* @property {Object} invalid |
|
|
|
* @property {Number} bestHeight |
|
|
|
* @property {Number} lastUpdate |
|
|
|
* @property {ChainBlock?} tip |
|
|
|
* @property {Number} height |
|
|
|
* @property {Boolean} segwitActive |
|
|
|
* @property {Boolean} csvActive |
|
|
|
* @property {Object} orphan - Orphan map. |
|
|
|
* @emits Chain#open |
|
|
|
* @emits Chain#error |
|
|
@ -102,10 +102,11 @@ function Chain(options) { |
|
|
|
this.locker = new bcoin.locker(this, this.add, this.pendingLimit); |
|
|
|
this.invalid = {}; |
|
|
|
this.bestHeight = -1; |
|
|
|
this.lastUpdate = utils.now(); |
|
|
|
this.tip = null; |
|
|
|
this.height = -1; |
|
|
|
this.synced = false; |
|
|
|
this.segwitActive = null; |
|
|
|
this.csvActive = null; |
|
|
|
|
|
|
|
this.orphan = { |
|
|
|
map: {}, |
|
|
@ -243,22 +244,29 @@ Chain.prototype._init = function _init() { |
|
|
|
self.tip = tip; |
|
|
|
self.height = tip.height; |
|
|
|
|
|
|
|
if (self.bestHeight === -1) |
|
|
|
if (tip.height > self.bestHeight) { |
|
|
|
self.bestHeight = tip.height; |
|
|
|
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; |
|
|
|
self.emit('open'); |
|
|
|
self.emit('tip', tip); |
|
|
|
|
|
|
|
if (self.isFull()) |
|
|
|
if (!self.synced && self.isFull()) { |
|
|
|
self.synced = true; |
|
|
|
self.emit('full'); |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
@ -364,18 +372,14 @@ Chain.prototype._preload = function _preload(callback) { |
|
|
|
}); |
|
|
|
|
|
|
|
stream.on('error', function(err) { |
|
|
|
var start = Math.max(0, height - 2); |
|
|
|
self.reset(start, function(e) { |
|
|
|
if (e) |
|
|
|
return callback(e); |
|
|
|
return callback(err); |
|
|
|
}); |
|
|
|
stream.destroy(); |
|
|
|
return callback(err); |
|
|
|
}); |
|
|
|
|
|
|
|
stream.on('data', function(data) { |
|
|
|
var blocks = []; |
|
|
|
var need = 80 - buf.size; |
|
|
|
var lastEntry, block, entry, start; |
|
|
|
var lastEntry, block, data, entry; |
|
|
|
|
|
|
|
while (data.length >= need) { |
|
|
|
buf.data.push(data.slice(0, need)); |
|
|
@ -396,11 +400,12 @@ Chain.prototype._preload = function _preload(callback) { |
|
|
|
return; |
|
|
|
|
|
|
|
for (i = 0; i < blocks.length; i++) { |
|
|
|
block = blocks[i]; |
|
|
|
data = blocks[i]; |
|
|
|
|
|
|
|
try { |
|
|
|
data = parseHeader(data); |
|
|
|
} catch (e) { |
|
|
|
stream.destroy(); |
|
|
|
return callback(e); |
|
|
|
} |
|
|
|
|
|
|
@ -414,13 +419,8 @@ Chain.prototype._preload = function _preload(callback) { |
|
|
|
|
|
|
|
// Do some paranoid checks. |
|
|
|
if (lastEntry && data.prevBlock !== lastEntry.hash) { |
|
|
|
start = Math.max(0, height - 2); |
|
|
|
stream.destroy(); |
|
|
|
return self.reset(start, function(err) { |
|
|
|
if (err) |
|
|
|
return callback(err); |
|
|
|
return callback(new Error('Corrupt headers.')); |
|
|
|
}); |
|
|
|
return callback(new Error('Corrupt headers.')); |
|
|
|
} |
|
|
|
|
|
|
|
// Create headers object for validation. |
|
|
@ -429,13 +429,8 @@ Chain.prototype._preload = function _preload(callback) { |
|
|
|
// Verify the block headers. We don't want to |
|
|
|
// trust an external centralized source completely. |
|
|
|
if (!block.verifyHeaders()) { |
|
|
|
start = Math.max(0, height - 2); |
|
|
|
stream.destroy(); |
|
|
|
return self.reset(start, function(err) { |
|
|
|
if (err) |
|
|
|
return callback(err); |
|
|
|
return callback(new Error('Bad headers.')); |
|
|
|
}); |
|
|
|
return callback(new Error('Bad headers.')); |
|
|
|
} |
|
|
|
|
|
|
|
// Create a chain entry. |
|
|
@ -504,11 +499,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(); |
|
|
@ -518,9 +510,12 @@ Chain.prototype._verify = function _verify(block, prev, callback) { |
|
|
|
if (!block.verify(ret)) |
|
|
|
return done(new VerifyError(block, 'invalid', ret.reason, ret.score)); |
|
|
|
|
|
|
|
if (this.options.spv || block.type !== 'block') |
|
|
|
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) |
|
|
@ -540,106 +535,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.bip16time) |
|
|
|
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 done(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 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.type === 'segnet3' && 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 |
|
|
|
if (network.type === 'segnet3' && 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; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Locktime median time past is now enforced. |
|
|
|
// if (block.version >= 8 && prev.isUpgraded(8)) |
|
|
|
// lockFlags |= constants.flags.MEDIAN_TIME_PAST; |
|
|
|
|
|
|
|
if (network.type === 'segnet4') { |
|
|
|
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; |
|
|
|
} |
|
|
|
self._checkDeployments(block, prev, function(err, state) { |
|
|
|
if (err) |
|
|
|
return done(err); |
|
|
|
|
|
|
|
return finish(); |
|
|
|
}); |
|
|
|
} else { |
|
|
|
finish(); |
|
|
|
} |
|
|
|
// Expose the state of csv and segwit globally. |
|
|
|
self.csvActive = state.csv; |
|
|
|
self.segwitActive = state.segwit; |
|
|
|
|
|
|
|
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) { |
|
|
@ -657,6 +572,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, |
|
|
@ -667,7 +584,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; |
|
|
|
|
|
|
@ -685,15 +602,127 @@ 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 (bip141 - segnet3) |
|
|
|
// 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 (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); |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
|
* Check block for duplicate txids in blockchain |
|
|
|
* history (BIP30). Note that txids are only considered |
|
|
|
* duplicate if they are not yet completely spent. |
|
|
|
* Determine whether to check block for duplicate txids in blockchain |
|
|
|
* history (BIP30). If we're on a chain that has bip34 activated, we |
|
|
|
* can skip this. |
|
|
|
* @private |
|
|
|
* @see https://github.com/bitcoin/bips/blob/master/bip-0030.mediawiki |
|
|
|
* @param {Block|MerkleBlock} block |
|
|
@ -711,6 +740,38 @@ Chain.prototype._checkDuplicates = function _checkDuplicates(block, prev, callba |
|
|
|
if (block.isGenesis()) |
|
|
|
return callback(); |
|
|
|
|
|
|
|
if (network.block.bip34height === -1 || height <= network.block.bip34height) |
|
|
|
return this._findDuplicates(block, prev, callback); |
|
|
|
|
|
|
|
this.db.get(network.block.bip34height, function(err, entry) { |
|
|
|
if (err) |
|
|
|
return callback(err); |
|
|
|
|
|
|
|
// It was no longer possible to create duplicate |
|
|
|
// TXs once bip34 went into effect. We can check |
|
|
|
// for this to avoid a DB lookup. |
|
|
|
if (entry && entry.hash === network.block.bip34hash) |
|
|
|
return callback(); |
|
|
|
|
|
|
|
return self._findDuplicates(block, prev, callback); |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
|
* Check block for duplicate txids in blockchain |
|
|
|
* history (BIP30). Note that txids are only considered |
|
|
|
* duplicate if they are not yet completely spent. |
|
|
|
* @private |
|
|
|
* @see https://github.com/bitcoin/bips/blob/master/bip-0030.mediawiki |
|
|
|
* @param {Block|MerkleBlock} block |
|
|
|
* @param {ChainBlock} prev |
|
|
|
* @param {Function} callback - Returns [{@link VerifyError}]. |
|
|
|
*/ |
|
|
|
|
|
|
|
Chain.prototype._findDuplicates = function _findDuplicates(block, prev, callback) { |
|
|
|
var self = this; |
|
|
|
var height = prev.height + 1; |
|
|
|
|
|
|
|
// Check all transactions |
|
|
|
utils.forEachSerial(block.txs, function(tx, next) { |
|
|
|
var hash = tx.hash('hex'); |
|
|
@ -724,8 +785,8 @@ Chain.prototype._checkDuplicates = function _checkDuplicates(block, prev, callba |
|
|
|
// Blocks 91842 and 91880 created duplicate |
|
|
|
// txids by using the same exact output script |
|
|
|
// and extraNonce. |
|
|
|
if (network.type === 'main') { |
|
|
|
if (height === 91842 || height === 91880) |
|
|
|
if (constants.bip30[height]) { |
|
|
|
if (block.hash('hex') === constants.bip30[height]) |
|
|
|
return next(); |
|
|
|
} |
|
|
|
return next(new VerifyError(block, 'invalid', 'bad-txns-BIP30', 100)); |
|
|
@ -789,12 +850,12 @@ Chain.prototype._checkInputs = function _checkInputs(block, prev, flags, callbac |
|
|
|
// Check for block sigops limits |
|
|
|
// Start counting P2SH sigops once block |
|
|
|
// timestamps reach March 31st, 2012. |
|
|
|
if (block.ts >= constants.block.bip16time) |
|
|
|
if (block.ts >= constants.block.BIP16_TIME) |
|
|
|
sigops += tx.getSigops(true); |
|
|
|
else |
|
|
|
sigops += tx.getSigops(); |
|
|
|
|
|
|
|
if (sigops > constants.block.maxSigops) { |
|
|
|
if (sigops > constants.block.MAX_SIGOPS) { |
|
|
|
return callback(new VerifyError(block, |
|
|
|
'invalid', |
|
|
|
'bad-blk-sigops', |
|
|
@ -1051,8 +1112,8 @@ Chain.prototype.disconnect = function disconnect(entry, callback) { |
|
|
|
self.tip = entry; |
|
|
|
self.height = entry.height; |
|
|
|
|
|
|
|
if (self.bestHeight === -1) |
|
|
|
network.height = entry.height; |
|
|
|
self.bestHeight = entry.height; |
|
|
|
network.height = entry.height; |
|
|
|
|
|
|
|
self.emit('tip', entry); |
|
|
|
|
|
|
@ -1106,8 +1167,8 @@ Chain.prototype.connect = function connect(entry, callback) { |
|
|
|
self.tip = entry; |
|
|
|
self.height = entry.height; |
|
|
|
|
|
|
|
if (self.bestHeight === -1) |
|
|
|
network.height = entry.height; |
|
|
|
self.bestHeight = entry.height; |
|
|
|
network.height = entry.height; |
|
|
|
|
|
|
|
self.emit('tip', entry); |
|
|
|
|
|
|
@ -1137,6 +1198,9 @@ Chain.prototype._setBestChain = function _setBestChain(entry, prev, block, callb |
|
|
|
if (err) |
|
|
|
return callback(err); |
|
|
|
|
|
|
|
// Do "contextual" verification on our block |
|
|
|
// now that we're certain its previous |
|
|
|
// block is in the chain. |
|
|
|
self._verifyContext(block, prev, function(err) { |
|
|
|
if (err) { |
|
|
|
// Couldn't verify block. |
|
|
@ -1164,9 +1228,6 @@ Chain.prototype._setBestChain = function _setBestChain(entry, prev, block, callb |
|
|
|
self.tip = entry; |
|
|
|
self.height = entry.height; |
|
|
|
|
|
|
|
if (self.bestHeight === -1) |
|
|
|
network.height = entry.height; |
|
|
|
|
|
|
|
self.emit('tip', entry); |
|
|
|
|
|
|
|
return callback(); |
|
|
@ -1174,10 +1235,6 @@ Chain.prototype._setBestChain = function _setBestChain(entry, prev, block, callb |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
// Update the timestamp to |
|
|
|
// maintain a time delta of blocks. |
|
|
|
this.lastUpdate = utils.now(); |
|
|
|
|
|
|
|
// We don't have a genesis block yet. |
|
|
|
if (!this.tip) { |
|
|
|
if (entry.hash !== network.genesis.hash) { |
|
|
@ -1191,9 +1248,6 @@ Chain.prototype._setBestChain = function _setBestChain(entry, prev, block, callb |
|
|
|
} |
|
|
|
|
|
|
|
// Everything is in order. |
|
|
|
// Do "contextual" verification on our block |
|
|
|
// now that we're certain its previous |
|
|
|
// block is in the chain. |
|
|
|
if (entry.prevBlock === this.tip.hash) |
|
|
|
return done(); |
|
|
|
|
|
|
@ -1409,6 +1463,11 @@ Chain.prototype.add = function add(block, callback, force) { |
|
|
|
network.height = self.bestHeight; |
|
|
|
} |
|
|
|
|
|
|
|
if (height > self.bestHeight) { |
|
|
|
self.bestHeight = height; |
|
|
|
network.height = height; |
|
|
|
} |
|
|
|
|
|
|
|
// If previous block wasn't ever seen, |
|
|
|
// add it current to orphans and break. |
|
|
|
if (!prev) { |
|
|
@ -1554,8 +1613,10 @@ Chain.prototype.add = function add(block, callback, force) { |
|
|
|
} |
|
|
|
|
|
|
|
utils.nextTick(function() { |
|
|
|
if (self.isFull()) |
|
|
|
if (!self.synced && self.isFull()) { |
|
|
|
self.synced = true; |
|
|
|
self.emit('full'); |
|
|
|
} |
|
|
|
|
|
|
|
if (err) |
|
|
|
callback(err); |
|
|
@ -1794,14 +1855,7 @@ Chain.prototype.getOrphan = function getOrphan(hash) { |
|
|
|
*/ |
|
|
|
|
|
|
|
Chain.prototype.isFull = function isFull() { |
|
|
|
var delta; |
|
|
|
|
|
|
|
if (!this.tip) |
|
|
|
return false; |
|
|
|
|
|
|
|
delta = utils.now() - this.tip.ts; |
|
|
|
|
|
|
|
return delta < 4 * 60 * 60; |
|
|
|
return !this.isInitial(); |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
@ -1813,16 +1867,17 @@ Chain.prototype.isFull = function isFull() { |
|
|
|
*/ |
|
|
|
|
|
|
|
Chain.prototype.isInitial = function isInitial() { |
|
|
|
var now, delta; |
|
|
|
|
|
|
|
if (!this.tip) |
|
|
|
return true; |
|
|
|
|
|
|
|
now = utils.now(); |
|
|
|
delta = now - this.lastUpdate; |
|
|
|
if (this.synced) |
|
|
|
return false; |
|
|
|
|
|
|
|
if (this.height < network.checkpoints.lastHeight) |
|
|
|
return true; |
|
|
|
|
|
|
|
// Should mimic the original IsInitialBlockDownload() function |
|
|
|
return delta < 10 && this.tip.ts < now - 24 * 60 * 60; |
|
|
|
return this.height < this.bestHeight - 24 * 6 |
|
|
|
|| this.tip.ts < utils.now() - network.block.maxTipAge; |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
@ -2018,7 +2073,7 @@ Chain.prototype.getOrphanRoot = function getOrphanRoot(hash) { |
|
|
|
|
|
|
|
Chain.prototype.getCurrentTarget = function getCurrentTarget(callback) { |
|
|
|
if (!this.tip) |
|
|
|
return callback(null, utils.toCompact(network.powLimit)); |
|
|
|
return callback(null, utils.toCompact(network.pow.limit)); |
|
|
|
return this.getTargetAsync(this.tip, null, callback); |
|
|
|
}; |
|
|
|
|
|
|
@ -2033,12 +2088,12 @@ Chain.prototype.getCurrentTarget = function getCurrentTarget(callback) { |
|
|
|
Chain.prototype.getTargetAsync = function getTargetAsync(last, block, callback) { |
|
|
|
var self = this; |
|
|
|
|
|
|
|
if ((last.height + 1) % network.powDiffInterval !== 0) { |
|
|
|
if (!network.powAllowMinDifficultyBlocks) |
|
|
|
if ((last.height + 1) % network.pow.retargetInterval !== 0) { |
|
|
|
if (!network.pow.allowMinDifficultyBlocks) |
|
|
|
return utils.asyncify(callback)(null, this.getTarget(last, block)); |
|
|
|
} |
|
|
|
|
|
|
|
return last.getAncestors(network.powDiffInterval, function(err, ancestors) { |
|
|
|
return last.getAncestors(network.pow.retargetInterval, function(err, ancestors) { |
|
|
|
if (err) |
|
|
|
return callback(err); |
|
|
|
|
|
|
@ -2057,7 +2112,7 @@ Chain.prototype.getTargetAsync = function getTargetAsync(last, block, callback) |
|
|
|
*/ |
|
|
|
|
|
|
|
Chain.prototype.getTarget = function getTarget(last, block, ancestors) { |
|
|
|
var powLimit = utils.toCompact(network.powLimit); |
|
|
|
var powLimit = utils.toCompact(network.pow.limit); |
|
|
|
var ts, first, i; |
|
|
|
|
|
|
|
// Genesis |
|
|
@ -2068,16 +2123,16 @@ Chain.prototype.getTarget = function getTarget(last, block, ancestors) { |
|
|
|
ancestors = last.ancestors; |
|
|
|
|
|
|
|
// Do not retarget |
|
|
|
if ((last.height + 1) % network.powDiffInterval !== 0) { |
|
|
|
if (network.powAllowMinDifficultyBlocks) { |
|
|
|
if ((last.height + 1) % network.pow.retargetInterval !== 0) { |
|
|
|
if (network.pow.allowMinDifficultyBlocks) { |
|
|
|
// Special behavior for testnet: |
|
|
|
ts = block ? (block.ts || block) : utils.now(); |
|
|
|
if (ts > last.ts + network.powTargetSpacing * 2) |
|
|
|
if (ts > last.ts + network.pow.targetSpacing * 2) |
|
|
|
return powLimit; |
|
|
|
|
|
|
|
i = 1; |
|
|
|
while (ancestors[i] |
|
|
|
&& last.height % network.powDiffInterval !== 0 |
|
|
|
&& last.height % network.pow.retargetInterval !== 0 |
|
|
|
&& last.bits === powLimit) { |
|
|
|
last = ancestors[i++]; |
|
|
|
} |
|
|
@ -2086,7 +2141,7 @@ Chain.prototype.getTarget = function getTarget(last, block, ancestors) { |
|
|
|
} |
|
|
|
|
|
|
|
// Back 2 weeks |
|
|
|
first = ancestors[network.powDiffInterval - 1]; |
|
|
|
first = ancestors[network.pow.retargetInterval - 1]; |
|
|
|
|
|
|
|
assert(first); |
|
|
|
|
|
|
@ -2102,10 +2157,10 @@ Chain.prototype.getTarget = function getTarget(last, block, ancestors) { |
|
|
|
*/ |
|
|
|
|
|
|
|
Chain.prototype.retarget = function retarget(last, first) { |
|
|
|
var powTargetTimespan = new bn(network.powTargetTimespan); |
|
|
|
var powTargetTimespan = new bn(network.pow.targetTimespan); |
|
|
|
var actualTimespan, target; |
|
|
|
|
|
|
|
if (network.powNoRetargeting) |
|
|
|
if (network.pow.noRetargeting) |
|
|
|
return last.bits; |
|
|
|
|
|
|
|
actualTimespan = new bn(last.ts - first.ts); |
|
|
@ -2120,8 +2175,8 @@ Chain.prototype.retarget = function retarget(last, first) { |
|
|
|
target.imul(actualTimespan); |
|
|
|
target = target.div(powTargetTimespan); |
|
|
|
|
|
|
|
if (target.cmp(network.powLimit) > 0) |
|
|
|
target = network.powLimit.clone(); |
|
|
|
if (target.cmp(network.pow.limit) > 0) |
|
|
|
target = network.pow.limit.clone(); |
|
|
|
|
|
|
|
return utils.toCompact(target); |
|
|
|
}; |
|
|
@ -2152,6 +2207,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 |
|
|
@ -2222,7 +2300,7 @@ Chain.prototype.getState = function getState(prev, id, callback) { |
|
|
|
})(null, prev); |
|
|
|
|
|
|
|
function walkForward(state) { |
|
|
|
var entry; |
|
|
|
var entry, count, i; |
|
|
|
|
|
|
|
if (compute.length === 0) |
|
|
|
return callback(null, state); |
|
|
@ -2251,8 +2329,8 @@ Chain.prototype.getState = function getState(prev, id, callback) { |
|
|
|
if (medianTime >= timeTimeout) |
|
|
|
return walkForward(constants.thresholdStates.FAILED); |
|
|
|
|
|
|
|
var count = 0; |
|
|
|
var i = 0; |
|
|
|
count = 0; |
|
|
|
i = 0; |
|
|
|
|
|
|
|
(function next(err, entry) { |
|
|
|
if (err) |
|
|
@ -2333,70 +2411,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(); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}; |
|
|
@ -2416,14 +2470,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); |
|
|
@ -2448,10 +2497,10 @@ Chain.prototype.checkFinal = function checkFinal(prev, tx, flags, callback, forc |
|
|
|
|
|
|
|
Chain.prototype.getLocks = function getLocks(tx, flags, entry, callback) { |
|
|
|
var self = this; |
|
|
|
var mask = constants.sequenceLocktimeMask; |
|
|
|
var granularity = constants.sequenceLocktimeGranularity; |
|
|
|
var disableFlag = constants.sequenceLocktimeDisableFlag; |
|
|
|
var typeFlag = constants.sequenceLocktimeTypeFlag; |
|
|
|
var mask = constants.sequence.MASK; |
|
|
|
var granularity = constants.sequence.GRANULARITY; |
|
|
|
var disableFlag = constants.sequence.DISABLE_FLAG; |
|
|
|
var typeFlag = constants.sequence.TYPE_FLAG; |
|
|
|
var hasFlag = flags & constants.flags.VERIFY_SEQUENCE; |
|
|
|
var minHeight = -1; |
|
|
|
var minTime = -1; |
|
|
@ -2531,14 +2580,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); |
|
|
@ -2569,7 +2613,7 @@ return Chain; |
|
|
|
<br class="clear"> |
|
|
|
|
|
|
|
<footer> |
|
|
|
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.4.0</a> on Sat Apr 16 2016 21:21:59 GMT-0700 (PDT) |
|
|
|
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.4.0</a> on Sun Apr 17 2016 21:04:10 GMT-0700 (PDT) |
|
|
|
</footer> |
|
|
|
|
|
|
|
<script> prettyPrint(); </script> |
|
|
|