Dash Core  0.12.2.1
P2P Digital Currency
p2p-versionbits-warning.py
Go to the documentation of this file.
1 #!/usr/bin/env python2
2 # Copyright (c) 2016 The Bitcoin Core developers
3 # Distributed under the MIT/X11 software license, see the accompanying
4 # file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 #
6 
7 from test_framework.mininode import *
8 from test_framework.test_framework import BitcoinTestFramework
9 from test_framework.util import *
10 import time
11 from test_framework.blocktools import create_block, create_coinbase
12 
13 '''
14 Test version bits' warning system.
15 
16 Generate chains with block versions that appear to be signalling unknown
17 soft-forks, and test that warning alerts are generated.
18 '''
19 
20 VB_PERIOD = 144 # versionbits period length for regtest
21 VB_THRESHOLD = 108 # versionbits activation threshold for regtest
22 VB_TOP_BITS = 0x20000000
23 VB_UNKNOWN_BIT = 27 # Choose a bit unassigned to any deployment
24 
25 # TestNode: bare-bones "peer". Used mostly as a conduit for a test to sending
26 # p2p messages to a node, generating the messages in the main testing logic.
28  def __init__(self):
29  NodeConnCB.__init__(self)
30  self.connection = None
31  self.ping_counter = 1
33 
34  def add_connection(self, conn):
35  self.connection = conn
36 
37  def on_inv(self, conn, message):
38  pass
39 
40  # Wrapper for the NodeConn's send_message function
41  def send_message(self, message):
42  self.connection.send_message(message)
43 
44  def on_pong(self, conn, message):
45  self.last_pong = message
46 
47  # Sync up with the node after delivery of a block
48  def sync_with_ping(self, timeout=30):
50  received_pong = False
51  sleep_time = 0.05
52  while not received_pong and timeout > 0:
53  time.sleep(sleep_time)
54  timeout -= sleep_time
55  with mininode_lock:
56  if self.last_pong.nonce == self.ping_counter:
57  received_pong = True
58  self.ping_counter += 1
59  return received_pong
60 
61 
63  def setup_chain(self):
64  initialize_chain_clean(self.options.tmpdir, 1)
65 
66  def setup_network(self):
67  self.nodes = []
68  self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt")
69  # Open and close to create zero-length file
70  with open(self.alert_filename, 'w') as f:
71  pass
72  self.node_options = ["-debug", "-logtimemicros=1", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""]
73  self.nodes.append(start_node(0, self.options.tmpdir, self.node_options))
74 
75  import re
76  self.vb_pattern = re.compile("^Warning.*versionbit")
77 
78  # Send numblocks blocks via peer with nVersionToUse set.
79  def send_blocks_with_version(self, peer, numblocks, nVersionToUse):
80  tip = self.nodes[0].getbestblockhash()
81  height = self.nodes[0].getblockcount()
82  block_time = self.nodes[0].getblockheader(tip)["time"]+1
83  tip = int(tip, 16)
84 
85  for i in xrange(numblocks):
86  block = create_block(tip, create_coinbase(height+1), block_time)
87  block.nVersion = nVersionToUse
88  block.solve()
89  peer.send_message(msg_block(block))
90  block_time += 1
91  height += 1
92  tip = block.sha256
93  peer.sync_with_ping()
94 
96  with open(self.alert_filename, 'r') as f:
97  alert_text = f.read()
98  assert(self.vb_pattern.match(alert_text))
99 
100  def run_test(self):
101  # Setup the p2p connection and start up the network thread.
102  test_node = TestNode()
103 
104  connections = []
105  connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node))
106  test_node.add_connection(connections[0])
107 
108  NetworkThread().start() # Start up network handling in another thread
109 
110  # Test logic begins here
111  test_node.wait_for_verack()
112 
113  # 1. Have the node mine one period worth of blocks
114  self.nodes[0].generate(VB_PERIOD)
115 
116  # 2. Now build one period of blocks on the tip, with < VB_THRESHOLD
117  # blocks signaling some unknown bit.
118  nVersion = VB_TOP_BITS | (1<<VB_UNKNOWN_BIT)
119  self.send_blocks_with_version(test_node, VB_THRESHOLD-1, nVersion)
120 
121  # Fill rest of period with regular version blocks
122  self.nodes[0].generate(VB_PERIOD - VB_THRESHOLD + 1)
123  # Check that we're not getting any versionbit-related errors in
124  # getinfo()
125  assert(not self.vb_pattern.match(self.nodes[0].getinfo()["errors"]))
126 
127  # 3. Now build one period of blocks with >= VB_THRESHOLD blocks signaling
128  # some unknown bit
129  self.send_blocks_with_version(test_node, VB_THRESHOLD, nVersion)
130  self.nodes[0].generate(VB_PERIOD - VB_THRESHOLD)
131  # Might not get a versionbits-related alert yet, as we should
132  # have gotten a different alert due to more than 51/100 blocks
133  # being of unexpected version.
134  # Check that getinfo() shows some kind of error.
135  assert(len(self.nodes[0].getinfo()["errors"]) != 0)
136 
137  # Mine a period worth of expected blocks so the generic block-version warning
138  # is cleared, and restart the node. This should move the versionbit state
139  # to ACTIVE.
140  self.nodes[0].generate(VB_PERIOD)
141  stop_node(self.nodes[0], 0)
143  # Empty out the alert file
144  with open(self.alert_filename, 'w') as f:
145  pass
146  self.nodes[0] = start_node(0, self.options.tmpdir, ["-debug", "-logtimemicros=1", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""])
147 
148  # Connecting one block should be enough to generate an error.
149  self.nodes[0].generate(1)
150  assert(len(self.nodes[0].getinfo()["errors"]) != 0)
151  stop_node(self.nodes[0], 0)
154 
155  # Test framework expects the node to still be running...
156  self.nodes[0] = start_node(0, self.options.tmpdir, ["-debug", "-logtimemicros=1", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""])
157 
158 
159 if __name__ == '__main__':
def wait_bitcoinds()
Definition: util.py:337
def create_block(hashprev, coinbase, nTime=None)
Definition: blocktools.py:11
UniValue getblockheader(const UniValue &params, bool fHelp)
Definition: blockchain.cpp:336
def create_coinbase(height, pubkey=None)
Definition: blocktools.py:43
UniValue getblockcount(const UniValue &params, bool fHelp)
Definition: blockchain.cpp:131
def initialize_chain_clean(test_dir, num_nodes)
Definition: util.py:252
UniValue getinfo(const UniValue &params, bool fHelp)
Definition: misc.cpp:47
def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=None)
Definition: util.py:281
def stop_node(node, i)
Definition: util.py:323
UniValue generate(const UniValue &params, bool fHelp)
Definition: mining.cpp:122
UniValue getbestblockhash(const UniValue &params, bool fHelp)
Definition: blockchain.cpp:148
def p2p_port(n)
Definition: util.py:93
def send_blocks_with_version(self, peer, numblocks, nVersionToUse)