Skip to content

Commit 78f912c

Browse files
author
MarcoFalke
committed
Merge bitcoin#19804: test/refactor: reference p2p objects explicitly and remove confusing Test_Node.p2p property
10d6150 [test] remove confusing p2p property (gzhao408) 549d30f scripted-diff: replace p2p with p2ps[0] in p2p_invalid_tx (gzhao408) 7a0de46 [doc] sample code for test framework p2p objects (gzhao408) 784f757 [refactor] clarify tests by referencing p2p objects directly (gzhao408) Pull request description: The `TestNode` has a `p2p` property which is an alias for `p2ps[0]`. I think this should be removed because it can be confusing and misleading (to both the test writer and reviewer), especially if a TestNode has multiple p2ps connected (which is the case for many tests). Another example is when a test has multiple subtests that connect 1 p2p and use the `p2p` property to reference it. If the subtests don't completely clean up after themselves, the subtests may affect one another. The best way to refer to a connected p2p is use the object returned by `add_p2p_connection` like this: ```py p2p_conn = node.add_p2p_connection(P2PInterface()) ``` A good example is [p2p_invalid_locator.py](https://github.com/bitcoin/bitcoin/blob/master/test/functional/p2p_invalid_locator.py), which cleans up after itself (waits in both `wait_for_disconnect` and in `disconnect_p2ps`) but wouldn't need so much complexity if it just referenced the connections directly. If there is only one connected, it's not really that tedious to just use `node.p2ps[0]` instead of `node.p2p` (and it can always be aliased inside the test itself). ACKs for top commit: robot-dreams: utACK 10d6150 jnewbery: utACK 10d6150 guggero: Concept ACK 10d6150. Tree-SHA512: 5965548929794ec660dae03467640cb2156d7d826cefd26d3a126472cbc2494b855c1d26bbb7b412281fbdc92b9798b9765a85c27bc1a97f7798f27f64db6f13
2 parents 1b313ca + 10d6150 commit 78f912c

20 files changed

+99
-92
lines changed

test/functional/README.md

+20-4
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,9 @@ P2P messages. These can be found in the following source files:
8787

8888
#### Using the P2P interface
8989

90-
- [messages.py](test_framework/messages.py) contains all the definitions for objects that pass
90+
- `P2P`s can be used to test specific P2P protocol behavior.
91+
[p2p.py](test_framework/p2p.py) contains test framework p2p objects and
92+
[messages.py](test_framework/messages.py) contains all the definitions for objects passed
9193
over the network (`CBlock`, `CTransaction`, etc, along with the network-level
9294
wrappers for them, `msg_block`, `msg_tx`, etc).
9395

@@ -100,8 +102,22 @@ contains the higher level logic for processing P2P payloads and connecting to
100102
the Bitcoin Core node application logic. For custom behaviour, subclass the
101103
P2PInterface object and override the callback methods.
102104

103-
- Can be used to write tests where specific P2P protocol behavior is tested.
104-
Examples tests are [p2p_unrequested_blocks.py](p2p_unrequested_blocks.py),
105+
`P2PConnection`s can be used as such:
106+
107+
```python
108+
p2p_conn = node.add_p2p_connection(P2PInterface())
109+
p2p_conn.send_and_ping(msg)
110+
```
111+
112+
They can also be referenced by indexing into a `TestNode`'s `p2ps` list, which
113+
contains the list of test framework `p2p` objects connected to itself
114+
(it does not include any `TestNode`s):
115+
116+
```python
117+
node.p2ps[0].sync_with_ping()
118+
```
119+
120+
More examples can be found in [p2p_unrequested_blocks.py](p2p_unrequested_blocks.py),
105121
[p2p_compactblocks.py](p2p_compactblocks.py).
106122

107123
#### Prototyping tests
@@ -157,7 +173,7 @@ way is the use the `profile_with_perf` context manager, e.g.
157173
with node.profile_with_perf("send-big-msgs"):
158174
# Perform activity on the node you're interested in profiling, e.g.:
159175
for _ in range(10000):
160-
node.p2p.send_message(some_large_message)
176+
node.p2ps[0].send_message(some_large_message)
161177
```
162178

163179
To see useful textual output, run

test/functional/example_test.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ def run_test(self):
136136
"""Main test logic"""
137137

138138
# Create P2P connections will wait for a verack to make sure the connection is fully up
139-
self.nodes[0].add_p2p_connection(BaseNode())
139+
peer_messaging = self.nodes[0].add_p2p_connection(BaseNode())
140140

141141
# Generating a block on one of the nodes will get us out of IBD
142142
blocks = [int(self.nodes[0].generate(nblocks=1)[0], 16)]
@@ -173,7 +173,7 @@ def run_test(self):
173173
block.solve()
174174
block_message = msg_block(block)
175175
# Send message is used to send a P2P message to the node over our P2PInterface
176-
self.nodes[0].p2p.send_message(block_message)
176+
peer_messaging.send_message(block_message)
177177
self.tip = block.sha256
178178
blocks.append(self.tip)
179179
self.block_time += 1
@@ -191,25 +191,25 @@ def run_test(self):
191191
self.log.info("Add P2P connection to node2")
192192
self.nodes[0].disconnect_p2ps()
193193

194-
self.nodes[2].add_p2p_connection(BaseNode())
194+
peer_receiving = self.nodes[2].add_p2p_connection(BaseNode())
195195

196196
self.log.info("Test that node2 propagates all the blocks to us")
197197

198198
getdata_request = msg_getdata()
199199
for block in blocks:
200200
getdata_request.inv.append(CInv(MSG_BLOCK, block))
201-
self.nodes[2].p2p.send_message(getdata_request)
201+
peer_receiving.send_message(getdata_request)
202202

203203
# wait_until() will loop until a predicate condition is met. Use it to test properties of the
204204
# P2PInterface objects.
205-
self.nodes[2].p2p.wait_until(lambda: sorted(blocks) == sorted(list(self.nodes[2].p2p.block_receive_map.keys())), timeout=5)
205+
peer_receiving.wait_until(lambda: sorted(blocks) == sorted(list(peer_receiving.block_receive_map.keys())), timeout=5)
206206

207207
self.log.info("Check that each block was received only once")
208208
# The network thread uses a global lock on data access to the P2PConnection objects when sending and receiving
209209
# messages. The test thread should acquire the global lock before accessing any P2PConnection data to avoid locking
210210
# and synchronization issues. Note p2p.wait_until() acquires this global lock internally when testing the predicate.
211211
with p2p_lock:
212-
for block in self.nodes[2].p2p.block_receive_map.values():
212+
for block in peer_receiving.block_receive_map.values():
213213
assert_equal(block, 1)
214214

215215

test/functional/feature_block.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -1386,14 +1386,14 @@ def bootstrap_p2p(self, timeout=10):
13861386
"""Add a P2P connection to the node.
13871387
13881388
Helper to connect and wait for version handshake."""
1389-
self.nodes[0].add_p2p_connection(P2PDataStore())
1389+
self.helper_peer = self.nodes[0].add_p2p_connection(P2PDataStore())
13901390
# We need to wait for the initial getheaders from the peer before we
13911391
# start populating our blockstore. If we don't, then we may run ahead
13921392
# to the next subtest before we receive the getheaders. We'd then send
13931393
# an INV for the next block and receive two getheaders - one for the
13941394
# IBD and one for the INV. We'd respond to both and could get
13951395
# unexpectedly disconnected if the DoS score for that error is 50.
1396-
self.nodes[0].p2p.wait_for_getheaders(timeout=timeout)
1396+
self.helper_peer.wait_for_getheaders(timeout=timeout)
13971397

13981398
def reconnect_p2p(self, timeout=60):
13991399
"""Tear down and bootstrap the P2P connection to the node.
@@ -1407,7 +1407,7 @@ def send_blocks(self, blocks, success=True, reject_reason=None, force_send=False
14071407
"""Sends blocks to test node. Syncs and verifies that tip has advanced to most recent block.
14081408
14091409
Call with success = False if the tip shouldn't advance to the most recent block."""
1410-
self.nodes[0].p2p.send_blocks_and_test(blocks, self.nodes[0], success=success, reject_reason=reject_reason, force_send=force_send, timeout=timeout, expect_disconnect=reconnect)
1410+
self.helper_peer.send_blocks_and_test(blocks, self.nodes[0], success=success, reject_reason=reject_reason, force_send=force_send, timeout=timeout, expect_disconnect=reconnect)
14111411

14121412
if reconnect:
14131413
self.reconnect_p2p(timeout=timeout)

test/functional/feature_cltv.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def test_cltv_info(self, *, is_active):
7575
)
7676

7777
def run_test(self):
78-
self.nodes[0].add_p2p_connection(P2PInterface())
78+
peer = self.nodes[0].add_p2p_connection(P2PInterface())
7979

8080
self.test_cltv_info(is_active=False)
8181

@@ -99,7 +99,7 @@ def run_test(self):
9999
block.solve()
100100

101101
self.test_cltv_info(is_active=False) # Not active as of current tip and next block does not need to obey rules
102-
self.nodes[0].p2p.send_and_ping(msg_block(block))
102+
peer.send_and_ping(msg_block(block))
103103
self.test_cltv_info(is_active=True) # Not active as of current tip, but next block must obey rules
104104
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
105105

@@ -111,9 +111,9 @@ def run_test(self):
111111
block.solve()
112112

113113
with self.nodes[0].assert_debug_log(expected_msgs=['{}, bad-version(0x00000003)'.format(block.hash)]):
114-
self.nodes[0].p2p.send_and_ping(msg_block(block))
114+
peer.send_and_ping(msg_block(block))
115115
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
116-
self.nodes[0].p2p.sync_with_ping()
116+
peer.sync_with_ping()
117117

118118
self.log.info("Test that invalid-according-to-cltv transactions cannot appear in a block")
119119
block.nVersion = 4
@@ -136,9 +136,9 @@ def run_test(self):
136136
block.solve()
137137

138138
with self.nodes[0].assert_debug_log(expected_msgs=['CheckInputScripts on {} failed with non-mandatory-script-verify-flag (Negative locktime)'.format(block.vtx[-1].hash)]):
139-
self.nodes[0].p2p.send_and_ping(msg_block(block))
139+
peer.send_and_ping(msg_block(block))
140140
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
141-
self.nodes[0].p2p.sync_with_ping()
141+
peer.sync_with_ping()
142142

143143
self.log.info("Test that a version 4 block with a valid-according-to-CLTV transaction is accepted")
144144
spendtx = cltv_validate(self.nodes[0], spendtx, CLTV_HEIGHT - 1)
@@ -150,7 +150,7 @@ def run_test(self):
150150
block.solve()
151151

152152
self.test_cltv_info(is_active=True) # Not active as of current tip, but next block must obey rules
153-
self.nodes[0].p2p.send_and_ping(msg_block(block))
153+
peer.send_and_ping(msg_block(block))
154154
self.test_cltv_info(is_active=True) # Active as of current tip
155155
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
156156

test/functional/feature_csv_activation.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -182,10 +182,10 @@ def send_blocks(self, blocks, success=True, reject_reason=None):
182182
"""Sends blocks to test node. Syncs and verifies that tip has advanced to most recent block.
183183
184184
Call with success = False if the tip shouldn't advance to the most recent block."""
185-
self.nodes[0].p2p.send_blocks_and_test(blocks, self.nodes[0], success=success, reject_reason=reject_reason)
185+
self.helper_peer.send_blocks_and_test(blocks, self.nodes[0], success=success, reject_reason=reject_reason)
186186

187187
def run_test(self):
188-
self.nodes[0].add_p2p_connection(P2PDataStore())
188+
self.helper_peer = self.nodes[0].add_p2p_connection(P2PDataStore())
189189

190190
self.log.info("Generate blocks in the past for coinbase outputs.")
191191
long_past_time = int(time.time()) - 600 * 1000 # enough to build up to 1000 blocks 10 minutes apart without worrying about getting into the future

test/functional/feature_dersig.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ def test_dersig_info(self, *, is_active):
5959
)
6060

6161
def run_test(self):
62-
self.nodes[0].add_p2p_connection(P2PInterface())
62+
peer = self.nodes[0].add_p2p_connection(P2PInterface())
6363

6464
self.test_dersig_info(is_active=False)
6565

@@ -84,7 +84,7 @@ def run_test(self):
8484
block.solve()
8585

8686
self.test_dersig_info(is_active=False) # Not active as of current tip and next block does not need to obey rules
87-
self.nodes[0].p2p.send_and_ping(msg_block(block))
87+
peer.send_and_ping(msg_block(block))
8888
self.test_dersig_info(is_active=True) # Not active as of current tip, but next block must obey rules
8989
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
9090

@@ -97,9 +97,9 @@ def run_test(self):
9797
block.solve()
9898

9999
with self.nodes[0].assert_debug_log(expected_msgs=['{}, bad-version(0x00000002)'.format(block.hash)]):
100-
self.nodes[0].p2p.send_and_ping(msg_block(block))
100+
peer.send_and_ping(msg_block(block))
101101
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
102-
self.nodes[0].p2p.sync_with_ping()
102+
peer.sync_with_ping()
103103

104104
self.log.info("Test that transactions with non-DER signatures cannot appear in a block")
105105
block.nVersion = 3
@@ -123,9 +123,9 @@ def run_test(self):
123123
block.solve()
124124

125125
with self.nodes[0].assert_debug_log(expected_msgs=['CheckInputScripts on {} failed with non-mandatory-script-verify-flag (Non-canonical DER signature)'.format(block.vtx[-1].hash)]):
126-
self.nodes[0].p2p.send_and_ping(msg_block(block))
126+
peer.send_and_ping(msg_block(block))
127127
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
128-
self.nodes[0].p2p.sync_with_ping()
128+
peer.sync_with_ping()
129129

130130
self.log.info("Test that a version 3 block with a DERSIG-compliant transaction is accepted")
131131
block.vtx[1] = create_transaction(self.nodes[0], self.coinbase_txids[1], self.nodeaddress, amount=1.0)
@@ -134,7 +134,7 @@ def run_test(self):
134134
block.solve()
135135

136136
self.test_dersig_info(is_active=True) # Not active as of current tip, but next block must obey rules
137-
self.nodes[0].p2p.send_and_ping(msg_block(block))
137+
peer.send_and_ping(msg_block(block))
138138
self.test_dersig_info(is_active=True) # Active as of current tip
139139
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
140140

test/functional/feature_maxuploadtarget.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -145,16 +145,16 @@ def run_test(self):
145145
self.restart_node(0, ["[email protected]", "-maxuploadtarget=1"])
146146

147147
# Reconnect to self.nodes[0]
148-
self.nodes[0].add_p2p_connection(TestP2PConn())
148+
peer = self.nodes[0].add_p2p_connection(TestP2PConn())
149149

150150
#retrieve 20 blocks which should be enough to break the 1MB limit
151151
getdata_request.inv = [CInv(MSG_BLOCK, big_new_block)]
152152
for i in range(20):
153-
self.nodes[0].p2p.send_and_ping(getdata_request)
154-
assert_equal(self.nodes[0].p2p.block_receive_map[big_new_block], i+1)
153+
peer.send_and_ping(getdata_request)
154+
assert_equal(peer.block_receive_map[big_new_block], i+1)
155155

156156
getdata_request.inv = [CInv(MSG_BLOCK, big_old_block)]
157-
self.nodes[0].p2p.send_and_ping(getdata_request)
157+
peer.send_and_ping(getdata_request)
158158

159159
self.log.info("Peer still connected after trying to download old block (download permission)")
160160
peer_info = self.nodes[0].getpeerinfo()

test/functional/feature_versionbits_warning.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -61,23 +61,23 @@ def versionbits_in_alert_file(self):
6161

6262
def run_test(self):
6363
node = self.nodes[0]
64-
node.add_p2p_connection(P2PInterface())
64+
peer = node.add_p2p_connection(P2PInterface())
6565

6666
node_deterministic_address = node.get_deterministic_priv_key().address
6767
# Mine one period worth of blocks
6868
node.generatetoaddress(VB_PERIOD, node_deterministic_address)
6969

7070
self.log.info("Check that there is no warning if previous VB_BLOCKS have <VB_THRESHOLD blocks with unknown versionbits version.")
7171
# Build one period of blocks with < VB_THRESHOLD blocks signaling some unknown bit
72-
self.send_blocks_with_version(node.p2p, VB_THRESHOLD - 1, VB_UNKNOWN_VERSION)
72+
self.send_blocks_with_version(peer, VB_THRESHOLD - 1, VB_UNKNOWN_VERSION)
7373
node.generatetoaddress(VB_PERIOD - VB_THRESHOLD + 1, node_deterministic_address)
7474

7575
# Check that we're not getting any versionbit-related errors in get*info()
7676
assert not VB_PATTERN.match(node.getmininginfo()["warnings"])
7777
assert not VB_PATTERN.match(node.getnetworkinfo()["warnings"])
7878

7979
# Build one period of blocks with VB_THRESHOLD blocks signaling some unknown bit
80-
self.send_blocks_with_version(node.p2p, VB_THRESHOLD, VB_UNKNOWN_VERSION)
80+
self.send_blocks_with_version(peer, VB_THRESHOLD, VB_UNKNOWN_VERSION)
8181
node.generatetoaddress(VB_PERIOD - VB_THRESHOLD, node_deterministic_address)
8282

8383
self.log.info("Check that there is a warning if previous VB_BLOCKS have >=VB_THRESHOLD blocks with unknown versionbits version.")

test/functional/mempool_packages.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def chain_transaction(self, node, parent_txid, vout, value, fee, num_outputs):
5858

5959
def run_test(self):
6060
# Mine some blocks and have them mature.
61-
self.nodes[0].add_p2p_connection(P2PTxInvStore()) # keep track of invs
61+
peer_inv_store = self.nodes[0].add_p2p_connection(P2PTxInvStore()) # keep track of invs
6262
self.nodes[0].generate(101)
6363
utxo = self.nodes[0].listunspent(10)
6464
txid = utxo[0]['txid']
@@ -80,7 +80,7 @@ def run_test(self):
8080

8181
# Wait until mempool transactions have passed initial broadcast (sent inv and received getdata)
8282
# Otherwise, getrawmempool may be inconsistent with getmempoolentry if unbroadcast changes in between
83-
self.nodes[0].p2p.wait_for_broadcast(witness_chain)
83+
peer_inv_store.wait_for_broadcast(witness_chain)
8484

8585
# Check mempool has MAX_ANCESTORS transactions in it, and descendant and ancestor
8686
# count and fees should look correct

test/functional/mining_basic.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -234,9 +234,9 @@ def chain_tip(b_hash, *, status='headers-only', branchlen=1):
234234
assert_raises_rpc_error(-25, 'time-too-old', lambda: node.submitheader(hexdata=CBlockHeader(bad_block_time).serialize().hex()))
235235

236236
# Should ask for the block from a p2p node, if they announce the header as well:
237-
node.add_p2p_connection(P2PDataStore())
238-
node.p2p.wait_for_getheaders(timeout=5) # Drop the first getheaders
239-
node.p2p.send_blocks_and_test(blocks=[block], node=node)
237+
peer = node.add_p2p_connection(P2PDataStore())
238+
peer.wait_for_getheaders(timeout=5) # Drop the first getheaders
239+
peer.send_blocks_and_test(blocks=[block], node=node)
240240
# Must be active now:
241241
assert chain_tip(block.hash, status='active', branchlen=0) in node.getchaintips()
242242

test/functional/p2p_blocksonly.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def set_test_params(self):
1717
self.extra_args = [["-blocksonly"]]
1818

1919
def run_test(self):
20-
self.nodes[0].add_p2p_connection(P2PInterface())
20+
block_relay_peer = self.nodes[0].add_p2p_connection(P2PInterface())
2121

2222
self.log.info('Check that txs from p2p are rejected and result in disconnect')
2323
prevtx = self.nodes[0].getblock(self.nodes[0].getblockhash(1), 2)['tx'][0]
@@ -41,20 +41,20 @@ def run_test(self):
4141
)['hex']
4242
assert_equal(self.nodes[0].getnetworkinfo()['localrelay'], False)
4343
with self.nodes[0].assert_debug_log(['transaction sent in violation of protocol peer=0']):
44-
self.nodes[0].p2p.send_message(msg_tx(FromHex(CTransaction(), sigtx)))
45-
self.nodes[0].p2p.wait_for_disconnect()
44+
block_relay_peer.send_message(msg_tx(FromHex(CTransaction(), sigtx)))
45+
block_relay_peer.wait_for_disconnect()
4646
assert_equal(self.nodes[0].getmempoolinfo()['size'], 0)
4747

4848
# Remove the disconnected peer and add a new one.
4949
del self.nodes[0].p2ps[0]
50-
self.nodes[0].add_p2p_connection(P2PInterface())
50+
tx_relay_peer = self.nodes[0].add_p2p_connection(P2PInterface())
5151

5252
self.log.info('Check that txs from rpc are not rejected and relayed to other peers')
5353
assert_equal(self.nodes[0].getpeerinfo()[0]['relaytxes'], True)
5454
txid = self.nodes[0].testmempoolaccept([sigtx])[0]['txid']
5555
with self.nodes[0].assert_debug_log(['received getdata for: wtx {} peer=1'.format(txid)]):
5656
self.nodes[0].sendrawtransaction(sigtx)
57-
self.nodes[0].p2p.wait_for_tx(txid)
57+
tx_relay_peer.wait_for_tx(txid)
5858
assert_equal(self.nodes[0].getmempoolinfo()['size'], 1)
5959

6060
self.log.info('Check that txs from peers with relay-permission are not rejected and relayed to others')

0 commit comments

Comments
 (0)