def run_test(self): node0 = self.nodes[0].add_p2p_connection(P2PInterface()) # Set node time to 60 days ago self.nodes[0].setmocktime(int(time.time()) - 60 * 24 * 60 * 60) # Generating a chain of 10 blocks block_hashes = self.nodes[0].generate(nblocks=10) # Create longer chain starting 2 blocks before current tip height = len(block_hashes) - 2 block_hash = block_hashes[height - 1] block_time = self.nodes[0].getblockheader(block_hash)["mediantime"] + 1 new_blocks = self.build_chain(5, block_hash, height, block_time) # Force reorg to a longer chain node0.send_message(msg_headers(new_blocks)) node0.wait_for_getdata() for block in new_blocks: node0.send_and_ping(msg_block(block)) # Check that reorg succeeded assert_equal(self.nodes[0].getblockcount(), 13) stale_hash = int(block_hashes[-1], 16) # Check that getdata request for stale block succeeds self.send_block_request(stale_hash, node0) test_function = lambda: self.last_block_equals(stale_hash, node0) wait_until(test_function, timeout=3) # Check that getheader request for stale block header succeeds self.send_header_request(stale_hash, node0) test_function = lambda: self.last_header_equals(stale_hash, node0) wait_until(test_function, timeout=3) # Longest chain is extended so stale is much older than chain tip self.nodes[0].setmocktime(0) tip = self.nodes[0].generate(nblocks=1)[0] assert_equal(self.nodes[0].getblockcount(), 14) # Send getdata & getheaders to refresh last received getheader message block_hash = int(tip, 16) self.send_block_request(block_hash, node0) self.send_header_request(block_hash, node0) node0.sync_with_ping() # Request for very old stale block should now fail self.send_block_request(stale_hash, node0) time.sleep(3) assert not self.last_block_equals(stale_hash, node0) # Request for very old stale block header should now fail self.send_header_request(stale_hash, node0) time.sleep(3) assert not self.last_header_equals(stale_hash, node0) # Verify we can fetch very old blocks and headers on the active chain block_hash = int(block_hashes[2], 16) self.send_block_request(block_hash, node0) self.send_header_request(block_hash, node0) node0.sync_with_ping() self.send_block_request(block_hash, node0) test_function = lambda: self.last_block_equals(block_hash, node0) wait_until(test_function, timeout=3) self.send_header_request(block_hash, node0) test_function = lambda: self.last_header_equals(block_hash, node0) wait_until(test_function, timeout=3)
def run_test(self): # Setup the p2p connections and start up the network thread. test_node = TestNode() # connects to node0 (not whitelisted) white_node = TestNode() # connects to node1 (whitelisted) connections = [] connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node)) connections.append(NodeConn('127.0.0.1', p2p_port(1), self.nodes[1], white_node)) test_node.add_connection(connections[0]) white_node.add_connection(connections[1]) NetworkThread().start() # Start up network handling in another thread # Test logic begins here test_node.wait_for_verack() white_node.wait_for_verack() # 1. Have both nodes mine a block (leave IBD) [ n.generate(1) for n in self.nodes ] tips = [ int ("0x" + n.getbestblockhash() + "L", 0) for n in self.nodes ] # 2. Send one block that builds on each tip. # This should be accepted. blocks_h2 = [] # the height 2 blocks on each node's chain block_time = time.time() + 1 for i in xrange(2): blocks_h2.append(create_block(tips[i], create_coinbase(), block_time)) blocks_h2[i].solve() block_time += 1 test_node.send_message(msg_block(blocks_h2[0])) white_node.send_message(msg_block(blocks_h2[1])) [ x.sync_with_ping() for x in [test_node, white_node] ] assert_equal(self.nodes[0].getblockcount(), 2) assert_equal(self.nodes[1].getblockcount(), 2) print "First height 2 block accepted by both nodes" # 3. Send another block that builds on the original tip. blocks_h2f = [] # Blocks at height 2 that fork off the main chain for i in xrange(2): blocks_h2f.append(create_block(tips[i], create_coinbase(), blocks_h2[i].nTime+1)) blocks_h2f[i].solve() test_node.send_message(msg_block(blocks_h2f[0])) white_node.send_message(msg_block(blocks_h2f[1])) [ x.sync_with_ping() for x in [test_node, white_node] ] for x in self.nodes[0].getchaintips(): if x['hash'] == blocks_h2f[0].hash: assert_equal(x['status'], "headers-only") for x in self.nodes[1].getchaintips(): if x['hash'] == blocks_h2f[1].hash: assert_equal(x['status'], "valid-headers") print "Second height 2 block accepted only from whitelisted peer" # 4. Now send another block that builds on the forking chain. blocks_h3 = [] for i in xrange(2): blocks_h3.append(create_block(blocks_h2f[i].sha256, create_coinbase(), blocks_h2f[i].nTime+1)) blocks_h3[i].solve() test_node.send_message(msg_block(blocks_h3[0])) white_node.send_message(msg_block(blocks_h3[1])) [ x.sync_with_ping() for x in [test_node, white_node] ] # Since the earlier block was not processed by node0, the new block # can't be fully validated. for x in self.nodes[0].getchaintips(): if x['hash'] == blocks_h3[0].hash: assert_equal(x['status'], "headers-only") # But this block should be accepted by node0 since it has more work. try: self.nodes[0].getblock(blocks_h3[0].hash) print "Unrequested more-work block accepted from non-whitelisted peer" except: raise AssertionError("Unrequested more work block was not processed") # Node1 should have accepted and reorged. assert_equal(self.nodes[1].getblockcount(), 3) print "Successfully reorged to length 3 chain from whitelisted peer" # 4b. Now mine 288 more blocks and deliver; all should be processed but # the last (height-too-high) on node0. Node1 should process the tip if # we give it the headers chain leading to the tip. tips = blocks_h3 headers_message = msg_headers() all_blocks = [] # node0's blocks for j in xrange(2): for i in xrange(288): next_block = create_block(tips[j].sha256, create_coinbase(), tips[j].nTime+1) next_block.solve() if j==0: test_node.send_message(msg_block(next_block)) all_blocks.append(next_block) else: headers_message.headers.append(CBlockHeader(next_block)) tips[j] = next_block time.sleep(2) for x in all_blocks: try: self.nodes[0].getblock(x.hash) if x == all_blocks[287]: raise AssertionError("Unrequested block too far-ahead should have been ignored") except: if x == all_blocks[287]: print "Unrequested block too far-ahead not processed" else: raise AssertionError("Unrequested block with more work should have been accepted") headers_message.headers.pop() # Ensure the last block is unrequested white_node.send_message(headers_message) # Send headers leading to tip white_node.send_message(msg_block(tips[1])) # Now deliver the tip try: white_node.sync_with_ping() self.nodes[1].getblock(tips[1].hash) print "Unrequested block far ahead of tip accepted from whitelisted peer" except: raise AssertionError("Unrequested block from whitelisted peer not accepted") # 5. Test handling of unrequested block on the node that didn't process # Should still not be processed (even though it has a child that has more # work). test_node.send_message(msg_block(blocks_h2f[0])) # Here, if the sleep is too short, the test could falsely succeed (if the # node hasn't processed the block by the time the sleep returns, and then # the node processes it and incorrectly advances the tip). # But this would be caught later on, when we verify that an inv triggers # a getdata request for this block. test_node.sync_with_ping() assert_equal(self.nodes[0].getblockcount(), 2) print "Unrequested block that would complete more-work chain was ignored" # 6. Try to get node to request the missing block. # Poke the node with an inv for block at height 3 and see if that # triggers a getdata on block 2 (it should if block 2 is missing). with mininode_lock: # Clear state so we can check the getdata request test_node.last_getdata = None test_node.send_message(msg_inv([CInv(2, blocks_h3[0].sha256)])) test_node.sync_with_ping() with mininode_lock: getdata = test_node.last_getdata # Check that the getdata includes the right block assert_equal(getdata.inv[0].hash, blocks_h2f[0].sha256) print "Inv at tip triggered getdata for unprocessed block" # 7. Send the missing block for the third time (now it is requested) test_node.send_message(msg_block(blocks_h2f[0])) test_node.sync_with_ping() assert_equal(self.nodes[0].getblockcount(), 290) print "Successfully reorged to longer chain from non-whitelisted peer" [ c.disconnect_node() for c in connections ]
def send_header_for_blocks(self, new_blocks): headers_message = msg_headers() headers_message.headers = [CBlockHeader(b) for b in new_blocks] self.send_message(headers_message)
def run_test(self): node0 = self.nodes[0].add_p2p_connection(P2PInterface()) # Set node time to 60 days ago self.nodes[0].setmocktime(int(time.time()) - 60 * 24 * 60 * 60) # Generating a chain of 10 blocks block_hashes = self.nodes[0].generatetoaddress( 10, self.nodes[0].get_deterministic_priv_key().address) # Create longer chain starting 2 blocks before current tip height = len(block_hashes) - 2 block_hash = block_hashes[height - 1] block_time = self.nodes[0].getblockheader(block_hash)["mediantime"] + 1 new_blocks = self.build_chain(5, block_hash, height, block_time) # Force reorg to a longer chain node0.send_message(msg_headers(new_blocks)) node0.wait_for_getdata() for block in new_blocks: node0.send_and_ping(msg_block(block)) # Check that reorg succeeded assert_equal(self.nodes[0].getblockcount(), 13) stale_hash = int(block_hashes[-1], 16) # Check that getdata request for stale block succeeds self.send_block_request(stale_hash, node0) test_function = lambda: self.last_block_equals(stale_hash, node0) wait_until(test_function, timeout=3) # Check that getheader request for stale block header succeeds self.send_header_request(stale_hash, node0) test_function = lambda: self.last_header_equals(stale_hash, node0) wait_until(test_function, timeout=3) # Longest chain is extended so stale is much older than chain tip self.nodes[0].setmocktime(0) tip = self.nodes[0].generatetoaddress( 1, self.nodes[0].get_deterministic_priv_key().address)[0] assert_equal(self.nodes[0].getblockcount(), 14) # Send getdata & getheaders to refresh last received getheader message block_hash = int(tip, 16) self.send_block_request(block_hash, node0) self.send_header_request(block_hash, node0) node0.sync_with_ping() # Request for very old stale block should now fail self.send_block_request(stale_hash, node0) time.sleep(3) assert not self.last_block_equals(stale_hash, node0) # Request for very old stale block header should now fail self.send_header_request(stale_hash, node0) time.sleep(3) assert not self.last_header_equals(stale_hash, node0) # Verify we can fetch very old blocks and headers on the active chain block_hash = int(block_hashes[2], 16) self.send_block_request(block_hash, node0) self.send_header_request(block_hash, node0) node0.sync_with_ping() self.send_block_request(block_hash, node0) test_function = lambda: self.last_block_equals(block_hash, node0) wait_until(test_function, timeout=3) self.send_header_request(block_hash, node0) test_function = lambda: self.last_header_equals(block_hash, node0) wait_until(test_function, timeout=3)
def run_test(self): # Setup the p2p connections and start up the network thread. test_node = TestNode() # connects to node0 (not whitelisted) white_node = TestNode() # connects to node1 (whitelisted) connections = [] connections.append( NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node)) connections.append( NodeConn('127.0.0.1', p2p_port(1), self.nodes[1], white_node)) test_node.add_connection(connections[0]) white_node.add_connection(connections[1]) NetworkThread().start() # Start up network handling in another thread # Test logic begins here test_node.wait_for_verack() white_node.wait_for_verack() # 1. Have both nodes mine a block (leave IBD) [n.generate(1) for n in self.nodes] tips = [int("0x" + n.getbestblockhash() + "L", 0) for n in self.nodes] # 2. Send one block that builds on each tip. # This should be accepted. blocks_h2 = [] # the height 2 blocks on each node's chain block_time = time.time() + 1 for i in xrange(2): blocks_h2.append( create_block(tips[i], create_coinbase_h(2), block_time)) blocks_h2[i].solve() block_time += 1 test_node.send_message(msg_block(blocks_h2[0])) white_node.send_message(msg_block(blocks_h2[1])) [x.sync_with_ping() for x in [test_node, white_node]] assert_equal(self.nodes[0].getblockcount(), 2) assert_equal(self.nodes[1].getblockcount(), 2) print "First height 2 block accepted by both nodes" # 3. Send another block that builds on the original tip. blocks_h2f = [] # Blocks at height 2 that fork off the main chain for i in xrange(2): blocks_h2f.append( create_block(tips[i], create_coinbase_h(2), blocks_h2[i].nTime + 1)) blocks_h2f[i].solve() test_node.send_message(msg_block(blocks_h2f[0])) white_node.send_message(msg_block(blocks_h2f[1])) [x.sync_with_ping() for x in [test_node, white_node]] for x in self.nodes[0].getchaintips(): if x['hash'] == blocks_h2f[0].hash: assert_equal(x['status'], "headers-only") for x in self.nodes[1].getchaintips(): if x['hash'] == blocks_h2f[1].hash: assert_equal(x['status'], "valid-headers") print "Second height 2 block accepted only from whitelisted peer" # 4. Now send another block that builds on the forking chain. blocks_h3 = [] for i in xrange(2): blocks_h3.append( create_block(blocks_h2f[i].sha256, create_coinbase_h(3), blocks_h2f[i].nTime + 1)) blocks_h3[i].solve() test_node.send_message(msg_block(blocks_h3[0])) white_node.send_message(msg_block(blocks_h3[1])) [x.sync_with_ping() for x in [test_node, white_node]] # Since the earlier block was not processed by node0, the new block # can't be fully validated. for x in self.nodes[0].getchaintips(): if x['hash'] == blocks_h3[0].hash: assert_equal(x['status'], "headers-only") # But this block should be accepted by node0 since it has more work. try: self.nodes[0].getblock(blocks_h3[0].hash) print "Unrequested more-work block accepted from non-whitelisted peer" except: raise AssertionError( "Unrequested more work block was not processed") # Node1 should have accepted and reorged. assert_equal(self.nodes[1].getblockcount(), 3) print "Successfully reorged to length 3 chain from whitelisted peer" # 4b. Now mine 288 more blocks and deliver; all should be processed but # the last (height-too-high) on node0. Node1 should process the tip if # we give it the headers chain leading to the tip. tips = blocks_h3 headers_message_1 = msg_headers() headers_message_2 = msg_headers() all_blocks = [] # node0's blocks for j in xrange(2): chainHeight = 3 for i in xrange(288): next_block = create_block(tips[j].sha256, create_coinbase_h(chainHeight + 1), tips[j].nTime + 1, get_nBits(chainHeight)) next_block.solve() chainHeight += 1 if j == 0: test_node.send_message(msg_block(next_block)) all_blocks.append(next_block) else: # we can not send more than zend::MAX_HEADERS_RESULTS=160 to a zend node if len(headers_message_1.headers) < 160: headers_message_1.headers.append( CBlockHeader(next_block)) else: headers_message_2.headers.append( CBlockHeader(next_block)) tips[j] = next_block if ((i + 1) % 10) == 0: print "... {} blocks created".format(i + 1) time.sleep(2) for x in all_blocks: try: self.nodes[0].getblock(x.hash) if x == all_blocks[287]: raise AssertionError( "Unrequested block too far-ahead should have been ignored" ) except: if x == all_blocks[287]: print "Unrequested block too far-ahead not processed" else: raise AssertionError( "Unrequested block with more work should have been accepted" ) headers_message_2.headers.pop() # Ensure the last block is unrequested white_node.send_message( headers_message_1) # Send headers leading to tip white_node.send_message( headers_message_2) # Send headers leading to tip white_node.send_message(msg_block(tips[1])) # Now deliver the tip try: white_node.sync_with_ping() self.nodes[1].getblock(tips[1].hash) print "Unrequested block far ahead of tip accepted from whitelisted peer" except: raise AssertionError( "Unrequested block from whitelisted peer not accepted") # 5. Test handling of unrequested block on the node that didn't process # Should still not be processed (even though it has a child that has more # work). test_node.send_message(msg_block(blocks_h2f[0])) # Here, if the sleep is too short, the test could falsely succeed (if the # node hasn't processed the block by the time the sleep returns, and then # the node processes it and incorrectly advances the tip). # But this would be caught later on, when we verify that an inv triggers # a getdata request for this block. test_node.sync_with_ping() assert_equal(self.nodes[0].getblockcount(), 2) print "Unrequested block that would complete more-work chain was ignored" # 6. Try to get node to request the missing block. # Poke the node with an inv for block at height 3 and see if that # triggers a getdata on block 2 (it should if block 2 is missing). with mininode_lock: # Clear state so we can check the getdata request test_node.last_getdata = None test_node.send_message(msg_inv([CInv(2, blocks_h3[0].sha256)])) test_node.sync_with_ping() with mininode_lock: getdata = test_node.last_getdata # Check that the getdata includes the right block assert_equal(getdata.inv[0].hash, blocks_h2f[0].sha256) print "Inv at tip triggered getdata for unprocessed block" # 7. Send the missing block for the third time (now it is requested) test_node.send_message(msg_block(blocks_h2f[0])) test_node.sync_with_ping() assert_equal(self.nodes[0].getblockcount(), 290) print "Successfully reorged to longer chain from non-whitelisted peer" [c.disconnect_node() for c in connections]
def run_test(self): # Setup the p2p connections and start up the network thread. test_node = NodeConnCB() # connects to node0 (not whitelisted) white_node = NodeConnCB() # connects to node1 (whitelisted) min_work_node = NodeConnCB() # connects to node2 (not whitelisted) connections = [] connections.append( NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node)) connections.append( NodeConn('127.0.0.1', p2p_port(1), self.nodes[1], white_node)) connections.append( NodeConn('127.0.0.1', p2p_port(2), self.nodes[2], min_work_node)) test_node.add_connection(connections[0]) white_node.add_connection(connections[1]) min_work_node.add_connection(connections[2]) NetworkThread().start() # Start up network handling in another thread # Test logic begins here test_node.wait_for_verack() white_node.wait_for_verack() min_work_node.wait_for_verack() # 1. Have nodes mine a block (nodes1/2 leave IBD) [n.generate(1) for n in self.nodes] tips = [int("0x" + n.getbestblockhash(), 0) for n in self.nodes] # 2. Send one block that builds on each tip. # This should be accepted by nodes 1/2 blocks_h2 = [] # the height 2 blocks on each node's chain block_time = int(time.time()) + 1 for i in range(3): blocks_h2.append( create_block(tips[i], create_coinbase(2), block_time)) blocks_h2[i].solve() block_time += 1 test_node.send_message(msg_block(blocks_h2[0])) white_node.send_message(msg_block(blocks_h2[1])) min_work_node.send_message(msg_block(blocks_h2[2])) for x in [test_node, white_node, min_work_node]: x.sync_with_ping() assert_equal(self.nodes[0].getblockcount(), 2) assert_equal(self.nodes[1].getblockcount(), 2) assert_equal(self.nodes[2].getblockcount(), 1) self.log.info( "First height 2 block accepted by node0/node1; correctly rejected by node2" ) # 3. Send another block that builds on the original tip. blocks_h2f = [] # Blocks at height 2 that fork off the main chain for i in range(2): blocks_h2f.append( create_block(tips[i], create_coinbase(2), blocks_h2[i].nTime + 1)) blocks_h2f[i].solve() test_node.send_message(msg_block(blocks_h2f[0])) white_node.send_message(msg_block(blocks_h2f[1])) for x in [test_node, white_node]: x.sync_with_ping() for x in self.nodes[0].getchaintips(): if x['hash'] == blocks_h2f[0].hash: assert_equal(x['status'], "headers-only") for x in self.nodes[1].getchaintips(): if x['hash'] == blocks_h2f[1].hash: assert_equal(x['status'], "valid-headers") self.log.info( "Second height 2 block accepted only from whitelisted peer") # 4. Now send another block that builds on the forking chain. blocks_h3 = [] for i in range(2): blocks_h3.append( create_block(blocks_h2f[i].sha256, create_coinbase(3), blocks_h2f[i].nTime + 1)) blocks_h3[i].solve() test_node.send_message(msg_block(blocks_h3[0])) white_node.send_message(msg_block(blocks_h3[1])) for x in [test_node, white_node]: x.sync_with_ping() # Since the earlier block was not processed by node0, the new block # can't be fully validated. for x in self.nodes[0].getchaintips(): if x['hash'] == blocks_h3[0].hash: assert_equal(x['status'], "headers-only") # But this block should be accepted by node0 since it has more work. self.nodes[0].getblock(blocks_h3[0].hash) self.log.info( "Unrequested more-work block accepted from non-whitelisted peer") # Node1 should have accepted and reorged. assert_equal(self.nodes[1].getblockcount(), 3) self.log.info( "Successfully reorged to length 3 chain from whitelisted peer") # 4b. Now mine 288 more blocks and deliver; all should be processed but # the last (height-too-high) on node0. Node1 should process the tip if # we give it the headers chain leading to the tip. tips = blocks_h3 headers_message = msg_headers() all_blocks = [] # node0's blocks for j in range(2): for i in range(288): next_block = create_block(tips[j].sha256, create_coinbase(i + 4), tips[j].nTime + 1) next_block.solve() if j == 0: test_node.send_message(msg_block(next_block)) all_blocks.append(next_block) else: headers_message.headers.append(CBlockHeader(next_block)) tips[j] = next_block time.sleep(2) # Blocks 1-287 should be accepted, block 288 should be ignored because it's too far ahead for x in all_blocks[:-1]: self.nodes[0].getblock(x.hash) assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, all_blocks[-1].hash) headers_message.headers.pop() # Ensure the last block is unrequested white_node.send_message(headers_message) # Send headers leading to tip white_node.send_message(msg_block(tips[1])) # Now deliver the tip white_node.sync_with_ping() self.nodes[1].getblock(tips[1].hash) self.log.info( "Unrequested block far ahead of tip accepted from whitelisted peer" ) # 5. Test handling of unrequested block on the node that didn't process # Should still not be processed (even though it has a child that has more # work). test_node.send_message(msg_block(blocks_h2f[0])) # Here, if the sleep is too short, the test could falsely succeed (if the # node hasn't processed the block by the time the sleep returns, and then # the node processes it and incorrectly advances the tip). # But this would be caught later on, when we verify that an inv triggers # a getdata request for this block. test_node.sync_with_ping() assert_equal(self.nodes[0].getblockcount(), 2) self.log.info( "Unrequested block that would complete more-work chain was ignored" ) # 6. Try to get node to request the missing block. # Poke the node with an inv for block at height 3 and see if that # triggers a getdata on block 2 (it should if block 2 is missing). with mininode_lock: # Clear state so we can check the getdata request test_node.last_message.pop("getdata", None) test_node.send_message(msg_inv([CInv(2, blocks_h3[0].sha256)])) test_node.sync_with_ping() with mininode_lock: getdata = test_node.last_message["getdata"] # Check that the getdata includes the right block assert_equal(getdata.inv[0].hash, blocks_h2f[0].sha256) self.log.info("Inv at tip triggered getdata for unprocessed block") # 7. Send the missing block for the third time (now it is requested) test_node.send_message(msg_block(blocks_h2f[0])) test_node.sync_with_ping() assert_equal(self.nodes[0].getblockcount(), 290) self.log.info( "Successfully reorged to longer chain from non-whitelisted peer") # 8. Connect node2 to node0 and ensure it is able to sync connect_nodes(self.nodes[0], 2) sync_blocks([self.nodes[0], self.nodes[2]]) self.log.info("Successfully synced nodes 2 and 0") [c.disconnect_node() for c in connections]
def send_header(self, block): msg = msg_headers() msg.headers = [CBlockHeader(block)] self.send_message(msg)
def run_test(self): self.log.info("Read headers data") self.headers_file_path = os.path.join( os.path.dirname(os.path.realpath(__file__)), self.options.datafile) with open(self.headers_file_path, encoding='utf-8') as headers_data: h_lines = [l.strip() for l in headers_data.readlines()] # The headers data is taken from testnet3 for early blocks from genesis until the first checkpoint. There are # two headers with valid POW at height 1 and 2, forking off from genesis. They are indicated by the FORK_PREFIX. FORK_PREFIX = 'fork:' self.headers = [l for l in h_lines if not l.startswith(FORK_PREFIX)] self.headers_fork = [ l[len(FORK_PREFIX):] for l in h_lines if l.startswith(FORK_PREFIX) ] self.headers = [FromHex(CBlockHeader(), h) for h in self.headers] self.headers_fork = [ FromHex(CBlockHeader(), h) for h in self.headers_fork ] self.log.info( "Feed all non-fork headers, including and up to the first checkpoint" ) self.nodes[0].add_p2p_connection(P2PInterface()) self.nodes[0].p2p.send_and_ping(msg_headers(self.headers)) assert { 'height': 546, 'hash': '000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70', 'branchlen': 546, 'status': 'headers-only', } in self.nodes[0].getchaintips() self.log.info("Feed all fork headers (fails due to checkpoint)") with self.nodes[0].assert_debug_log(['bad-fork-prior-to-checkpoint']): self.nodes[0].p2p.send_message(msg_headers(self.headers_fork)) self.nodes[0].p2p.wait_for_disconnect() self.log.info("Feed all fork headers (succeeds without checkpoint)") # On node 0 it succeeds because checkpoints are disabled self.restart_node(0, extra_args=['-nocheckpoints']) self.nodes[0].add_p2p_connection(P2PInterface()) self.nodes[0].p2p.send_and_ping(msg_headers(self.headers_fork)) assert { "height": 2, "hash": "00000000b0494bd6c3d5ff79c497cfce40831871cbf39b1bc28bd1dac817dc39", "branchlen": 2, "status": "headers-only", } in self.nodes[0].getchaintips() # On node 1 it succeeds because no checkpoint has been reached yet by a chain tip self.nodes[1].add_p2p_connection(P2PInterface()) self.nodes[1].p2p.send_and_ping(msg_headers(self.headers_fork)) assert { "height": 2, "hash": "00000000b0494bd6c3d5ff79c497cfce40831871cbf39b1bc28bd1dac817dc39", "branchlen": 2, "status": "headers-only", } in self.nodes[1].getchaintips()
self.headers_fork = [FromHex(CBlockHeader(), h) for h in self.headers_fork] self.log.info("Feed all non-fork headers, including and up to the first checkpoint") self.nodes[0].add_p2p_connection(P2PInterface()) self.nodes[0].p2p.send_message(msg_headers(self.headers)) self.nodes[0].p2p.sync_with_ping() assert { 'height': 546, 'hash': 'xxxx', 'branchlen': 546, 'status': 'headers-only', } in self.nodes[0].getchaintips() self.log.info("Feed all fork headers (failes due to checkpoint)") with self.nodes[0].assert_debug_log(['bad-fork-prior-to-checkpoint']) self.nodes[0].p2p.send_message(msg_headers(self.headers_fork)) self.nodes[0].p2p.wait_for_disconnect() self.log.info("Feed all fork headers (succeeds with out checkpoint)") self.restart_node(0, extra_args=['-nocheckpoints']) self.nodes[0].add_p2p_connection(P2PInterface()) self.nodes[0].p2p.send_message(msg_headers(self.headers_fork)) self.nodes[0].p2p.sync_with_ping() assert { "height": 2, "hash": "xxxx", "branchlen": 2, "status": "headers-only", } in self.nodes[0].getchaintips()
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)) getDataMessages = [] def on_getdata(conn, message): getDataMessages.append(message) node0.on_getdata = on_getdata # ***** 1. ***** # starting_blocks are needed to provide spendable outputs starting_blocks = MIN_TTOR_VALIDATION_DISTANCE + 1 for i in range(starting_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(starting_blocks): out.append(self.chain.get_spendable_output()) self.nodes[0].waitforblockheight(starting_blocks) tip_block_index = block_count - 1 self.log.info("Block tip height: %d " % block_count) # ***** 2. ***** # branch with blocks that do not violate TTOR valid_ttor_branch_height = MIN_TTOR_VALIDATION_DISTANCE + 1 for i in range(0, valid_ttor_branch_height): block = self.chain.next_block(block_count, spend=out[i], extra_txns=8) block_count += 1 node0.send_message(msg_block(block)) chaintip_valid_branch = block self.nodes[0].waitforblockheight(starting_blocks + valid_ttor_branch_height) self.log.info("Node's active chain height: %d " % (starting_blocks + valid_ttor_branch_height)) # ***** 3. ***** # branch with invalid transaction order that will try to cause a reorg self.chain.set_tip(tip_block_index) blocks_invalid_ttor = [] headers_message = msg_headers() headers_message.headers = [] invalid_ttor_branch_height = MIN_TTOR_VALIDATION_DISTANCE + 1 for i in range(0, invalid_ttor_branch_height): spend = out[i] block = self.chain.next_block(block_count) add_txns = self.get_chained_transactions(spend, num_of_transactions=10) # change order of transaction that output uses transaction that comes later (makes block violate TTOR) temp1 = add_txns[1] temp2 = add_txns[2] add_txns[1] = temp2 add_txns[2] = temp1 self.chain.update_block(block_count, add_txns) blocks_invalid_ttor.append(block) block_count += 1 if (i == 0): first_block = block # wait with sending header for the last block if (i != MIN_TTOR_VALIDATION_DISTANCE): headers_message.headers.append(CBlockHeader(block)) self.log.info("Sending %d headers..." % MIN_TTOR_VALIDATION_DISTANCE) node0.send_message(headers_message) # Wait to make sure we do not receive GETDATA messages yet. time.sleep(1) # Check that getData is not received until this chain is long at least as the active chain. assert_equal(len(getDataMessages), 0) self.log.info("Sending 1 more header...") # Send HEADERS message for the last block. headers_message.headers = [CBlockHeader(block)] node0.send_message(headers_message) node0.wait_for_getdata() self.log.info("Received GETDATA.") assert_equal(len(getDataMessages), 1) # Send the first block on invalid chain. Chain should be invalidated. node0.send_message(msg_block(first_block)) def wait_to_invalidate_fork(): chaintips = self.nodes[0].getchaintips() if len(chaintips) > 1: chaintips_status = [ chaintips[0]["status"], chaintips[1]["status"] ] if "active" in chaintips_status and "invalid" in chaintips_status: active_chain_tip_hash = chaintips[0]["hash"] if chaintips[ 0]["status"] == "active" else chaintips[1]["hash"] invalid_fork_tip_hash = chaintips[0]["hash"] if chaintips[ 0]["status"] == "invalid" else chaintips[1]["hash"] assert (active_chain_tip_hash != invalid_fork_tip_hash) for block in blocks_invalid_ttor: if block.hash == invalid_fork_tip_hash: return True return False else: return False else: return False wait_until(wait_to_invalidate_fork) # chaintip of valid branch should be active assert_equal(self.nodes[0].getbestblockhash(), chaintip_valid_branch.hash) # check log file that reorg didnt happen disconnect_block_log = False for line in open( glob.glob(self.options.tmpdir + "/node0" + "/regtest/bitcoind.log")[0]): if f"Disconnect block" in line: disconnect_block_log = True self.log.info("Found line: %s", line.strip()) break # we should not find information about disconnecting blocks assert_equal(disconnect_block_log, False) # check log file that contains information about TTOR violation ttor_violation_log = False for line in open( glob.glob(self.options.tmpdir + "/node0" + "/regtest/bitcoind.log")[0]): if f"violates TTOR order" in line: ttor_violation_log = True self.log.info("Found line: %s", line.strip()) break # we should find information about TTOR being violated assert_equal(ttor_violation_log, True)
def run_test(self): # node = self.nodes[0] # mining_info = node.getmininginfo() # self.log.info('getmininginfo') #为什么这个区块数是200 # assert_equal(mining_info['blocks'], 200) # assert_equal(mining_info['chain'], 'regtest') # assert_equal(mining_info['currentblocktx'], 0) # assert_equal(mining_info['currentblockweight'], 0) # assert_equal(mining_info['difficulty'], Decimal('4.656542373906925E-10')) # assert_equal(mining_info['networkhashps'], Decimal('0.003333333333333334')) # assert_equal(mining_info['pooledtx'], 0) #logfilePath = self.options.tmpdir + '/test_framework.log' #self.log.info(logfilePath) #subprocess.call(['open', '-W', '-a', 'Terminal.app', 'tail', '-f', logfilePath]) #subprocess.call(['tail', '-f', logfilePath]) #nodetest = P2PInterface(); #node0表示测试节点 self.nodes[0]表示bitcoin实际节点 node0 = self.nodes[0].add_p2p_connection(P2PInterface()) #节点信息这里是指连上bitcoin实际节点的节点信息 #networkinfo = self.nodes[0].getnetworkinfo() #self.log.info(networkinfo) #url = urllib.parse.urlparse(self.nodes[0].url) #self.log.info(url) network_thread_start() node0.wait_for_verack() # Set node time to 60 days ago # 将时间调整到2个月之前 mocktime = int(time.time()) - 60 * 24 * 60 * 60 self.nodes[0].setmocktime(mocktime) nblocks = 10 #nblocks = 5 # Generating a chain of 10 blocks #生成10个区块链 block_hashes = self.nodes[0].generate(nblocks) #for hash in block_hashes: # self.log.info(' Node: [%d]:%s' % (i, hash)) #for i in range(block_hashes): # self.log.info(' Node: [%d]:%s' % (i, block_hashes[i])) for i, hash in enumerate(block_hashes): self.log.info('[notice] [%d]:%s' % (i, hash)) #self.log.info('%d:%s'% (i,int(hash, 16))) self.log.info('[notice] generate node %d' % len(block_hashes)) # 在regnet情况下创世块的hash是0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206 #getblockhash0 = self.nodes[0].getblockhash(0) # Create longer chain starting 2 blocks before current tip height = len(block_hashes) - 2 block_hash = block_hashes[height - 1] self.log.info('[notice] starting %d:%s' % (height, block_hash)) # median time 中位时间 block_time = self.nodes[0].getblockheader(block_hash)["mediantime"] + 1 new_blocks = self.build_chain(5, block_hash, height, block_time) for i, hash in enumerate(new_blocks): self.log.info('[notice] n [%d]:%s' % (i, hash.hash)) #self.log.info('%d'% (int(hash.hash, 16))) # Force reorg to a longer chain # 向self.nodes[0]实际节点发送headers消息告诉它最新的节点数据 node0.send_message(msg_headers(new_blocks)) node0.wait_for_getdata() for block in new_blocks: node0.send_and_ping(msg_block(block)) #blockcount = self.nodes[0].getblockcount() # Check that reorg succeeded # 检测self.nodes[0]该节点上区块数量 assert_equal(self.nodes[0].getblockcount(), 13) #取出block_hashes里面最后一条hash数据并且将它转化成16进制 stale_hash = int(block_hashes[-1], 16) self.log.info('[notice] stale_hash:%s' % stale_hash) # Check that getdata request for stale block succeeds # 检测getdata请求发送陈旧的块的hash给self.nodes[0] self.send_block_request(stale_hash, node0) test_function = lambda: self.last_block_equals(stale_hash, node0) wait_until(test_function, timeout=3) # Check that getheader request for stale block header succeeds self.send_header_request(stale_hash, node0) test_function = lambda: self.last_header_equals(stale_hash, node0) wait_until(test_function, timeout=3) # Longest chain is extended so stale is much older than chain tip self.nodes[0].setmocktime(0) tip = self.nodes[0].generate(nblocks=1)[0] assert_equal(self.nodes[0].getblockcount(), 14) # Send getdata & getheaders to refresh last received getheader message block_hash = int(tip, 16) self.send_block_request(block_hash, node0) self.send_header_request(block_hash, node0) node0.sync_with_ping() # Request for very old stale block should now fail self.send_block_request(stale_hash, node0) time.sleep(3) assert not self.last_block_equals(stale_hash, node0) # Request for very old stale block header should now fail self.send_header_request(stale_hash, node0) time.sleep(3) assert not self.last_header_equals(stale_hash, node0) # Verify we can fetch very old blocks and headers on the active chain block_hash = int(block_hashes[2], 16) self.send_block_request(block_hash, node0) self.send_header_request(block_hash, node0) node0.sync_with_ping() self.send_block_request(block_hash, node0) test_function = lambda: self.last_block_equals(block_hash, node0) wait_until(test_function, timeout=3) self.send_header_request(block_hash, node0) test_function = lambda: self.last_header_equals(block_hash, node0) wait_until(test_function, timeout=3)