Skip to content

Commit 5e5725c

Browse files
committed
[tests] Add p2p connection to TestNode
p2p connections can now be added to TestNode instances. This commit also updates the example test to use the new p2p interface in TestNode to demonstrate usage. A future commit will update the existing tests to use p2p through the TestNode.
1 parent b86c1cd commit 5e5725c

File tree

2 files changed

+50
-18
lines changed

2 files changed

+50
-18
lines changed

test/functional/example_test.py

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
from test_framework.mininode import (
1919
CInv,
2020
NetworkThread,
21-
NodeConn,
2221
NodeConnCB,
2322
mininode_lock,
2423
msg_block,
@@ -28,7 +27,6 @@
2827
from test_framework.util import (
2928
assert_equal,
3029
connect_nodes,
31-
p2p_port,
3230
wait_until,
3331
)
3432

@@ -134,16 +132,13 @@ def run_test(self):
134132
"""Main test logic"""
135133

136134
# Create a P2P connection to one of the nodes
137-
node0 = BaseNode()
138-
connections = []
139-
connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0))
140-
node0.add_connection(connections[0])
135+
self.nodes[0].add_p2p_connection(BaseNode())
141136

142137
# Start up network handling in another thread. This needs to be called
143138
# after the P2P connections have been created.
144139
NetworkThread().start()
145140
# wait_for_verack ensures that the P2P connection is fully up.
146-
node0.wait_for_verack()
141+
self.nodes[0].p2p.wait_for_verack()
147142

148143
# Generating a block on one of the nodes will get us out of IBD
149144
blocks = [int(self.nodes[0].generate(nblocks=1)[0], 16)]
@@ -180,7 +175,7 @@ def run_test(self):
180175
block.solve()
181176
block_message = msg_block(block)
182177
# Send message is used to send a P2P message to the node over our NodeConn connection
183-
node0.send_message(block_message)
178+
self.nodes[0].p2p.send_message(block_message)
184179
self.tip = block.sha256
185180
blocks.append(self.tip)
186181
self.block_time += 1
@@ -193,28 +188,26 @@ def run_test(self):
193188
connect_nodes(self.nodes[1], 2)
194189

195190
self.log.info("Add P2P connection to node2")
196-
node2 = BaseNode()
197-
connections.append(NodeConn('127.0.0.1', p2p_port(2), self.nodes[2], node2))
198-
node2.add_connection(connections[1])
199-
node2.wait_for_verack()
191+
self.nodes[2].add_p2p_connection(BaseNode())
192+
self.nodes[2].p2p.wait_for_verack()
200193

201194
self.log.info("Wait for node2 reach current tip. Test that it has propagated all the blocks to us")
202195

203196
getdata_request = msg_getdata()
204197
for block in blocks:
205198
getdata_request.inv.append(CInv(2, block))
206-
node2.send_message(getdata_request)
199+
self.nodes[2].p2p.send_message(getdata_request)
207200

208201
# wait_until() will loop until a predicate condition is met. Use it to test properties of the
209202
# NodeConnCB objects.
210-
wait_until(lambda: sorted(blocks) == sorted(list(node2.block_receive_map.keys())), timeout=5, lock=mininode_lock)
203+
wait_until(lambda: sorted(blocks) == sorted(list(self.nodes[2].p2p.block_receive_map.keys())), timeout=5, lock=mininode_lock)
211204

212205
self.log.info("Check that each block was received only once")
213206
# The network thread uses a global lock on data access to the NodeConn objects when sending and receiving
214207
# messages. The test thread should acquire the global lock before accessing any NodeConn data to avoid locking
215208
# and synchronization issues. Note wait_until() acquires this global lock when testing the predicate.
216209
with mininode_lock:
217-
for block in node2.block_receive_map.values():
210+
for block in self.nodes[2].p2p.block_receive_map.values():
218211
assert_equal(block, 1)
219212

220213
if __name__ == '__main__':

test/functional/test_framework/test_node.py

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@
1313
import subprocess
1414
import time
1515

16+
from .authproxy import JSONRPCException
17+
from .mininode import NodeConn
1618
from .util import (
1719
assert_equal,
1820
get_rpc_proxy,
1921
rpc_url,
2022
wait_until,
23+
p2p_port,
2124
)
22-
from .authproxy import JSONRPCException
2325

2426
BITCOIND_PROC_WAIT_TIMEOUT = 60
2527

@@ -31,9 +33,11 @@ class TestNode():
3133
- state about the node (whether it's running, etc)
3234
- a Python subprocess.Popen object representing the running process
3335
- an RPC connection to the node
36+
- one or more P2P connections to the node
37+
3438
35-
To make things easier for the test writer, a bit of magic is happening under the covers.
36-
Any unrecognised messages will be dispatched to the RPC connection."""
39+
To make things easier for the test writer, any unrecognised messages will
40+
be dispatched to the RPC connection."""
3741

3842
def __init__(self, i, dirname, extra_args, rpchost, timewait, binary, stderr, mocktime, coverage_dir):
3943
self.index = i
@@ -63,6 +67,8 @@ def __init__(self, i, dirname, extra_args, rpchost, timewait, binary, stderr, mo
6367
self.url = None
6468
self.log = logging.getLogger('TestFramework.node%d' % i)
6569

70+
self.p2ps = []
71+
6672
def __getattr__(self, name):
6773
"""Dispatches any unrecognised messages to the RPC connection."""
6874
assert self.rpc_connected and self.rpc is not None, "Error: no RPC connection"
@@ -119,6 +125,7 @@ def stop_node(self):
119125
self.stop()
120126
except http.client.CannotSendRequest:
121127
self.log.exception("Unable to stop node.")
128+
del self.p2ps[:]
122129

123130
def is_node_stopped(self):
124131
"""Checks whether the node has stopped.
@@ -151,6 +158,38 @@ def node_encrypt_wallet(self, passphrase):
151158
self.encryptwallet(passphrase)
152159
self.wait_until_stopped()
153160

161+
def add_p2p_connection(self, p2p_conn, **kwargs):
162+
"""Add a p2p connection to the node.
163+
164+
This method adds the p2p connection to the self.p2ps list and also
165+
returns the connection to the caller."""
166+
if 'dstport' not in kwargs:
167+
kwargs['dstport'] = p2p_port(self.index)
168+
if 'dstaddr' not in kwargs:
169+
kwargs['dstaddr'] = '127.0.0.1'
170+
self.p2ps.append(p2p_conn)
171+
kwargs.update({'rpc': self.rpc, 'callback': p2p_conn})
172+
p2p_conn.add_connection(NodeConn(**kwargs))
173+
174+
return p2p_conn
175+
176+
@property
177+
def p2p(self):
178+
"""Return the first p2p connection
179+
180+
Convenience property - most tests only use a single p2p connection to each
181+
node, so this saves having to write node.p2ps[0] many times."""
182+
assert self.p2ps, "No p2p connection"
183+
return self.p2ps[0]
184+
185+
def disconnect_p2p(self, index=0):
186+
"""Close the p2p connection to the node."""
187+
# Connection could have already been closed by other end. Calling disconnect_p2p()
188+
# on an already disconnected p2p connection is not an error.
189+
if self.p2ps[index].connection is not None:
190+
self.p2ps[index].connection.disconnect_node()
191+
del self.p2ps[index]
192+
154193
class TestNodeCLI():
155194
"""Interface to bitcoin-cli for an individual node"""
156195

0 commit comments

Comments
 (0)