Skip to content

Commit

Permalink
Merge pull request bitcoin#21 from jameshilliard/segwit2x-bip91updates
Browse files Browse the repository at this point in the history
Update to be compatible with latest BIP91 spec
  • Loading branch information
jgarzik committed Jun 16, 2017
2 parents c11c37b + dbfc931 commit 572278e
Show file tree
Hide file tree
Showing 9 changed files with 216 additions and 20 deletions.
1 change: 1 addition & 0 deletions qa/pull-tester/rpc-tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@
'rpcnamedargs.py',
'listsinceblock.py',
'p2p-leaktests.py',
'bip91.py',
]
if ENABLE_ZMQ:
testScripts.append('zmq_test.py')
Expand Down
195 changes: 195 additions & 0 deletions qa/rpc-tests/bip91.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
#!/usr/bin/env python3
# Copyright (c) 2015-2016 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.

from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
from test_framework.mininode import NetworkThread
from test_framework.blocktools import create_coinbase, create_block
import time

'''
This test is meant to exercise BIP91
regtest lock-in with 108/144 for BIP141 and 29/48 for BIP91
mine 143 blocks to transition from DEFINED to STARTED
mine 28 blocks signalling readiness and 20 not in order to fail to change state this period for BIP91
mine 29 blocks signalling readiness and 19 blocks not signalling readiness for BIP91 (STARTED->LOCKED_IN)
bit 1 is optional for the following 48 blocks when BIP91 is LOCKED_IN (LOCKED_IN->ACTIVE)
bit 1 is mandatory for the following 144 blocks until BIP141 is locked_in
bit 1 is optional after BIP141 is locked_in
'''

class BIP91Test(BitcoinTestFramework):

def __init__(self):
super().__init__()
self.num_nodes = 2
self.setup_clean_chain = True

def setup_network(self):
self.nodes = []
self.nodes.append(start_node(0, self.options.tmpdir, ["-debug", "-whitelist=127.0.0.1"]))
self.nodes.append(start_node(1, self.options.tmpdir, ["-debug", "-whitelist=127.0.0.1"])) # connect to a dummy node to allow getblocktemplate
connect_nodes(self.nodes[0], 1)

def run_test(self):
NetworkThread().start() # Start up network handling in another thread
self.tip = int("0x" + self.nodes[0].getbestblockhash(), 0)
self.height = 1 # height of the next block to build
self.last_block_time = int(time.time())

assert_equal(self.get_bip9_status('segwit2x')['status'], 'defined')
assert_equal(self.get_bip9_status('segwit2x')['since'], 0)
assert_equal(self.get_bip9_status('segwit')['status'], 'defined')
assert_equal(self.get_bip9_status('segwit')['since'], 0)


# Test 1
# Advance from DEFINED to STARTED
self.generate_blocks(1,4)
tmpl = self.nodes[0].getblocktemplate({})
assert_equal(tmpl['version'], 0x20000000)

self.generate_blocks(142, 4)
assert_equal(self.get_bip9_status('segwit2x')['status'], 'started')
assert_equal(self.get_bip9_status('segwit2x')['since'], 48)
assert_equal(self.get_bip9_status('segwit')['status'], 'started')
assert_equal(self.get_bip9_status('segwit')['since'], 144)
tmpl = self.nodes[0].getblocktemplate({})
assert_equal(tmpl['version'], 0x10000001|0x20000012) # 0x10000001 are TESTDUMMY and CSV

# Test 2
# Fail to achieve LOCKED_IN 28 out of 48 signal bit 4
self.generate_blocks(20, 0x20000010) # signalling bit 4
self.generate_blocks(8, 0x20000012) # signalling bit 1 and 4
self.generate_blocks(10, 0x20000002) # signalling bit 1
self.generate_blocks(10, 4) # not signalling
assert_equal(self.get_bip9_status('segwit2x')['status'], 'started')
assert_equal(self.get_bip9_status('segwit2x')['since'], 48)
assert_equal(self.get_bip9_status('segwit')['status'], 'started')
assert_equal(self.get_bip9_status('segwit')['since'], 144)
tmpl = self.nodes[0].getblocktemplate({})
assert_equal(tmpl['version'], 0x10000001|0x20000012)


# Test 3
# 28 out of 48 signal bit 4 to achieve LOCKED_IN
self.generate_blocks(20, 0x20000010) # signalling bit 4
self.generate_blocks(9, 0x20000012) # signalling bit 1 and 4
self.generate_blocks(10, 0x20000002) # signalling bit 1
self.generate_blocks(9, 4) # not signalling
assert_equal(self.get_bip9_status('segwit2x')['status'], 'locked_in')
assert_equal(self.get_bip9_status('segwit2x')['since'], 240)
assert_equal(self.get_bip9_status('segwit')['status'], 'started')
assert_equal(self.get_bip9_status('segwit')['since'], 144)
tmpl = self.nodes[0].getblocktemplate({})
assert_equal(tmpl['version'], 0x10000001|0x20000012)

# Test 4
# No restriction when bit 4 is LOCKED_IN
self.generate_blocks(5, 4)
self.generate_blocks(5, 0x20000000)
self.generate_blocks(5, 0x20000010)
self.generate_blocks(5, 0x40000002)
self.generate_blocks(5, 0x60000002)
self.generate_blocks(5, 0x12)
self.generate_blocks(5, 0x20000002)
self.generate_blocks(5, 0x20000012)
self.generate_blocks(8, 0x20000102)
assert_equal(self.get_bip9_status('segwit2x')['status'], 'active')
assert_equal(self.get_bip9_status('segwit2x')['since'], 288)
assert_equal(self.get_bip9_status('segwit')['status'], 'started')
assert_equal(self.get_bip9_status('segwit')['since'], 144)
tmpl = self.nodes[0].getblocktemplate({})
assert_equal(tmpl['version'], 0x10000001|0x20000002)

# Test 5
# bit 1 signalling becomes mandatory after bit 4 is ACTIVE
self.generate_blocks(1, 4, 'bad-no-segwit')
self.generate_blocks(1, 0x20000000, 'bad-no-segwit')
self.generate_blocks(1, 0x20000010, 'bad-no-segwit')
self.generate_blocks(1, 0x40000002, 'bad-no-segwit')
self.generate_blocks(1, 0x60000002, 'bad-no-segwit')
self.generate_blocks(1, 0x12, 'bad-no-segwit')
self.generate_blocks(35, 0x20000002)
self.generate_blocks(35, 0x20000012)
self.generate_blocks(73, 0x20000102)

assert_equal(self.get_bip9_status('segwit2x')['status'], 'active')
assert_equal(self.get_bip9_status('segwit2x')['since'], 288)
assert_equal(self.get_bip9_status('segwit')['status'], 'started')
assert_equal(self.get_bip9_status('segwit')['since'], 144)
tmpl = self.nodes[0].getblocktemplate({})
assert_equal(tmpl['version'], 0x10000001|0x20000002)

self.generate_blocks(1, 4, 'bad-no-segwit')
self.generate_blocks(1, 0x20000000, 'bad-no-segwit')
self.generate_blocks(1, 0x20000010, 'bad-no-segwit')
self.generate_blocks(1, 0x40000002, 'bad-no-segwit')
self.generate_blocks(1, 0x60000002, 'bad-no-segwit')
self.generate_blocks(1, 0x12, 'bad-no-segwit')
self.generate_blocks(1, 0x20000002)

# Test 6
# bit 1 signalling becomes optional after bit 1 locked_in

assert_equal(self.get_bip9_status('segwit2x')['status'], 'active')
assert_equal(self.get_bip9_status('segwit2x')['since'], 288)
assert_equal(self.get_bip9_status('segwit')['status'], 'locked_in')
assert_equal(self.get_bip9_status('segwit')['since'], 432)
tmpl = self.nodes[0].getblocktemplate({})
assert_equal(tmpl['version'], 0x10000001|0x20000002)

self.generate_blocks(20, 0x20000002)
self.generate_blocks(20, 0x20000012)
self.generate_blocks(20, 0x20000102)
self.generate_blocks(20, 0x20000000)
self.generate_blocks(20, 0x20000010)
self.generate_blocks(20, 0x40000002)
self.generate_blocks(23, 0x60000002)


assert_equal(self.get_bip9_status('segwit2x')['status'], 'active')
assert_equal(self.get_bip9_status('segwit2x')['since'], 288)
assert_equal(self.get_bip9_status('segwit')['status'], 'locked_in')
assert_equal(self.get_bip9_status('segwit')['since'], 432)

self.generate_blocks(1, 4)


assert_equal(self.get_bip9_status('segwit2x')['status'], 'active')
assert_equal(self.get_bip9_status('segwit2x')['since'], 288)
assert_equal(self.get_bip9_status('segwit')['status'], 'active')
assert_equal(self.get_bip9_status('segwit')['since'], 576)
tmpl = self.nodes[0].getblocktemplate({})
assert_equal(tmpl['version'], 0x10000001|0x20000000)

self.generate_blocks(1, 0x20000002)
self.generate_blocks(1, 0x20000012)
self.generate_blocks(1, 0x20000102)
self.generate_blocks(1, 0x20000000)
self.generate_blocks(1, 0x20000010)
self.generate_blocks(1, 0x40000002)
self.generate_blocks(1, 0x60000002)
self.generate_blocks(1, 4)

def generate_blocks(self, number, version, error = None):
for i in range(number):
block = create_block(self.tip, create_coinbase(self.height), self.last_block_time + 1)
block.nVersion = version
block.rehash()
block.solve()
assert_equal(self.nodes[0].submitblock(bytes_to_hex_str(block.serialize())), error)
if (error == None):
self.last_block_time += 1
self.tip = block.sha256
self.height += 1

def get_bip9_status(self, key):
info = self.nodes[0].getblockchaininfo()
return info['bip9_softforks'][key]


if __name__ == '__main__':
BIP91Test().main()
2 changes: 1 addition & 1 deletion qa/rpc-tests/p2p-compactblocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ def setup_network(self):

# Start up node0 to be a version 1, pre-segwit node.
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir,
[["-debug", "-logtimemicros=1", "-bip9params=segwit:0:0", "-bip9params=segwit2x:0:0"],
[["-debug", "-logtimemicros=1", "-bip9params=segwit:0:0", "-blockversion=536870915"], # signal segwit and csv
["-debug", "-logtimemicros", "-txindex"]])
connect_nodes(self.nodes[0], 1)

Expand Down
3 changes: 2 additions & 1 deletion qa/rpc-tests/p2p-versionbits-warning.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ def setup_network(self):
# Open and close to create zero-length file
with open(self.alert_filename, 'w', encoding='utf8') as _:
pass
self.extra_args = [["-debug", "-logtimemicros=1", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""]]
self.extra_args = [["-blockversion=4", "-debug", "-logtimemicros=1", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""]]
# Have to use version 4 as the default version will trigger BIP91 and make the subsequent tests fail
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args)

# Send numblocks blocks via peer with nVersionToUse set.
Expand Down
4 changes: 4 additions & 0 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ class CMainParams : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT2X].bit = 4;
consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT2X].nStartTime = 1496275200; // June 1st, 2017.
consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT2X].nTimeout = 1510704000; // November 15th, 2017.
consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT2X].nOverrideMinerConfirmationWindow = 336; // ~2.33 days
consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT2X].nOverrideRuleChangeActivationThreshold = 269; // 80%

// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000003f94d1ad391682fe038bf5");
Expand Down Expand Up @@ -300,6 +302,8 @@ class CRegTestParams : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT2X].bit = 4;
consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT2X].nStartTime = 0;
consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT2X].nTimeout = 999999999999ULL;
consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT2X].nOverrideMinerConfirmationWindow = 48;
consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT2X].nOverrideRuleChangeActivationThreshold = 29; // 60%

// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x00");
Expand Down
4 changes: 4 additions & 0 deletions src/consensus/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ struct BIP9Deployment {
int64_t nStartTime;
/** Timeout/expiry MedianTime for the deployment attempt. */
int64_t nTimeout;
/** Overriding the default threshold if not zero. */
uint32_t nOverrideRuleChangeActivationThreshold = 0;
/** Overriding the default confirmation window if not zero . */
uint32_t nOverrideMinerConfirmationWindow = 0;
};

/**
Expand Down
11 changes: 2 additions & 9 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1852,9 +1852,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
}

// SEGWIT2X signalling.
if ( VersionBitsState(pindex->pprev, chainparams.GetConsensus(), Consensus::DEPLOYMENT_SEGWIT2X, versionbitscache) == THRESHOLD_ACTIVE &&
!IsWitnessLockedIn(pindex->pprev, chainparams.GetConsensus()) && // Segwit is not locked in
!IsWitnessEnabled(pindex->pprev, chainparams.GetConsensus()) ) // and is not active.
if (VersionBitsState(pindex->pprev, chainparams.GetConsensus(), Consensus::DEPLOYMENT_SEGWIT2X, versionbitscache) == THRESHOLD_ACTIVE &&
VersionBitsState(pindex->pprev, chainparams.GetConsensus(), Consensus::DEPLOYMENT_SEGWIT, versionbitscache) == THRESHOLD_STARTED)
{
bool fVersionBits = (pindex->nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS;
bool fSegbit = (pindex->nVersion & VersionBitsMask(chainparams.GetConsensus(), Consensus::DEPLOYMENT_SEGWIT)) != 0;
Expand Down Expand Up @@ -2930,12 +2929,6 @@ bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& pa
return (VersionBitsState(pindexPrev, params, Consensus::DEPLOYMENT_SEGWIT, versionbitscache) == THRESHOLD_ACTIVE);
}

// Check if Segregated Witness is Locked In
bool IsWitnessLockedIn(const CBlockIndex* pindexPrev, const Consensus::Params& params)
{
LOCK(cs_main);
return (VersionBitsState(pindexPrev, params, Consensus::DEPLOYMENT_SEGWIT, versionbitscache) == THRESHOLD_LOCKED_IN);
}

// Compute at which vout of the block's coinbase transaction the witness
// commitment occurs, or -1 if not found.
Expand Down
3 changes: 0 additions & 3 deletions src/validation.h
Original file line number Diff line number Diff line change
Expand Up @@ -503,9 +503,6 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams,
/** Check whether witness commitments are required for block. */
bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params);

/** Check whether witness commitments are required for block. */
bool IsWitnessLockedIn(const CBlockIndex* pindexPrev, const Consensus::Params& params);

/** When there are blocks in the active chain with missing data, rewind the chainstate and remove them from the block index */
bool RewindBlockIndex(const CChainParams& params);

Expand Down
13 changes: 7 additions & 6 deletions src/versionbits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,13 +151,14 @@ class VersionBitsConditionChecker : public AbstractThresholdConditionChecker {
protected:
int64_t BeginTime(const Consensus::Params& params) const { return params.vDeployments[id].nStartTime; }
int64_t EndTime(const Consensus::Params& params) const { return params.vDeployments[id].nTimeout; }
int Period(const Consensus::Params& params) const { return params.nMinerConfirmationWindow; }
int Period(const Consensus::Params& params) const {
if (params.vDeployments[id].nOverrideMinerConfirmationWindow > 0)
return params.vDeployments[id].nOverrideMinerConfirmationWindow;
return params.nMinerConfirmationWindow;
}
int Threshold(const Consensus::Params& params) const {
if (params.nRuleChangeActivationThreshold == 1916 && params.nMinerConfirmationWindow == 2016 &&
params.vDeployments[id].bit == params.vDeployments[Consensus::DEPLOYMENT_SEGWIT2X].bit &&
params.vDeployments[id].nStartTime == params.vDeployments[Consensus::DEPLOYMENT_SEGWIT2X].nStartTime) {
return 1612; // 80% threshold for SEGWIT2X only
}
if (params.vDeployments[id].nOverrideRuleChangeActivationThreshold > 0)
return params.vDeployments[id].nOverrideRuleChangeActivationThreshold;
return params.nRuleChangeActivationThreshold;
}

Expand Down

0 comments on commit 572278e

Please sign in to comment.