diff --git a/lib/blockchain/chain.js b/lib/blockchain/chain.js index 5105e8de..8956ea1b 100644 --- a/lib/blockchain/chain.js +++ b/lib/blockchain/chain.js @@ -23,6 +23,7 @@ const CoinView = require('../coins/coinview'); const Script = require('../script/script'); const {VerifyError} = require('../protocol/errors'); const co = require('../utils/co'); +const thresholdStates = common.thresholdStates; /** * Represents a blockchain. @@ -106,6 +107,12 @@ Chain.prototype._open = async function open() { if (this.options.coinCache) this.logger.info('Coin cache is enabled.'); + if (this.options.bip91) + this.logger.warning('BIP91 enabled. Segsignal will be enforced.'); + + if (this.options.bip148) + this.logger.warning('BIP148 enabled. UASF will be enforced.'); + await this.db.open(); tip = await this.db.getTip(); @@ -219,6 +226,7 @@ Chain.prototype.isGenesis = function isGenesis(block) { */ Chain.prototype.verify = async function verify(block, prev, flags) { + let deployments = this.network.deployments; let hash = block.hash('hex'); let now = this.network.now(); let height = prev.height + 1; @@ -315,6 +323,12 @@ Chain.prototype.verify = async function verify(block, prev, flags) { // Get the new deployment state. state = await this.getDeployments(block.ts, prev); + // Enforce BIP91/BIP148. + if (state.hasBIP91() || state.hasBIP148()) { + if (!consensus.hasBit(block.version, deployments.segwit.bit)) + throw new VerifyError(block, 'invalid', 'bad-no-segwit', 0); + } + // Get timestamp for tx.isFinal(). ts = state.hasMTP() ? mtp : block.ts; @@ -404,7 +418,7 @@ Chain.prototype.getDeployments = async function getDeployments(ts, prev) { let deployments = this.network.deployments; let height = prev.height + 1; let state = new DeploymentState(); - let active; + let witness; // For some reason bitcoind has p2sh in the // mandatory flags by default, when in reality @@ -431,21 +445,47 @@ Chain.prototype.getDeployments = async function getDeployments(ts, prev) { // CHECKSEQUENCEVERIFY and median time // past locktimes are now usable (bip9 & bip113). - active = await this.isActive(prev, deployments.csv); - if (active) { + if (await this.isActive(prev, deployments.csv)) { state.flags |= Script.flags.VERIFY_CHECKSEQUENCEVERIFY; state.lockFlags |= common.lockFlags.VERIFY_SEQUENCE; state.lockFlags |= common.lockFlags.MEDIAN_TIME_PAST; } - // Segregrated witness is now usable (bip141). - active = await this.isActive(prev, deployments.segwit); - if (active) { + // Check the state of the segwit deployment. + witness = await this.getState(prev, deployments.segwit); + + // Segregrated witness (bip141) is now usable + // along with SCRIPT_VERIFY_NULLDUMMY (bip147). + if (witness === thresholdStates.ACTIVE) { state.flags |= Script.flags.VERIFY_WITNESS; - // BIP147 state.flags |= Script.flags.VERIFY_NULLDUMMY; } + // Segsignal is now enforced (bip91). + if (this.options.bip91) { + if (witness === thresholdStates.STARTED) { + if (await this.isActive(prev, deployments.segsignal)) + state.bip91 = true; + } + } + + // UASF is now enforced (bip148) (mainnet-only). + if (this.options.bip148 && this.network === Network.main) { + if (witness !== thresholdStates.LOCKED_IN + && witness !== thresholdStates.ACTIVE) { + // The BIP148 MTP check is nonsensical in + // that it includes the _current_ entry's + // timestamp. This requires some hackery, + // since bcoin only operates on the sane + // assumption that deployment checks should + // only ever examine the values of the + // previous block (necessary for mining). + let mtp = await prev.getMedianTime(ts); + if (mtp >= 1501545600 && mtp <= 1510704000) + state.bip148 = true; + } + } + return state; }; @@ -473,6 +513,12 @@ Chain.prototype.setDeploymentState = function setDeploymentState(state) { if (!this.state.hasWitness() && state.hasWitness()) this.logger.warning('Segwit has been activated.'); + if (!this.state.hasBIP91() && state.hasBIP91()) + this.logger.warning('BIP91 has been activated.'); + + if (!this.state.hasBIP148() && state.hasBIP148()) + this.logger.warning('BIP148 has been activated.'); + this.state = state; }; @@ -2056,7 +2102,7 @@ Chain.prototype.findLocator = async function findLocator(locator) { Chain.prototype.isActive = async function isActive(prev, deployment) { let state = await this.getState(prev, deployment); - return state === common.thresholdStates.ACTIVE; + return state === thresholdStates.ACTIVE; }; /** @@ -2071,22 +2117,27 @@ Chain.prototype.isActive = async function isActive(prev, deployment) { */ Chain.prototype.getState = async function getState(prev, deployment) { - let period = this.network.minerWindow; + let window = this.network.minerWindow; let threshold = this.network.activationThreshold; - let thresholdStates = common.thresholdStates; let bit = deployment.bit; let compute = []; let entry, state; - if (((prev.height + 1) % period) !== 0) { - let height = prev.height - ((prev.height + 1) % period); + if (deployment.threshold !== -1) + threshold = deployment.threshold; + + if (deployment.window !== -1) + window = deployment.window; + + if (((prev.height + 1) % window) !== 0) { + let height = prev.height - ((prev.height + 1) % window); prev = await prev.getAncestor(height); if (!prev) return thresholdStates.DEFINED; assert(prev.height === height); - assert(((prev.height + 1) % period) === 0); + assert(((prev.height + 1) % window) === 0); } entry = prev; @@ -2111,7 +2162,7 @@ Chain.prototype.getState = async function getState(prev, deployment) { compute.push(entry); - height = entry.height - period; + height = entry.height - window; entry = await entry.getAncestor(height); } @@ -2144,7 +2195,7 @@ Chain.prototype.getState = async function getState(prev, deployment) { break; } - for (let i = 0; i < period; i++) { + for (let i = 0; i < window; i++) { if (block.hasBit(bit)) count++; @@ -2193,8 +2244,8 @@ Chain.prototype.computeBlockVersion = async function computeBlockVersion(prev) { for (let deployment of this.network.deploys) { let state = await this.getState(prev, deployment); - if (state === common.thresholdStates.LOCKED_IN - || state === common.thresholdStates.STARTED) { + if (state === thresholdStates.LOCKED_IN + || state === thresholdStates.STARTED) { version |= 1 << deployment.bit; } } @@ -2356,11 +2407,12 @@ function ChainOptions(options) { this.bufferKeys = ChainDB.layout.binary; this.spv = false; + this.bip91 = false; + this.bip148 = false; this.prune = false; this.indexTX = false; this.indexAddress = false; - this.forceWitness = false; - this.forcePrune = false; + this.forceFlags = false; this.coinCache = 0; this.entryCache = 5000; @@ -2445,16 +2497,19 @@ ChainOptions.prototype.fromOptions = function fromOptions(options) { this.indexAddress = options.indexAddress; } - if (options.forceWitness != null) { - assert(typeof options.forceWitness === 'boolean'); - this.forceWitness = options.forceWitness; + if (options.forceFlags != null) { + assert(typeof options.forceFlags === 'boolean'); + this.forceFlags = options.forceFlags; } - if (options.forcePrune != null) { - assert(typeof options.forcePrune === 'boolean'); - this.forcePrune = options.forcePrune; - if (options.forcePrune) - this.prune = true; + if (options.bip91 != null) { + assert(typeof options.bip91 === 'boolean'); + this.bip91 = options.bip91; + } + + if (options.bip148 != null) { + assert(typeof options.bip148 === 'boolean'); + this.bip148 = options.bip148; } if (options.coinCache != null) { @@ -2507,6 +2562,8 @@ function DeploymentState() { this.flags &= ~Script.flags.VERIFY_P2SH; this.lockFlags = common.lockFlags.MANDATORY_LOCKTIME_FLAGS; this.bip34 = false; + this.bip91 = false; + this.bip148 = false; } /** @@ -2572,6 +2629,24 @@ DeploymentState.prototype.hasWitness = function hasWitness() { return (this.flags & Script.flags.VERIFY_WITNESS) !== 0; }; +/** + * Test whether bip91 is active. + * @returns {Boolean} + */ + +DeploymentState.prototype.hasBIP91 = function hasBIP91() { + return this.bip91; +}; + +/** + * Test whether bip148 is active. + * @returns {Boolean} + */ + +DeploymentState.prototype.hasBIP148 = function hasBIP148() { + return this.bip148; +}; + /** * Orphan * @constructor diff --git a/lib/blockchain/chaindb.js b/lib/blockchain/chaindb.js index d3538275..dbbc2577 100644 --- a/lib/blockchain/chaindb.js +++ b/lib/blockchain/chaindb.js @@ -495,7 +495,7 @@ ChainDB.prototype.getFlags = async function getFlags() { ChainDB.prototype.verifyFlags = async function verifyFlags(state) { let options = this.options; let flags = await this.getFlags(); - let needsWitness = false; + let needsSave = false; let needsPrune = false; if (!flags) @@ -511,13 +511,25 @@ ChainDB.prototype.verifyFlags = async function verifyFlags(state) { throw new Error('Cannot retroactively disable SPV.'); if (!flags.witness) { - if (!options.forceWitness) + if (!options.forceFlags) throw new Error('Cannot retroactively enable witness.'); - needsWitness = true; + needsSave = true; + } + + if (options.bip91 !== flags.bip91) { + if (!options.forceFlags) + throw new Error('Cannot retroactively alter BIP91 flag.'); + needsSave = true; + } + + if (options.bip148 !== flags.bip148) { + if (!options.forceFlags) + throw new Error('Cannot retroactively alter BIP148 flag.'); + needsSave = true; } if (options.prune && !flags.prune) { - if (!options.forcePrune) + if (!options.forceFlags) throw new Error('Cannot retroactively prune.'); needsPrune = true; } @@ -537,8 +549,8 @@ ChainDB.prototype.verifyFlags = async function verifyFlags(state) { if (!options.indexAddress && flags.indexAddress) throw new Error('Cannot retroactively disable address indexing.'); - if (needsWitness) { - await this.logger.info('Writing witness bit to chain flags.'); + if (needsSave) { + await this.logger.info('Rewriting chain flags.'); await this.saveFlags(); } @@ -589,7 +601,7 @@ ChainDB.prototype.saveDeployments = function saveDeployments() { */ ChainDB.prototype.writeDeployments = function writeDeployments(batch) { - let bw = new StaticWriter(1 + 9 * this.network.deploys.length); + let bw = new StaticWriter(1 + 17 * this.network.deploys.length); bw.writeU8(this.network.deploys.length); @@ -597,6 +609,8 @@ ChainDB.prototype.writeDeployments = function writeDeployments(batch) { bw.writeU8(deployment.bit); bw.writeU32(deployment.startTime); bw.writeU32(deployment.timeout); + bw.write32(deployment.threshold); + bw.write32(deployment.window); } batch.put(layout.V, bw.render()); @@ -623,11 +637,15 @@ ChainDB.prototype.checkDeployments = async function checkDeployments() { let bit = br.readU8(); let start = br.readU32(); let timeout = br.readU32(); + let threshold = br.read32(); + let window = br.read32(); let deployment = this.network.byBit(bit); if (deployment && start === deployment.startTime - && timeout === deployment.timeout) { + && timeout === deployment.timeout + && threshold === deployment.threshold + && window === deployment.window) { continue; } @@ -644,8 +662,17 @@ ChainDB.prototype.checkDeployments = async function checkDeployments() { */ ChainDB.prototype.verifyDeployments = async function verifyDeployments() { - let invalid = await this.checkDeployments(); - let batch; + let invalid, batch; + + try { + invalid = await this.checkDeployments(); + } catch (e) { + if (e.type !== 'EncodingError') + throw e; + invalid = []; + for (let {bit} of this.network.deploys) + invalid.push(bit); + } if (invalid.length === 0) return true; @@ -1946,6 +1973,8 @@ function ChainFlags(options) { this.network = Network.primary; this.spv = false; this.witness = true; + this.bip91 = false; + this.bip148 = false; this.prune = false; this.indexTX = false; this.indexAddress = false; @@ -1962,6 +1991,16 @@ ChainFlags.prototype.fromOptions = function fromOptions(options) { this.spv = options.spv; } + if (options.bip91 != null) { + assert(typeof options.bip91 === 'boolean'); + this.bip91 = options.bip91; + } + + if (options.bip148 != null) { + assert(typeof options.bip148 === 'boolean'); + this.bip148 = options.bip148; + } + if (options.prune != null) { assert(typeof options.prune === 'boolean'); this.prune = options.prune; @@ -2003,6 +2042,12 @@ ChainFlags.prototype.toRaw = function toRaw() { if (this.indexAddress) flags |= 1 << 4; + if (this.bip91) + flags |= 1 << 5; + + if (this.bip148) + flags |= 1 << 6; + bw.writeU32(this.network.magic); bw.writeU32(flags); bw.writeU32(0); @@ -2023,6 +2068,8 @@ ChainFlags.prototype.fromRaw = function fromRaw(data) { this.prune = (flags & 4) !== 0; this.indexTX = (flags & 8) !== 0; this.indexAddress = (flags & 16) !== 0; + this.bip91 = (flags & 32) !== 0; + this.bip148 = (flags & 64) !== 0; return this; }; diff --git a/lib/blockchain/chainentry.js b/lib/blockchain/chainentry.js index fd3eeaa4..e4782082 100644 --- a/lib/blockchain/chainentry.js +++ b/lib/blockchain/chainentry.js @@ -267,14 +267,23 @@ ChainEntry.prototype.getNextEntry = async function getNextEntry() { /** * Calculate median time past. * @method + * @param {Number?} ts * @returns {Promise} - Returns Number. */ -ChainEntry.prototype.getMedianTime = async function getMedianTime() { +ChainEntry.prototype.getMedianTime = async function getMedianTime(ts) { let timespan = ChainEntry.MEDIAN_TIMESPAN; let entry = this; let median = []; + // In case we ever want to check + // the MTP of the _current_ block + // (necessary for BIP148). + if (ts != null) { + median.push(ts); + timespan -= 1; + } + for (let i = 0; i < timespan && entry; i++) { let cache; @@ -331,10 +340,7 @@ ChainEntry.prototype.hasUnknown = function hasUnknown() { */ ChainEntry.prototype.hasBit = function hasBit(bit) { - let bits = this.version & consensus.VERSION_TOP_MASK; - let topBits = consensus.VERSION_TOP_BITS; - let mask = 1 << bit; - return (bits >>> 0) === topBits && (this.version & mask) !== 0; + return consensus.hasBit(this.version, bit); }; /** diff --git a/lib/http/rpc.js b/lib/http/rpc.js index ca670dae..3f9d582c 100644 --- a/lib/http/rpc.js +++ b/lib/http/rpc.js @@ -1247,6 +1247,9 @@ RPC.prototype.getBlockTemplate = async function getBlockTemplate(args, help) { if (lpid) await this.handleLongpoll(lpid); + if (!rules) + rules = []; + return await this.createTemplate(maxVersion, coinbase, rules); }; @@ -1322,6 +1325,14 @@ RPC.prototype._createTemplate = async function _createTemplate(maxVersion, coinb }); } + if (this.chain.options.bip91) { + rules.push('segwit'); + rules.push('segsignal'); + } + + if (this.chain.options.bip148) + rules.push('segwit'); + // Calculate version based on given rules. for (let deploy of this.network.deploys) { let state = await this.chain.getState(this.chain.tip, deploy); @@ -1335,15 +1346,16 @@ RPC.prototype._createTemplate = async function _createTemplate(maxVersion, coinb version |= 1 << deploy.bit; case common.thresholdStates.STARTED: if (!deploy.force) { - if (!rules || rules.indexOf(name) === -1) + if (rules.indexOf(name) === -1) version &= ~(1 << deploy.bit); - name = '!' + name; + if (deploy.required) + name = '!' + name; } vbavailable[name] = deploy.bit; break; case common.thresholdStates.ACTIVE: - if (!deploy.force) { - if (!rules || rules.indexOf(name) === -1) { + if (!deploy.force && deploy.required) { + if (rules.indexOf(name) === -1) { throw new RPCError(errs.INVALID_PARAMETER, `Client must support ${name}.`); } @@ -1431,7 +1443,7 @@ RPC.prototype._createTemplate = async function _createTemplate(maxVersion, coinb json.coinbasevalue = attempt.getReward(); } - if (rules && rules.indexOf('segwit') !== -1) + if (rules.indexOf('segwit') !== -1) json.default_witness_commitment = attempt.getWitnessScript().toJSON(); return json; diff --git a/lib/node/fullnode.js b/lib/node/fullnode.js index 7ce70dd0..d66cd5bb 100644 --- a/lib/node/fullnode.js +++ b/lib/node/fullnode.js @@ -56,8 +56,9 @@ function FullNode(options) { prefix: this.config.prefix, maxFiles: this.config.num('max-files'), cacheSize: this.config.mb('cache-size'), - forceWitness: this.config.bool('force-witness'), - forcePrune: this.config.bool('force-prune'), + forceFlags: this.config.bool('force-flags'), + bip91: this.config.bool('bip91'), + bip148: this.config.bool('bip148'), prune: this.config.bool('prune'), checkpoints: this.config.bool('checkpoints'), coinCache: this.config.mb('coin-cache'), diff --git a/lib/node/spvnode.js b/lib/node/spvnode.js index 7b9448e7..8401fec8 100644 --- a/lib/node/spvnode.js +++ b/lib/node/spvnode.js @@ -52,8 +52,10 @@ function SPVNode(options) { maxFiles: this.config.num('max-files'), cacheSize: this.config.mb('cache-size'), entryCache: this.config.num('entry-cache'), - forceWitness: this.config.bool('force-witness'), + forceFlags: this.config.bool('force-flags'), checkpoints: this.config.bool('checkpoints'), + bip91: this.config.bool('bip91'), + bip148: this.config.bool('bip148'), spv: true }); diff --git a/lib/protocol/consensus.js b/lib/protocol/consensus.js index bf589a8c..ea671eeb 100644 --- a/lib/protocol/consensus.js +++ b/lib/protocol/consensus.js @@ -332,3 +332,17 @@ exports.getReward = function getReward(height, interval) { return exports.HALF_REWARD >>> (halvings - 1); }; + +/** + * Test version bit. + * @param {Number} version + * @param {Number} bit + * @returns {Boolean} + */ + +exports.hasBit = function hasBit(version, bit) { + let bits = version & exports.VERSION_TOP_MASK; + let topBits = exports.VERSION_TOP_BITS; + let mask = 1 << bit; + return (bits >>> 0) === topBits && (version & mask) !== 0; +}; diff --git a/lib/protocol/networks.js b/lib/protocol/networks.js index d3a3d731..4b86d888 100644 --- a/lib/protocol/networks.js +++ b/lib/protocol/networks.js @@ -335,18 +335,14 @@ main.minerWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing */ main.deployments = { - testdummy: { - name: 'testdummy', - bit: 28, - startTime: 1199145601, // January 1, 2008 - timeout: 1230767999, // December 31, 2008 - force: true - }, csv: { name: 'csv', bit: 0, startTime: 1462060800, // May 1st, 2016 timeout: 1493596800, // May 1st, 2017 + threshold: -1, + window: -1, + required: false, force: true }, segwit: { @@ -354,7 +350,30 @@ main.deployments = { bit: 1, startTime: 1479168000, // November 15th, 2016. timeout: 1510704000, // November 15th, 2017. + threshold: -1, + window: -1, + required: true, force: false + }, + segsignal: { + name: 'segsignal', + bit: 4, + startTime: 1496275200, // June 1st, 2017. + timeout: 1510704000, // November 15th, 2017. + threshold: 269, // 80% + window: 336, // ~2.33 days + required: false, + force: false + }, + testdummy: { + name: 'testdummy', + bit: 28, + startTime: 1199145601, // January 1, 2008 + timeout: 1230767999, // December 31, 2008 + threshold: -1, + window: -1, + required: false, + force: true } }; @@ -367,6 +386,7 @@ main.deployments = { main.deploys = [ main.deployments.csv, main.deployments.segwit, + main.deployments.segsignal, main.deployments.testdummy ]; @@ -555,18 +575,14 @@ testnet.activationThreshold = 1512; // 75% for testchains testnet.minerWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing testnet.deployments = { - testdummy: { - name: 'testdummy', - bit: 28, - startTime: 1199145601, // January 1, 2008 - timeout: 1230767999, // December 31, 2008 - force: true - }, csv: { name: 'csv', bit: 0, startTime: 1456790400, // March 1st, 2016 timeout: 1493596800, // May 1st, 2017 + threshold: -1, + window: -1, + required: false, force: true }, segwit: { @@ -574,13 +590,37 @@ testnet.deployments = { bit: 1, startTime: 1462060800, // May 1st 2016 timeout: 1493596800, // May 1st 2017 + threshold: -1, + window: -1, + required: true, force: false + }, + segsignal: { + name: 'segsignal', + bit: 4, + startTime: 0xffffffff, + timeout: 0xffffffff, + threshold: 269, + window: 336, + required: false, + force: false + }, + testdummy: { + name: 'testdummy', + bit: 28, + startTime: 1199145601, // January 1, 2008 + timeout: 1230767999, // December 31, 2008 + threshold: -1, + window: -1, + required: false, + force: true } }; testnet.deploys = [ testnet.deployments.csv, testnet.deployments.segwit, + testnet.deployments.segsignal, testnet.deployments.testdummy ]; @@ -695,18 +735,14 @@ regtest.activationThreshold = 108; // 75% for testchains regtest.minerWindow = 144; // Faster than normal for regtest (144 instead of 2016) regtest.deployments = { - testdummy: { - name: 'testdummy', - bit: 28, - startTime: 0, - timeout: 0xffffffff, - force: true - }, csv: { name: 'csv', bit: 0, startTime: 0, timeout: 0xffffffff, + threshold: -1, + window: -1, + required: false, force: true }, segwit: { @@ -714,13 +750,37 @@ regtest.deployments = { bit: 1, startTime: 0, timeout: 0xffffffff, + threshold: -1, + window: -1, + required: true, force: false + }, + segsignal: { + name: 'segsignal', + bit: 4, + startTime: 0xffffffff, + timeout: 0xffffffff, + threshold: 269, + window: 336, + required: false, + force: false + }, + testdummy: { + name: 'testdummy', + bit: 28, + startTime: 0, + timeout: 0xffffffff, + threshold: -1, + window: -1, + required: false, + force: true } }; regtest.deploys = [ regtest.deployments.csv, regtest.deployments.segwit, + regtest.deployments.segsignal, regtest.deployments.testdummy ]; @@ -837,18 +897,14 @@ segnet4.activationThreshold = 108; segnet4.minerWindow = 144; segnet4.deployments = { - testdummy: { - name: 'testdummy', - bit: 28, - startTime: 1199145601, // January 1, 2008 - timeout: 1230767999, // December 31, 2008 - force: true - }, csv: { name: 'csv', bit: 0, startTime: 1456790400, // March 1st, 2016 timeout: 1493596800, // May 1st, 2017 + threshold: -1, + window: -1, + required: false, force: true }, segwit: { @@ -856,13 +912,37 @@ segnet4.deployments = { bit: 1, startTime: 0, timeout: 0xffffffff, + threshold: -1, + window: -1, + required: true, force: false + }, + segsignal: { + name: 'segsignal', + bit: 4, + startTime: 0xffffffff, + timeout: 0xffffffff, + threshold: 269, + window: 336, + required: false, + force: false + }, + testdummy: { + name: 'testdummy', + bit: 28, + startTime: 1199145601, // January 1, 2008 + timeout: 1230767999, // December 31, 2008 + threshold: -1, + window: -1, + required: false, + force: true } }; segnet4.deploys = [ segnet4.deployments.csv, segnet4.deployments.segwit, + segnet4.deployments.segsignal, segnet4.deployments.testdummy ]; @@ -979,18 +1059,14 @@ simnet.activationThreshold = 75; // 75% for testchains simnet.minerWindow = 100; // nPowTargetTimespan / nPowTargetSpacing simnet.deployments = { - testdummy: { - name: 'testdummy', - bit: 28, - startTime: 1199145601, // January 1, 2008 - timeout: 1230767999, // December 31, 2008 - force: true - }, csv: { name: 'csv', bit: 0, startTime: 0, // March 1st, 2016 timeout: 0xffffffff, // May 1st, 2017 + threshold: -1, + window: -1, + required: false, force: true }, segwit: { @@ -998,13 +1074,37 @@ simnet.deployments = { bit: 1, startTime: 0, // May 1st 2016 timeout: 0xffffffff, // May 1st 2017 + threshold: -1, + window: -1, + required: true, force: false + }, + segsignal: { + name: 'segsignal', + bit: 4, + startTime: 0xffffffff, + timeout: 0xffffffff, + threshold: 269, + window: 336, + required: false, + force: false + }, + testdummy: { + name: 'testdummy', + bit: 28, + startTime: 1199145601, // January 1, 2008 + timeout: 1230767999, // December 31, 2008 + threshold: -1, + window: -1, + required: false, + force: true } }; simnet.deploys = [ simnet.deployments.csv, simnet.deployments.segwit, + simnet.deployments.segsignal, simnet.deployments.testdummy ];