Skip to content

Commit fabe56e

Browse files
author
MarcoFalke
committed
test: Add basic addr relay test
1 parent 3c762e7 commit fabe56e

File tree

2 files changed

+38
-146
lines changed

2 files changed

+38
-146
lines changed

test/functional/p2p_addr_relay.py

Lines changed: 37 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -1,176 +1,67 @@
11
#!/usr/bin/env python3
2-
# Copyright (c) 2019 The Bitcoin Core developers
2+
# Copyright (c) 2020 The Bitcoin Core developers
33
# Distributed under the MIT software license, see the accompanying
44
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
55
"""
6-
Test transaction download behavior
6+
Test addr relay
77
"""
88

99
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,
1714
)
1815
from test_framework.mininode import (
1916
P2PInterface,
20-
mininode_lock,
2117
)
2218
from test_framework.test_framework import BitcoinTestFramework
2319
from test_framework.util import (
2420
assert_equal,
25-
wait_until,
2621
)
27-
from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE
28-
2922
import time
3023

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)
4132

4233

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)
4940

50-
# Python test constants
51-
NUM_INBOUND = 10
52-
MAX_GETDATA_INBOUND_WAIT = GETDATA_TX_INTERVAL + MAX_GETDATA_RANDOM_DELAY + INBOUND_PEER_TX_DELAY
5341

54-
55-
class TxDownloadTest(BitcoinTestFramework):
42+
class AddrTest(BitcoinTestFramework):
5643
def set_test_params(self):
5744
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
15646

15747
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()
16951

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)
17156

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()
17364

17465

17566
if __name__ == '__main__':
176-
TxDownloadTest().main()
67+
AddrTest().main()

test/functional/test_runner.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@
144144
'rpc_blockchain.py',
145145
'rpc_deprecated.py',
146146
'wallet_disable.py',
147+
'p2p_addr_relay.py',
147148
'rpc_net.py',
148149
'wallet_keypool.py',
149150
'p2p_mempool.py',

0 commit comments

Comments
 (0)