You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

p2p-compactblocks.py 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  1. #!/usr/bin/env python3
  2. # Copyright (c) 2016 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. from test_framework.mininode import *
  6. from test_framework.test_framework import BitcoinTestFramework
  7. from test_framework.util import *
  8. from test_framework.blocktools import create_block, create_coinbase
  9. from test_framework.siphash import siphash256
  10. from test_framework.script import CScript, OP_TRUE
  11. '''
  12. CompactBlocksTest -- test compact blocks (BIP 152)
  13. '''
  14. # TestNode: A peer we use to send messages to bitcoind, and store responses.
  15. class TestNode(SingleNodeConnCB):
  16. def __init__(self):
  17. SingleNodeConnCB.__init__(self)
  18. self.last_sendcmpct = None
  19. self.last_headers = None
  20. self.last_inv = None
  21. self.last_cmpctblock = None
  22. self.block_announced = False
  23. self.last_getdata = None
  24. self.last_getblocktxn = None
  25. self.last_block = None
  26. self.last_blocktxn = None
  27. def on_sendcmpct(self, conn, message):
  28. self.last_sendcmpct = message
  29. def on_block(self, conn, message):
  30. self.last_block = message
  31. def on_cmpctblock(self, conn, message):
  32. self.last_cmpctblock = message
  33. self.block_announced = True
  34. def on_headers(self, conn, message):
  35. self.last_headers = message
  36. self.block_announced = True
  37. def on_inv(self, conn, message):
  38. self.last_inv = message
  39. self.block_announced = True
  40. def on_getdata(self, conn, message):
  41. self.last_getdata = message
  42. def on_getblocktxn(self, conn, message):
  43. self.last_getblocktxn = message
  44. def on_blocktxn(self, conn, message):
  45. self.last_blocktxn = message
  46. # Requires caller to hold mininode_lock
  47. def received_block_announcement(self):
  48. return self.block_announced
  49. def clear_block_announcement(self):
  50. with mininode_lock:
  51. self.block_announced = False
  52. self.last_inv = None
  53. self.last_headers = None
  54. self.last_cmpctblock = None
  55. def get_headers(self, locator, hashstop):
  56. msg = msg_getheaders()
  57. msg.locator.vHave = locator
  58. msg.hashstop = hashstop
  59. self.connection.send_message(msg)
  60. def send_header_for_blocks(self, new_blocks):
  61. headers_message = msg_headers()
  62. headers_message.headers = [CBlockHeader(b) for b in new_blocks]
  63. self.send_message(headers_message)
  64. def request_headers_and_sync(self, locator, hashstop=0):
  65. self.clear_block_announcement()
  66. self.get_headers(locator, hashstop)
  67. assert(wait_until(self.received_block_announcement, timeout=30))
  68. assert(self.received_block_announcement())
  69. self.clear_block_announcement()
  70. class CompactBlocksTest(BitcoinTestFramework):
  71. def __init__(self):
  72. super().__init__()
  73. self.setup_clean_chain = True
  74. self.num_nodes = 1
  75. self.utxos = []
  76. def setup_network(self):
  77. self.nodes = []
  78. # Turn off segwit in this test, as compact blocks don't currently work
  79. # with segwit. (After BIP 152 is updated to support segwit, we can
  80. # test behavior with and without segwit enabled by adding a second node
  81. # to the test.)
  82. self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, [["-debug", "-logtimemicros=1", "-bip9params=segwit:0:0"]])
  83. def build_block_on_tip(self):
  84. height = self.nodes[0].getblockcount()
  85. tip = self.nodes[0].getbestblockhash()
  86. mtp = self.nodes[0].getblockheader(tip)['mediantime']
  87. block = create_block(int(tip, 16), create_coinbase(height + 1), mtp + 1)
  88. block.solve()
  89. return block
  90. # Create 10 more anyone-can-spend utxo's for testing.
  91. def make_utxos(self):
  92. block = self.build_block_on_tip()
  93. self.test_node.send_and_ping(msg_block(block))
  94. assert(int(self.nodes[0].getbestblockhash(), 16) == block.sha256)
  95. self.nodes[0].generate(100)
  96. total_value = block.vtx[0].vout[0].nValue
  97. out_value = total_value // 10
  98. tx = CTransaction()
  99. tx.vin.append(CTxIn(COutPoint(block.vtx[0].sha256, 0), b''))
  100. for i in range(10):
  101. tx.vout.append(CTxOut(out_value, CScript([OP_TRUE])))
  102. tx.rehash()
  103. block2 = self.build_block_on_tip()
  104. block2.vtx.append(tx)
  105. block2.hashMerkleRoot = block2.calc_merkle_root()
  106. block2.solve()
  107. self.test_node.send_and_ping(msg_block(block2))
  108. assert_equal(int(self.nodes[0].getbestblockhash(), 16), block2.sha256)
  109. self.utxos.extend([[tx.sha256, i, out_value] for i in range(10)])
  110. return
  111. # Test "sendcmpct":
  112. # - No compact block announcements or getdata(MSG_CMPCT_BLOCK) unless
  113. # sendcmpct is sent.
  114. # - If sendcmpct is sent with version > 1, the message is ignored.
  115. # - If sendcmpct is sent with boolean 0, then block announcements are not
  116. # made with compact blocks.
  117. # - If sendcmpct is then sent with boolean 1, then new block announcements
  118. # are made with compact blocks.
  119. def test_sendcmpct(self):
  120. print("Testing SENDCMPCT p2p message... ")
  121. # Make sure we get a version 0 SENDCMPCT message from our peer
  122. def received_sendcmpct():
  123. return (self.test_node.last_sendcmpct is not None)
  124. got_message = wait_until(received_sendcmpct, timeout=30)
  125. assert(received_sendcmpct())
  126. assert(got_message)
  127. assert_equal(self.test_node.last_sendcmpct.version, 1)
  128. tip = int(self.nodes[0].getbestblockhash(), 16)
  129. def check_announcement_of_new_block(node, peer, predicate):
  130. peer.clear_block_announcement()
  131. node.generate(1)
  132. got_message = wait_until(lambda: peer.block_announced, timeout=30)
  133. assert(peer.block_announced)
  134. assert(got_message)
  135. with mininode_lock:
  136. assert(predicate(peer))
  137. # We shouldn't get any block announcements via cmpctblock yet.
  138. check_announcement_of_new_block(self.nodes[0], self.test_node, lambda p: p.last_cmpctblock is None)
  139. # Try one more time, this time after requesting headers.
  140. self.test_node.request_headers_and_sync(locator=[tip])
  141. check_announcement_of_new_block(self.nodes[0], self.test_node, lambda p: p.last_cmpctblock is None and p.last_inv is not None)
  142. # Test a few ways of using sendcmpct that should NOT
  143. # result in compact block announcements.
  144. # Before each test, sync the headers chain.
  145. self.test_node.request_headers_and_sync(locator=[tip])
  146. # Now try a SENDCMPCT message with too-high version
  147. sendcmpct = msg_sendcmpct()
  148. sendcmpct.version = 2
  149. self.test_node.send_and_ping(sendcmpct)
  150. check_announcement_of_new_block(self.nodes[0], self.test_node, lambda p: p.last_cmpctblock is None)
  151. # Headers sync before next test.
  152. self.test_node.request_headers_and_sync(locator=[tip])
  153. # Now try a SENDCMPCT message with valid version, but announce=False
  154. self.test_node.send_and_ping(msg_sendcmpct())
  155. check_announcement_of_new_block(self.nodes[0], self.test_node, lambda p: p.last_cmpctblock is None)
  156. # Headers sync before next test.
  157. self.test_node.request_headers_and_sync(locator=[tip])
  158. # Finally, try a SENDCMPCT message with announce=True
  159. sendcmpct.version = 1
  160. sendcmpct.announce = True
  161. self.test_node.send_and_ping(sendcmpct)
  162. check_announcement_of_new_block(self.nodes[0], self.test_node, lambda p: p.last_cmpctblock is not None)
  163. # Try one more time (no headers sync should be needed!)
  164. check_announcement_of_new_block(self.nodes[0], self.test_node, lambda p: p.last_cmpctblock is not None)
  165. # Try one more time, after turning on sendheaders
  166. self.test_node.send_and_ping(msg_sendheaders())
  167. check_announcement_of_new_block(self.nodes[0], self.test_node, lambda p: p.last_cmpctblock is not None)
  168. # Now turn off announcements
  169. sendcmpct.announce = False
  170. self.test_node.send_and_ping(sendcmpct)
  171. check_announcement_of_new_block(self.nodes[0], self.test_node, lambda p: p.last_cmpctblock is None and p.last_headers is not None)
  172. # This test actually causes bitcoind to (reasonably!) disconnect us, so do this last.
  173. def test_invalid_cmpctblock_message(self):
  174. print("Testing invalid index in cmpctblock message...")
  175. self.nodes[0].generate(101)
  176. block = self.build_block_on_tip()
  177. cmpct_block = P2PHeaderAndShortIDs()
  178. cmpct_block.header = CBlockHeader(block)
  179. cmpct_block.prefilled_txn_length = 1
  180. # This index will be too high
  181. prefilled_txn = PrefilledTransaction(1, block.vtx[0])
  182. cmpct_block.prefilled_txn = [prefilled_txn]
  183. self.test_node.send_and_ping(msg_cmpctblock(cmpct_block))
  184. assert(int(self.nodes[0].getbestblockhash(), 16) == block.hashPrevBlock)
  185. # Compare the generated shortids to what we expect based on BIP 152, given
  186. # bitcoind's choice of nonce.
  187. def test_compactblock_construction(self):
  188. print("Testing compactblock headers and shortIDs are correct...")
  189. # Generate a bunch of transactions.
  190. self.nodes[0].generate(101)
  191. num_transactions = 25
  192. address = self.nodes[0].getnewaddress()
  193. for i in range(num_transactions):
  194. self.nodes[0].sendtoaddress(address, 0.1)
  195. self.test_node.sync_with_ping()
  196. # Now mine a block, and look at the resulting compact block.
  197. self.test_node.clear_block_announcement()
  198. block_hash = int(self.nodes[0].generate(1)[0], 16)
  199. # Store the raw block in our internal format.
  200. block = FromHex(CBlock(), self.nodes[0].getblock("%02x" % block_hash, False))
  201. [tx.calc_sha256() for tx in block.vtx]
  202. block.rehash()
  203. # Don't care which type of announcement came back for this test; just
  204. # request the compact block if we didn't get one yet.
  205. wait_until(self.test_node.received_block_announcement, timeout=30)
  206. with mininode_lock:
  207. if self.test_node.last_cmpctblock is None:
  208. self.test_node.clear_block_announcement()
  209. inv = CInv(4, block_hash) # 4 == "CompactBlock"
  210. self.test_node.send_message(msg_getdata([inv]))
  211. wait_until(self.test_node.received_block_announcement, timeout=30)
  212. # Now we should have the compactblock
  213. header_and_shortids = None
  214. with mininode_lock:
  215. assert(self.test_node.last_cmpctblock is not None)
  216. # Convert the on-the-wire representation to absolute indexes
  217. header_and_shortids = HeaderAndShortIDs(self.test_node.last_cmpctblock.header_and_shortids)
  218. # Check that we got the right block!
  219. header_and_shortids.header.calc_sha256()
  220. assert_equal(header_and_shortids.header.sha256, block_hash)
  221. # Make sure the prefilled_txn appears to have included the coinbase
  222. assert(len(header_and_shortids.prefilled_txn) >= 1)
  223. assert_equal(header_and_shortids.prefilled_txn[0].index, 0)
  224. # Check that all prefilled_txn entries match what's in the block.
  225. for entry in header_and_shortids.prefilled_txn:
  226. entry.tx.calc_sha256()
  227. assert_equal(entry.tx.sha256, block.vtx[entry.index].sha256)
  228. # Check that the cmpctblock message announced all the transactions.
  229. assert_equal(len(header_and_shortids.prefilled_txn) + len(header_and_shortids.shortids), len(block.vtx))
  230. # And now check that all the shortids are as expected as well.
  231. # Determine the siphash keys to use.
  232. [k0, k1] = header_and_shortids.get_siphash_keys()
  233. index = 0
  234. while index < len(block.vtx):
  235. if (len(header_and_shortids.prefilled_txn) > 0 and
  236. header_and_shortids.prefilled_txn[0].index == index):
  237. # Already checked prefilled transactions above
  238. header_and_shortids.prefilled_txn.pop(0)
  239. else:
  240. shortid = calculate_shortid(k0, k1, block.vtx[index].sha256)
  241. assert_equal(shortid, header_and_shortids.shortids[0])
  242. header_and_shortids.shortids.pop(0)
  243. index += 1
  244. # Test that bitcoind requests compact blocks when we announce new blocks
  245. # via header or inv, and that responding to getblocktxn causes the block
  246. # to be successfully reconstructed.
  247. def test_compactblock_requests(self):
  248. print("Testing compactblock requests... ")
  249. # Try announcing a block with an inv or header, expect a compactblock
  250. # request
  251. for announce in ["inv", "header"]:
  252. block = self.build_block_on_tip()
  253. with mininode_lock:
  254. self.test_node.last_getdata = None
  255. if announce == "inv":
  256. self.test_node.send_message(msg_inv([CInv(2, block.sha256)]))
  257. else:
  258. self.test_node.send_header_for_blocks([block])
  259. success = wait_until(lambda: self.test_node.last_getdata is not None, timeout=30)
  260. assert(success)
  261. assert_equal(len(self.test_node.last_getdata.inv), 1)
  262. assert_equal(self.test_node.last_getdata.inv[0].type, 4)
  263. assert_equal(self.test_node.last_getdata.inv[0].hash, block.sha256)
  264. # Send back a compactblock message that omits the coinbase
  265. comp_block = HeaderAndShortIDs()
  266. comp_block.header = CBlockHeader(block)
  267. comp_block.nonce = 0
  268. comp_block.shortids = [1] # this is useless, and wrong
  269. self.test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
  270. assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.hashPrevBlock)
  271. # Expect a getblocktxn message.
  272. with mininode_lock:
  273. assert(self.test_node.last_getblocktxn is not None)
  274. absolute_indexes = self.test_node.last_getblocktxn.block_txn_request.to_absolute()
  275. assert_equal(absolute_indexes, [0]) # should be a coinbase request
  276. # Send the coinbase, and verify that the tip advances.
  277. msg = msg_blocktxn()
  278. msg.block_transactions.blockhash = block.sha256
  279. msg.block_transactions.transactions = [block.vtx[0]]
  280. self.test_node.send_and_ping(msg)
  281. assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
  282. # Create a chain of transactions from given utxo, and add to a new block.
  283. def build_block_with_transactions(self, utxo, num_transactions):
  284. block = self.build_block_on_tip()
  285. for i in range(num_transactions):
  286. tx = CTransaction()
  287. tx.vin.append(CTxIn(COutPoint(utxo[0], utxo[1]), b''))
  288. tx.vout.append(CTxOut(utxo[2] - 1000, CScript([OP_TRUE])))
  289. tx.rehash()
  290. utxo = [tx.sha256, 0, tx.vout[0].nValue]
  291. block.vtx.append(tx)
  292. block.hashMerkleRoot = block.calc_merkle_root()
  293. block.solve()
  294. return block
  295. # Test that we only receive getblocktxn requests for transactions that the
  296. # node needs, and that responding to them causes the block to be
  297. # reconstructed.
  298. def test_getblocktxn_requests(self):
  299. print("Testing getblocktxn requests...")
  300. # First try announcing compactblocks that won't reconstruct, and verify
  301. # that we receive getblocktxn messages back.
  302. utxo = self.utxos.pop(0)
  303. block = self.build_block_with_transactions(utxo, 5)
  304. self.utxos.append([block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue])
  305. comp_block = HeaderAndShortIDs()
  306. comp_block.initialize_from_block(block)
  307. self.test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
  308. with mininode_lock:
  309. assert(self.test_node.last_getblocktxn is not None)
  310. absolute_indexes = self.test_node.last_getblocktxn.block_txn_request.to_absolute()
  311. assert_equal(absolute_indexes, [1, 2, 3, 4, 5])
  312. msg = msg_blocktxn()
  313. msg.block_transactions = BlockTransactions(block.sha256, block.vtx[1:])
  314. self.test_node.send_and_ping(msg)
  315. assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
  316. utxo = self.utxos.pop(0)
  317. block = self.build_block_with_transactions(utxo, 5)
  318. self.utxos.append([block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue])
  319. # Now try interspersing the prefilled transactions
  320. comp_block.initialize_from_block(block, prefill_list=[0, 1, 5])
  321. self.test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
  322. with mininode_lock:
  323. assert(self.test_node.last_getblocktxn is not None)
  324. absolute_indexes = self.test_node.last_getblocktxn.block_txn_request.to_absolute()
  325. assert_equal(absolute_indexes, [2, 3, 4])
  326. msg.block_transactions = BlockTransactions(block.sha256, block.vtx[2:5])
  327. self.test_node.send_and_ping(msg)
  328. assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
  329. # Now try giving one transaction ahead of time.
  330. utxo = self.utxos.pop(0)
  331. block = self.build_block_with_transactions(utxo, 5)
  332. self.utxos.append([block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue])
  333. self.test_node.send_and_ping(msg_tx(block.vtx[1]))
  334. assert(block.vtx[1].hash in self.nodes[0].getrawmempool())
  335. # Prefill 4 out of the 6 transactions, and verify that only the one
  336. # that was not in the mempool is requested.
  337. comp_block.initialize_from_block(block, prefill_list=[0, 2, 3, 4])
  338. self.test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
  339. with mininode_lock:
  340. assert(self.test_node.last_getblocktxn is not None)
  341. absolute_indexes = self.test_node.last_getblocktxn.block_txn_request.to_absolute()
  342. assert_equal(absolute_indexes, [5])
  343. msg.block_transactions = BlockTransactions(block.sha256, [block.vtx[5]])
  344. self.test_node.send_and_ping(msg)
  345. assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
  346. # Now provide all transactions to the node before the block is
  347. # announced and verify reconstruction happens immediately.
  348. utxo = self.utxos.pop(0)
  349. block = self.build_block_with_transactions(utxo, 10)
  350. self.utxos.append([block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue])
  351. for tx in block.vtx[1:]:
  352. self.test_node.send_message(msg_tx(tx))
  353. self.test_node.sync_with_ping()
  354. # Make sure all transactions were accepted.
  355. mempool = self.nodes[0].getrawmempool()
  356. for tx in block.vtx[1:]:
  357. assert(tx.hash in mempool)
  358. # Clear out last request.
  359. with mininode_lock:
  360. self.test_node.last_getblocktxn = None
  361. # Send compact block
  362. comp_block.initialize_from_block(block, prefill_list=[0])
  363. self.test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
  364. with mininode_lock:
  365. # Shouldn't have gotten a request for any transaction
  366. assert(self.test_node.last_getblocktxn is None)
  367. # Tip should have updated
  368. assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
  369. # Incorrectly responding to a getblocktxn shouldn't cause the block to be
  370. # permanently failed.
  371. def test_incorrect_blocktxn_response(self):
  372. print("Testing handling of incorrect blocktxn responses...")
  373. if (len(self.utxos) == 0):
  374. self.make_utxos()
  375. utxo = self.utxos.pop(0)
  376. block = self.build_block_with_transactions(utxo, 10)
  377. self.utxos.append([block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue])
  378. # Relay the first 5 transactions from the block in advance
  379. for tx in block.vtx[1:6]:
  380. self.test_node.send_message(msg_tx(tx))
  381. self.test_node.sync_with_ping()
  382. # Make sure all transactions were accepted.
  383. mempool = self.nodes[0].getrawmempool()
  384. for tx in block.vtx[1:6]:
  385. assert(tx.hash in mempool)
  386. # Send compact block
  387. comp_block = HeaderAndShortIDs()
  388. comp_block.initialize_from_block(block, prefill_list=[0])
  389. self.test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
  390. absolute_indexes = []
  391. with mininode_lock:
  392. assert(self.test_node.last_getblocktxn is not None)
  393. absolute_indexes = self.test_node.last_getblocktxn.block_txn_request.to_absolute()
  394. assert_equal(absolute_indexes, [6, 7, 8, 9, 10])
  395. # Now give an incorrect response.
  396. # Note that it's possible for bitcoind to be smart enough to know we're
  397. # lying, since it could check to see if the shortid matches what we're
  398. # sending, and eg disconnect us for misbehavior. If that behavior
  399. # change were made, we could just modify this test by having a
  400. # different peer provide the block further down, so that we're still
  401. # verifying that the block isn't marked bad permanently. This is good
  402. # enough for now.
  403. msg = msg_blocktxn()
  404. msg.block_transactions = BlockTransactions(block.sha256, [block.vtx[5]] + block.vtx[7:])
  405. self.test_node.send_and_ping(msg)
  406. # Tip should not have updated
  407. assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.hashPrevBlock)
  408. # We should receive a getdata request
  409. success = wait_until(lambda: self.test_node.last_getdata is not None, timeout=10)
  410. assert(success)
  411. assert_equal(len(self.test_node.last_getdata.inv), 1)
  412. assert_equal(self.test_node.last_getdata.inv[0].type, 2)
  413. assert_equal(self.test_node.last_getdata.inv[0].hash, block.sha256)
  414. # Deliver the block
  415. self.test_node.send_and_ping(msg_block(block))
  416. assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
  417. def test_getblocktxn_handler(self):
  418. print("Testing getblocktxn handler...")
  419. # bitcoind won't respond for blocks whose height is more than 15 blocks
  420. # deep.
  421. MAX_GETBLOCKTXN_DEPTH = 15
  422. chain_height = self.nodes[0].getblockcount()
  423. current_height = chain_height
  424. while (current_height >= chain_height - MAX_GETBLOCKTXN_DEPTH):
  425. block_hash = self.nodes[0].getblockhash(current_height)
  426. block = FromHex(CBlock(), self.nodes[0].getblock(block_hash, False))
  427. msg = msg_getblocktxn()
  428. msg.block_txn_request = BlockTransactionsRequest(int(block_hash, 16), [])
  429. num_to_request = random.randint(1, len(block.vtx))
  430. msg.block_txn_request.from_absolute(sorted(random.sample(range(len(block.vtx)), num_to_request)))
  431. self.test_node.send_message(msg)
  432. success = wait_until(lambda: self.test_node.last_blocktxn is not None, timeout=10)
  433. assert(success)
  434. [tx.calc_sha256() for tx in block.vtx]
  435. with mininode_lock:
  436. assert_equal(self.test_node.last_blocktxn.block_transactions.blockhash, int(block_hash, 16))
  437. all_indices = msg.block_txn_request.to_absolute()
  438. for index in all_indices:
  439. tx = self.test_node.last_blocktxn.block_transactions.transactions.pop(0)
  440. tx.calc_sha256()
  441. assert_equal(tx.sha256, block.vtx[index].sha256)
  442. self.test_node.last_blocktxn = None
  443. current_height -= 1
  444. # Next request should be ignored, as we're past the allowed depth.
  445. block_hash = self.nodes[0].getblockhash(current_height)
  446. msg.block_txn_request = BlockTransactionsRequest(int(block_hash, 16), [0])
  447. self.test_node.send_and_ping(msg)
  448. with mininode_lock:
  449. assert_equal(self.test_node.last_blocktxn, None)
  450. def test_compactblocks_not_at_tip(self):
  451. print("Testing compactblock requests/announcements not at chain tip...")
  452. # Test that requesting old compactblocks doesn't work.
  453. MAX_CMPCTBLOCK_DEPTH = 11
  454. new_blocks = []
  455. for i in range(MAX_CMPCTBLOCK_DEPTH):
  456. self.test_node.clear_block_announcement()
  457. new_blocks.append(self.nodes[0].generate(1)[0])
  458. wait_until(self.test_node.received_block_announcement, timeout=30)
  459. self.test_node.clear_block_announcement()
  460. self.test_node.send_message(msg_getdata([CInv(4, int(new_blocks[0], 16))]))
  461. success = wait_until(lambda: self.test_node.last_cmpctblock is not None, timeout=30)
  462. assert(success)
  463. self.test_node.clear_block_announcement()
  464. self.nodes[0].generate(1)
  465. wait_until(self.test_node.received_block_announcement, timeout=30)
  466. self.test_node.clear_block_announcement()
  467. self.test_node.send_message(msg_getdata([CInv(4, int(new_blocks[0], 16))]))
  468. success = wait_until(lambda: self.test_node.last_block is not None, timeout=30)
  469. assert(success)
  470. with mininode_lock:
  471. self.test_node.last_block.block.calc_sha256()
  472. assert_equal(self.test_node.last_block.block.sha256, int(new_blocks[0], 16))
  473. # Generate an old compactblock, and verify that it's not accepted.
  474. cur_height = self.nodes[0].getblockcount()
  475. hashPrevBlock = int(self.nodes[0].getblockhash(cur_height-5), 16)
  476. block = self.build_block_on_tip()
  477. block.hashPrevBlock = hashPrevBlock
  478. block.solve()
  479. comp_block = HeaderAndShortIDs()
  480. comp_block.initialize_from_block(block)
  481. self.test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
  482. tips = self.nodes[0].getchaintips()
  483. found = False
  484. for x in tips:
  485. if x["hash"] == block.hash:
  486. assert_equal(x["status"], "headers-only")
  487. found = True
  488. break
  489. assert(found)
  490. # Requesting this block via getblocktxn should silently fail
  491. # (to avoid fingerprinting attacks).
  492. msg = msg_getblocktxn()
  493. msg.block_txn_request = BlockTransactionsRequest(block.sha256, [0])
  494. with mininode_lock:
  495. self.test_node.last_blocktxn = None
  496. self.test_node.send_and_ping(msg)
  497. with mininode_lock:
  498. assert(self.test_node.last_blocktxn is None)
  499. def run_test(self):
  500. # Setup the p2p connections and start up the network thread.
  501. self.test_node = TestNode()
  502. connections = []
  503. connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], self.test_node))
  504. self.test_node.add_connection(connections[0])
  505. NetworkThread().start() # Start up network handling in another thread
  506. # Test logic begins here
  507. self.test_node.wait_for_verack()
  508. # We will need UTXOs to construct transactions in later tests.
  509. self.make_utxos()
  510. self.test_sendcmpct()
  511. self.test_compactblock_construction()
  512. self.test_compactblock_requests()
  513. self.test_getblocktxn_requests()
  514. self.test_getblocktxn_handler()
  515. self.test_compactblocks_not_at_tip()
  516. self.test_incorrect_blocktxn_response()
  517. self.test_invalid_cmpctblock_message()
  518. if __name__ == '__main__':
  519. CompactBlocksTest().main()