p2p_compactblocks.py raw

   1  #!/usr/bin/env python3
   2  # Copyright (c) 2016-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  """Test compact blocks (BIP 152)."""
   6  import random
   7  
   8  from test_framework.blocktools import (
   9      COINBASE_MATURITY,
  10      NORMAL_GBT_REQUEST_PARAMS,
  11      add_witness_commitment,
  12      create_block,
  13  )
  14  from test_framework.messages import (
  15      BlockTransactions,
  16      BlockTransactionsRequest,
  17      CBlock,
  18      CBlockHeader,
  19      CInv,
  20      COutPoint,
  21      CTransaction,
  22      CTxIn,
  23      CTxInWitness,
  24      CTxOut,
  25      from_hex,
  26      HeaderAndShortIDs,
  27      MSG_BLOCK,
  28      MSG_CMPCT_BLOCK,
  29      MSG_WITNESS_FLAG,
  30      P2PHeaderAndShortIDs,
  31      PrefilledTransaction,
  32      calculate_shortid,
  33      msg_block,
  34      msg_blocktxn,
  35      msg_cmpctblock,
  36      msg_getblocktxn,
  37      msg_getdata,
  38      msg_getheaders,
  39      msg_headers,
  40      msg_inv,
  41      msg_no_witness_block,
  42      msg_no_witness_blocktxn,
  43      msg_sendcmpct,
  44      msg_sendheaders,
  45      msg_tx,
  46      ser_uint256,
  47      tx_from_hex,
  48  )
  49  from test_framework.p2p import (
  50      P2PInterface,
  51      p2p_lock,
  52  )
  53  from test_framework.script import (
  54      CScript,
  55      OP_DROP,
  56      OP_TRUE,
  57      OP_RETURN,
  58  )
  59  from test_framework.test_framework import BitcoinTestFramework
  60  from test_framework.test_node import TestNode
  61  from test_framework.util import (
  62      assert_not_equal,
  63      assert_equal,
  64      softfork_active,
  65  )
  66  from test_framework.wallet import MiniWallet
  67  
  68  
  69  # TestP2PConn: A peer we use to send messages to bitcoind, and store responses.
  70  class TestP2PConn(P2PInterface):
  71      def __init__(self):
  72          super().__init__()
  73          self.last_sendcmpct = []
  74          self.block_announced = False
  75          # Store the hashes of blocks we've seen announced.
  76          # This is for synchronizing the p2p message traffic,
  77          # so we can eg wait until a particular block is announced.
  78          self.announced_blockhashes = set()
  79  
  80      def on_sendcmpct(self, message):
  81          self.last_sendcmpct.append(message)
  82  
  83      def on_cmpctblock(self, message):
  84          self.block_announced = True
  85          self.announced_blockhashes.add(self.last_message["cmpctblock"].header_and_shortids.header.hash_int)
  86  
  87      def on_headers(self, message):
  88          self.block_announced = True
  89          for x in self.last_message["headers"].headers:
  90              self.announced_blockhashes.add(x.hash_int)
  91  
  92      def on_inv(self, message):
  93          for x in self.last_message["inv"].inv:
  94              if x.type == MSG_BLOCK:
  95                  self.block_announced = True
  96                  self.announced_blockhashes.add(x.hash)
  97  
  98      # Requires caller to hold p2p_lock
  99      def received_block_announcement(self):
 100          return self.block_announced
 101  
 102      def clear_block_announcement(self):
 103          with p2p_lock:
 104              self.block_announced = False
 105              self.last_message.pop("inv", None)
 106              self.last_message.pop("headers", None)
 107              self.last_message.pop("cmpctblock", None)
 108  
 109      def clear_getblocktxn(self):
 110          with p2p_lock:
 111              self.last_message.pop("getblocktxn", None)
 112  
 113      def get_headers(self, locator, hashstop):
 114          msg = msg_getheaders()
 115          msg.locator.vHave = locator
 116          msg.hashstop = hashstop
 117          self.send_without_ping(msg)
 118  
 119      def send_header_for_blocks(self, new_blocks):
 120          headers_message = msg_headers()
 121          headers_message.headers = [CBlockHeader(b) for b in new_blocks]
 122          self.send_without_ping(headers_message)
 123  
 124      def request_headers_and_sync(self, locator, hashstop=0):
 125          self.clear_block_announcement()
 126          self.get_headers(locator, hashstop)
 127          self.wait_until(self.received_block_announcement, timeout=30)
 128          self.clear_block_announcement()
 129  
 130      # Block until a block announcement for a particular block hash is
 131      # received.
 132      def wait_for_block_announcement(self, block_hash, timeout=30):
 133          def received_hash():
 134              return (block_hash in self.announced_blockhashes)
 135          self.wait_until(received_hash, timeout=timeout)
 136  
 137      def send_await_disconnect(self, message, timeout=30):
 138          """Sends a message to the node and wait for disconnect.
 139  
 140          This is used when we want to send a message into the node that we expect
 141          will get us disconnected, eg an invalid block."""
 142          self.send_without_ping(message)
 143          self.wait_for_disconnect(timeout=timeout)
 144  
 145  class CompactBlocksTest(BitcoinTestFramework):
 146      def set_test_params(self):
 147          self.setup_clean_chain = True
 148          self.num_nodes = 1
 149          self.extra_args = [[
 150              "-acceptnonstdtxn=1",
 151          ]]
 152          self.utxos = []
 153  
 154      def getblocktxn_expected(self, peer, blockhash, indices=None):
 155          with p2p_lock:
 156              assert "getblocktxn" in peer.last_message
 157              gbt = peer.last_message["getblocktxn"].block_txn_request
 158  
 159          assert_equal(gbt.blockhash, blockhash)
 160          if indices is not None:
 161              assert_equal(gbt.to_absolute(), indices)
 162          if isinstance(peer, TestNode):
 163              assert_not_equal(peer.getbestblockhash(), blockhash)
 164  
 165      def build_block_on_tip(self, node):
 166          block = create_block(tmpl=node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS))
 167          block.solve()
 168          return block
 169  
 170      # Create 12 more anyone-can-spend utxo's for testing.
 171      def make_utxos(self):
 172          COUNT = 12
 173          block = self.build_block_on_tip(self.nodes[0])
 174          self.segwit_node.send_and_ping(msg_no_witness_block(block))
 175          assert_equal(self.nodes[0].getbestblockhash(), block.hash_hex)
 176          self.generate(self.wallet, COINBASE_MATURITY)
 177  
 178          total_value = block.vtx[0].vout[0].nValue
 179          out_value = total_value // COUNT
 180          tx = CTransaction()
 181          tx.vin.append(CTxIn(COutPoint(block.vtx[0].txid_int, 0), b''))
 182          for _ in range(COUNT):
 183              tx.vout.append(CTxOut(out_value, CScript([OP_TRUE])))
 184  
 185          block2 = self.build_block_on_tip(self.nodes[0])
 186          block2.vtx.append(tx)
 187          block2.hashMerkleRoot = block2.calc_merkle_root()
 188          block2.solve()
 189          self.segwit_node.send_and_ping(msg_no_witness_block(block2))
 190          assert_equal(self.nodes[0].getbestblockhash(), block2.hash_hex)
 191          self.utxos.extend([[tx.txid_int, i, out_value] for i in range(COUNT)])
 192  
 193      def announce_cmpct_block(self, node, peer, txn_count=5, solicit=False):
 194          utxo = self.utxos.pop(0)
 195          block = self.build_block_with_transactions(node, utxo, txn_count)
 196  
 197          cmpct_block = HeaderAndShortIDs()
 198          cmpct_block.initialize_from_block(block)
 199          msg = msg_cmpctblock(cmpct_block.to_p2p())
 200          if solicit:
 201              peer.send_without_ping(msg_headers([block]))
 202              peer.wait_for_getdata([block.hash_int], timeout=30)
 203  
 204          peer.clear_getblocktxn()
 205          peer.send_and_ping(msg)
 206          self.getblocktxn_expected(peer, block.hash_int)
 207          return block, cmpct_block
 208  
 209      # Test "sendcmpct" (between peers preferring the same version):
 210      # - No compact block announcements unless sendcmpct is sent.
 211      # - If sendcmpct is sent with version = 1, the message is ignored.
 212      # - If sendcmpct is sent with version > 2, the message is ignored.
 213      # - If sendcmpct is sent with boolean 0, then block announcements are not
 214      #   made with compact blocks.
 215      # - If sendcmpct is then sent with boolean 1, then new block announcements
 216      #   are made with compact blocks.
 217      def test_sendcmpct(self, test_node):
 218          node = self.nodes[0]
 219  
 220          # Make sure we get a SENDCMPCT message from our peer
 221          def received_sendcmpct():
 222              return (len(test_node.last_sendcmpct) > 0)
 223          test_node.wait_until(received_sendcmpct, timeout=30)
 224          with p2p_lock:
 225              # Check that version 2 is received.
 226              assert_equal(test_node.last_sendcmpct[0].version, 2)
 227              test_node.last_sendcmpct = []
 228  
 229          tip = int(node.getbestblockhash(), 16)
 230  
 231          def check_announcement_of_new_block(node, peer, predicate):
 232              peer.clear_block_announcement()
 233              block_hash = int(self.generate(node, 1)[0], 16)
 234              peer.wait_for_block_announcement(block_hash, timeout=30)
 235              assert peer.block_announced
 236  
 237              with p2p_lock:
 238                  assert predicate(peer), (
 239                      "block_hash={!r}, cmpctblock={!r}, inv={!r}".format(
 240                          block_hash, peer.last_message.get("cmpctblock", None), peer.last_message.get("inv", None)))
 241  
 242          # We shouldn't get any block announcements via cmpctblock yet.
 243          check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" not in p.last_message)
 244  
 245          # Try one more time, this time after requesting headers.
 246          test_node.request_headers_and_sync(locator=[tip])
 247          check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" not in p.last_message and "inv" in p.last_message)
 248  
 249          # Test a few ways of using sendcmpct that should NOT
 250          # result in compact block announcements.
 251          # Before each test, sync the headers chain.
 252          test_node.request_headers_and_sync(locator=[tip])
 253  
 254          # Now try a SENDCMPCT message with too-low version
 255          test_node.send_and_ping(msg_sendcmpct(announce=True, version=1))
 256          check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" not in p.last_message)
 257  
 258          # Headers sync before next test.
 259          test_node.request_headers_and_sync(locator=[tip])
 260  
 261          # Now try a SENDCMPCT message with too-high version
 262          test_node.send_and_ping(msg_sendcmpct(announce=True, version=3))
 263          check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" not in p.last_message)
 264  
 265          # Headers sync before next test.
 266          test_node.request_headers_and_sync(locator=[tip])
 267  
 268          # Now try a SENDCMPCT message with valid version, but announce=False
 269          test_node.send_and_ping(msg_sendcmpct(announce=False, version=2))
 270          check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" not in p.last_message)
 271  
 272          # Headers sync before next test.
 273          test_node.request_headers_and_sync(locator=[tip])
 274  
 275          # Finally, try a SENDCMPCT message with announce=True
 276          test_node.send_and_ping(msg_sendcmpct(announce=True, version=2))
 277          check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" in p.last_message)
 278  
 279          # Try one more time (no headers sync should be needed!)
 280          check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" in p.last_message)
 281  
 282          # Try one more time, after turning on sendheaders
 283          test_node.send_and_ping(msg_sendheaders())
 284          check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" in p.last_message)
 285  
 286          # Try one more time, after sending a version=1, announce=false message.
 287          test_node.send_and_ping(msg_sendcmpct(announce=False, version=1))
 288          check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" in p.last_message)
 289  
 290          # Now turn off announcements
 291          test_node.send_and_ping(msg_sendcmpct(announce=False, version=2))
 292          check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" not in p.last_message and "headers" in p.last_message)
 293  
 294      # BIP152 mandates that the announce field of a sendcmpct message is a
 295      # boolean and MUST have a value of either 1 or 0. Sending any other value
 296      # should be treated as misbehavior and lead to a disconnect.
 297      def test_invalid_sendcmpct_announce(self):
 298          node = self.nodes[0]
 299          bad_peer = node.add_p2p_connection(TestP2PConn())
 300          msg = msg_sendcmpct(announce=2, version=2)
 301          with node.assert_debug_log(['invalid sendcmpct announce field']):
 302              bad_peer.send_await_disconnect(msg)
 303  
 304      # This test actually causes bitcoind to (reasonably!) disconnect us, so do this last.
 305      # Note, these are not consensus-invalid blocks, but improperly constructed cmpctblock messages.
 306      def test_invalid_cmpctblock_message(self):
 307          # Make a high-bandwidth peer
 308          hb_peer = self.nodes[0].add_p2p_connection(TestP2PConn())
 309          self.request_cb_announcements(hb_peer)
 310          self.make_peer_hb_to_candidate(self.nodes[0], hb_peer)
 311          self.assert_highbandwidth_states(self.nodes[0], idx=-1, hb_to=True, hb_from=True)
 312  
 313          # Make a low-bandwidth peer
 314          lb_peer = self.nodes[0].add_p2p_connection(TestP2PConn())
 315          self.request_cb_announcements(lb_peer)
 316          self.assert_highbandwidth_states(self.nodes[0], idx=-1, hb_to=False, hb_from=True)
 317  
 318          # Construct an invalid cmpctblock message
 319          self.generate(self.nodes[0], COINBASE_MATURITY + 1)
 320          block = self.build_block_on_tip(self.nodes[0])
 321          cmpct_block = P2PHeaderAndShortIDs()
 322          cmpct_block.header = CBlockHeader(block)
 323          cmpct_block.prefilled_txn_length = 1
 324          too_high_prefill_idx = 1
 325          prefilled_txn = PrefilledTransaction(too_high_prefill_idx, block.vtx[0])
 326          cmpct_block.prefilled_txn = [prefilled_txn]
 327  
 328          # Test an unsolicited invalid cmpctblock from an HB peer.
 329          hb_peer.send_await_disconnect(msg_cmpctblock(cmpct_block))
 330          assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.hashPrevBlock)
 331  
 332          # Test a solicited invalid cmpctblock from a non-HB peer.
 333          lb_peer.send_header_for_blocks([block])
 334          lb_peer.wait_for_getdata([block.hash_int], timeout=30)
 335          lb_peer.send_await_disconnect(msg_cmpctblock(cmpct_block))
 336          assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.hashPrevBlock)
 337  
 338      # Compare the generated shortids to what we expect based on BIP 152, given
 339      # bitcoind's choice of nonce.
 340      def test_compactblock_construction(self, test_node):
 341          node = self.nodes[0]
 342          # Generate a bunch of transactions.
 343          self.generate(node, COINBASE_MATURITY + 1)
 344          num_transactions = 25
 345  
 346          segwit_tx_generated = False
 347          for _ in range(num_transactions):
 348              hex_tx = self.wallet.send_self_transfer(from_node=self.nodes[0])['hex']
 349              tx = tx_from_hex(hex_tx)
 350              if not tx.wit.is_null():
 351                  segwit_tx_generated = True
 352  
 353          assert segwit_tx_generated  # check that our test is not broken
 354  
 355          # Wait until we've seen the block announcement for the resulting tip
 356          tip = int(node.getbestblockhash(), 16)
 357          test_node.wait_for_block_announcement(tip)
 358  
 359          # Make sure we will receive a fast-announce compact block
 360          self.request_cb_announcements(test_node)
 361  
 362          # Now mine a block, and look at the resulting compact block.
 363          test_node.clear_block_announcement()
 364          block_hash = int(self.generate(node, 1)[0], 16)
 365  
 366          # Store the raw block in our internal format.
 367          block = from_hex(CBlock(), node.getblock("%064x" % block_hash, False))
 368  
 369          # Wait until the block was announced (via compact blocks)
 370          test_node.wait_until(lambda: "cmpctblock" in test_node.last_message, timeout=30)
 371  
 372          # Now fetch and check the compact block
 373          header_and_shortids = None
 374          with p2p_lock:
 375              # Convert the on-the-wire representation to absolute indexes
 376              header_and_shortids = HeaderAndShortIDs(test_node.last_message["cmpctblock"].header_and_shortids)
 377          self.check_compactblock_construction_from_block(header_and_shortids, block_hash, block)
 378  
 379          # Now fetch the compact block using a normal non-announce getdata
 380          test_node.clear_block_announcement()
 381          inv = CInv(MSG_CMPCT_BLOCK, block_hash)
 382          test_node.send_without_ping(msg_getdata([inv]))
 383  
 384          test_node.wait_until(lambda: "cmpctblock" in test_node.last_message, timeout=30)
 385  
 386          # Now fetch and check the compact block
 387          header_and_shortids = None
 388          with p2p_lock:
 389              # Convert the on-the-wire representation to absolute indexes
 390              header_and_shortids = HeaderAndShortIDs(test_node.last_message["cmpctblock"].header_and_shortids)
 391          self.check_compactblock_construction_from_block(header_and_shortids, block_hash, block)
 392  
 393      def check_compactblock_construction_from_block(self, header_and_shortids, block_hash, block):
 394          # Check that we got the right block!
 395          assert_equal(header_and_shortids.header.hash_int, block_hash)
 396  
 397          # Make sure the prefilled_txn appears to have included the coinbase
 398          assert len(header_and_shortids.prefilled_txn) >= 1
 399          assert_equal(header_and_shortids.prefilled_txn[0].index, 0)
 400  
 401          # Check that all prefilled_txn entries match what's in the block.
 402          for entry in header_and_shortids.prefilled_txn:
 403              # This checks the non-witness parts of the tx agree
 404              assert_equal(entry.tx.txid_hex, block.vtx[entry.index].txid_hex)
 405  
 406              # And this checks the witness
 407              assert_equal(entry.tx.wtxid_hex, block.vtx[entry.index].wtxid_hex)
 408  
 409          # Check that the cmpctblock message announced all the transactions.
 410          assert_equal(len(header_and_shortids.prefilled_txn) + len(header_and_shortids.shortids), len(block.vtx))
 411  
 412          # And now check that all the shortids are as expected as well.
 413          # Determine the siphash keys to use.
 414          [k0, k1] = header_and_shortids.get_siphash_keys()
 415  
 416          index = 0
 417          while index < len(block.vtx):
 418              if (len(header_and_shortids.prefilled_txn) > 0 and
 419                      header_and_shortids.prefilled_txn[0].index == index):
 420                  # Already checked prefilled transactions above
 421                  header_and_shortids.prefilled_txn.pop(0)
 422              else:
 423                  tx_hash = block.vtx[index].wtxid_int
 424                  shortid = calculate_shortid(k0, k1, tx_hash)
 425                  assert_equal(shortid, header_and_shortids.shortids[0])
 426                  header_and_shortids.shortids.pop(0)
 427              index += 1
 428  
 429      # Test that bitcoind requests compact blocks when we announce new blocks
 430      # via header or inv, and that responding to getblocktxn causes the block
 431      # to be successfully reconstructed.
 432      def test_compactblock_requests(self, test_node):
 433          node = self.nodes[0]
 434          # Try announcing a block with an inv or header, expect a compactblock
 435          # request
 436          for announce in ["inv", "header"]:
 437              block = self.build_block_on_tip(node)
 438  
 439              if announce == "inv":
 440                  test_node.send_without_ping(msg_inv([CInv(MSG_BLOCK, block.hash_int)]))
 441                  test_node.wait_for_getheaders(timeout=30)
 442                  test_node.send_header_for_blocks([block])
 443              else:
 444                  test_node.send_header_for_blocks([block])
 445              test_node.wait_for_getdata([block.hash_int], timeout=30)
 446              assert_equal(test_node.last_message["getdata"].inv[0].type, 4)
 447  
 448              # Send back a compactblock message that omits the coinbase
 449              comp_block = HeaderAndShortIDs()
 450              comp_block.header = CBlockHeader(block)
 451              comp_block.nonce = 0
 452              [k0, k1] = comp_block.get_siphash_keys()
 453              coinbase_hash = block.vtx[0].wtxid_int
 454              comp_block.shortids = [calculate_shortid(k0, k1, coinbase_hash)]
 455              test_node.clear_getblocktxn()
 456              test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
 457              assert_equal(int(node.getbestblockhash(), 16), block.hashPrevBlock)
 458              # Expect a getblocktxn message that requests the coinbase.
 459              self.getblocktxn_expected(test_node, block.hash_int, indices=[0])
 460  
 461              # Send the coinbase, and verify that the tip advances.
 462              msg = msg_blocktxn()
 463              msg.block_transactions.blockhash = block.hash_int
 464              msg.block_transactions.transactions = [block.vtx[0]]
 465              test_node.send_and_ping(msg)
 466              assert_equal(node.getbestblockhash(), block.hash_hex)
 467  
 468      # Create a chain of transactions from given utxo, and add to a new block.
 469      def build_block_with_transactions(self, node, utxo, num_transactions):
 470          block = self.build_block_on_tip(node)
 471  
 472          for _ in range(num_transactions):
 473              tx = CTransaction()
 474              tx.vin.append(CTxIn(COutPoint(utxo[0], utxo[1]), b''))
 475              tx.vout.append(CTxOut(utxo[2] - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE])))
 476              utxo = [tx.txid_int, 0, tx.vout[0].nValue]
 477              block.vtx.append(tx)
 478  
 479          block.hashMerkleRoot = block.calc_merkle_root()
 480          block.solve()
 481          return block
 482  
 483      # Test that we only receive getblocktxn requests for transactions that the
 484      # node needs, and that responding to them causes the block to be
 485      # reconstructed.
 486      def test_getblocktxn_requests(self, test_node):
 487          node = self.nodes[0]
 488  
 489          def test_getblocktxn_response(compact_block, peer, expected_result):
 490              msg = msg_cmpctblock(compact_block.to_p2p())
 491              peer.clear_getblocktxn()
 492              peer.send_and_ping(msg)
 493              self.getblocktxn_expected(peer, compact_block.header.hash_int, expected_result)
 494  
 495          def test_tip_after_message(node, peer, msg, tip):
 496              peer.send_and_ping(msg)
 497              assert_equal(int(node.getbestblockhash(), 16), tip)
 498  
 499          # First try announcing compactblocks that won't reconstruct, and verify
 500          # that we receive getblocktxn messages back.
 501          utxo = self.utxos.pop(0)
 502  
 503          block = self.build_block_with_transactions(node, utxo, 5)
 504          self.utxos.append([block.vtx[-1].txid_int, 0, block.vtx[-1].vout[0].nValue])
 505          comp_block = HeaderAndShortIDs()
 506          comp_block.initialize_from_block(block, use_witness=True)
 507  
 508          test_getblocktxn_response(comp_block, test_node, [1, 2, 3, 4, 5])
 509  
 510          msg_bt = msg_no_witness_blocktxn()
 511          msg_bt = msg_blocktxn()  # serialize with witnesses
 512          msg_bt.block_transactions = BlockTransactions(block.hash_int, block.vtx[1:])
 513          test_tip_after_message(node, test_node, msg_bt, block.hash_int)
 514  
 515          utxo = self.utxos.pop(0)
 516          block = self.build_block_with_transactions(node, utxo, 5)
 517          self.utxos.append([block.vtx[-1].txid_int, 0, block.vtx[-1].vout[0].nValue])
 518  
 519          # Now try interspersing the prefilled transactions
 520          comp_block.initialize_from_block(block, prefill_list=[0, 1, 5], use_witness=True)
 521          test_getblocktxn_response(comp_block, test_node, [2, 3, 4])
 522          msg_bt.block_transactions = BlockTransactions(block.hash_int, block.vtx[2:5])
 523          test_tip_after_message(node, test_node, msg_bt, block.hash_int)
 524  
 525          # Now try giving one transaction ahead of time.
 526          utxo = self.utxos.pop(0)
 527          block = self.build_block_with_transactions(node, utxo, 5)
 528          self.utxos.append([block.vtx[-1].txid_int, 0, block.vtx[-1].vout[0].nValue])
 529          test_node.send_and_ping(msg_tx(block.vtx[1]))
 530          assert block.vtx[1].txid_hex in node.getrawmempool()
 531  
 532          # Prefill 4 out of the 6 transactions, and verify that only the one
 533          # that was not in the mempool is requested.
 534          comp_block.initialize_from_block(block, prefill_list=[0, 2, 3, 4], use_witness=True)
 535          test_getblocktxn_response(comp_block, test_node, [5])
 536  
 537          msg_bt.block_transactions = BlockTransactions(block.hash_int, [block.vtx[5]])
 538          test_tip_after_message(node, test_node, msg_bt, block.hash_int)
 539  
 540          # Now provide all transactions to the node before the block is
 541          # announced and verify reconstruction happens immediately.
 542          utxo = self.utxos.pop(0)
 543          block = self.build_block_with_transactions(node, utxo, 10)
 544          self.utxos.append([block.vtx[-1].txid_int, 0, block.vtx[-1].vout[0].nValue])
 545          for tx in block.vtx[1:]:
 546              test_node.send_without_ping(msg_tx(tx))
 547          test_node.sync_with_ping()
 548          # Make sure all transactions were accepted.
 549          mempool = node.getrawmempool()
 550          for tx in block.vtx[1:]:
 551              assert tx.txid_hex in mempool
 552  
 553          # Clear out last request.
 554          test_node.clear_getblocktxn()
 555  
 556          # Send compact block
 557          comp_block.initialize_from_block(block, prefill_list=[0], use_witness=True)
 558          test_tip_after_message(node, test_node, msg_cmpctblock(comp_block.to_p2p()), block.hash_int)
 559          with p2p_lock:
 560              # Shouldn't have gotten a request for any transaction
 561              assert "getblocktxn" not in test_node.last_message
 562  
 563      # Incorrectly responding to a getblocktxn shouldn't cause the block to be
 564      # permanently failed.
 565      def test_incorrect_blocktxn_response(self, test_node):
 566          node = self.nodes[0]
 567          utxo = self.utxos.pop(0)
 568  
 569          block = self.build_block_with_transactions(node, utxo, 10)
 570          self.utxos.append([block.vtx[-1].txid_int, 0, block.vtx[-1].vout[0].nValue])
 571          # Relay the first 5 transactions from the block in advance
 572          for tx in block.vtx[1:6]:
 573              test_node.send_without_ping(msg_tx(tx))
 574          test_node.sync_with_ping()
 575          # Make sure all transactions were accepted.
 576          mempool = node.getrawmempool()
 577          for tx in block.vtx[1:6]:
 578              assert tx.txid_hex in mempool
 579  
 580          # Send compact block
 581          comp_block = HeaderAndShortIDs()
 582          comp_block.initialize_from_block(block, prefill_list=[0], use_witness=True)
 583          test_node.clear_getblocktxn()
 584          test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
 585          expected_indices = [6, 7, 8, 9, 10]
 586          self.getblocktxn_expected(test_node, block.hash_int, expected_indices)
 587  
 588          # Now give an incorrect response.
 589          # Note that it's possible for bitcoind to be smart enough to know we're
 590          # lying, since it could check to see if the shortid matches what we're
 591          # sending, and eg disconnect us for misbehavior.  If that behavior
 592          # change was made, we could just modify this test by having a
 593          # different peer provide the block further down, so that we're still
 594          # verifying that the block isn't marked bad permanently. This is good
 595          # enough for now.
 596          msg = msg_blocktxn()
 597          msg.block_transactions = BlockTransactions(block.hash_int, [block.vtx[5]] + block.vtx[7:])
 598          test_node.send_and_ping(msg)
 599  
 600          # Tip should not have updated
 601          assert_equal(int(node.getbestblockhash(), 16), block.hashPrevBlock)
 602  
 603          # We should receive a getdata request
 604          test_node.wait_for_getdata([block.hash_int], timeout=10)
 605          assert test_node.last_message["getdata"].inv[0].type in (MSG_BLOCK, MSG_BLOCK | MSG_WITNESS_FLAG)
 606  
 607          # Deliver the block
 608          test_node.send_and_ping(msg_block(block))
 609          assert_equal(node.getbestblockhash(), block.hash_hex)
 610  
 611      # Multiple blocktxn responses will cause a node to get disconnected.
 612      def test_multiple_blocktxn_response(self, test_node):
 613          node = self.nodes[0]
 614          utxo = self.utxos.pop(0)
 615  
 616          block = self.build_block_with_transactions(node, utxo, 2)
 617  
 618          # The attacker sends the block header so that we request it.
 619          test_node.send_without_ping(msg_headers([block]))
 620          test_node.wait_for_getdata([block.hash_int], timeout=30)
 621  
 622          # Send compact block
 623          comp_block = HeaderAndShortIDs()
 624          comp_block.initialize_from_block(block, prefill_list=[0], use_witness=True)
 625          test_node.clear_getblocktxn()
 626          test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
 627          self.getblocktxn_expected(test_node, block.hash_int, indices=[1,2])
 628  
 629          # Send a blocktxn that does not succeed in reconstruction, triggering
 630          # getdata fallback.
 631          msg = msg_blocktxn()
 632          msg.block_transactions = BlockTransactions(block.hash_int, [block.vtx[2]] + [block.vtx[1]])
 633          test_node.send_and_ping(msg)
 634  
 635          # Tip should not have updated
 636          assert_equal(int(node.getbestblockhash(), 16), block.hashPrevBlock)
 637  
 638          # We should receive a getdata request
 639          test_node.wait_for_getdata([block.hash_int], timeout=10)
 640          assert test_node.last_message["getdata"].inv[0].type in (MSG_BLOCK, MSG_BLOCK | MSG_WITNESS_FLAG)
 641  
 642          # Send the same blocktxn and assert the sender gets disconnected.
 643          with node.assert_debug_log(['previous compact block reconstruction attempt failed']):
 644              test_node.send_without_ping(msg)
 645              test_node.wait_for_disconnect()
 646  
 647      def test_getblocktxn_handler(self, test_node):
 648          node = self.nodes[0]
 649          # bitcoind will not send blocktxn responses for blocks whose height is
 650          # more than 10 blocks deep.
 651          MAX_GETBLOCKTXN_DEPTH = 10
 652          chain_height = node.getblockcount()
 653          current_height = chain_height
 654          while (current_height >= chain_height - MAX_GETBLOCKTXN_DEPTH):
 655              block_hash = node.getblockhash(current_height)
 656              block = from_hex(CBlock(), node.getblock(block_hash, False))
 657  
 658              msg = msg_getblocktxn()
 659              msg.block_txn_request = BlockTransactionsRequest(int(block_hash, 16), [])
 660              num_to_request = random.randint(1, len(block.vtx))
 661              msg.block_txn_request.from_absolute(sorted(random.sample(range(len(block.vtx)), num_to_request)))
 662              test_node.send_without_ping(msg)
 663              test_node.wait_until(lambda: "blocktxn" in test_node.last_message, timeout=10)
 664  
 665              with p2p_lock:
 666                  assert_equal(test_node.last_message["blocktxn"].block_transactions.blockhash, int(block_hash, 16))
 667                  all_indices = msg.block_txn_request.to_absolute()
 668                  for index in all_indices:
 669                      tx = test_node.last_message["blocktxn"].block_transactions.transactions.pop(0)
 670                      assert_equal(tx.txid_hex, block.vtx[index].txid_hex)
 671                      # Check that the witness matches
 672                      assert_equal(tx.wtxid_hex, block.vtx[index].wtxid_hex)
 673                  test_node.last_message.pop("blocktxn", None)
 674              current_height -= 1
 675  
 676          # Next request should send a full block response, as we're past the
 677          # allowed depth for a blocktxn response.
 678          block_hash = node.getblockhash(current_height)
 679          msg.block_txn_request = BlockTransactionsRequest(int(block_hash, 16), [0])
 680          with p2p_lock:
 681              test_node.last_message.pop("block", None)
 682              test_node.last_message.pop("blocktxn", None)
 683          test_node.send_and_ping(msg)
 684          with p2p_lock:
 685              assert_equal(test_node.last_message["block"].block.hash_hex, block_hash)
 686              assert "blocktxn" not in test_node.last_message
 687  
 688          # Request with out-of-bounds tx index results in disconnect
 689          bad_peer = self.nodes[0].add_p2p_connection(TestP2PConn())
 690          block_hash = node.getblockhash(chain_height)
 691          block = from_hex(CBlock(), node.getblock(block_hash, False))
 692          msg.block_txn_request = BlockTransactionsRequest(int(block_hash, 16), [len(block.vtx)])
 693          with node.assert_debug_log(['getblocktxn with out-of-bounds tx indices']):
 694              bad_peer.send_without_ping(msg)
 695              bad_peer.wait_for_disconnect()
 696  
 697      def test_low_work_compactblocks(self, test_node):
 698          # A compactblock with insufficient work won't get its header included
 699          node = self.nodes[0]
 700          hashPrevBlock = int(node.getblockhash(node.getblockcount() - 150), 16)
 701          block = self.build_block_on_tip(node)
 702          block.hashPrevBlock = hashPrevBlock
 703          block.solve()
 704  
 705          comp_block = HeaderAndShortIDs()
 706          comp_block.initialize_from_block(block)
 707          with self.nodes[0].assert_debug_log(['[net] Ignoring low-work compact block from peer 0']):
 708              test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
 709  
 710          tips = node.getchaintips()
 711          found = False
 712          for x in tips:
 713              if x["hash"] == block.hash_hex:
 714                  found = True
 715                  break
 716          assert not found
 717  
 718      def test_compactblocks_not_at_tip(self, test_node):
 719          node = self.nodes[0]
 720          # Test that requesting old compactblocks doesn't work.
 721          MAX_CMPCTBLOCK_DEPTH = 5
 722          new_blocks = []
 723          for _ in range(MAX_CMPCTBLOCK_DEPTH + 1):
 724              test_node.clear_block_announcement()
 725              new_blocks.append(self.generate(node, 1)[0])
 726              test_node.wait_until(test_node.received_block_announcement, timeout=30)
 727  
 728          test_node.clear_block_announcement()
 729          test_node.send_without_ping(msg_getdata([CInv(MSG_CMPCT_BLOCK, int(new_blocks[0], 16))]))
 730          test_node.wait_until(lambda: "cmpctblock" in test_node.last_message, timeout=30)
 731  
 732          test_node.clear_block_announcement()
 733          self.generate(node, 1)
 734          test_node.wait_until(test_node.received_block_announcement, timeout=30)
 735          test_node.clear_block_announcement()
 736          with p2p_lock:
 737              test_node.last_message.pop("block", None)
 738          test_node.send_without_ping(msg_getdata([CInv(MSG_CMPCT_BLOCK, int(new_blocks[0], 16))]))
 739          test_node.wait_until(lambda: "block" in test_node.last_message, timeout=30)
 740          with p2p_lock:
 741              assert_equal(test_node.last_message["block"].block.hash_hex, new_blocks[0])
 742  
 743          # Generate an old compactblock, and verify that it's not accepted.
 744          cur_height = node.getblockcount()
 745          hashPrevBlock = int(node.getblockhash(cur_height - 5), 16)
 746          block = self.build_block_on_tip(node)
 747          block.hashPrevBlock = hashPrevBlock
 748          block.solve()
 749  
 750          comp_block = HeaderAndShortIDs()
 751          comp_block.initialize_from_block(block)
 752          test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
 753  
 754          tips = node.getchaintips()
 755          found = False
 756          for x in tips:
 757              if x["hash"] == block.hash_hex:
 758                  assert_equal(x["status"], "headers-only")
 759                  found = True
 760                  break
 761          assert found
 762  
 763          # Requesting this block via getblocktxn should silently fail
 764          # (to avoid fingerprinting attacks).
 765          msg = msg_getblocktxn()
 766          msg.block_txn_request = BlockTransactionsRequest(block.hash_int, [0])
 767          with p2p_lock:
 768              test_node.last_message.pop("blocktxn", None)
 769          test_node.send_and_ping(msg)
 770          with p2p_lock:
 771              assert "blocktxn" not in test_node.last_message
 772  
 773      def test_end_to_end_block_relay(self, listeners):
 774          node = self.nodes[0]
 775          utxo = self.utxos.pop(0)
 776  
 777          block = self.build_block_with_transactions(node, utxo, 10)
 778  
 779          [l.clear_block_announcement() for l in listeners]
 780  
 781          # serialize without witness (this block has no witnesses anyway).
 782          # TODO: repeat this test with witness tx's to a segwit node.
 783          node.submitblock(block.serialize().hex())
 784  
 785          for l in listeners:
 786              l.wait_until(lambda: "cmpctblock" in l.last_message, timeout=30)
 787          with p2p_lock:
 788              for l in listeners:
 789                  assert_equal(l.last_message["cmpctblock"].header_and_shortids.header.hash_int, block.hash_int)
 790  
 791      # Test that we don't get disconnected if we relay a compact block with valid header,
 792      # but invalid transactions.
 793      def test_invalid_tx_in_compactblock(self, test_node):
 794          node = self.nodes[0]
 795          utxo = self.utxos.pop(0)
 796  
 797          block = self.build_block_with_transactions(node, utxo, 5)
 798          block.hashMerkleRoot = block.calc_merkle_root()
 799          # Drop the coinbase witness but include the witness commitment.
 800          add_witness_commitment(block)
 801          block.vtx[0].wit.vtxinwit = []
 802          block.solve()
 803  
 804          # Now send the compact block with all transactions prefilled, and
 805          # verify that we don't get disconnected.
 806          comp_block = HeaderAndShortIDs()
 807          comp_block.initialize_from_block(block, prefill_list=list(range(len(block.vtx))), use_witness=True)
 808          msg = msg_cmpctblock(comp_block.to_p2p())
 809          test_node.send_and_ping(msg)
 810  
 811          # Check that the tip didn't advance
 812          assert_not_equal(node.getbestblockhash(), block.hash_hex)
 813          test_node.sync_with_ping()
 814  
 815          # Re-establish a proper witness commitment with the coinbase witness, but
 816          # invalidate the last tx in the block.
 817          block.vtx[4].vin[0].scriptSig = CScript([OP_RETURN])
 818          block.hashMerkleRoot = block.calc_merkle_root()
 819          add_witness_commitment(block)
 820          block.solve()
 821  
 822          # This will lead to a consensus failure for which we also won't be disconnected but which
 823          # will be cached.
 824          comp_block.initialize_from_block(block, prefill_list=list(range(len(block.vtx))), use_witness=True)
 825          msg = msg_cmpctblock(comp_block.to_p2p())
 826          test_node.send_and_ping(msg)
 827  
 828          # The tip still didn't advance.
 829          assert_not_equal(node.getbestblockhash(), block.hash_hex)
 830          test_node.sync_with_ping()
 831  
 832          # The failure above was cached. Submitting the compact block again will return a cached
 833          # consensus error (the code path is different) and still not get us disconnected (nor
 834          # advance the tip).
 835          test_node.send_and_ping(msg)
 836          assert_not_equal(node.getbestblockhash(), block.hash_hex)
 837          test_node.sync_with_ping()
 838  
 839          # Now, announcing a second block building on top of the invalid one will get us disconnected.
 840          block.hashPrevBlock = block.hash_int
 841          block.solve()
 842          comp_block.initialize_from_block(block, prefill_list=list(range(len(block.vtx))), use_witness=True)
 843          msg = msg_cmpctblock(comp_block.to_p2p())
 844          test_node.send_await_disconnect(msg)
 845  
 846      # peer generates a block and sends it to node, which makes the peer a
 847      # candidate for high-bandwidth 'to' (up to 3 peers according to BIP 152)
 848      def make_peer_hb_to_candidate(self, node, peer):
 849          block = self.build_block_on_tip(node)
 850          peer.send_and_ping(msg_block(block))
 851  
 852      # Helper for enabling cb announcements
 853      # Send the sendcmpct request and sync headers
 854      def request_cb_announcements(self, peer):
 855          node = self.nodes[0]
 856          tip = node.getbestblockhash()
 857          peer.get_headers(locator=[int(tip, 16)], hashstop=0)
 858          peer.send_and_ping(msg_sendcmpct(announce=True, version=2))
 859  
 860      def test_compactblock_reconstruction_stalling_peer(self, stalling_peer, delivery_peer):
 861          node = self.nodes[0]
 862  
 863          self.make_peer_hb_to_candidate(node, delivery_peer)
 864          block, cmpct_block = self.announce_cmpct_block(node, stalling_peer)
 865  
 866          for tx in block.vtx[1:]:
 867              delivery_peer.send_without_ping(msg_tx(tx))
 868          delivery_peer.sync_with_ping()
 869          mempool = node.getrawmempool()
 870          for tx in block.vtx[1:]:
 871              assert tx.txid_hex in mempool
 872  
 873          delivery_peer.send_and_ping(msg_cmpctblock(cmpct_block.to_p2p()))
 874          assert_equal(node.getbestblockhash(), block.hash_hex)
 875  
 876          self.utxos.append([block.vtx[-1].txid_int, 0, block.vtx[-1].vout[0].nValue])
 877  
 878          # Now test that delivering an invalid compact block won't break relay
 879  
 880          block, cmpct_block = self.announce_cmpct_block(node, stalling_peer)
 881          for tx in block.vtx[1:]:
 882              delivery_peer.send_without_ping(msg_tx(tx))
 883          delivery_peer.sync_with_ping()
 884  
 885          cmpct_block.prefilled_txn[0].tx.wit.vtxinwit = [CTxInWitness()]
 886          cmpct_block.prefilled_txn[0].tx.wit.vtxinwit[0].scriptWitness.stack = [ser_uint256(0)]
 887  
 888          cmpct_block.use_witness = True
 889          delivery_peer.send_and_ping(msg_cmpctblock(cmpct_block.to_p2p()))
 890          assert_not_equal(node.getbestblockhash(), block.hash_hex)
 891  
 892          msg = msg_no_witness_blocktxn()
 893          msg.block_transactions.blockhash = block.hash_int
 894          msg.block_transactions.transactions = block.vtx[1:]
 895          stalling_peer.send_and_ping(msg)
 896          assert_equal(node.getbestblockhash(), block.hash_hex)
 897  
 898      # assert the RPC getpeerinfo boolean fields `bip152_hb_{to, from}`
 899      # match the given parameters for the peer at idx of a given node
 900      @staticmethod
 901      def assert_highbandwidth_states(node, idx, hb_to, hb_from):
 902          peerinfo = node.getpeerinfo()[idx]
 903          assert_equal(peerinfo['bip152_hb_to'], hb_to)
 904          assert_equal(peerinfo['bip152_hb_from'], hb_from)
 905  
 906      def test_highbandwidth_mode_states_via_getpeerinfo(self):
 907          # create new p2p connection for a fresh state w/o any prior sendcmpct messages sent
 908          hb_test_node = self.nodes[0].add_p2p_connection(TestP2PConn())
 909  
 910          # Newly created hb_test_node is the last connection.
 911          hb_test_node_idx = -1
 912  
 913          # initially, neither node has selected the other peer as high-bandwidth yet
 914          self.assert_highbandwidth_states(self.nodes[0], hb_test_node_idx, hb_to=False, hb_from=False)
 915  
 916          # peer requests high-bandwidth mode by sending sendcmpct(1)
 917          hb_test_node.send_and_ping(msg_sendcmpct(announce=True, version=2))
 918          self.assert_highbandwidth_states(self.nodes[0], hb_test_node_idx, hb_to=False, hb_from=True)
 919  
 920          # peer generates a block and sends it to node, which should
 921          # select the peer as high-bandwidth (up to 3 peers according to BIP 152)
 922          block = self.build_block_on_tip(self.nodes[0])
 923          hb_test_node.send_and_ping(msg_block(block))
 924          self.assert_highbandwidth_states(self.nodes[0], hb_test_node_idx, hb_to=True, hb_from=True)
 925  
 926          # peer requests low-bandwidth mode by sending sendcmpct(0)
 927          hb_test_node.send_and_ping(msg_sendcmpct(announce=False, version=2))
 928          self.assert_highbandwidth_states(self.nodes[0], hb_test_node_idx, hb_to=True, hb_from=False)
 929  
 930      def test_compactblock_reconstruction_parallel_reconstruction(self, stalling_peer, delivery_peer, inbound_peer, outbound_peer):
 931          """ All p2p connections are inbound except outbound_peer. We test that ultimate parallel slot
 932              can only be taken by an outbound node unless prior attempts were done by an outbound
 933          """
 934          node = self.nodes[0]
 935  
 936          for name, peer in [("delivery", delivery_peer), ("inbound", inbound_peer), ("outbound", outbound_peer)]:
 937              self.log.info(f"Setting {name} as high bandwidth peer")
 938              self.make_peer_hb_to_candidate(node, peer)
 939              block, cmpct_block = self.announce_cmpct_block(node, peer, 1)
 940              msg = msg_blocktxn()
 941              msg.block_transactions.blockhash = block.hash_int
 942              msg.block_transactions.transactions = block.vtx[1:]
 943              peer.send_and_ping(msg)
 944              assert_equal(node.getbestblockhash(), block.hash_hex)
 945              peer.clear_getblocktxn()
 946  
 947          # Test the simple parallel download case...
 948          for num_missing in [1, 5, 20]:
 949              delivery_peer.clear_getblocktxn()
 950              inbound_peer.clear_getblocktxn()
 951              outbound_peer.clear_getblocktxn()
 952  
 953              # Remaining low-bandwidth peer is stalling_peer, who announces first
 954              assert_equal([peer['bip152_hb_to'] for peer in node.getpeerinfo()], [False, True, True, True])
 955  
 956              block, cmpct_block = self.announce_cmpct_block(node, stalling_peer, num_missing, solicit=True)
 957  
 958              delivery_peer.send_and_ping(msg_cmpctblock(cmpct_block.to_p2p()))
 959              # The second peer to announce should still get a getblocktxn
 960              self.getblocktxn_expected(delivery_peer, block.hash_int)
 961  
 962              inbound_peer.send_and_ping(msg_cmpctblock(cmpct_block.to_p2p()))
 963              with p2p_lock:
 964                  # The third inbound peer to announce should *not* get a getblocktxn
 965                  assert "getblocktxn" not in inbound_peer.last_message
 966              assert_not_equal(node.getbestblockhash(), block.hash_hex)
 967  
 968              outbound_peer.send_and_ping(msg_cmpctblock(cmpct_block.to_p2p()))
 969              # The third peer to announce should get a getblocktxn if outbound
 970              self.getblocktxn_expected(outbound_peer, block.hash_int)
 971  
 972              # Second peer completes the compact block first
 973              msg = msg_blocktxn()
 974              msg.block_transactions.blockhash = block.hash_int
 975              msg.block_transactions.transactions = block.vtx[1:]
 976              delivery_peer.send_and_ping(msg)
 977              assert_equal(node.getbestblockhash(), block.hash_hex)
 978  
 979              # Nothing bad should happen if we get a late fill from the first peer...
 980              stalling_peer.send_and_ping(msg)
 981              self.utxos.append([block.vtx[-1].txid_int, 0, block.vtx[-1].vout[0].nValue])
 982  
 983      def test_compact_blocks_ignored(self):
 984          node = self.nodes[0]
 985  
 986          def build_compact_block():
 987              # generate a compact block to send that will force a GETBLOCKTXN
 988              utxo = self.utxos.pop(0)
 989              block = self.build_block_with_transactions(node, utxo, 10)
 990              cmpct_block = HeaderAndShortIDs()
 991              cmpct_block.initialize_from_block(block)
 992              msg = msg_cmpctblock(cmpct_block.to_p2p())
 993              return block, msg
 994  
 995          # Sends a compact block, uses presence of GETBLOCKTXN as a heuristic
 996          # for whether or not the node processed the CMPCTBLOCK.
 997          # Note: since we never fulfill the GETBLOCKTXN, this will never change
 998          # the HB status of a connection.
 999          def ignores_compact_block(conn, solicited=False):
1000              block, msg = build_compact_block()
1001              conn.clear_getblocktxn()
1002              if solicited:
1003                  conn.send_without_ping(msg_headers([block]))
1004                  conn.wait_for_getdata([block.hash_int], timeout=30)
1005              conn.send_and_ping(msg)
1006              with p2p_lock:
1007                  return "getblocktxn" not in conn.last_message
1008  
1009          # create new p2p connection for a fresh state w/o any prior sendcmpct messages sent
1010          unsolicited_peer = self.nodes[0].add_p2p_connection(TestP2PConn())
1011          self.assert_highbandwidth_states(node, idx=-1, hb_to=False, hb_from=False)
1012  
1013          self.log.info("Test that a node ignores unsolicited CMPCTBLOCK messages from peers that have not sent SENDCMPCT.")
1014          assert ignores_compact_block(unsolicited_peer, solicited=False)
1015  
1016          self.log.info("Test that a node ignores solicited CMPCTBLOCK messages from peers that have not sent SENDCMPCT.")
1017          assert ignores_compact_block(unsolicited_peer, solicited=True)
1018  
1019          # Unsolicited peer announces CMPCTBLOCK support with SENDCMPCT message,
1020          # but still non-HB.
1021          unsolicited_peer.send_and_ping(msg_sendcmpct())
1022          self.assert_highbandwidth_states(node, idx=-1, hb_to=False, hb_from=False)
1023  
1024          self.log.info("Test that a node ignores unsolicited CMPCTBLOCK messages from non-HB peers.")
1025          assert ignores_compact_block(unsolicited_peer, solicited=False)
1026          self.assert_highbandwidth_states(node, idx=-1, hb_to=False, hb_from=False)
1027  
1028          self.log.info("Test that a node does not ignore solicited CMPCTBLOCK messages from non-HB peers.")
1029          assert not ignores_compact_block(unsolicited_peer, solicited=True)
1030          self.assert_highbandwidth_states(node, idx=-1, hb_to=False, hb_from=False)
1031  
1032          # The node will ask for transactions from an unsolicited compact block
1033          # it receives from a high bandwidth peer, we need to use one set up earlier,
1034          # since all the slots are full.
1035          self.log.info("Test that a node does not ignore unsolicited CMPCTBLOCK messages from HB peers.")
1036  
1037          hb_peer_idx = -2
1038          self.assert_highbandwidth_states(node, idx=hb_peer_idx, hb_to=True, hb_from=False)
1039          hb_peer = self.nodes[0].p2ps[hb_peer_idx]
1040          assert not ignores_compact_block(hb_peer, solicited=False)
1041  
1042      def run_test(self):
1043          self.wallet = MiniWallet(self.nodes[0])
1044  
1045          # Setup the p2p connections
1046          self.segwit_node = self.nodes[0].add_p2p_connection(TestP2PConn())
1047          self.additional_segwit_node = self.nodes[0].add_p2p_connection(TestP2PConn())
1048          self.onemore_inbound_node = self.nodes[0].add_p2p_connection(TestP2PConn())
1049          self.outbound_node = self.nodes[0].add_outbound_p2p_connection(TestP2PConn(), p2p_idx=3, connection_type="outbound-full-relay")
1050  
1051          # We will need UTXOs to construct transactions in later tests.
1052          self.make_utxos()
1053  
1054          assert softfork_active(self.nodes[0], "segwit")
1055  
1056          self.log.info("Testing SENDCMPCT p2p message... ")
1057          self.test_sendcmpct(self.segwit_node)
1058          self.test_sendcmpct(self.additional_segwit_node)
1059          self.test_sendcmpct(self.onemore_inbound_node)
1060          self.test_sendcmpct(self.outbound_node)
1061  
1062          self.log.info("Testing compactblock construction...")
1063          self.test_compactblock_construction(self.segwit_node)
1064  
1065          self.log.info("Testing compactblock requests (segwit node)... ")
1066          self.test_compactblock_requests(self.segwit_node)
1067  
1068          self.log.info("Testing getblocktxn requests (segwit node)...")
1069          self.test_getblocktxn_requests(self.segwit_node)
1070  
1071          self.log.info("Testing getblocktxn handler (segwit node should return witnesses)...")
1072          self.test_getblocktxn_handler(self.segwit_node)
1073  
1074          self.log.info("Testing compactblock requests/announcements not at chain tip...")
1075          self.test_compactblocks_not_at_tip(self.segwit_node)
1076  
1077          self.log.info("Testing handling of low-work compact blocks...")
1078          self.test_low_work_compactblocks(self.segwit_node)
1079  
1080          self.log.info("Testing handling of incorrect blocktxn responses...")
1081          self.test_incorrect_blocktxn_response(self.segwit_node)
1082  
1083          self.log.info("Testing reconstructing compact blocks with a stalling peer...")
1084          self.test_compactblock_reconstruction_stalling_peer(self.segwit_node, self.additional_segwit_node)
1085  
1086          self.log.info("Testing reconstructing compact blocks from multiple peers...")
1087          self.test_compactblock_reconstruction_parallel_reconstruction(stalling_peer=self.segwit_node, inbound_peer=self.onemore_inbound_node, delivery_peer=self.additional_segwit_node, outbound_peer=self.outbound_node)
1088  
1089          # Test that if we submitblock to node1, we'll get a compact block
1090          # announcement to all peers.
1091          # (Post-segwit activation, blocks won't propagate from node0 to node1
1092          # automatically, so don't bother testing a block announced to node0.)
1093          self.log.info("Testing end-to-end block relay...")
1094          self.request_cb_announcements(self.segwit_node)
1095          self.request_cb_announcements(self.additional_segwit_node)
1096          self.test_end_to_end_block_relay([self.segwit_node, self.additional_segwit_node])
1097  
1098          self.log.info("Testing handling of invalid compact blocks...")
1099          self.test_invalid_tx_in_compactblock(self.segwit_node)
1100  
1101          # The previous test will lead to a disconnection. Reconnect before continuing.
1102          self.segwit_node = self.nodes[0].add_p2p_connection(TestP2PConn())
1103          self.segwit_node.send_and_ping(msg_sendcmpct())
1104  
1105          self.log.info("Testing handling of multiple blocktxn responses...")
1106          self.test_multiple_blocktxn_response(self.segwit_node)
1107  
1108          # The previous test will lead to a disconnection. Reconnect before continuing.
1109          self.segwit_node = self.nodes[0].add_p2p_connection(TestP2PConn())
1110          self.segwit_node.send_and_ping(msg_sendcmpct())
1111  
1112          self.log.info("Testing invalid announce field in sendcmpct message...")
1113          self.test_invalid_sendcmpct_announce()
1114  
1115          self.log.info("Testing invalid index in cmpctblock message...")
1116          self.test_invalid_cmpctblock_message()
1117  
1118          self.log.info("Testing high-bandwidth mode states via getpeerinfo...")
1119          self.test_highbandwidth_mode_states_via_getpeerinfo()
1120  
1121          self.log.info("Testing CMPCTBLOCK messages are ignored when expected...")
1122          self.test_compact_blocks_ignored()
1123  
1124  
1125  if __name__ == '__main__':
1126      CompactBlocksTest(__file__).main()
1127