chainblock methods. version bits deployment checking.

This commit is contained in:
Christopher Jeffrey 2016-03-08 01:14:03 -08:00
parent adeee1eb67
commit 7c6f71b785
5 changed files with 303 additions and 38 deletions

View File

@ -476,7 +476,7 @@ Chain.prototype._verify = function _verify(block, prev, callback) {
return done(null, false);
}
prev.alloc(function(err) {
prev.ensureAncestors(function(err) {
if (err)
return callback(err);
@ -955,6 +955,14 @@ Chain.prototype._setBestChain = function _setBestChain(entry, block, callback) {
this.lastUpdate = utils.now();
// Start fsyncing writes once we're no
// longer dealing with historical data.
if (this.isFull()) {
this.db.fsync = true;
if (this.blockdb)
this.blockdb.fsync = true;
}
if (!this.tip) {
if (entry.hash !== network.genesis.hash)
return callback(new Error('Bad genesis block.'));
@ -981,14 +989,6 @@ Chain.prototype._setBestChain = function _setBestChain(entry, block, callback) {
self.tip = entry;
self.height = entry.height;
// Start fsyncing writes once we're no
// longer dealing with historical data.
if (self.isFull()) {
self.db.fsync = true;
if (self.blockdb)
self.blockdb.fsync = true;
}
return callback();
});
});
@ -1895,6 +1895,163 @@ Chain.prototype.retarget = function retarget(last, first) {
return utils.toCompact(target);
};
// https://github.com/bitcoin/bitcoin/pull/7648/files
Chain.prototype.getState = function getState(prev, block, id, callback) {
var self = this;
var period = network.minerConfirmationWindow;
var threshold = network.ruleChangeActivationThreshold;
var deployment = network.deployments[id];
var timeStart, timeTimeout, compute, height;
if (!deployment)
return callback(null, constants.thresholdStates.FAILED);
timeStart = deployment.startTime;
timeTimeout = deployment.timeout;
compute = [];
if (block && block.isGenesis())
return callback(null, constants.thresholdStates.DEFINED);
if (((prev.height + 1) % period) !== 0) {
height = prev.height - ((prev.height + 1) % period);
return prev.getAncestorByHeight(height, function(err, ancestor) {
if (err)
return callback(err);
if (!ancestor)
return callback(null, constants.thresholdStates.DEFINED);
return self.getState(ancestor, block, callback);
});
}
function condition(entry) {
var bits = entry.version & constants.versionbits.TOP_MASK;
var topBits = constants.versionbits.TOP_BITS;
var mask = 1 << deployment.bit;
return (bits === topBits) && (entry.version & mask) !== 0;
}
(function walk(err, entry) {
if (err)
return callback(err);
if (!entry)
return walkForward(constants.thresholdStates.DEFINED);
return entry.getMedianTimeAsync(function(err, medianTime) {
if (err)
return walk(err);
if (medianTime < timeStart)
return walkForward(constants.thresholdStates.DEFINED);
compute.push(entry);
height = entry.height - period;
return entry.getAncestorByHeight(height, walk);
});
})(null, prev);
function walkForward(state) {
var entry;
if (compute.length === 0)
return callback(null, state);
entry = compute.pop();
switch (state) {
case constants.thresholdStates.DEFINED:
return entry.getMedianTimeAsync(function(err, medianTime) {
if (err)
return callback(err);
if (medianTime >= timeTimeout)
return walkForward(constants.thresholdStates.FAILED);
if (medianTime >= timeStart)
return walkForward(constants.thresholdStates.STARTED);
return walkForward(state);
});
case constants.thresholdStates.STARTED:
return entry.getMedianTimeAsync(function(err, medianTime) {
if (err)
return callback(err);
if (medianTime >= timeTimeout)
return walkForward(constants.thresholdStates.FAILED);
var count = 0;
var i = 0;
(function next(err, entry) {
if (err)
return callback(err);
if (!entry)
return doneCounting();
if (i++ >= period)
return doneCounting();
if (condition(entry))
count++;
return self.db.get(entry.prevBlock, next);
})(null, entry);
function doneCounting(err) {
if (err)
return callback(err);
if (count >= threshold)
return walkForward(constants.thresholdStates.LOCKED_IN);
return walkForward(state);
}
});
case constants.thresholdStates.LOCKED_IN:
return walkForward(constants.thresholdStates.ACTIVE);
case constants.thresholdStates.FAILED:
case constants.thresholdStates.ACTIVE:
return walkForward(state);
}
assert(false, 'Bad state.');
}
};
Chain.prototype.computeBlockVersion = function computeBlockVersion(prev, callback) {
var version = 0;
utils.forEachSerial(Object.keys(network.deployments), function(id, next) {
var deployment = network.deployments[id];
self.getState(prev, null, id, function(err, state) {
if (err)
return next(err);
if (state === constants.thresholdStates.LOCKED_IN
|| state === constants.thresholdStates.STARTED) {
version |= (1 << deployment.bit);
}
next();
});
}, function(err) {
if (err)
return callback(err);
if (version === 0)
return callback(null, constants.versionbits.LAST_OLD_BLOCK_VERSION);
version |= constants.versionbits.TOP_BITS;
return callback(null, version);
});
};
/**
* Expose
*/

View File

@ -153,7 +153,7 @@ ChainBlock.prototype.isSuperMajorityAsync = function isSuperMajority(version, re
})(null, this);
};
ChainBlock.prototype.alloc = function alloc(callback) {
ChainBlock.prototype.ensureAncestors = function ensureAncestors(callback) {
var majorityWindow = network.block.majorityWindow;
var medianTimespan = constants.block.medianTimespan;
var powDiffInterval = network.powDiffInterval;
@ -161,25 +161,52 @@ ChainBlock.prototype.alloc = function alloc(callback) {
var max = Math.max(majorityWindow, medianTimespan);
if ((this.height + 1) % powDiffInterval === 0 || allowMinDiff)
max = Math.max(max, powDiffInterval);
return this._alloc(max, callback);
assert(this.previous.length === 0);
return this.alloc(max, callback);
};
ChainBlock.prototype._alloc = function _alloc(max, callback) {
ChainBlock.prototype.alloc = function alloc(max, callback) {
var self = this;
var i;
return this.getAncestors(max, function(err, previous) {
if (err)
return callback(err);
assert(previous);
self.previous.length = 0;
for (i = 0; i < previous.length; i++)
self.previous.push(previous[i]);
return callback();
});
};
ChainBlock.prototype.getAncestors = function getAncestors(max, callback) {
var self = this;
var entry = this;
var previous = this.previous.slice();
assert(this.previous.length === 0);
if (max === 0)
return callback(null, []);
if (previous.length)
entry = previous.pop();
assert(utils.isFinite(max));
// Try to do this iteratively and synchronously
// so we don't have to wait on nextTicks.
for (;;) {
this.previous.push(entry);
previous.push(entry);
if (this.previous.length >= max)
return callback();
if (previous.length >= max)
return callback(null, previous);
if (!this.chain.db.hasCache(entry.prevBlock)) {
this.previous.pop();
previous.pop();
break;
}
@ -187,18 +214,16 @@ ChainBlock.prototype._alloc = function _alloc(max, callback) {
}
(function next(err, entry) {
if (err) {
self.free();
if (err)
return callback(err);
}
if (!entry)
return callback();
return callback(null, previous);
self.previous.push(entry);
previous.push(entry);
if (self.previous.length >= max)
return callback();
if (previous.length >= max)
return callback(null, previous);
self.chain.db.get(entry.prevBlock, next);
})(null, entry);
@ -208,13 +233,29 @@ ChainBlock.prototype.free = function free() {
this.previous.length = 0;
};
ChainBlock.prototype.getMedianTime = function getMedianTime() {
ChainBlock.prototype.getAncestorByHeight = function getAncestorByHeight(height, callback) {
assert(height <= this.height);
return this.getAncestor(this.height - height, callback);
};
ChainBlock.prototype.getAncestor = function getAncestor(index, callback) {
return this.getAncestors(index + 1, function(err, previous) {
if (err)
return callback(err);
return callback(null, previous[previous.length - 1]);
});
};
ChainBlock.prototype.getMedianTime = function getMedianTime(previous) {
var entry = this;
var median = [];
var timeSpan = constants.block.medianTimespan;
var i;
for (i = 0; i < timeSpan && entry; i++, entry = this.previous[i])
if (!previous)
previous = this.previous;
for (i = 0; i < timeSpan && entry; i++, entry = previous[i])
median.push(entry.ts);
median = median.sort();
@ -222,29 +263,43 @@ ChainBlock.prototype.getMedianTime = function getMedianTime() {
return median[median.length / 2 | 0];
};
ChainBlock.prototype.isOutdated = function isOutdated(version) {
return this.isSuperMajority(version, network.block.majorityRejectOutdated);
ChainBlock.prototype.isOutdated = function isOutdated(version, previous) {
return this.isSuperMajority(version, network.block.majorityRejectOutdated, previous);
};
ChainBlock.prototype.isUpgraded = function isUpgraded(version) {
return this.isSuperMajority(version, network.block.majorityEnforceUpgrade);
ChainBlock.prototype.isUpgraded = function isUpgraded(version, previous) {
return this.isSuperMajority(version, network.block.majorityEnforceUpgrade, previous);
};
ChainBlock.prototype.isSuperMajority = function isSuperMajority(version, required) {
ChainBlock.prototype.isSuperMajority = function isSuperMajority(version, required, previous) {
var entry = this;
var found = 0;
var majorityWindow = network.block.majorityWindow;
var i;
if (!previous)
previous = this.previous;
for (i = 0; i < majorityWindow && found < required && entry; i++) {
if (entry.version >= version)
found++;
entry = this.previous[i + 1];
entry = previous[i + 1];
}
return found >= required;
};
ChainBlock.prototype.getMedianTimeAsync = function getMedianTimeAsync(callback) {
var self = this;
return this.getAncestors(constants.block.medianTimespan, function(err, previous) {
if (err)
return callback(err);
return callback(null, self.getMedianTime(previous));
});
};
/**
* Expose
*/

View File

@ -26,9 +26,9 @@ if (profiler) {
function Profile(name) {
if (profiler) {
name = 'profile-' + (name ? name + '-' : '') + Profile.uid++;
utils.debug('Starting CPU profile: %s', name);
this.profile = profiler.startProfiling(name, true);
this.name = name;
utils.debug('Starting CPU profile: %s', this.name);
}
}
@ -86,9 +86,9 @@ Profile.prototype.save = function save(callback) {
function Snapshot(name) {
if (profiler) {
name = 'snapshot-' + (name ? name + '-' : '') + Snapshot.uid++;
utils.debug('Taking heap snapshot: %s', name);
this.snapshot = profiler.takeSnapshot(name);
this.name = name;
utils.debug('Taking heap snapshot: %s', this.name);
}
}
@ -161,16 +161,18 @@ exports.takeSnapshot = function takeSnapshot(name) {
};
exports.snapshot = function snapshot(name, callback) {
var mem = process.memoryUsage();
var snapshot;
var snapshot, mem;
if (typeof name === 'function') {
callback = name;
name = null;
}
utils.debug('Memory: rss=%dmb, heap=%dmb',
utils.mb(mem.rss), utils.mb(mem.heapUsed));
if (bcoin.debug) {
mem = process.memoryUsage();
utils.debug('Memory: rss=%dmb, heap=%dmb',
utils.mb(mem.rss), utils.mb(mem.heapUsed));
}
if (!profiler)
return callback ? utils.nextTick(callback) : null;

View File

@ -325,3 +325,20 @@ exports.flags.STANDARD_VERIFY_FLAGS =
// | exports.flags.VERIFY_WITNESS
// | exports.flags.VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM
// | exports.flags.VERIFY_CHECKSEQUENCEVERIFY;
exports.versionbits = {
// What block version to use for new blocks (pre versionbits)
LAST_OLD_BLOCK_VERSION: 4,
// What bits to set in version for versionbits blocks
TOP_BITS: 0x20000000,
// What bitmask determines whether versionbits is in use
TOP_MASK: 0xe0000000
};
exports.thresholdStates = {
DEFINED: 0,
STARTED: 1,
LOCKED_IN: 2,
ACTIVE: 3,
FAILED: 4
};

View File

@ -149,6 +149,16 @@ main.segwitHeight = 2000000000;
main.genesisBlock = '0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000';
main.ruleChangeActivationThreshold = 1916; // 95% of 2016
main.minerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing
main.deployments = {
csv: {
bit: 0,
startTime: 1459468800, // April 1st, 2016
timeout: 1491004800 // April 1st, 2017
}
};
/**
* Testnet (v3)
* https://en.bitcoin.it/wiki/Testnet
@ -262,6 +272,16 @@ testnet.segwitHeight = 2000000000;
testnet.genesisBlock = '0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff001d1aa4ae180101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000';
testnet.ruleChangeActivationThreshold = 1512; // 75% for testchains
testnet.minerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing
testnet.deployments = {
csv: {
bit: 0,
startTime: 1459468800,
timeout: 1491004800
}
};
/**
* Regtest
*/
@ -357,6 +377,16 @@ regtest.segwitHeight = 0;
regtest.genesisBlock = '0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff7f20020000000101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000';
regtest.ruleChangeActivationThreshold = 108; // 75% for testchains
regtest.minerConfirmationWindow = 144; // Faster than normal for regtest (144 instead of 2016)
regtest.deployments = {
csv: {
bit: 0,
startTime: 0,
timeout: 999999999999
}
};
/**
* Segnet
*/
@ -465,6 +495,10 @@ segnet.segwitHeight = 0;
segnet.genesisBlock = '0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a7d719856ffff001d000000000101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000';
segnet.ruleChangeActivationThreshold = 108;
segnet.minerConfirmationWindow = 144;
segnet.deployments = {};
network.xprivkeys = {
'76066276': 'main',
'70615956': 'testnet',