def run_test(self): block_count = 0 # Create a P2P connections node0 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0) node0.add_connection(connection) node1 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node1) node1.add_connection(connection) NetworkThread().start() # wait_for_verack ensures that the P2P connection is fully up. node0.wait_for_verack() node1.wait_for_verack() # send one to get out of IBD state self.chain.set_genesis_hash(int(self.nodes[0].getbestblockhash(), 16)) block = self.chain.next_block(block_count) block_count += 1 node0.send_message(msg_block(block)) self.nodes[0].waitforblockheight(1) block = self.chain.next_block(block_count) # set block validating status to wait after validation self.nodes[0].waitaftervalidatingblock(block.hash, "add") # make sure block hashes are in waiting list wait_for_waiting_blocks({block.hash}, self.nodes[0], self.log) node0.send_message(msg_block(block)) node1.send_message(msg_block(block)) # make sure we started validating blocks. # One is validating the other is ignored. wait_for_validating_blocks({block.hash}, self.nodes[0], self.log) def wait_for_log(): line_text = block.hash + " will not be considered by the current" for line in open(glob.glob(self.options.tmpdir + "/node0" + "/regtest/bitcoind.log")[0]): if line_text in line: self.log.info("Found line: %s", line) return True return False # wait for the log of the ignored block. wait_until(wait_for_log) # remove block validating status to finish validation self.nodes[0].waitaftervalidatingblock(block.hash, "remove") # wait till validation of block finishes node0.sync_with_ping() self.nodes[0].waitforblockheight(2) assert_equal(block.hash, self.nodes[0].getbestblockhash())
def __send_blocking_validation_waiting_block(self, block, node0): # set block validating status to wait after validation self.nodes[0].waitaftervalidatingblock(block.hash, "add") # make sure block hash is in the waiting list wait_for_waiting_blocks({block.hash}, self.nodes[0], self.log) node0.send_message(msg_block(block))
def run_test(self): block_count = 0 # Create a P2P connections node0 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0) node0.add_connection(connection) NetworkThread().start() # wait_for_verack ensures that the P2P connection is fully up. node0.wait_for_verack() self.chain.set_genesis_hash(int(self.nodes[0].getbestblockhash(), 16)) _, out, block_count = prepare_init_chain(self.chain, 101, 100, start_block=0, block_0=False, node=node0) self.log.info("waiting for block height 101 via rpc") self.nodes[0].waitforblockheight(101) # wait till validation of block or blocks finishes node0.sync_with_ping() block1 = self.chain.next_block(block_count, spend=out[0], extra_txns=8) block_count += 1 # send block but block him at validation point self.nodes[0].waitaftervalidatingblock(block1.hash, "add") node0.send_message(msg_block(block1)) self.log.info(f"block1 hash: {block1.hash}") # make sure block hash is in waiting list wait_for_waiting_blocks({block1.hash}, self.nodes[0], self.log) # send child block block2 = self.chain.next_block(block_count, spend=out[1], extra_txns=10) block_count += 1 node0.send_message(msg_block(block2)) self.log.info(f"block2 hash: {block2.hash}") wait_until(lambda: check_for_log_msg( self, block2.hash + " will not be considered by the current", "/node0")) self.nodes[0].waitaftervalidatingblock(block1.hash, "remove") # wait till validation of block or blocks finishes node0.sync_with_ping() # block that arrived last on competing chain should be active assert_equal(block2.hash, self.nodes[0].getbestblockhash())
def run_test(self): block_count = 0 # Create a P2P connections node0 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0) node0.add_connection(connection) node1 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node1) node1.add_connection(connection) NetworkThread().start() # wait_for_verack ensures that the P2P connection is fully up. node0.wait_for_verack() node1.wait_for_verack() # send one to get out of IBD state self.chain.set_genesis_hash(int(self.nodes[0].getbestblockhash(), 16)) block = self.chain.next_block(block_count) block_count += 1 node0.send_message(msg_block(block)) self.nodes[0].waitforblockheight(1) block = self.chain.next_block(block_count) # set block validating status to wait after validation self.nodes[0].waitaftervalidatingblock(block.hash, "add") # make sure block hashes are in waiting list wait_for_waiting_blocks({block.hash}, self.nodes[0], self.log) node0.send_message(msg_block(block)) node1.send_message(msg_block(block)) # make sure we started validating blocks. # One is validating the other is ignored. wait_for_validating_blocks({block.hash}, self.nodes[0], self.log) # wait for the log of the ignored block. wait_until(lambda: check_for_log_msg(self, block.hash + " will not be considered by the current", "/node0")) # remove block validating status to finish validation self.nodes[0].waitaftervalidatingblock(block.hash, "remove") # wait till validation of block finishes node0.sync_with_ping() self.nodes[0].waitforblockheight(2) assert_equal(block.hash, self.nodes[0].getbestblockhash())
def run_test(self): block_count = 0 # Create a P2P connections node0 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0) node0.add_connection(connection) NetworkThread().start() node0.wait_for_verack() self.chain.set_genesis_hash(int(self.nodes[0].getbestblockhash(), 16)) block = self.chain.next_block(block_count) block_count += 1 node0.send_message(msg_block(block)) block = self.chain.next_block(block_count) self.log.info(f"block hash: {block.hash}") self.nodes[0].waitaftervalidatingblock(block.hash, "add") # make sure block hash is in waiting list wait_for_waiting_blocks({block.hash}, self.nodes[0], self.log) self.log.info("sending block") node0.send_message(msg_block(block)) # make sure we started validating block wait_for_validating_blocks({block.hash}, self.nodes[0], self.log) # sleep a bit and check that in the meantime validation hasn't proceeded time.sleep(1) assert (block.hash != self.nodes[0].getbestblockhash()) # after validating the block we release its waiting status self.nodes[0].waitaftervalidatingblock(block.hash, "remove") # wait till validation of block or blocks finishes node0.sync_with_ping() assert_equal(block.hash, self.nodes[0].getbestblockhash())
def run_test(self): block_count = 0 # Create a P2P connections node0 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0) node0.add_connection(connection) node1 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node1) node1.add_connection(connection) node2 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node2) node2.add_connection(connection) NetworkThread().start() # wait_for_verack ensures that the P2P connection is fully up. node0.wait_for_verack() node1.wait_for_verack() node2.wait_for_verack() self.chain.set_genesis_hash(int(self.nodes[0].getbestblockhash(), 16)) block = self.chain.next_block(block_count) block_count += 1 self.chain.save_spendable_output() node0.send_message(msg_block(block)) for i in range(100): block = self.chain.next_block(block_count) block_count += 1 self.chain.save_spendable_output() node0.send_message(msg_block(block)) out = [] for i in range(100): out.append(self.chain.get_spendable_output()) self.log.info("waiting for block height 101 via rpc") self.nodes[0].waitforblockheight(101) tip_block_num = block_count - 1 # adding extra transactions to get different block hashes block2_hard = self.chain.next_block(block_count, spend=out[0], extra_txns=8) block_count += 1 self.chain.set_tip(tip_block_num) block3_easier = self.chain.next_block(block_count, spend=out[0], extra_txns=2) block_count += 1 self.chain.set_tip(tip_block_num) block4_hard = self.chain.next_block(block_count, spend=out[0], extra_txns=10) block_count += 1 # send two "hard" blocks, with waitaftervalidatingblock we artificially # extend validation time. self.log.info(f"hard block2 hash: {block2_hard.hash}") self.nodes[0].waitaftervalidatingblock(block2_hard.hash, "add") self.log.info(f"hard block4 hash: {block4_hard.hash}") self.nodes[0].waitaftervalidatingblock(block4_hard.hash, "add") # make sure block hashes are in waiting list wait_for_waiting_blocks({block2_hard.hash, block4_hard.hash}, self.nodes[0], self.log) node0.send_message(msg_block(block2_hard)) node1.send_message(msg_block(block4_hard)) # make sure we started validating blocks wait_for_validating_blocks({block2_hard.hash, block4_hard.hash}, self.nodes[0], self.log) self.log.info(f"easier block3 hash: {block3_easier.hash}") node2.send_message(msg_block(block3_easier)) self.nodes[0].waitforblockheight(102) assert_equal(block3_easier.hash, self.nodes[0].getbestblockhash()) # now we can remove waiting status from blocks and finish their validation self.nodes[0].waitaftervalidatingblock(block2_hard.hash, "remove") self.nodes[0].waitaftervalidatingblock(block4_hard.hash, "remove") # wait till validation of block or blocks finishes node0.sync_with_ping() # easier block should still be on tip assert_equal(block3_easier.hash, self.nodes[0].getbestblockhash())
def run_test(self): block_count = 0 # Create a P2P connections node0 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0) node0.add_connection(connection) node1 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node1) node1.add_connection(connection) node2 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node2) node2.add_connection(connection) NetworkThread().start() # wait_for_verack ensures that the P2P connection is fully up. node0.wait_for_verack() node1.wait_for_verack() node2.wait_for_verack() self.chain.set_genesis_hash(int(self.nodes[0].getbestblockhash(), 16)) block = self.chain.next_block(block_count) block_count += 1 self.chain.save_spendable_output() node0.send_message(msg_block(block)) for i in range(100): block = self.chain.next_block(block_count) block_count += 1 self.chain.save_spendable_output() node0.send_message(msg_block(block)) out = [] for i in range(100): out.append(self.chain.get_spendable_output()) self.log.info("waiting for block height 101 via rpc") self.nodes[0].waitforblockheight(101) tip_block_num = block_count-1 # left branch block2 = self.chain.next_block(block_count, spend=out[0], extra_txns=8) block2_num = block_count block_count += 1 node0.send_message(msg_block(block2)) self.log.info(f"block2 hash: {block2.hash}") self.nodes[0].waitforblockheight(102) # send blocks 3,4 for parallel validation on left branch block3 = self.chain.next_block(block_count, spend=out[1], extra_txns=10) block_count += 1 self.chain.set_tip(block2_num) block4 = self.chain.next_block(block_count, spend=out[1], extra_txns=8) block_count += 1 # send two "hard" blocks, with waitaftervalidatingblock we artificially # extend validation time. self.log.info(f"block3 hash: {block3.hash}") self.nodes[0].waitaftervalidatingblock(block3.hash, "add") self.log.info(f"block4 hash: {block4.hash}") self.nodes[0].waitaftervalidatingblock(block4.hash, "add") # make sure block hashes are in waiting list wait_for_waiting_blocks({block3.hash, block4.hash}, self.nodes[0], self.log) node0.send_message(msg_block(block3)) node2.send_message(msg_block(block4)) # make sure we started validating blocks wait_for_validating_blocks({block3.hash, block4.hash}, self.nodes[0], self.log) # right branch self.chain.set_tip(tip_block_num) block5 = self.chain.next_block(block_count, spend=out[0], extra_txns=10) block_count += 1 node1.send_message(msg_block(block5)) self.log.info(f"block5 hash: {block5.hash}") # and two blocks to extend second branch to cause reorg # - they must be sent from the same node as otherwise they will be # rejected with "prev block not found" block6 = self.chain.next_block(block_count) node1.send_message(msg_block(block6)) self.log.info(f"block6 hash: {block6.hash}") block_count += 1 block7 = self.chain.next_block(block_count) node1.send_message(msg_block(block7)) self.log.info(f"block7 hash: {block7.hash}") block_count += 1 self.nodes[0].waitforblockheight(104) assert_equal(block7.hash, self.nodes[0].getbestblockhash()) self.log.info("releasing wait status on parallel blocks to finish their validation") self.nodes[0].waitaftervalidatingblock(block3.hash, "remove") self.nodes[0].waitaftervalidatingblock(block4.hash, "remove") # wait till validation of block or blocks finishes node0.sync_with_ping() # block that arrived last on competing chain should be active assert_equal(block7.hash, self.nodes[0].getbestblockhash())
def run_test(self): block_count = 0 # Create a P2P connection node0 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0) node0.add_connection(connection) node1 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node1) node1.add_connection(connection) node2 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node2) node2.add_connection(connection) NetworkThread().start() # wait_for_verack ensures that the P2P connection is fully up. node0.wait_for_verack() node1.wait_for_verack() node2.wait_for_verack() self.chain.set_genesis_hash(int(self.nodes[0].getbestblockhash(), 16)) block = self.chain.next_block(block_count) block_count += 1 self.chain.save_spendable_output() node0.send_message(msg_block(block)) for i in range(100): block = self.chain.next_block(block_count) block_count += 1 self.chain.save_spendable_output() node0.send_message(msg_block(block)) out = [] for i in range(100): out.append(self.chain.get_spendable_output()) self.log.info("waiting for block height 101 via rpc") self.nodes[0].waitforblockheight(101) tip_block_num = block_count-1 block2_hard = self.chain.next_block(block_count, spend=out[0], extra_txns=8) block_count += 1 self.chain.set_tip(tip_block_num) block3_easier = self.chain.next_block(block_count, spend=out[0], extra_txns=2) easier_block_num = block_count block_count += 1 self.chain.set_tip(tip_block_num) block4_hard = self.chain.next_block(block_count, spend=out[0], extra_txns=10) block_count += 1 # make child block of easier block self.chain.set_tip(easier_block_num) block5 = self.chain.next_block(block_count) block5_num = block_count block_count += 1 # send two "hard" blocks, with waitaftervalidatingblock we artificially # extend validation time. self.log.info(f"hard block2 hash: {block2_hard.hash}") self.nodes[0].waitaftervalidatingblock(block2_hard.hash, "add") self.log.info(f"hard block4 hash: {block4_hard.hash}") self.nodes[0].waitaftervalidatingblock(block4_hard.hash, "add") # make sure block hashes are in waiting list wait_for_waiting_blocks({block2_hard.hash, block4_hard.hash}, self.nodes[0], self.log) # send blocks via different p2p connection node0.send_message(msg_block(block2_hard)) node1.send_message(msg_block(block4_hard)) # make sure we started validating blocks wait_for_validating_blocks({block2_hard.hash, block4_hard.hash}, self.nodes[0], self.log) # send easier block through different p2p connection too node2.send_message(msg_block(block3_easier)) self.log.info(f"easier block hash: {block3_easier.hash}") self.nodes[0].waitforblockheight(102) assert_equal(block3_easier.hash, self.nodes[0].getbestblockhash()) # child block of block3_easier self.log.info(f"child block hash: {block5.hash}") self.nodes[0].waitaftervalidatingblock(block5.hash, "add") # make sure child block is in waiting list and then send it wait_for_not_validating_blocks({block5.hash}, self.nodes[0], self.log) node2.send_message(msg_block(block5)) # make sure we started validating child block wait_for_validating_blocks({block5.hash}, self.nodes[0], self.log) # finish validation on block2_hard self.nodes[0].waitaftervalidatingblock(block2_hard.hash, "remove") wait_for_not_validating_blocks({block2_hard.hash}, self.nodes[0], self.log) # finish validation on child block self.nodes[0].waitaftervalidatingblock(block5.hash, "remove") wait_for_not_validating_blocks({block5.hash}, self.nodes[0], self.log) # block5 should be active at this point assert_equal(block5.hash, self.nodes[0].getbestblockhash()) # finish validation on block4_hard self.nodes[0].waitaftervalidatingblock(block4_hard.hash, "remove") wait_for_not_validating_blocks({block4_hard.hash}, self.nodes[0], self.log) # block5 should still be active at this point assert_equal(block5.hash, self.nodes[0].getbestblockhash()) # Make three siblings and send them via same p2p connection. block6_hard = self.chain.next_block(block_count, spend=out[1], extra_txns=8) block_count += 1 self.chain.set_tip(block5_num) block7_easier = self.chain.next_block(block_count, spend=out[1], extra_txns=2) block_count += 1 self.chain.set_tip(block5_num) block8_hard = self.chain.next_block(block_count, spend=out[1], extra_txns=10) block_count += 1 self.log.info(f"hard block6 hash: {block6_hard.hash}") self.nodes[0].waitaftervalidatingblock(block6_hard.hash, "add") self.log.info(f"hard block8 hash: {block8_hard.hash}") self.nodes[0].waitaftervalidatingblock(block8_hard.hash, "add") # make sure block hashes are in waiting list wait_for_waiting_blocks({block6_hard.hash, block8_hard.hash}, self.nodes[0], self.log) # sending blocks via same p2p connection node0.send_message(msg_block(block6_hard)) node0.send_message(msg_block(block8_hard)) # make sure we started validating blocks wait_for_validating_blocks({block6_hard.hash, block8_hard.hash}, self.nodes[0], self.log) # send easier block through same p2p connection too node0.send_message(msg_block(block7_easier)) self.nodes[0].waitforblockheight(104) assert_equal(block7_easier.hash, self.nodes[0].getbestblockhash()) # now we can remove waiting status from blocks and finish their validation self.nodes[0].waitaftervalidatingblock(block6_hard.hash, "remove") self.nodes[0].waitaftervalidatingblock(block8_hard.hash, "remove") # wait till validation of block or blocks finishes node0.sync_with_ping() # easier block should still be on tip assert_equal(block7_easier.hash, self.nodes[0].getbestblockhash())
def run_test(self): block_count = 0 # Create a P2P connections node0 = NodeConnCB() connection0 = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0) node0.add_connection(connection0) node1 = NodeConnCB() connection1 = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node1) node1.add_connection(connection1) # *** Prepare node connection for early announcements testing node2 = NodeConnCB() node2.add_connection( NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node2)) NetworkThread().start() # wait_for_verack ensures that the P2P connection is fully up. node0.wait_for_verack() node1.wait_for_verack() # *** Activate early announcement functionality for this connection # After this point the early announcements are not received yet - # we still need to set latest announced block (CNode::pindexBestKnownBlock) # which is set for e.g. by calling best headers message with locator # set to non-null node2.wait_for_verack() node2.send_message(msg_sendcmpct(announce=True)) self.chain.set_genesis_hash(int(self.nodes[0].getbestblockhash(), 16)) _, outs, block_count = prepare_init_chain(self.chain, 101, 1, block_0=False, start_block=0, node=node0) out = outs[0] self.log.info("waiting for block height 101 via rpc") self.nodes[0].waitforblockheight(101) tip_block_num = block_count - 1 # adding extra transactions to get different block hashes block2_hard = self.chain.next_block(block_count, spend=out, extra_txns=8) block_count += 1 self.chain.set_tip(tip_block_num) block3_easier = self.chain.next_block(block_count, spend=out, extra_txns=2) block_count += 1 self.chain.set_tip(tip_block_num) block4_hard = self.chain.next_block(block_count, spend=out, extra_txns=10) block_count += 1 # send three "hard" blocks, with waitaftervalidatingblock we artificially # extend validation time. self.log.info(f"hard block2 hash: {block2_hard.hash}") self.nodes[0].waitaftervalidatingblock(block2_hard.hash, "add") self.log.info(f"hard block4 hash: {block4_hard.hash}") self.nodes[0].waitaftervalidatingblock(block4_hard.hash, "add") # make sure block hashes are in waiting list wait_for_waiting_blocks({block2_hard.hash, block4_hard.hash}, self.nodes[0], self.log) # *** Complete early announcement setup by sending getheaders message # with a non-null locator (pointing to the last block that we know # of on python side - we claim that we know of all the blocks that # bitcoind node knows of) # # We also set on_cmpctblock handler as early announced blocks are # announced via compact block messages instead of inv messages node2.send_and_ping( msg_getheaders( locator_have=[int(self.nodes[0].getbestblockhash(), 16)])) receivedAnnouncement = False waiting_for_announcement_block_hash = block2_hard.sha256 def on_cmpctblock(conn, message): nonlocal receivedAnnouncement message.header_and_shortids.header.calc_sha256() if message.header_and_shortids.header.sha256 == waiting_for_announcement_block_hash: receivedAnnouncement = True node2.on_cmpctblock = on_cmpctblock # send one block via p2p and one via rpc node0.send_message(msg_block(block2_hard)) # *** make sure that we receive announcement of the block before it has # been validated wait_until(lambda: receivedAnnouncement) # making rpc call submitblock in a separate thread because waitaftervalidation is blocking # the return of submitblock submitblock_thread = threading.Thread(target=self.nodes[0].submitblock, args=(ToHex(block4_hard), )) submitblock_thread.start() # because self.nodes[0] rpc is blocked we use another rpc client rpc_client = get_rpc_proxy(rpc_url( get_datadir_path(self.options.tmpdir, 0), 0), 0, coveragedir=self.options.coveragedir) wait_for_validating_blocks({block2_hard.hash, block4_hard.hash}, rpc_client, self.log) # *** prepare to intercept block3_easier announcement - it will not be # announced before validation is complete as early announcement is # limited to announcing one block per height (siblings are ignored) # but after validation is complete we should still get the announcing # compact block message receivedAnnouncement = False waiting_for_announcement_block_hash = block3_easier.sha256 self.log.info(f"easy block3 hash: {block3_easier.hash}") node1.send_message(msg_block(block3_easier)) # *** Make sure that we receive compact block announcement of the block # after the validation is complete even though it was not the first # block that was received by bitcoind node. # # Also make sure that we receive inv announcement of the block after # the validation is complete by the nodes that are not using early # announcement functionality. wait_until(lambda: receivedAnnouncement) node0.wait_for_inv([CInv(CInv.BLOCK, block3_easier.sha256)]) # node 1 was the sender but receives inv for block non the less # (with early announcement that's not the case - sender does not receive the announcement) node1.wait_for_inv([CInv(CInv.BLOCK, block3_easier.sha256)]) rpc_client.waitforblockheight(102) assert_equal(block3_easier.hash, rpc_client.getbestblockhash()) # now we can remove waiting status from blocks and finish their validation rpc_client.waitaftervalidatingblock(block2_hard.hash, "remove") rpc_client.waitaftervalidatingblock(block4_hard.hash, "remove") submitblock_thread.join() # wait till validation of block or blocks finishes node0.sync_with_ping() # easier block should still be on tip assert_equal(block3_easier.hash, self.nodes[0].getbestblockhash())
def run_test(self): block_count = 0 # Create a P2P connections node0 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0) node0.add_connection(connection) NetworkThread().start() # wait_for_verack ensures that the P2P connection is fully up. node0.wait_for_verack() self.chain.set_genesis_hash(int(self.nodes[0].getbestblockhash(), 16)) block = self.chain.next_block(block_count) block_count += 1 self.chain.save_spendable_output() node0.send_message(msg_block(block)) for i in range(100): block = self.chain.next_block(block_count) block_count += 1 self.chain.save_spendable_output() node0.send_message(msg_block(block)) out = [] for i in range(100): out.append(self.chain.get_spendable_output()) self.log.info("waiting for block height 101 via rpc") self.nodes[0].waitforblockheight(101) # wait till validation of block or blocks finishes node0.sync_with_ping() block1 = self.chain.next_block(block_count, spend=out[0], extra_txns=8) block_count += 1 # send block but block him at validation point self.nodes[0].waitaftervalidatingblock(block1.hash, "add") node0.send_message(msg_block(block1)) self.log.info(f"block1 hash: {block1.hash}") # make sure block hash is in waiting list wait_for_waiting_blocks({block1.hash}, self.nodes[0], self.log) # send child block block2 = self.chain.next_block(block_count, spend=out[1], extra_txns=10) block_count += 1 node0.send_message(msg_block(block2)) self.log.info(f"block2 hash: {block2.hash}") def wait_for_log(): line_text = block2.hash + " will not be considered by the current" for line in open( glob.glob(self.options.tmpdir + "/node0" + "/regtest/bitcoind.log")[0]): if line_text in line: self.log.info("Found line: %s", line) return True return False wait_until(wait_for_log) self.nodes[0].waitaftervalidatingblock(block1.hash, "remove") # wait till validation of block or blocks finishes node0.sync_with_ping() # block that arrived last on competing chain should be active assert_equal(block2.hash, self.nodes[0].getbestblockhash())
def run_test(self): block_count = 0 # Create a P2P connection node0 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0) node0.add_connection(connection) node1 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node1) node1.add_connection(connection) node2 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node2) node2.add_connection(connection) NetworkThread().start() # wait_for_verack ensures that the P2P connection is fully up. node0.wait_for_verack() node1.wait_for_verack() node2.wait_for_verack() self.chain.set_genesis_hash(int(self.nodes[0].getbestblockhash(), 16)) _, out, block_count = prepare_init_chain(self.chain, 101, 100, block_0=False, start_block=0, node=node0) self.log.info("waiting for block height 101 via rpc") self.nodes[0].waitforblockheight(101) tip_block_num = block_count - 1 block2_hard = self.chain.next_block(block_count, spend=out[0], extra_txns=8) block_count += 1 self.chain.set_tip(tip_block_num) block3_easier = self.chain.next_block(block_count, spend=out[0], extra_txns=2) block_count += 1 self.chain.set_tip(tip_block_num) block4_hard = self.chain.next_block(block_count, spend=out[0], extra_txns=10) block_count += 1 # send two "hard" blocks, with waitaftervalidatingblock we artificially # extend validation time. self.log.info(f"hard block2 hash: {block2_hard.hash}") self.nodes[0].waitaftervalidatingblock(block2_hard.hash, "add") self.log.info(f"hard block4 hash: {block4_hard.hash}") self.nodes[0].waitaftervalidatingblock(block4_hard.hash, "add") # make sure block hashes are in waiting list wait_for_waiting_blocks({block2_hard.hash, block4_hard.hash}, self.nodes[0], self.log) node0.send_message(msg_block(block2_hard)) node1.send_message(msg_block(block4_hard)) # make sure we started validating blocks wait_for_validating_blocks({block2_hard.hash, block4_hard.hash}, self.nodes[0], self.log) self.log.info(f"easier hash: {block3_easier.hash}") node2.send_message(msg_block(block3_easier)) self.nodes[0].waitforblockheight(102) assert_equal(block3_easier.hash, self.nodes[0].getbestblockhash()) # now we can remove waiting status from blocks and finish their validation self.nodes[0].waitaftervalidatingblock(block2_hard.hash, "remove") self.nodes[0].waitaftervalidatingblock(block4_hard.hash, "remove") # wait till validation of block or blocks finishes node0.sync_with_ping() # now we want our precious block to be one of the harder blocks (block4_hard) self.nodes[0].preciousblock(block4_hard.hash) assert_equal(block4_hard.hash, self.nodes[0].getbestblockhash())
def run_test(self): block_count = 0 # Create a P2P connection node0 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0) node0.add_connection(connection) node1 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node1) node1.add_connection(connection) node2 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node2) node2.add_connection(connection) node3 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node3) node3.add_connection(connection) NetworkThread().start() # wait_for_verack ensures that the P2P connection is fully up. node0.wait_for_verack() node1.wait_for_verack() node2.wait_for_verack() node3.wait_for_verack() self.chain.set_genesis_hash(int(self.nodes[0].getbestblockhash(), 16)) block = self.chain.next_block(block_count) block_count += 1 self.chain.save_spendable_output() node0.send_message(msg_block(block)) for i in range(100): block = self.chain.next_block(block_count) block_count += 1 self.chain.save_spendable_output() node0.send_message(msg_block(block)) out = [] for i in range(100): out.append(self.chain.get_spendable_output()) self.log.info("waiting for block height 101 via rpc") self.nodes[0].waitforblockheight(101) tip_block_num = block_count - 1 block2 = self.chain.next_block(block_count, spend=out[0], extra_txns=8) block_count += 1 self.chain.set_tip(tip_block_num) block3 = self.chain.next_block(block_count, spend=out[0], extra_txns=10) block_count += 1 self.chain.set_tip(tip_block_num) block4 = self.chain.next_block(block_count, spend=out[0], extra_txns=12) block_count += 1 self.chain.set_tip(tip_block_num) block5 = self.chain.next_block(block_count, spend=out[0], extra_txns=14) block5_num = block_count block_count += 1 block6 = self.chain.next_block(block_count, spend=out[1], extra_txns=8) block_count += 1 self.chain.set_tip(block5_num) block7 = self.chain.next_block(block_count, spend=out[1], extra_txns=10) self.log.info(f"block2 hash: {block2.hash}") self.nodes[0].waitaftervalidatingblock(block2.hash, "add") self.log.info(f"block3 hash: {block3.hash}") self.nodes[0].waitaftervalidatingblock(block3.hash, "add") self.log.info(f"block4 hash: {block4.hash}") self.nodes[0].waitaftervalidatingblock(block4.hash, "add") # make sure block hashes are in waiting list wait_for_waiting_blocks({block2.hash, block3.hash, block4.hash}, self.nodes[0], self.log) node0.send_message(msg_block(block2)) # make sure we started validating block2 first as we expect this one to # be terminated later on in the test before its validation is complete # (algorithm for premature termination selects based on block height and # and validation duration - those that are in validation with smaller # height and longer are terminated first) wait_for_validating_blocks({block2.hash}, self.nodes[0], self.log) node1.send_message(msg_block(block3)) node2.send_message(msg_block(block4)) # make sure we started validating blocks wait_for_validating_blocks({block2.hash, block3.hash, block4.hash}, self.nodes[0], self.log) node3.send_message(msg_block(block5)) self.log.info(f"block5 hash: {block5.hash}") # check log file for logging about which block validation was terminated termination_log_found = False for line in open( glob.glob(self.options.tmpdir + "/node0" + "/regtest/bitcoind.log")[0]): if f"Block {block2.hash} will not be considered by the current tip activation as the maximum parallel block" in line: termination_log_found = True self.log.info("Found line: %s", line.strip()) break self.log.info(f"block6 hash: {block6.hash}") self.nodes[0].waitaftervalidatingblock(block6.hash, "add") self.log.info(f"block7 hash: {block7.hash}") self.nodes[0].waitaftervalidatingblock(block7.hash, "add") wait_for_waiting_blocks({block6.hash, block7.hash}, self.nodes[0], self.log) node3.send_message(msg_block(block6)) wait_for_validating_blocks({block6.hash}, self.nodes[0], self.log) node3.send_message(msg_block(block7)) wait_for_validating_blocks({block7.hash}, self.nodes[0], self.log) self.nodes[0].waitaftervalidatingblock(block2.hash, "remove") # block2 should be canceled. wait_for_not_validating_blocks({block2.hash}, self.nodes[0], self.log) self.log.info("removing wait status from block7") self.nodes[0].waitaftervalidatingblock(block7.hash, "remove") # finish block7 validation wait_for_not_validating_blocks({block7.hash}, self.nodes[0], self.log) # remove wait status from block to finish its validations so the test exits properly self.nodes[0].waitaftervalidatingblock(block3.hash, "remove") self.nodes[0].waitaftervalidatingblock(block4.hash, "remove") self.nodes[0].waitaftervalidatingblock(block6.hash, "remove") # wait till validation of block or blocks finishes node0.sync_with_ping() # block7 should be active in the end assert_equal(block7.hash, self.nodes[0].getbestblockhash()) # check log file for logging about which block validation was terminated termination_log_found = False for line in open( glob.glob(self.options.tmpdir + "/node0" + "/regtest/bitcoind.log")[0]): if f"Block {block2.hash} validation was terminated before completion." in line: termination_log_found = True self.log.info("Found line: %s", line.strip()) break assert_equal(termination_log_found, True)
def run_test(self): block_count = 0 node0 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0) node0.add_connection(connection) node1 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node1) node1.add_connection(connection) node2 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node2) node2.add_connection(connection) NetworkThread().start() # wait_for_verack ensures that the P2P connection is fully up. node0.wait_for_verack() node1.wait_for_verack() node2.wait_for_verack() self.log.info("Sending blocks to get spendable output") self.chain.set_genesis_hash(int(self.nodes[0].getbestblockhash(), 16)) block = self.chain.next_block(block_count) block_count += 1 self.chain.save_spendable_output() node0.send_message(msg_block(block)) for i in range(100): block = self.chain.next_block(block_count) block_count += 1 self.chain.save_spendable_output() node0.send_message(msg_block(block)) out = [] for i in range(100): out.append(self.chain.get_spendable_output()) self.log.info("waiting for block height 101 via rpc") self.nodes[0].waitforblockheight(101) tip_block_num = block_count - 1 block2 = self.chain.next_block(block_count, spend=out[0], extra_txns=1) block2_count = block_count block_count += 1 self.log.info(f"blockA hash: {block2.hash}") node0.send_message(msg_block(block2)) self.nodes[0].waitforblockheight(102) block3_hard = self.chain.next_block(block_count, spend=out[1], extra_txns=8) block_count += 1 self.chain.set_tip(block2_count) block4_easier = self.chain.next_block(block_count, spend=out[1], extra_txns=2) block_count += 1 self.chain.set_tip(block2_count) block5_hard = self.chain.next_block(block_count, spend=out[1], extra_txns=10) block_count += 1 # send two "hard" blocks, with waitaftervalidatingblock we artificially # extend validation time. self.log.info(f"hard block3 hash: {block3_hard.hash}") self.nodes[0].waitaftervalidatingblock(block3_hard.hash, "add") self.log.info(f"hard block5 hash: {block5_hard.hash}") self.nodes[0].waitaftervalidatingblock(block5_hard.hash, "add") # make sure block hashes are in waiting list wait_for_waiting_blocks({block3_hard.hash, block5_hard.hash}, self.nodes[0], self.log) self.log.info( "Sending blocks 3,4,5 on branch 2 for parallel validation") node0.send_message(msg_block(block3_hard)) node2.send_message(msg_block(block5_hard)) # make sure we started validating blocks wait_for_validating_blocks({block3_hard.hash, block5_hard.hash}, self.nodes[0], self.log) self.log.info(f"easier hash: {block4_easier.hash}") node1.send_message(msg_block(block4_easier)) self.nodes[0].waitforblockheight(103) # Because 4 is easy to validate it will be validated first and set as active tip assert_equal(block4_easier.hash, self.nodes[0].getbestblockhash()) # now we can remove waiting status from blocks and finish their validation self.nodes[0].waitaftervalidatingblock(block3_hard.hash, "remove") self.nodes[0].waitaftervalidatingblock(block5_hard.hash, "remove") # wait till validation of block or blocks finishes node0.sync_with_ping() # easier block should still be on tip assert_equal(block4_easier.hash, self.nodes[0].getbestblockhash()) self.log.info("Sending blocks 6,7,8 on competing chain to cause reorg") self.chain.set_tip(tip_block_num) block6 = self.chain.next_block(block_count, spend=out[0], extra_txns=2) block_count += 1 self.log.info(f"block6 hash: {block6.hash}") node0.send_message(msg_block(block6)) block7 = self.chain.next_block(block_count) block_count += 1 self.log.info(f"block7: {block7.hash}") node0.send_message(msg_block(block7)) # send one to cause reorg this should be active block8 = self.chain.next_block(block_count) block_count += 1 self.log.info(f"block8: {block8.hash}") node0.send_message(msg_block(block8)) self.nodes[0].waitforblockheight(104) assert_equal(block8.hash, self.nodes[0].getbestblockhash()) self.log.info( "Invalidating block7 on competing chain to reorg to first branch again" ) self.log.info(f"invalidating hash {block7.hash}") self.nodes[0].invalidateblock(block7.hash) #after invalidating, active block should be the one first validated on first branch assert_equal(block4_easier.hash, self.nodes[0].getbestblockhash())
def run_test(self): block_count = 0 # Create a P2P connections node0 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0) node0.add_connection(connection) node1 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node1) node1.add_connection(connection) NetworkThread().start() # wait_for_verack ensures that the P2P connection is fully up. node0.wait_for_verack() node1.wait_for_verack() self.chain.set_genesis_hash(int(self.nodes[0].getbestblockhash(), 16)) _, outs, block_count = prepare_init_chain(self.chain, 101, 1, block_0=False, start_block=0, node=node0) out = outs[0] self.log.info("waiting for block height 101 via rpc") self.nodes[0].waitforblockheight(101) tip_block_num = block_count - 1 # adding extra transactions to get different block hashes block2_hard = self.chain.next_block(block_count, spend=out, extra_txns=8) block_count += 1 self.chain.set_tip(tip_block_num) block3_easier = self.chain.next_block(block_count, spend=out, extra_txns=2) block_count += 1 mining_candidate = self.nodes[0].getminingcandidate() block4_hard = self.chain.next_block(block_count) block4_hard.hashPrevBlock = int(mining_candidate["prevhash"], 16) block4_hard.nTime = mining_candidate["time"] block4_hard.nVersion = mining_candidate["version"] block4_hard.solve() mining_solution = {"id": mining_candidate["id"], "nonce": block4_hard.nNonce, "coinbase": ToHex(block4_hard.vtx[0]), "time": mining_candidate["time"], "version": mining_candidate["version"]} # send three "hard" blocks, with waitaftervalidatingblock we artificially # extend validation time. self.log.info(f"hard block2 hash: {block2_hard.hash}") self.nodes[0].waitaftervalidatingblock(block2_hard.hash, "add") self.log.info(f"hard block4 hash: {block4_hard.hash}") self.nodes[0].waitaftervalidatingblock(block4_hard.hash, "add") # make sure block hashes are in waiting list wait_for_waiting_blocks({block2_hard.hash, block4_hard.hash}, self.nodes[0], self.log) # send one block via p2p and one via rpc node0.send_message(msg_block(block2_hard)) # making rpc call submitminingsolution in a separate thread because waitaftervalidation is blocking # the return of submitminingsolution submitminingsolution_thread = threading.Thread(target=self.nodes[0].submitminingsolution, args=(mining_solution,)) submitminingsolution_thread.start() # because self.nodes[0] rpc is blocked we use another rpc client rpc_client = get_rpc_proxy(rpc_url(get_datadir_path(self.options.tmpdir, 0), 0), 0, coveragedir=self.options.coveragedir) wait_for_validating_blocks({block2_hard.hash, block4_hard.hash}, rpc_client, self.log) self.log.info(f"easy block3 hash: {block3_easier.hash}") node1.send_message(msg_block(block3_easier)) rpc_client.waitforblockheight(102) assert_equal(block3_easier.hash, rpc_client.getbestblockhash()) # now we can remove waiting status from blocks and finish their validation rpc_client.waitaftervalidatingblock(block2_hard.hash, "remove") rpc_client.waitaftervalidatingblock(block4_hard.hash, "remove") submitminingsolution_thread.join() # wait till validation of block or blocks finishes node0.sync_with_ping() # easier block should still be on tip assert_equal(block3_easier.hash, self.nodes[0].getbestblockhash())
def run_test(self): block_count = 0 # Create a P2P connections node0 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0) node0.add_connection(connection) node1 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node1) node1.add_connection(connection) NetworkThread().start() # wait_for_verack ensures that the P2P connection is fully up. node0.wait_for_verack() node1.wait_for_verack() self.chain.set_genesis_hash(int(self.nodes[0].getbestblockhash(), 16)) block = self.chain.next_block(block_count) block_count += 1 self.chain.save_spendable_output() node0.send_message(msg_block(block)) num_blocks = 150 for i in range(num_blocks): block = self.chain.next_block(block_count) block_count += 1 self.chain.save_spendable_output() node0.send_message(msg_block(block)) out = [] for i in range(num_blocks): out.append(self.chain.get_spendable_output()) self.log.info("waiting for block height 151 via rpc") self.nodes[0].waitforblockheight(num_blocks + 1) tip_block_num = block_count - 1 # left branch block2 = self.chain.next_block(block_count, spend=out[0:9], extra_txns=8) block_count += 1 node0.send_message(msg_block(block2)) self.log.info(f"block2 hash: {block2.hash}") self.nodes[0].waitforblockheight(num_blocks + 2) # send blocks 3,4 for parallel validation on left branch self.chain.set_tip(tip_block_num) block3 = self.chain.next_block(block_count, spend=out[10:19], extra_txns=10) block_count += 1 block4 = self.chain.next_block(block_count, spend=out[20:29], extra_txns=8) block_count += 1 # send two "hard" blocks, with waitaftervalidatingblock we artificially # extend validation time. self.log.info(f"block3 hash: {block3.hash}") self.log.info(f"block4 hash: {block4.hash}") self.nodes[0].waitaftervalidatingblock(block4.hash, "add") # make sure block hashes are in waiting list wait_for_waiting_blocks({block4.hash}, self.nodes[0], self.log) node1.send_message(msg_block(block3)) node1.send_message(msg_block(block4)) # make sure we started validating blocks wait_for_validating_blocks({block4.hash}, self.nodes[0], self.log) # right branch self.chain.set_tip(tip_block_num) block5 = self.chain.next_block(block_count) # Add some txns from block2 & block3 to block5, just to check that they get # filtered from the mempool and not re-added block5_duplicated_txns = block3.vtx[1:3] + block2.vtx[1:3] self.chain.update_block(block_count, block5_duplicated_txns) block_count += 1 node0.send_message(msg_block(block5)) self.log.info(f"block5 hash: {block5.hash}") # and two blocks to extend second branch to cause reorg # - they must be sent from the same node as otherwise they will be # rejected with "prev block not found" as we don't wait for the first # block to arrive so there is a race condition which block is seen # first when using multiple connections block6 = self.chain.next_block(block_count) node0.send_message(msg_block(block6)) self.log.info(f"block6 hash: {block6.hash}") block_count += 1 block7 = self.chain.next_block(block_count) node0.send_message(msg_block(block7)) self.log.info(f"block7 hash: {block7.hash}") block_count += 1 self.nodes[0].waitforblockheight(num_blocks + 4) assert_equal(block7.hash, self.nodes[0].getbestblockhash()) self.log.info( "releasing wait status on parallel blocks to finish their validation" ) self.nodes[0].waitaftervalidatingblock(block4.hash, "remove") # wait till validation of block or blocks finishes node0.sync_with_ping() # block that arrived last on competing chain should be active assert_equal(block7.hash, self.nodes[0].getbestblockhash()) # make sure that transactions from block2 and 3 (except coinbase, and those also # in block 5) are in mempool not_expected_in_mempool = set() for txn in block5_duplicated_txns: not_expected_in_mempool.add(txn.hash) expected_in_mempool = set() for txn in block2.vtx[1:] + block3.vtx[1:]: expected_in_mempool.add(txn.hash) expected_in_mempool = expected_in_mempool.difference( not_expected_in_mempool) mempool = self.nodes[0].getrawmempool() assert_equal(expected_in_mempool, set(mempool))
def run_test(self): block_count = 0 # Create a P2P connections node0 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0) node0.add_connection(connection) node1 = NodeConnCB() connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node1) node1.add_connection(connection) NetworkThread().start() # wait_for_verack ensures that the P2P connection is fully up. node0.wait_for_verack() node1.wait_for_verack() self.chain.set_genesis_hash(int(self.nodes[0].getbestblockhash(), 16)) block = self.chain.next_block(block_count) block_count += 1 self.chain.save_spendable_output() node0.send_message(msg_block(block)) for i in range(100): block = self.chain.next_block(block_count) block_count += 1 self.chain.save_spendable_output() node0.send_message(msg_block(block)) out = [] for i in range(100): out.append(self.chain.get_spendable_output()) self.log.info("waiting for block height 101 via rpc") self.nodes[0].waitforblockheight(101) tip_block_num = block_count - 1 # left branch block2 = self.chain.next_block(block_count, spend=out[0], extra_txns=8) block2_num = block_count block_count += 1 node0.send_message(msg_block(block2)) self.log.info(f"block2 hash: {block2.hash}") self.nodes[0].waitforblockheight(102) # send blocks 3,4 to force a reorg self.chain.set_tip(tip_block_num) block3 = self.chain.next_block(block_count, spend=out[1], extra_txns=10) block_count += 1 block4 = self.chain.next_block(block_count, spend=out[1], extra_txns=8) block_count += 1 self.log.info(f"block3 hash: {block3.hash}") self.nodes[0].waitaftervalidatingblock(block3.hash, "add") # make sure block hash is in waiting list wait_for_waiting_blocks({block3.hash}, self.nodes[0], self.log) node0.send_message(msg_block(block3)) node0.send_message(msg_block(block4)) # make sure we started validating blocks wait_for_validating_blocks({block3.hash}, self.nodes[0], self.log) self.stop_node(0)