Changeset View
Changeset View
Standalone View
Standalone View
test/functional/example_test.py
Show First 20 Lines • Show All 134 Lines • ▼ Show 20 Lines | class ExampleTest(BitcoinTestFramework): | ||||
def run_test(self): | def run_test(self): | ||||
"""Main test logic""" | """Main test logic""" | ||||
# Create P2P connections will wait for a verack to make sure the | # Create P2P connections will wait for a verack to make sure the | ||||
# connection is fully up | # connection is fully up | ||||
peer_messaging = self.nodes[0].add_p2p_connection(BaseNode()) | peer_messaging = self.nodes[0].add_p2p_connection(BaseNode()) | ||||
# Generating a block on one of the nodes will get us out of IBD | # Generating a block on one of the nodes will get us out of IBD | ||||
blocks = [int(self.generate(self.nodes[0], sync_fun=lambda: self.sync_all( | blocks = [ | ||||
self.nodes[0:2]), nblocks=1)[0], 16)] | int( | ||||
self.generate( | |||||
self.nodes[0], | |||||
sync_fun=lambda: self.sync_all(self.nodes[0:2]), | |||||
nblocks=1, | |||||
)[0], | |||||
16, | |||||
) | |||||
] | |||||
# Notice above how we called an RPC by calling a method with the same | # Notice above how we called an RPC by calling a method with the same | ||||
# name on the node object. Notice also how we used a keyword argument | # name on the node object. Notice also how we used a keyword argument | ||||
# to specify a named RPC argument. Neither of those are defined on the | # to specify a named RPC argument. Neither of those are defined on the | ||||
# node object. Instead there's some __getattr__() magic going on under | # node object. Instead there's some __getattr__() magic going on under | ||||
# the covers to dispatch unrecognised attribute calls to the RPC | # the covers to dispatch unrecognised attribute calls to the RPC | ||||
# interface. | # interface. | ||||
# Logs are nice. Do plenty of them. They can be used in place of comments for | # Logs are nice. Do plenty of them. They can be used in place of comments for | ||||
# breaking the test into sub-sections. | # breaking the test into sub-sections. | ||||
self.log.info("Starting test!") | self.log.info("Starting test!") | ||||
self.log.info("Calling a custom function") | self.log.info("Calling a custom function") | ||||
custom_function() | custom_function() | ||||
self.log.info("Calling a custom method") | self.log.info("Calling a custom method") | ||||
self.custom_method() | self.custom_method() | ||||
self.log.info("Create some blocks") | self.log.info("Create some blocks") | ||||
self.tip = int(self.nodes[0].getbestblockhash(), 16) | self.tip = int(self.nodes[0].getbestblockhash(), 16) | ||||
self.block_time = self.nodes[0].getblock( | self.block_time = ( | ||||
self.nodes[0].getbestblockhash())['time'] + 1 | self.nodes[0].getblock(self.nodes[0].getbestblockhash())["time"] + 1 | ||||
) | |||||
height = self.nodes[0].getblockcount() | height = self.nodes[0].getblockcount() | ||||
for _ in range(10): | for _ in range(10): | ||||
# Use the blocktools functionality to manually build a block. | # Use the blocktools functionality to manually build a block. | ||||
# Calling the generate() rpc is easier, but this allows us to exactly | # Calling the generate() rpc is easier, but this allows us to exactly | ||||
# control the blocks and transactions. | # control the blocks and transactions. | ||||
block = create_block( | block = create_block(self.tip, create_coinbase(height + 1), self.block_time) | ||||
self.tip, create_coinbase( | |||||
height + 1), self.block_time) | |||||
block.solve() | block.solve() | ||||
block_message = msg_block(block) | block_message = msg_block(block) | ||||
# Send message is used to send a P2P message to the node over our | # Send message is used to send a P2P message to the node over our | ||||
# P2PInterface | # P2PInterface | ||||
peer_messaging.send_message(block_message) | peer_messaging.send_message(block_message) | ||||
self.tip = block.sha256 | self.tip = block.sha256 | ||||
blocks.append(self.tip) | blocks.append(self.tip) | ||||
self.block_time += 1 | self.block_time += 1 | ||||
height += 1 | height += 1 | ||||
self.log.info( | self.log.info("Wait for node1 to reach current tip (height 11) using RPC") | ||||
"Wait for node1 to reach current tip (height 11) using RPC") | |||||
self.nodes[1].waitforblockheight(11) | self.nodes[1].waitforblockheight(11) | ||||
self.log.info("Connect node2 and node1") | self.log.info("Connect node2 and node1") | ||||
self.connect_nodes(1, 2) | self.connect_nodes(1, 2) | ||||
self.log.info("Wait for node2 to receive all the blocks from node1") | self.log.info("Wait for node2 to receive all the blocks from node1") | ||||
self.sync_all() | self.sync_all() | ||||
self.log.info("Add P2P connection to node2") | self.log.info("Add P2P connection to node2") | ||||
self.nodes[0].disconnect_p2ps() | self.nodes[0].disconnect_p2ps() | ||||
peer_receiving = self.nodes[2].add_p2p_connection(BaseNode()) | peer_receiving = self.nodes[2].add_p2p_connection(BaseNode()) | ||||
self.log.info("Test that node2 propagates all the blocks to us") | self.log.info("Test that node2 propagates all the blocks to us") | ||||
getdata_request = msg_getdata() | getdata_request = msg_getdata() | ||||
for block in blocks: | for block in blocks: | ||||
getdata_request.inv.append(CInv(MSG_BLOCK, block)) | getdata_request.inv.append(CInv(MSG_BLOCK, block)) | ||||
peer_receiving.send_message(getdata_request) | peer_receiving.send_message(getdata_request) | ||||
# wait_until() will loop until a predicate condition is met. Use it to test properties of the | # wait_until() will loop until a predicate condition is met. Use it to test properties of the | ||||
# P2PInterface objects. | # P2PInterface objects. | ||||
peer_receiving.wait_until( | peer_receiving.wait_until( | ||||
lambda: sorted(blocks) == sorted(peer_receiving.block_receive_map.keys()), | lambda: sorted(blocks) == sorted(peer_receiving.block_receive_map.keys()), | ||||
timeout=5) | timeout=5, | ||||
) | |||||
self.log.info("Check that each block was received only once") | self.log.info("Check that each block was received only once") | ||||
# The network thread uses a global lock on data access to the P2PConnection objects when sending and receiving | # The network thread uses a global lock on data access to the P2PConnection objects when sending and receiving | ||||
# messages. The test thread should acquire the global lock before accessing any P2PConnection data to avoid locking | # messages. The test thread should acquire the global lock before accessing any P2PConnection data to avoid locking | ||||
# and synchronization issues. Note p2p.wait_until() acquires this | # and synchronization issues. Note p2p.wait_until() acquires this | ||||
# global lock internally when testing the predicate. | # global lock internally when testing the predicate. | ||||
with p2p_lock: | with p2p_lock: | ||||
for block in peer_receiving.block_receive_map.values(): | for block in peer_receiving.block_receive_map.values(): | ||||
assert_equal(block, 1) | assert_equal(block, 1) | ||||
if __name__ == '__main__': | if __name__ == "__main__": | ||||
ExampleTest().main() | ExampleTest().main() |