chainblock methods. version bits deployment checking.
This commit is contained in:
parent
adeee1eb67
commit
7c6f71b785
@ -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
|
||||
*/
|
||||
|
||||
@ -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
|
||||
*/
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
};
|
||||
|
||||
@ -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',
|
||||
|
||||
Loading…
Reference in New Issue
Block a user