def test_build_trackers(generate_dummy_tracker): # build_trackers builds two dictionaries: trackers (uuid: TransactionTracker) and tx_tracker_map (txid:uuid) # These are populated with data pulled from the database and used as initial state by the Responder during bootstrap trackers_data = {} # Create some trackers data for i in range(10): tracker = generate_dummy_tracker() trackers_data[uuid4().hex] = tracker.to_dict() # Add some additional trackers that share the same locator to test all the builder's cases if i % 2 == 0: penalty_txid = tracker.penalty_txid tracker = generate_dummy_tracker() tracker.penalty_txid = penalty_txid trackers_data[uuid4().hex] = tracker.to_dict() trackers, tx_tracker_map = Builder.build_trackers(trackers_data) # Check that the built trackers match the data for uuid, tracker in trackers.items(): assert uuid in trackers_data.keys() assert tracker.get("penalty_txid") == trackers_data[uuid].get( "penalty_txid") assert tracker.get("locator") == trackers_data[uuid].get("locator") assert tracker.get("user_id") == trackers_data[uuid].get("user_id") assert uuid in tx_tracker_map[tracker.get("penalty_txid")]
def test_build_trackers(): trackers_data = {} # Create some trackers data for i in range(10): tracker = generate_dummy_tracker() trackers_data[uuid4().hex] = tracker.to_dict() # Add some additional trackers that share the same locator to test all the builder's cases if i % 2 == 0: penalty_txid = tracker.penalty_txid tracker = generate_dummy_tracker() tracker.penalty_txid = penalty_txid trackers_data[uuid4().hex] = tracker.to_dict() trackers, tx_tracker_map = Builder.build_trackers(trackers_data) # Check that the built trackers match the data for uuid, tracker in trackers.items(): assert uuid in trackers_data.keys() assert tracker.get("penalty_txid") == trackers_data[uuid].get( "penalty_txid") assert tracker.get("locator") == trackers_data[uuid].get("locator") assert tracker.get("appointment_end") == trackers_data[uuid].get( "appointment_end") assert uuid in tx_tracker_map[tracker.get("penalty_txid")]
def main(command_line_conf): global db_manager, chain_monitor signal(SIGINT, handle_signals) signal(SIGTERM, handle_signals) signal(SIGQUIT, handle_signals) # Loads config and sets up the data folder and log file config_loader = ConfigLoader(DATA_DIR, CONF_FILE_NAME, DEFAULT_CONF, command_line_conf) config = config_loader.build_config() setup_data_folder(DATA_DIR) setup_logging(config.get("LOG_FILE"), LOG_PREFIX) logger.info("Starting TEOS") db_manager = DBManager(config.get("DB_PATH")) bitcoind_connect_params = { k: v for k, v in config.items() if k.startswith("BTC") } bitcoind_feed_params = { k: v for k, v in config.items() if k.startswith("FEED") } if not can_connect_to_bitcoind(bitcoind_connect_params): logger.error("Can't connect to bitcoind. Shutting down") elif not in_correct_network(bitcoind_connect_params, config.get("BTC_NETWORK")): logger.error( "bitcoind is running on a different network, check conf.py and bitcoin.conf. Shutting down" ) else: try: secret_key_der = Cryptographer.load_key_file( config.get("TEOS_SECRET_KEY")) if not secret_key_der: raise IOError("TEOS private key can't be loaded") block_processor = BlockProcessor(bitcoind_connect_params) carrier = Carrier(bitcoind_connect_params) responder = Responder(db_manager, carrier, block_processor) watcher = Watcher( db_manager, block_processor, responder, secret_key_der, config.get("MAX_APPOINTMENTS"), config.get("EXPIRY_DELTA"), ) # Create the chain monitor and start monitoring the chain chain_monitor = ChainMonitor(watcher.block_queue, watcher.responder.block_queue, block_processor, bitcoind_feed_params) watcher_appointments_data = db_manager.load_watcher_appointments() responder_trackers_data = db_manager.load_responder_trackers() if len(watcher_appointments_data) == 0 and len( responder_trackers_data) == 0: logger.info("Fresh bootstrap") watcher.awake() watcher.responder.awake() else: logger.info("Bootstrapping from backed up data") # Update the Watcher backed up data if found. if len(watcher_appointments_data) != 0: watcher.appointments, watcher.locator_uuid_map = Builder.build_appointments( watcher_appointments_data) # Update the Responder with backed up data if found. if len(responder_trackers_data) != 0: watcher.responder.trackers, watcher.responder.tx_tracker_map = Builder.build_trackers( responder_trackers_data) # Awaking components so the states can be updated. watcher.awake() watcher.responder.awake() last_block_watcher = db_manager.load_last_block_hash_watcher() last_block_responder = db_manager.load_last_block_hash_responder( ) # Populate the block queues with data if they've missed some while offline. If the blocks of both match # we don't perform the search twice. # FIXME: 32-reorgs-offline dropped txs are not used at this point. last_common_ancestor_watcher, dropped_txs_watcher = block_processor.find_last_common_ancestor( last_block_watcher) missed_blocks_watcher = block_processor.get_missed_blocks( last_common_ancestor_watcher) if last_block_watcher == last_block_responder: dropped_txs_responder = dropped_txs_watcher missed_blocks_responder = missed_blocks_watcher else: last_common_ancestor_responder, dropped_txs_responder = block_processor.find_last_common_ancestor( last_block_responder) missed_blocks_responder = block_processor.get_missed_blocks( last_common_ancestor_responder) # If only one of the instances needs to be updated, it can be done separately. if len(missed_blocks_watcher ) == 0 and len(missed_blocks_responder) != 0: Builder.populate_block_queue(watcher.responder.block_queue, missed_blocks_responder) watcher.responder.block_queue.join() elif len(missed_blocks_responder ) == 0 and len(missed_blocks_watcher) != 0: Builder.populate_block_queue(watcher.block_queue, missed_blocks_watcher) watcher.block_queue.join() # Otherwise they need to be updated at the same time, block by block elif len(missed_blocks_responder) != 0 and len( missed_blocks_watcher) != 0: Builder.update_states(watcher, missed_blocks_watcher, missed_blocks_responder) # Fire the API and the ChainMonitor # FIXME: 92-block-data-during-bootstrap-db chain_monitor.monitor_chain() API(Inspector(block_processor, config.get("MIN_TO_SELF_DELAY")), watcher).start() except Exception as e: logger.error("An error occurred: {}. Shutting down".format(e)) exit(1)
def bootstrap_components(self): """ Performs the initial setup of the components. It loads the appointments and tracker for the watcher and the responder (if any), and awakes the components. It also populates the block queues with any missing data, in case the tower has been offline for some time. Finally, it starts the chain monitor. """ # Make sure that the ChainMonitor starts listening to new blocks while we bootstrap self.chain_monitor.monitor_chain() watcher_appointments_data = self.db_manager.load_watcher_appointments() responder_trackers_data = self.db_manager.load_responder_trackers() if len(watcher_appointments_data) == 0 and len( responder_trackers_data) == 0: self.logger.info("Fresh bootstrap") self.watcher_thread = self.watcher.awake() self.responder_thread = self.watcher.responder.awake() else: self.logger.info("Bootstrapping from backed up data") # Update the Watcher backed up data if found. if len(watcher_appointments_data) != 0: self.watcher.appointments, self.watcher.locator_uuid_map = Builder.build_appointments( watcher_appointments_data) # Update the Responder with backed up data if found. if len(responder_trackers_data) != 0: self.watcher.responder.trackers, self.watcher.responder.tx_tracker_map = Builder.build_trackers( responder_trackers_data) # Awaking components so the states can be updated. self.watcher_thread = self.watcher.awake() self.responder_thread = self.watcher.responder.awake() last_block_watcher = self.db_manager.load_last_block_hash_watcher() last_block_responder = self.db_manager.load_last_block_hash_responder( ) # Populate the block queues with data if they've missed some while offline. If the blocks of both match # we don't perform the search twice. # FIXME: 32-reorgs-offline dropped txs are not used at this point. last_common_ancestor_watcher, dropped_txs_watcher = self.block_processor.find_last_common_ancestor( last_block_watcher) missed_blocks_watcher = self.block_processor.get_missed_blocks( last_common_ancestor_watcher) if last_block_watcher == last_block_responder: dropped_txs_responder = dropped_txs_watcher missed_blocks_responder = missed_blocks_watcher else: last_common_ancestor_responder, dropped_txs_responder = self.block_processor.find_last_common_ancestor( last_block_responder) missed_blocks_responder = self.block_processor.get_missed_blocks( last_common_ancestor_responder) # If only one of the instances needs to be updated, it can be done separately. if len(missed_blocks_watcher ) == 0 and len(missed_blocks_responder) != 0: Builder.populate_block_queue( self.watcher.responder.block_queue, missed_blocks_responder) self.watcher.responder.block_queue.join() elif len(missed_blocks_responder ) == 0 and len(missed_blocks_watcher) != 0: Builder.populate_block_queue(self.watcher.block_queue, missed_blocks_watcher) self.watcher.block_queue.join() # Otherwise they need to be updated at the same time, block by block elif len(missed_blocks_responder) != 0 and len( missed_blocks_watcher) != 0: Builder.update_states( self.watcher.block_queue, self.watcher.responder.block_queue, missed_blocks_watcher, missed_blocks_responder, ) # Activate ChainMonitor self.chain_monitor.activate()