[qa] Improve prioritisetransaction functional test
[bitcoinplatinum.git] / test / functional / p2p-fingerprint.py
blob93ef73e25eddcd07976bee10fedaf4bb31528be7
1 #!/usr/bin/env python3
2 # Copyright (c) 2017 The Bitcoin Core developers
3 # Distributed under the MIT software license, see the accompanying
4 # file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 """Test various fingerprinting protections.
7 If an stale block more than a month old or its header are requested by a peer,
8 the node should pretend that it does not have it to avoid fingerprinting.
9 """
11 import time
13 from test_framework.blocktools import (create_block, create_coinbase)
14 from test_framework.mininode import (
15 CInv,
16 P2PInterface,
17 msg_headers,
18 msg_block,
19 msg_getdata,
20 msg_getheaders,
21 network_thread_start,
22 wait_until,
24 from test_framework.test_framework import BitcoinTestFramework
25 from test_framework.util import (
26 assert_equal,
29 class P2PFingerprintTest(BitcoinTestFramework):
30 def set_test_params(self):
31 self.setup_clean_chain = True
32 self.num_nodes = 1
34 # Build a chain of blocks on top of given one
35 def build_chain(self, nblocks, prev_hash, prev_height, prev_median_time):
36 blocks = []
37 for _ in range(nblocks):
38 coinbase = create_coinbase(prev_height + 1)
39 block_time = prev_median_time + 1
40 block = create_block(int(prev_hash, 16), coinbase, block_time)
41 block.solve()
43 blocks.append(block)
44 prev_hash = block.hash
45 prev_height += 1
46 prev_median_time = block_time
47 return blocks
49 # Send a getdata request for a given block hash
50 def send_block_request(self, block_hash, node):
51 msg = msg_getdata()
52 msg.inv.append(CInv(2, block_hash)) # 2 == "Block"
53 node.send_message(msg)
55 # Send a getheaders request for a given single block hash
56 def send_header_request(self, block_hash, node):
57 msg = msg_getheaders()
58 msg.hashstop = block_hash
59 node.send_message(msg)
61 # Check whether last block received from node has a given hash
62 def last_block_equals(self, expected_hash, node):
63 block_msg = node.last_message.get("block")
64 return block_msg and block_msg.block.rehash() == expected_hash
66 # Check whether last block header received from node has a given hash
67 def last_header_equals(self, expected_hash, node):
68 headers_msg = node.last_message.get("headers")
69 return (headers_msg and
70 headers_msg.headers and
71 headers_msg.headers[0].rehash() == expected_hash)
73 # Checks that stale blocks timestamped more than a month ago are not served
74 # by the node while recent stale blocks and old active chain blocks are.
75 # This does not currently test that stale blocks timestamped within the
76 # last month but that have over a month's worth of work are also withheld.
77 def run_test(self):
78 node0 = self.nodes[0].add_p2p_connection(P2PInterface())
80 network_thread_start()
81 node0.wait_for_verack()
83 # Set node time to 60 days ago
84 self.nodes[0].setmocktime(int(time.time()) - 60 * 24 * 60 * 60)
86 # Generating a chain of 10 blocks
87 block_hashes = self.nodes[0].generate(nblocks=10)
89 # Create longer chain starting 2 blocks before current tip
90 height = len(block_hashes) - 2
91 block_hash = block_hashes[height - 1]
92 block_time = self.nodes[0].getblockheader(block_hash)["mediantime"] + 1
93 new_blocks = self.build_chain(5, block_hash, height, block_time)
95 # Force reorg to a longer chain
96 node0.send_message(msg_headers(new_blocks))
97 node0.wait_for_getdata()
98 for block in new_blocks:
99 node0.send_and_ping(msg_block(block))
101 # Check that reorg succeeded
102 assert_equal(self.nodes[0].getblockcount(), 13)
104 stale_hash = int(block_hashes[-1], 16)
106 # Check that getdata request for stale block succeeds
107 self.send_block_request(stale_hash, node0)
108 test_function = lambda: self.last_block_equals(stale_hash, node0)
109 wait_until(test_function, timeout=3)
111 # Check that getheader request for stale block header succeeds
112 self.send_header_request(stale_hash, node0)
113 test_function = lambda: self.last_header_equals(stale_hash, node0)
114 wait_until(test_function, timeout=3)
116 # Longest chain is extended so stale is much older than chain tip
117 self.nodes[0].setmocktime(0)
118 tip = self.nodes[0].generate(nblocks=1)[0]
119 assert_equal(self.nodes[0].getblockcount(), 14)
121 # Send getdata & getheaders to refresh last received getheader message
122 block_hash = int(tip, 16)
123 self.send_block_request(block_hash, node0)
124 self.send_header_request(block_hash, node0)
125 node0.sync_with_ping()
127 # Request for very old stale block should now fail
128 self.send_block_request(stale_hash, node0)
129 time.sleep(3)
130 assert not self.last_block_equals(stale_hash, node0)
132 # Request for very old stale block header should now fail
133 self.send_header_request(stale_hash, node0)
134 time.sleep(3)
135 assert not self.last_header_equals(stale_hash, node0)
137 # Verify we can fetch very old blocks and headers on the active chain
138 block_hash = int(block_hashes[2], 16)
139 self.send_block_request(block_hash, node0)
140 self.send_header_request(block_hash, node0)
141 node0.sync_with_ping()
143 self.send_block_request(block_hash, node0)
144 test_function = lambda: self.last_block_equals(block_hash, node0)
145 wait_until(test_function, timeout=3)
147 self.send_header_request(block_hash, node0)
148 test_function = lambda: self.last_header_equals(block_hash, node0)
149 wait_until(test_function, timeout=3)
151 if __name__ == '__main__':
152 P2PFingerprintTest().main()