Skip to content

Commit cfb0d74

Browse files
committed
Merge bitcoin/bitcoin#33121: test: fix p2p_leak_tx.py
14ae71f test: make notfound_on_unannounced more reliable (David Gumberg) 99bc552 test: fix (w)txid confusion in p2p_leak_tx.py (Martin Zumsande) 576dd97 test: increase timeout in p2p_leak_tx.py (Martin Zumsande) Pull request description: This fixes two issues with `p2p_leak_tx.py`: 1.) #33090: As far as I can see, this is just the randomness of `NextInvToInbounds`/ `rand_exp_duration`, which has a probability of `e^-(60s/5s) = 6.14×10^−6` to result in a period > 60s (our waiting time), so that the test would fail every 160k runs... Doubling the timeout should be sufficient to lower the probability drastically. 2.) The subtest `test_notfound_on_unannounced_tx` has some (w)txid confusion: we send a `MSG_TX`-type getdata with a `wtxid` in it, which necessarily always results in a NOTFOUND. Fixed this, and change the subtest to be more deterministic based on `mocktime`. ACKs for top commit: stratospher: ACK 14ae71f. nice restructuring using mocktime! davidgumberg: reACK bitcoin/bitcoin@14ae71f vasild: ACK 14ae71f Tree-SHA512: be5a4ca7bf56f82b6fa04d90ef9312dc2e6f8ff7ddf70b39d979dc42fbdd823157109b8b5dc46eb7f81ac1e816f40e6966b3c8a7d384aadee01e2189c20d3e3a
2 parents 86eaa4d + 14ae71f commit cfb0d74

File tree

1 file changed

+35
-26
lines changed

1 file changed

+35
-26
lines changed

test/functional/p2p_leak_tx.py

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import time
1616

1717
class P2PNode(P2PDataStore):
18-
def on_inv(self, msg):
18+
def on_inv(self, message):
1919
pass
2020

2121

@@ -26,27 +26,28 @@ def set_test_params(self):
2626
def run_test(self):
2727
self.gen_node = self.nodes[0] # The block and tx generating node
2828
self.miniwallet = MiniWallet(self.gen_node)
29+
self.mocktime = int(time.time())
2930

3031
self.test_tx_in_block()
3132
self.test_notfound_on_replaced_tx()
3233
self.test_notfound_on_unannounced_tx()
3334

3435
def test_tx_in_block(self):
3536
self.log.info("Check that a transaction in the last block is uploaded (beneficial for compact block relay)")
37+
self.gen_node.setmocktime(self.mocktime)
3638
inbound_peer = self.gen_node.add_p2p_connection(P2PNode())
3739

3840
self.log.debug("Generate transaction and block")
3941
inbound_peer.last_message.pop("inv", None)
4042

41-
self.gen_node.setmocktime(int(time.time())) # pause time based activities
4243
wtxid = self.miniwallet.send_self_transfer(from_node=self.gen_node)["wtxid"]
4344
rawmp = self.gen_node.getrawmempool(False, True)
4445
pi = self.gen_node.getpeerinfo()[0]
4546
assert_equal(rawmp["mempool_sequence"], 2) # our tx cause mempool activity
4647
assert_equal(pi["last_inv_sequence"], 1) # that is after the last inv
4748
assert_equal(pi["inv_to_send"], 1) # and our tx has been queued
48-
self.gen_node.setmocktime(0)
49-
49+
self.mocktime += 120
50+
self.gen_node.setmocktime(self.mocktime)
5051
inbound_peer.wait_until(lambda: "inv" in inbound_peer.last_message and inbound_peer.last_message.get("inv").inv[0].hash == int(wtxid, 16))
5152

5253
rawmp = self.gen_node.getrawmempool(False, True)
@@ -65,15 +66,20 @@ def test_tx_in_block(self):
6566

6667
def test_notfound_on_replaced_tx(self):
6768
self.gen_node.disconnect_p2ps()
69+
self.gen_node.setmocktime(self.mocktime)
6870
inbound_peer = self.gen_node.add_p2p_connection(P2PTxInvStore())
6971

7072
self.log.info("Transaction tx_a is broadcast")
7173
tx_a = self.miniwallet.send_self_transfer(from_node=self.gen_node)
74+
self.mocktime += 120
75+
self.gen_node.setmocktime(self.mocktime)
7276
inbound_peer.wait_for_broadcast(txns=[tx_a["wtxid"]])
7377

7478
tx_b = tx_a["tx"]
7579
tx_b.vout[0].nValue -= 9000
7680
self.gen_node.sendrawtransaction(tx_b.serialize().hex())
81+
self.mocktime += 120
82+
self.gen_node.setmocktime(self.mocktime)
7783
inbound_peer.wait_until(lambda: "tx" in inbound_peer.last_message and inbound_peer.last_message.get("tx").tx.wtxid_hex == tx_b.wtxid_hex)
7884

7985
self.log.info("Re-request of tx_a after replacement is answered with notfound")
@@ -96,28 +102,31 @@ def test_notfound_on_unannounced_tx(self):
96102
self.gen_node.disconnect_p2ps()
97103
inbound_peer = self.gen_node.add_p2p_connection(P2PNode()) # An "attacking" inbound peer
98104

99-
MAX_REPEATS = 100
100-
self.log.info("Running test up to {} times.".format(MAX_REPEATS))
101-
for i in range(MAX_REPEATS):
102-
self.log.info('Run repeat {}'.format(i + 1))
103-
txid = self.miniwallet.send_self_transfer(from_node=self.gen_node)["wtxid"]
104-
105-
want_tx = msg_getdata()
106-
want_tx.inv.append(CInv(t=MSG_TX, h=int(txid, 16)))
107-
with p2p_lock:
108-
inbound_peer.last_message.pop('notfound', None)
109-
inbound_peer.send_and_ping(want_tx)
110-
111-
if inbound_peer.last_message.get('notfound'):
112-
self.log.debug('tx {} was not yet announced to us.'.format(txid))
113-
self.log.debug("node has responded with a notfound message. End test.")
114-
assert_equal(inbound_peer.last_message['notfound'].vec[0].hash, int(txid, 16))
115-
with p2p_lock:
116-
inbound_peer.last_message.pop('notfound')
117-
break
118-
else:
119-
self.log.debug('tx {} was already announced to us. Try test again.'.format(txid))
120-
assert int(txid, 16) in [inv.hash for inv in inbound_peer.last_message['inv'].inv]
105+
# Set a mock time so that time does not pass, and gen_node never announces the transaction
106+
self.gen_node.setmocktime(self.mocktime)
107+
wtxid = int(self.miniwallet.send_self_transfer(from_node=self.gen_node)["wtxid"], 16)
108+
109+
want_tx = msg_getdata()
110+
want_tx.inv.append(CInv(t=MSG_WTX, h=wtxid))
111+
with p2p_lock:
112+
inbound_peer.last_message.pop('notfound', None)
113+
inbound_peer.send_and_ping(want_tx)
114+
inbound_peer.wait_until(lambda: "notfound" in inbound_peer.last_message)
115+
with p2p_lock:
116+
assert_equal(inbound_peer.last_message.get("notfound").vec[0].hash, wtxid)
117+
inbound_peer.last_message.pop('notfound')
118+
119+
# Move mocktime forward and wait for the announcement.
120+
inbound_peer.last_message.pop('inv', None)
121+
self.mocktime += 120
122+
self.gen_node.setmocktime(self.mocktime)
123+
inbound_peer.wait_for_inv([CInv(t=MSG_WTX, h=wtxid)], timeout=120)
124+
125+
# Send the getdata again, this time the node should send us a TX message.
126+
inbound_peer.last_message.pop('tx', None)
127+
inbound_peer.send_and_ping(want_tx)
128+
self.wait_until(lambda: "tx" in inbound_peer.last_message)
129+
assert_equal(wtxid, int(inbound_peer.last_message["tx"].tx.wtxid_hex, 16))
121130

122131

123132
if __name__ == '__main__':

0 commit comments

Comments
 (0)