/ test / functional / p2p_private_broadcast.py
p2p_private_broadcast.py
  1  #!/usr/bin/env python3
  2  # Copyright (c) 2017-present The Bitcoin Core developers
  3  # Distributed under the MIT software license, see the accompanying
  4  # file COPYING or http://www.opensource.org/licenses/mit-license.php.
  5  """
  6  Test how locally submitted transactions are sent to the network when private broadcast is used.
  7  """
  8  
  9  import re
 10  import time
 11  import threading
 12  
 13  from test_framework.p2p import (
 14      P2PDataStore,
 15      P2PInterface,
 16      P2P_SERVICES,
 17      P2P_VERSION,
 18  )
 19  from test_framework.messages import (
 20      CAddress,
 21      CInv,
 22      MSG_WTX,
 23      malleate_tx_to_invalid_witness,
 24      msg_inv,
 25      msg_tx,
 26  )
 27  from test_framework.netutil import (
 28      format_addr_port
 29  )
 30  from test_framework.script_util import build_malleated_tx_package
 31  from test_framework.socks5 import (
 32      Socks5Configuration,
 33      Socks5Server,
 34  )
 35  from test_framework.test_framework import (
 36      BitcoinTestFramework,
 37  )
 38  from test_framework.util import (
 39      assert_equal,
 40      assert_greater_than_or_equal,
 41      assert_not_equal,
 42      assert_raises_rpc_error,
 43      p2p_port,
 44      tor_port,
 45  )
 46  from test_framework.wallet import (
 47      MiniWallet,
 48  )
 49  
 50  NUM_PRIVATE_BROADCAST_PER_TX = 3
 51  
 52  # Fill addrman with these addresses. Must have enough Tor addresses, so that even
 53  # if all 10 default connections are opened to a Tor address (!?) there must be more
 54  # for private broadcast.
 55  ADDRMAN_ADDRESSES = [
 56      "20.0.0.1",
 57      "30.0.0.1",
 58      "40.0.0.1",
 59      "50.0.0.1",
 60      "60.0.0.1",
 61      "70.0.0.1",
 62      "80.0.0.1",
 63      "90.0.0.1",
 64      "100.0.0.1",
 65      "110.0.0.1",
 66      "120.0.0.1",
 67      "130.0.0.1",
 68      "140.0.0.1",
 69      "150.0.0.1",
 70      "160.0.0.1",
 71      "170.0.0.1",
 72      "180.0.0.1",
 73      "190.0.0.1",
 74      "200.0.0.1",
 75      "210.0.0.1",
 76  
 77      "[20::1]",
 78      "[30::1]",
 79      "[40::1]",
 80      "[50::1]",
 81      "[60::1]",
 82      "[70::1]",
 83      "[80::1]",
 84      "[90::1]",
 85      "[100::1]",
 86      "[110::1]",
 87      "[120::1]",
 88      "[130::1]",
 89      "[140::1]",
 90      "[150::1]",
 91      "[160::1]",
 92      "[170::1]",
 93      "[180::1]",
 94      "[190::1]",
 95      "[200::1]",
 96      "[210::1]",
 97  
 98      "testonlyad777777777777777777777777777777777777777775b6qd.onion",
 99      "testonlyah77777777777777777777777777777777777777777z7ayd.onion",
100      "testonlyal77777777777777777777777777777777777777777vp6qd.onion",
101      "testonlyap77777777777777777777777777777777777777777r5qad.onion",
102      "testonlyat77777777777777777777777777777777777777777udsid.onion",
103      "testonlyax77777777777777777777777777777777777777777yciid.onion",
104      "testonlya777777777777777777777777777777777777777777rhgyd.onion",
105      "testonlybd77777777777777777777777777777777777777777rs4ad.onion",
106      "testonlybp77777777777777777777777777777777777777777zs2ad.onion",
107      "testonlybt777777777777777777777777777777777777777777x6id.onion",
108      "testonlybx777777777777777777777777777777777777777775styd.onion",
109      "testonlyb3777777777777777777777777777777777777777774ckid.onion",
110      "testonlycd77777777777777777777777777777777777777777733id.onion",
111      "testonlych77777777777777777777777777777777777777777t6kid.onion",
112      "testonlycl77777777777777777777777777777777777777777tt3ad.onion",
113      "testonlyct77777777777777777777777777777777777777777wvhyd.onion",
114      "testonlycx7777777777777777777777777777777777777777774bad.onion",
115      "testonlyc377777777777777777777777777777777777777777u6aid.onion",
116      "testonlydd777777777777777777777777777777777777777777u5ad.onion",
117      "testonlydh77777777777777777777777777777777777777777wgnyd.onion",
118  
119      "testonlyad77777777777777777777777777777777777777777q.b32.i2p",
120      "testonlyah77777777777777777777777777777777777777777q.b32.i2p",
121      "testonlyap77777777777777777777777777777777777777777q.b32.i2p",
122      "testonlyat77777777777777777777777777777777777777777q.b32.i2p",
123      "testonlyax77777777777777777777777777777777777777777q.b32.i2p",
124      "testonlya377777777777777777777777777777777777777777q.b32.i2p",
125      "testonlya777777777777777777777777777777777777777777q.b32.i2p",
126      "testonlybd77777777777777777777777777777777777777777q.b32.i2p",
127      "testonlybh77777777777777777777777777777777777777777q.b32.i2p",
128      "testonlybl77777777777777777777777777777777777777777q.b32.i2p",
129      "testonlybp77777777777777777777777777777777777777777q.b32.i2p",
130      "testonlybt77777777777777777777777777777777777777777q.b32.i2p",
131      "testonlybx77777777777777777777777777777777777777777q.b32.i2p",
132      "testonlyb777777777777777777777777777777777777777777q.b32.i2p",
133      "testonlych77777777777777777777777777777777777777777q.b32.i2p",
134      "testonlycp77777777777777777777777777777777777777777q.b32.i2p",
135      "testonlyct77777777777777777777777777777777777777777q.b32.i2p",
136      "testonlycx77777777777777777777777777777777777777777q.b32.i2p",
137      "testonlyc377777777777777777777777777777777777777777q.b32.i2p",
138      "testonlyc777777777777777777777777777777777777777777q.b32.i2p",
139  
140      "[fc00::1]",
141      "[fc00::2]",
142      "[fc00::3]",
143      "[fc00::5]",
144      "[fc00::6]",
145      "[fc00::7]",
146      "[fc00::8]",
147      "[fc00::9]",
148      "[fc00::10]",
149      "[fc00::11]",
150      "[fc00::12]",
151      "[fc00::13]",
152      "[fc00::15]",
153      "[fc00::16]",
154      "[fc00::17]",
155      "[fc00::18]",
156      "[fc00::19]",
157      "[fc00::20]",
158      "[fc00::22]",
159      "[fc00::23]",
160  ]
161  
162  
163  class P2PPrivateBroadcast(BitcoinTestFramework):
164      def set_test_params(self):
165          self.disable_autoconnect = False
166          self.num_nodes = 2
167  
168      def setup_nodes(self):
169          # Start a SOCKS5 proxy server.
170          socks5_server_config = Socks5Configuration()
171          # self.nodes[0] listens on p2p_port(0),
172          # self.nodes[1] listens on p2p_port(1),
173          # thus we tell the SOCKS5 server to listen on p2p_port(self.num_nodes) (self.num_nodes is 2)
174          socks5_server_config.addr = ("127.0.0.1", p2p_port(self.num_nodes))
175          socks5_server_config.unauth = True
176          socks5_server_config.auth = True
177  
178          self.socks5_server = Socks5Server(socks5_server_config)
179          self.socks5_server.start()
180  
181          self.destinations = []
182  
183          self.destinations_lock = threading.Lock()
184  
185          def find_connection_type_in_debug_log(to_addr, to_port):
186              """
187              Scan the debug log of tx_originator for a connection attempt to to_addr:to_port.
188              Return the connection type (outbound-full-relay, private-broadcast, etc) or
189              None if there is no connection attempt to to_addr:to_port.
190              """
191              with open(self.tx_originator_debug_log_path, mode="r", encoding="utf-8") as debug_log:
192                  for line in debug_log.readlines():
193                      match = re.match(f".*trying v. connection \\((.+)\\) to \\[?{to_addr}]?:{to_port},.*", line)
194                      if match:
195                          return match.group(1)
196              return None
197  
198          def destinations_factory(requested_to_addr, requested_to_port):
199              """
200              Instruct the SOCKS5 proxy to redirect connections:
201              * The first automatic outbound connection -> P2PDataStore
202              * The first private broadcast connection -> nodes[1]
203              * Anything else -> P2PInterface
204              """
205              conn_type = None
206              def found_connection_in_debug_log():
207                  nonlocal conn_type
208                  conn_type = find_connection_type_in_debug_log(requested_to_addr, requested_to_port)
209                  return conn_type is not None
210  
211              self.wait_until(found_connection_in_debug_log)
212  
213              with self.destinations_lock:
214                  i = len(self.destinations)
215                  actual_to_addr = ""
216                  actual_to_port = 0
217                  listener = None
218                  target_name = ""
219                  if conn_type == "private-broadcast" and not any(dest["conn_type"] == "private-broadcast" for dest in self.destinations):
220                      # Instruct the SOCKS5 server to redirect the first private
221                      # broadcast connection from nodes[0] to nodes[1]
222                      actual_to_addr = "127.0.0.1" # nodes[1] listen address
223                      actual_to_port = tor_port(1) # nodes[1] listen port for Tor
224                      target_name = "nodes[1]"
225                  else:
226                      # Create a Python P2P listening node and instruct the SOCKS5 proxy to
227                      # redirect the connection to it. The first outbound connection is used
228                      # later to serve GETDATA, thus make it P2PDataStore().
229                      if conn_type == "outbound-full-relay" and not any(dest["conn_type"] == "outbound-full-relay" for dest in self.destinations):
230                          listener = P2PDataStore()
231                          target_name = "Python P2PDataStore"
232                      else:
233                          listener = P2PInterface()
234                          target_name = "Python P2PInterface"
235                      listener.peer_connect_helper(dstaddr="0.0.0.0", dstport=0, net=self.chain, timeout_factor=self.options.timeout_factor)
236                      listener.peer_connect_send_version(services=P2P_SERVICES)
237  
238                      def on_listen_done(addr, port):
239                          nonlocal actual_to_addr
240                          nonlocal actual_to_port
241                          actual_to_addr = addr
242                          actual_to_port = port
243  
244                      # Use port=0 to let the OS assign an available port. This
245                      # avoids "address already in use" errors when tests run
246                      # concurrently or ports are still in TIME_WAIT state.
247                      self.network_thread.listen(
248                          addr="127.0.0.1",
249                          port=0,
250                          p2p=listener,
251                          callback=on_listen_done)
252                      # Wait until the callback has been called.
253                      self.wait_until(lambda: actual_to_port != 0)
254  
255                  self.log.debug(f"Instructing the SOCKS5 proxy to redirect connection i={i} ({conn_type}) for "
256                                 f"{format_addr_port(requested_to_addr, requested_to_port)} to "
257                                 f"{format_addr_port(actual_to_addr, actual_to_port)} ({target_name})")
258  
259                  self.destinations.append({
260                      "requested_to": format_addr_port(requested_to_addr, requested_to_port),
261                      "conn_type": conn_type,
262                      "node": listener,
263                  })
264                  assert_equal(len(self.destinations), i + 1)
265  
266                  return {
267                      "actual_to_addr": actual_to_addr,
268                      "actual_to_port": actual_to_port,
269                  }
270  
271          self.socks5_server.conf.destinations_factory = destinations_factory
272  
273          self.extra_args = [
274              [
275                  # Needed to be able to add CJDNS addresses to addrman (otherwise they are unroutable).
276                  "-cjdnsreachable",
277                  # Connecting, sending garbage, being disconnected messes up with this test's
278                  # check_broadcasts() which waits for a particular Python node to receive a connection.
279                  "-v2transport=0",
280                  "-test=addrman",
281                  "-privatebroadcast",
282                  f"-proxy={socks5_server_config.addr[0]}:{socks5_server_config.addr[1]}",
283                  # To increase coverage, make it think that the I2P network is reachable so that it
284                  # selects such addresses as well. Pick a proxy address where nobody is listening
285                  # and connection attempts fail quickly.
286                  "-i2psam=127.0.0.1:1",
287              ],
288              [
289                  "-connect=0",
290                  f"-bind=127.0.0.1:{tor_port(1)}=onion",
291              ],
292          ]
293          super().setup_nodes()
294  
295      def setup_network(self):
296          self.setup_nodes()
297  
298      def check_broadcasts(self, label, tx, broadcasts_to_expect, skip_destinations):
299          def wait_and_get_destination(n):
300              """Wait for self.destinations[] to have at least n elements and return the 'n'th."""
301              def get_destinations_len():
302                  with self.destinations_lock:
303                      return len(self.destinations)
304              self.wait_until(lambda: get_destinations_len() > n)
305              with self.destinations_lock:
306                  return self.destinations[n]
307  
308          broadcasts_done = 0
309          i = skip_destinations - 1
310          while broadcasts_done < broadcasts_to_expect:
311              i += 1
312              self.log.debug(f"{label}: waiting for outbound connection i={i}")
313              # At this point the connection may not yet have been established (A),
314              # may be active (B), or may have already been closed (C).
315              dest = wait_and_get_destination(i)
316              peer = dest["node"]
317              if peer is None:
318                  continue # That is the first private broadcast connection, redirected to nodes[1]
319              peer.wait_until(lambda: peer.message_count["version"] == 1, check_connected=False)
320              # Now it is either (B) or (C).
321              if peer.last_message["version"].nServices != 0:
322                  self.log.debug(f"{label}: outbound connection i={i} to {dest['requested_to']} not a private broadcast, ignoring it (maybe feeler or extra block only)")
323                  continue
324              self.log.debug(f"{label}: outbound connection i={i} to {dest['requested_to']} must be a private broadcast, checking it")
325              peer.wait_for_disconnect()
326              # Now it is (C).
327              assert_equal(peer.message_count, {
328                  "version": 1,
329                  "verack": 1,
330                  "inv": 1,
331                  "tx": 1,
332                  "ping": 1
333              })
334              dummy_address = CAddress()
335              dummy_address.nServices = 0
336              assert_equal(peer.last_message["version"].nVersion, P2P_VERSION)
337              assert_equal(peer.last_message["version"].nServices, 0)
338              assert_equal(peer.last_message["version"].nTime, 0)
339              assert_equal(peer.last_message["version"].addrTo, dummy_address)
340              assert_equal(peer.last_message["version"].addrFrom, dummy_address)
341              assert_equal(peer.last_message["version"].strSubVer, "/pynode:0.0.1/")
342              assert_equal(peer.last_message["version"].nStartingHeight, 0)
343              assert_equal(peer.last_message["version"].relay, 0)
344              assert_equal(peer.last_message["tx"].tx.txid_hex, tx["txid"])
345              self.log.info(f"{label}: ok: outbound connection i={i} is private broadcast of txid={tx['txid']}")
346              broadcasts_done += 1
347  
348          # Verify the tx we just observed is tracked in getprivatebroadcastinfo.
349          pbinfo = self.nodes[0].getprivatebroadcastinfo()
350          pending = [t for t in pbinfo["transactions"] if t["txid"] == tx["txid"] and t["wtxid"] == tx["wtxid"]]
351          assert_equal(len(pending), 1)
352          assert_equal(pending[0]["hex"].lower(), tx["hex"].lower())
353          peers = pending[0]["peers"]
354          assert len(peers) >= NUM_PRIVATE_BROADCAST_PER_TX
355          assert all("address" in p and "sent" in p for p in peers)
356          assert_greater_than_or_equal(sum(1 for p in peers if "received" in p), broadcasts_to_expect)
357  
358      def run_test(self):
359          tx_originator = self.nodes[0]
360          self.tx_originator_debug_log_path = tx_originator.debug_log_path
361          tx_receiver = self.nodes[1]
362          far_observer = tx_receiver.add_p2p_connection(P2PInterface())
363  
364          wallet = MiniWallet(tx_originator)
365  
366          # Fill tx_originator's addrman.
367          for addr in ADDRMAN_ADDRESSES:
368              res = tx_originator.addpeeraddress(address=addr, port=0 if addr.endswith(".i2p") else 8333, tried=False)
369              if not res["success"]:
370                  self.log.debug(f"Could not add {addr} to tx_originator's addrman (collision?)")
371  
372          txs = wallet.create_self_transfer_chain(chain_length=3)
373          self.log.info(f"Created txid={txs[0]['txid']}: for basic test")
374          self.log.info(f"Created txid={txs[1]['txid']}: for broadcast with dependency in mempool + rebroadcast")
375          self.log.info(f"Created txid={txs[2]['txid']}: for broadcast with dependency not in mempool")
376          tx_originator.sendrawtransaction(hexstring=txs[0]["hex"], maxfeerate=0.1)
377  
378          self.log.info("First private broadcast: waiting for the transaction to reach the recipient")
379          self.wait_until(lambda: len(tx_receiver.getrawmempool()) > 0)
380          self.log.info("First private broadcast: the recipient received the transaction")
381          far_observer.wait_for_tx(txs[0]["txid"])
382          self.log.info("First private broadcast: the recipient further relayed the transaction")
383  
384          # One already checked above, check the other NUM_PRIVATE_BROADCAST_PER_TX - 1 broadcasts.
385          self.check_broadcasts("Basic", txs[0], NUM_PRIVATE_BROADCAST_PER_TX - 1, 0)
386  
387          self.log.info("Resending the same transaction via RPC again (it is not in the mempool yet)")
388          ignoring_msg = f"Ignoring unnecessary request to schedule an already scheduled transaction: txid={txs[0]['txid']}, wtxid={txs[0]['wtxid']}"
389          with tx_originator.busy_wait_for_debug_log(expected_msgs=[ignoring_msg.encode()]):
390              tx_originator.sendrawtransaction(hexstring=txs[0]["hex"], maxfeerate=0)
391  
392          self.log.info("Sending a malleated transaction with an invalid witness via RPC")
393          malleated_invalid = malleate_tx_to_invalid_witness(txs[0])
394          assert_raises_rpc_error(-26, "mempool-script-verify-flag-failed",
395                                  tx_originator.sendrawtransaction,
396                                  hexstring=malleated_invalid.serialize_with_witness().hex(),
397                                  maxfeerate=0.1)
398  
399          self.log.info("Checking that the transaction is not in the originator node's mempool")
400          assert_equal(len(tx_originator.getrawmempool()), 0)
401  
402          wtxid_int = int(txs[0]["wtxid"], 16)
403          inv = CInv(MSG_WTX, wtxid_int)
404  
405          tx_returner = None # First outbound-full-relay, will be P2PDataStore.
406          other_peer = None # Any other outbound-full-relay, we use the second one.
407  
408          def set_tx_returner_and_other():
409              nonlocal tx_returner
410              nonlocal other_peer
411              tx_returner = None
412              other_peer = None
413              with self.destinations_lock:
414                  for dest in self.destinations:
415                      if dest["conn_type"] == "outbound-full-relay" and dest["node"] is not None:
416                          if tx_returner is None:
417                              assert(type(dest["node"]) is P2PDataStore)
418                              tx_returner = dest["node"]
419                          else:
420                              assert(type(dest["node"]) is P2PInterface)
421                              other_peer = dest["node"]
422                              return True
423              return False
424  
425          self.wait_until(set_tx_returner_and_other)
426  
427          tx_returner.wait_for_connect()
428          other_peer.wait_for_connect()
429  
430          self.log.info("Sending INV and waiting for GETDATA from node")
431          tx_returner.tx_store[wtxid_int] = txs[0]["tx"]
432          assert "getdata" not in tx_returner.last_message
433          received_back_msg = f"Received our privately broadcast transaction (txid={txs[0]['txid']}) from the network"
434          with tx_originator.assert_debug_log(expected_msgs=[received_back_msg]):
435              tx_returner.send_without_ping(msg_inv([inv]))
436              tx_returner.wait_until(lambda: "getdata" in tx_returner.last_message)
437              self.wait_until(lambda: len(tx_originator.getrawmempool()) > 0)
438  
439          self.log.info("Waiting for normal broadcast to another peer")
440          other_peer.wait_for_inv([inv])
441  
442          self.log.info("Checking getprivatebroadcastinfo no longer reports the transaction after it is received back")
443          pbinfo = tx_originator.getprivatebroadcastinfo()
444          pending = [t for t in pbinfo["transactions"] if t["txid"] == txs[0]["txid"] and t["wtxid"] == txs[0]["wtxid"]]
445          assert_equal(len(pending), 0)
446  
447          self.log.info("Sending a transaction that is already in the mempool")
448          skip_destinations = len(self.destinations)
449          tx_originator.sendrawtransaction(hexstring=txs[0]["hex"], maxfeerate=0)
450          self.check_broadcasts("Broadcast of mempool transaction", txs[0], NUM_PRIVATE_BROADCAST_PER_TX, skip_destinations)
451  
452          self.log.info("Sending a transaction with a dependency in the mempool")
453          skip_destinations = len(self.destinations)
454          tx_originator.sendrawtransaction(hexstring=txs[1]["hex"], maxfeerate=0.1)
455          self.check_broadcasts("Dependency in mempool", txs[1], NUM_PRIVATE_BROADCAST_PER_TX, skip_destinations)
456  
457          self.log.info("Sending a transaction with a dependency not in the mempool (should be rejected)")
458          assert_equal(len(tx_originator.getrawmempool()), 1)
459          assert_raises_rpc_error(-25, "bad-txns-inputs-missingorspent",
460                                  tx_originator.sendrawtransaction, hexstring=txs[2]["hex"], maxfeerate=0.1)
461          assert_raises_rpc_error(-25, "bad-txns-inputs-missingorspent",
462                                  tx_originator.sendrawtransaction, hexstring=txs[2]["hex"], maxfeerate=0)
463  
464          # Since txs[1] has not been received back by tx_originator,
465          # it should be re-broadcast after a while. Advance tx_originator's clock
466          # to trigger a re-broadcast. Should be more than the maximum returned by
467          # NextTxBroadcast() in net_processing.cpp.
468          self.log.info("Checking that rebroadcast works")
469          delta = 20 * 60 # 20min
470          skip_destinations = len(self.destinations)
471          rebroadcast_msg = f"Reattempting broadcast of stale txid={txs[1]['txid']}"
472          with tx_originator.busy_wait_for_debug_log(expected_msgs=[rebroadcast_msg.encode()]):
473              tx_originator.setmocktime(int(time.time()) + delta)
474              tx_originator.mockscheduler(delta)
475          self.check_broadcasts("Rebroadcast", txs[1], 1, skip_destinations)
476          tx_originator.setmocktime(0) # Let the clock tick again (it will go backwards due to this).
477  
478          self.log.info("Sending a pair of transactions with the same txid but different valid wtxids via RPC")
479          parent = wallet.create_self_transfer()["tx"]
480          parent_amount = parent.vout[0].nValue - 10000
481          child_amount = parent_amount - 10000
482          siblings_parent, sibling1, sibling2 = build_malleated_tx_package(
483              parent=parent,
484              rebalance_parent_output_amount=parent_amount,
485              child_amount=child_amount)
486          self.log.info(f"  - sibling1: txid={sibling1.txid_hex}, wtxid={sibling1.wtxid_hex}")
487          self.log.info(f"  - sibling2: txid={sibling2.txid_hex}, wtxid={sibling2.wtxid_hex}")
488          assert_equal(sibling1.txid_hex, sibling2.txid_hex)
489          assert_not_equal(sibling1.wtxid_hex, sibling2.wtxid_hex)
490          assert_equal(len(tx_originator.getrawmempool()), 1)
491          tx_returner.send_without_ping(msg_tx(siblings_parent))
492          self.wait_until(lambda: len(tx_originator.getrawmempool()) > 1)
493          self.log.info("  - siblings' parent added to the mempool")
494          tx_originator.sendrawtransaction(hexstring=sibling1.serialize_with_witness().hex(), maxfeerate=0.1)
495          self.log.info("  - sent sibling1: ok")
496          tx_originator.sendrawtransaction(hexstring=sibling2.serialize_with_witness().hex(), maxfeerate=0.1)
497          self.log.info("  - sent sibling2: ok")
498  
499          self.log.info("Checking abortprivatebroadcast removes a pending private-broadcast transaction")
500          tx_abort = wallet.create_self_transfer()
501          tx_originator.sendrawtransaction(hexstring=tx_abort["hex"], maxfeerate=0.1)
502          assert any(t["wtxid"] == tx_abort["wtxid"] for t in tx_originator.getprivatebroadcastinfo()["transactions"])
503          abort_res = tx_originator.abortprivatebroadcast(tx_abort["txid"])
504          assert_equal(len(abort_res["removed_transactions"]), 1)
505          assert_equal(abort_res["removed_transactions"][0]["txid"], tx_abort["txid"])
506          assert_equal(abort_res["removed_transactions"][0]["wtxid"], tx_abort["wtxid"])
507          assert_equal(abort_res["removed_transactions"][0]["hex"].lower(), tx_abort["hex"].lower())
508          assert all(t["wtxid"] != tx_abort["wtxid"] for t in tx_originator.getprivatebroadcastinfo()["transactions"])
509  
510          self.log.info("Checking abortprivatebroadcast fails for non-existent transaction")
511          assert_raises_rpc_error(
512              -5,
513              "Transaction not in private broadcast queue",
514              tx_originator.abortprivatebroadcast,
515              "0" * 64,
516          )
517  
518          # Stop the SOCKS5 proxy server to avoid it being upset by the bitcoin
519          # node disconnecting in the middle of the SOCKS5 handshake when we
520          # restart below.
521          self.socks5_server.stop()
522  
523          self.log.info("Trying to send a transaction when none of Tor or I2P is reachable")
524          self.restart_node(0, extra_args=[
525              "-privatebroadcast",
526              "-v2transport=0",
527              # A location where definitely a Tor control is not listening. This would allow
528              # Bitcoin Core to start, hoping/assuming that the location of the Tor proxy
529              # may be retrieved after startup from the Tor control, but it will not be, so
530              # the RPC should throw.
531              "-torcontrol=127.0.0.1:1",
532              "-listenonion",
533          ])
534          assert_raises_rpc_error(-1, "none of the Tor or I2P networks is reachable",
535                                  tx_originator.sendrawtransaction, hexstring=txs[0]["hex"], maxfeerate=0.1)
536  
537  
538  if __name__ == "__main__":
539      P2PPrivateBroadcast(__file__).main()