|
1 | 1 | #!/usr/bin/env python3 |
2 | | -# Copyright (c) 2019 The Bitcoin Core developers |
| 2 | +# Copyright (c) 2020 The Bitcoin Core developers |
3 | 3 | # Distributed under the MIT software license, see the accompanying |
4 | 4 | # file COPYING or http://www.opensource.org/licenses/mit-license.php. |
5 | 5 | """ |
6 | | -Test transaction download behavior |
| 6 | +Test addr relay |
7 | 7 | """ |
8 | 8 |
|
9 | 9 | from test_framework.messages import ( |
10 | | - CInv, |
11 | | - CTransaction, |
12 | | - FromHex, |
13 | | - MSG_TX, |
14 | | - MSG_TYPE_MASK, |
15 | | - msg_inv, |
16 | | - msg_notfound, |
| 10 | + CAddress, |
| 11 | + NODE_NETWORK, |
| 12 | + NODE_WITNESS, |
| 13 | + msg_addr, |
17 | 14 | ) |
18 | 15 | from test_framework.mininode import ( |
19 | 16 | P2PInterface, |
20 | | - mininode_lock, |
21 | 17 | ) |
22 | 18 | from test_framework.test_framework import BitcoinTestFramework |
23 | 19 | from test_framework.util import ( |
24 | 20 | assert_equal, |
25 | | - wait_until, |
26 | 21 | ) |
27 | | -from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE |
28 | | - |
29 | 22 | import time |
30 | 23 |
|
31 | | - |
32 | | -class TestP2PConn(P2PInterface): |
33 | | - def __init__(self): |
34 | | - super().__init__() |
35 | | - self.tx_getdata_count = 0 |
36 | | - |
37 | | - def on_getdata(self, message): |
38 | | - for i in message.inv: |
39 | | - if i.type & MSG_TYPE_MASK == MSG_TX: |
40 | | - self.tx_getdata_count += 1 |
| 24 | +ADDRS = [] |
| 25 | +for i in range(10): |
| 26 | + addr = CAddress() |
| 27 | + addr.time = int(time.time()) + i |
| 28 | + addr.nServices = NODE_NETWORK | NODE_WITNESS |
| 29 | + addr.ip = "123.123.123.{}".format(i % 256) |
| 30 | + addr.port = 8333 + i |
| 31 | + ADDRS.append(addr) |
41 | 32 |
|
42 | 33 |
|
43 | | -# Constants from net_processing |
44 | | -GETDATA_TX_INTERVAL = 60 # seconds |
45 | | -MAX_GETDATA_RANDOM_DELAY = 2 # seconds |
46 | | -INBOUND_PEER_TX_DELAY = 2 # seconds |
47 | | -MAX_GETDATA_IN_FLIGHT = 100 |
48 | | -TX_EXPIRY_INTERVAL = GETDATA_TX_INTERVAL * 10 |
| 34 | +class AddrReceiver(P2PInterface): |
| 35 | + def on_addr(self, message): |
| 36 | + for addr in message.addrs: |
| 37 | + assert_equal(addr.nServices, 9) |
| 38 | + assert addr.ip.startswith('123.123.123.') |
| 39 | + assert (8333 <= addr.port < 8343) |
49 | 40 |
|
50 | | -# Python test constants |
51 | | -NUM_INBOUND = 10 |
52 | | -MAX_GETDATA_INBOUND_WAIT = GETDATA_TX_INTERVAL + MAX_GETDATA_RANDOM_DELAY + INBOUND_PEER_TX_DELAY |
53 | 41 |
|
54 | | - |
55 | | -class TxDownloadTest(BitcoinTestFramework): |
| 42 | +class AddrTest(BitcoinTestFramework): |
56 | 43 | def set_test_params(self): |
57 | 44 | self.setup_clean_chain = False |
58 | | - self.num_nodes = 2 |
59 | | - |
60 | | - def test_tx_requests(self): |
61 | | - self.log.info("Test that we request transactions from all our peers, eventually") |
62 | | - |
63 | | - txid = 0xdeadbeef |
64 | | - |
65 | | - self.log.info("Announce the txid from each incoming peer to node 0") |
66 | | - msg = msg_inv([CInv(t=1, h=txid)]) |
67 | | - for p in self.nodes[0].p2ps: |
68 | | - p.send_message(msg) |
69 | | - p.sync_with_ping() |
70 | | - |
71 | | - outstanding_peer_index = [i for i in range(len(self.nodes[0].p2ps))] |
72 | | - |
73 | | - def getdata_found(peer_index): |
74 | | - p = self.nodes[0].p2ps[peer_index] |
75 | | - with mininode_lock: |
76 | | - return p.last_message.get("getdata") and p.last_message["getdata"].inv[-1].hash == txid |
77 | | - |
78 | | - node_0_mocktime = int(time.time()) |
79 | | - while outstanding_peer_index: |
80 | | - node_0_mocktime += MAX_GETDATA_INBOUND_WAIT |
81 | | - self.nodes[0].setmocktime(node_0_mocktime) |
82 | | - wait_until(lambda: any(getdata_found(i) for i in outstanding_peer_index)) |
83 | | - for i in outstanding_peer_index: |
84 | | - if getdata_found(i): |
85 | | - outstanding_peer_index.remove(i) |
86 | | - |
87 | | - self.nodes[0].setmocktime(0) |
88 | | - self.log.info("All outstanding peers received a getdata") |
89 | | - |
90 | | - def test_inv_block(self): |
91 | | - self.log.info("Generate a transaction on node 0") |
92 | | - tx = self.nodes[0].createrawtransaction( |
93 | | - inputs=[{ # coinbase |
94 | | - "txid": self.nodes[0].getblock(self.nodes[0].getblockhash(1))['tx'][0], |
95 | | - "vout": 0 |
96 | | - }], |
97 | | - outputs={ADDRESS_BCRT1_UNSPENDABLE: 50 - 0.00025}, |
98 | | - ) |
99 | | - tx = self.nodes[0].signrawtransactionwithkey( |
100 | | - hexstring=tx, |
101 | | - privkeys=[self.nodes[0].get_deterministic_priv_key().key], |
102 | | - )['hex'] |
103 | | - ctx = FromHex(CTransaction(), tx) |
104 | | - txid = int(ctx.rehash(), 16) |
105 | | - |
106 | | - self.log.info( |
107 | | - "Announce the transaction to all nodes from all {} incoming peers, but never send it".format(NUM_INBOUND)) |
108 | | - msg = msg_inv([CInv(t=1, h=txid)]) |
109 | | - for p in self.peers: |
110 | | - p.send_message(msg) |
111 | | - p.sync_with_ping() |
112 | | - |
113 | | - self.log.info("Put the tx in node 0's mempool") |
114 | | - self.nodes[0].sendrawtransaction(tx) |
115 | | - |
116 | | - # Since node 1 is connected outbound to an honest peer (node 0), it |
117 | | - # should get the tx within a timeout. (Assuming that node 0 |
118 | | - # announced the tx within the timeout) |
119 | | - # The timeout is the sum of |
120 | | - # * the worst case until the tx is first requested from an inbound |
121 | | - # peer, plus |
122 | | - # * the first time it is re-requested from the outbound peer, plus |
123 | | - # * 2 seconds to avoid races |
124 | | - assert self.nodes[1].getpeerinfo()[0]['inbound'] == False |
125 | | - timeout = 2 + (MAX_GETDATA_RANDOM_DELAY + INBOUND_PEER_TX_DELAY) + ( |
126 | | - GETDATA_TX_INTERVAL + MAX_GETDATA_RANDOM_DELAY) |
127 | | - self.log.info("Tx should be received at node 1 after {} seconds".format(timeout)) |
128 | | - self.sync_mempools(timeout=timeout) |
129 | | - |
130 | | - def test_in_flight_max(self): |
131 | | - self.log.info("Test that we don't request more than {} transactions from any peer, every {} minutes".format( |
132 | | - MAX_GETDATA_IN_FLIGHT, TX_EXPIRY_INTERVAL / 60)) |
133 | | - txids = [i for i in range(MAX_GETDATA_IN_FLIGHT + 2)] |
134 | | - |
135 | | - p = self.nodes[0].p2ps[0] |
136 | | - |
137 | | - with mininode_lock: |
138 | | - p.tx_getdata_count = 0 |
139 | | - |
140 | | - p.send_message(msg_inv([CInv(t=1, h=i) for i in txids])) |
141 | | - wait_until(lambda: p.tx_getdata_count >= MAX_GETDATA_IN_FLIGHT, lock=mininode_lock) |
142 | | - with mininode_lock: |
143 | | - assert_equal(p.tx_getdata_count, MAX_GETDATA_IN_FLIGHT) |
144 | | - |
145 | | - self.log.info("Now check that if we send a NOTFOUND for a transaction, we'll get one more request") |
146 | | - p.send_message(msg_notfound(vec=[CInv(t=1, h=txids[0])])) |
147 | | - wait_until(lambda: p.tx_getdata_count >= MAX_GETDATA_IN_FLIGHT + 1, timeout=10, lock=mininode_lock) |
148 | | - with mininode_lock: |
149 | | - assert_equal(p.tx_getdata_count, MAX_GETDATA_IN_FLIGHT + 1) |
150 | | - |
151 | | - WAIT_TIME = TX_EXPIRY_INTERVAL // 2 + TX_EXPIRY_INTERVAL |
152 | | - self.log.info("if we wait about {} minutes, we should eventually get more requests".format(WAIT_TIME / 60)) |
153 | | - self.nodes[0].setmocktime(int(time.time() + WAIT_TIME)) |
154 | | - wait_until(lambda: p.tx_getdata_count == MAX_GETDATA_IN_FLIGHT + 2) |
155 | | - self.nodes[0].setmocktime(0) |
| 45 | + self.num_nodes = 1 |
156 | 46 |
|
157 | 47 | def run_test(self): |
158 | | - # Setup the p2p connections |
159 | | - self.peers = [] |
160 | | - for node in self.nodes: |
161 | | - for i in range(NUM_INBOUND): |
162 | | - self.peers.append(node.add_p2p_connection(TestP2PConn())) |
163 | | - |
164 | | - self.log.info("Nodes are setup with {} incoming connections each".format(NUM_INBOUND)) |
165 | | - |
166 | | - # Test the in-flight max first, because we want no transactions in |
167 | | - # flight ahead of this test. |
168 | | - self.test_in_flight_max() |
| 48 | + self.log.info('Create connection that sends addr messages') |
| 49 | + addr_source = self.nodes[0].add_p2p_connection(P2PInterface()) |
| 50 | + msg = msg_addr() |
169 | 51 |
|
170 | | - self.test_inv_block() |
| 52 | + self.log.info('Send too large addr message') |
| 53 | + msg.addrs = ADDRS * 101 |
| 54 | + with self.nodes[0].assert_debug_log(['message addr size() = 1010']): |
| 55 | + addr_source.send_and_ping(msg) |
171 | 56 |
|
172 | | - self.test_tx_requests() |
| 57 | + self.log.info('Check that addr message content is relayed and added to addrman') |
| 58 | + addr_receiver = self.nodes[0].add_p2p_connection(AddrReceiver()) |
| 59 | + msg.addrs = ADDRS |
| 60 | + with self.nodes[0].assert_debug_log(['Added 10 addresses from 127.0.0.1: 0 tried']): |
| 61 | + addr_source.send_and_ping(msg) |
| 62 | + self.nodes[0].setmocktime(int(time.time()) + 30 * 60) |
| 63 | + addr_receiver.sync_with_ping() |
173 | 64 |
|
174 | 65 |
|
175 | 66 | if __name__ == '__main__': |
176 | | - TxDownloadTest().main() |
| 67 | + AddrTest().main() |
0 commit comments