def test_monitor_chain_single_update(block_processor): # This test tests that if both threads try to add the same block to the queue, only the first one will make it chain_monitor = ChainMonitor([Queue(), Queue()], block_processor, bitcoind_feed_params) chain_monitor.polling_delta = 2 # We will create a block and wait for the polling thread. Then check the queues to see that the block hash has only # been added once. chain_monitor.monitor_chain() chain_monitor.activate() generate_blocks(1) assert len(chain_monitor.receiving_queues) == 2 queue0_block = chain_monitor.receiving_queues[0].get() queue1_block = chain_monitor.receiving_queues[1].get() assert queue0_block == queue1_block assert chain_monitor.receiving_queues[0].empty() assert chain_monitor.receiving_queues[1].empty() # The delta for polling is 2 secs, so let's wait and see time.sleep(2) assert chain_monitor.receiving_queues[0].empty() assert chain_monitor.receiving_queues[1].empty() # We can also force an update and see that it won't go through assert chain_monitor.enqueue(queue0_block) is False chain_monitor.terminate() # The zmq thread needs a block generation to release from the recv method. generate_blocks(1)
def test_monitor_chain_polling(block_processor_mock, monkeypatch): # Monkeypatch the BlockProcessor so the best tip remains unchanged fixed_tip = get_random_value_hex(32) monkeypatch.setattr(block_processor_mock, "get_best_block_hash", lambda blocking: fixed_tip) chain_monitor = ChainMonitor([Queue(), Queue()], block_processor_mock, bitcoind_feed_params) chain_monitor.last_tips = [fixed_tip] chain_monitor.polling_delta = 0.1 # monitor_chain_polling runs until not terminated polling_thread = Thread(target=chain_monitor.monitor_chain_polling, daemon=True) polling_thread.start() # Check that nothing changes as long as a block is not generated for _ in range(5): assert chain_monitor.queue.empty() time.sleep(0.1) # And that it does if we generate a block monkeypatch.setattr(block_processor_mock, "get_best_block_hash", lambda blocking: get_random_value_hex(32)) time.sleep(0.1) chain_monitor.queue.get() assert chain_monitor.queue.empty() # Check that the bitcoind_reachable event is cleared if the connection is lost, and set once it's recovered monkeypatch.setattr(block_processor_mock, "get_best_block_hash", mock_connection_refused_return) time.sleep(0.5) assert not chain_monitor.bitcoind_reachable.is_set() monkeypatch.delattr(block_processor_mock, "get_best_block_hash") time.sleep(0.5) assert chain_monitor.bitcoind_reachable.is_set() chain_monitor.terminate()
def test_monitor_chain_polling(db_manager, block_processor): # Try polling with the Watcher watcher_queue = Queue() chain_monitor = ChainMonitor(watcher_queue, Queue(), block_processor, bitcoind_feed_params) chain_monitor.best_tip = block_processor.get_best_block_hash() chain_monitor.polling_delta = 0.1 # monitor_chain_polling runs until terminate if set polling_thread = Thread(target=chain_monitor.monitor_chain_polling, daemon=True) polling_thread.start() # Check that nothing changes as long as a block is not generated for _ in range(5): assert chain_monitor.watcher_queue.empty() time.sleep(0.1) # And that it does if we generate a block generate_block() chain_monitor.watcher_queue.get() assert chain_monitor.watcher_queue.empty() chain_monitor.terminate = True polling_thread.join()
def test_monitor_chain_single_update(db_manager, block_processor): # This test tests that if both threads try to add the same block to the queue, only the first one will make it chain_monitor = ChainMonitor(Queue(), Queue(), block_processor, bitcoind_feed_params) chain_monitor.best_tip = None chain_monitor.polling_delta = 2 # We will create a block and wait for the polling thread. Then check the queues to see that the block hash has only # been added once. chain_monitor.monitor_chain() generate_block() watcher_block = chain_monitor.watcher_queue.get() responder_block = chain_monitor.responder_queue.get() assert watcher_block == responder_block assert chain_monitor.watcher_queue.empty() assert chain_monitor.responder_queue.empty() # The delta for polling is 2 secs, so let's wait and see time.sleep(2) assert chain_monitor.watcher_queue.empty() assert chain_monitor.responder_queue.empty() # We can also force an update and see that it won't go through assert chain_monitor.update_state(watcher_block) is False
def test_terminate(block_processor): queue = Queue() chain_monitor = ChainMonitor([queue, Queue()], block_processor, bitcoind_feed_params) chain_monitor.polling_delta = 0.1 chain_monitor.monitor_chain() chain_monitor.activate() chain_monitor.terminate() assert chain_monitor.status == ChainMonitorStatus.TERMINATED # generate a new block generate_blocks(1) time.sleep(0.11) # wait longer than the polling_delta # there should be only the ChainMonitor.END_MESSAGE message in the receiving queue, as the new block was generated # after terminating assert queue.qsize() == 1 assert queue.get() == ChainMonitor.END_MESSAGE
def test_monitor_chain_polling(block_processor): chain_monitor = ChainMonitor([Queue(), Queue()], block_processor, bitcoind_feed_params) chain_monitor.last_tips = [block_processor.get_best_block_hash()] chain_monitor.polling_delta = 0.1 # monitor_chain_polling runs until not terminated polling_thread = Thread(target=chain_monitor.monitor_chain_polling, daemon=True) polling_thread.start() # Check that nothing changes as long as a block is not generated for _ in range(5): assert chain_monitor.queue.empty() time.sleep(0.1) # And that it does if we generate a block generate_blocks(1) chain_monitor.queue.get() assert chain_monitor.queue.empty() chain_monitor.terminate()
def test_monitor_chain_and_activate(block_processor): # In this test, we generate some blocks after `monitor_chain`, then `activate` and generate few more blocks. # We verify that all the generated blocks are indeed sent to the queues in the right order. queue1 = Queue() queue2 = Queue() # We add some initial blocks to the receiving queues, to simulate a bootstrap with previous information pre_blocks = [get_random_value_hex(32) for _ in range(5)] for block in pre_blocks: queue1.put(block) queue2.put(block) # We don't activate the ChainMonitor but we start listening; therefore received blocks should accumulate in the # internal queue chain_monitor = ChainMonitor([queue1, queue2], block_processor, bitcoind_feed_params) chain_monitor.polling_delta = 0.1 chain_monitor.monitor_chain() assert chain_monitor.status == ChainMonitorStatus.LISTENING # we generate some blocks while the monitor is listening but not active init_blocks = generate_blocks_with_delay(3, 0.15) time.sleep(0.11) # higher than the polling interval chain_monitor.activate() # generate some more blocks after activating after_blocks = generate_blocks_with_delay(3, 0.15) # we now check that all the blocks are in the receiving queues in the correct order all_blocks = pre_blocks + init_blocks + after_blocks for block in all_blocks: assert queue1.get(timeout=0.1) == block assert queue2.get(timeout=0.1) == block chain_monitor.terminate() # The zmq thread needs a block generation to release from the recv method. generate_blocks(1)
def test_terminate(block_processor_mock, monkeypatch): # Test that the ChainMonitor is stopped on a terminate signal queue = Queue() chain_monitor = ChainMonitor([queue, Queue()], block_processor_mock, bitcoind_feed_params) chain_monitor.polling_delta = 0.1 # Activate the monitor chain_monitor.monitor_chain() chain_monitor.activate() # Ask it to terminate chain_monitor.terminate() assert chain_monitor.status == ChainMonitorStatus.TERMINATED # Mock generating a block generate a new block monkeypatch.setattr(block_processor_mock, "get_best_block_hash", lambda blocking: get_random_value_hex(32)) time.sleep(0.11) # wait longer than the polling_delta # there should be only the ChainMonitor.END_MESSAGE message in the receiving queue, as the new block was generated # after terminating assert queue.qsize() == 1 assert queue.get() == ChainMonitor.END_MESSAGE
def test_monitor_chain(block_processor): # We don't activate it but we start listening; therefore received blocks should accumulate in the internal queue chain_monitor = ChainMonitor([Queue(), Queue()], block_processor, bitcoind_feed_params) chain_monitor.polling_delta = 0.1 chain_monitor.monitor_chain() assert chain_monitor.status == ChainMonitorStatus.LISTENING # The tip is updated before starting the threads, so it should have been added to last_tips. assert len(chain_monitor.last_tips) > 0 # Blocks should be received and added to the queue count = 0 for _ in range(5): generate_blocks(1) count += 1 time.sleep(0.11) # higher than the polling interval assert chain_monitor.receiving_queues[0].empty() assert chain_monitor.receiving_queues[1].empty() assert chain_monitor.queue.qsize() == count chain_monitor.terminate() # The zmq thread needs a block generation to release from the recv method. generate_blocks(1)