def _repair_transaction_data(self, transaction_id): """Repair forged transaction_data or asset_file by getting legitimate one from other nodes Args: transaction_id (bytes): target transaction_id """ #print("_repair_transaction_data:") self.stats.update_stats_increment("transaction", "repair_request", 1) forged_asset_files = set() if len(self.data_handler.db_adaptors) > 1: valid_txobj = None db_nums_with_invalid_data = list() for idx in range(1, len(self.data_handler.db_adaptors)): result_txobj, result_asset_files = self.data_handler.search_transaction(transaction_id=transaction_id, db_num=idx) txobj_is_valid, valid_assets, invalid_assets = bbclib.validate_transaction_object(result_txobj[0], result_asset_files) if txobj_is_valid and valid_txobj is None: valid_txobj = result_txobj[0] if not txobj_is_valid: db_nums_with_invalid_data.append(idx) if len(invalid_assets) > 0: for ent in invalid_assets: forged_asset_files.add(ent) if valid_txobj is None: self.stats.update_stats_increment("transaction", "fail_to_repair_in_local", 1) self.logger.fatal("Failed to repair transaction locally (transaction_id=%s in domain=%s)" % (transaction_id.hex(), self.domain_id.hex())) else: for i in db_nums_with_invalid_data: self.data_handler.restore_transaction_data(db_num=i, transaction_id=transaction_id, txobj=valid_txobj) self.stats.update_stats_increment("transaction", "success_repair", 1) self._output_log({"transaction_id": transaction_id.hex(), "request_at": int(time.time()), "repaired_by": "locally", "repaired_at": int(time.time())}) if len(forged_asset_files) > 0: for asgid, ast in forged_asset_files: self._repair_asset_file(asset_group_id=asgid, asset_id=ast, need_check=False) if self.data_handler.replication_strategy == DataHandler.REPLICATION_EXT: return random_nonce = bbclib.get_random_value(4) while random_nonce in self.requesting_list: random_nonce = bbclib.get_random_value(4) self.requesting_list[random_nonce] = { "transaction_id": transaction_id.hex(), "request_at": int(time.time()) } msg = { KeyType.domain_id: self.domain_id, KeyType.infra_msg_type: InfraMessageCategory.CATEGORY_DATA, KeyType.infra_command: DataHandler.REPAIR_TRANSACTION_DATA, KeyType.command: RepairManager.REQUEST_TO_SEND_TRANSACTION_DATA, KeyType.transaction_id: transaction_id, KeyType.nonce: random_nonce, } self.network.broadcast_message_in_network(domain_id=self.domain_id, payload_type=PayloadType.Type_any, msg=msg) return
def get_stats(self): """Get statistics of bbc_core Returns: bytes: query_id """ dat = self._make_message_structure(MsgType.REQUEST_GET_STATS) admin_info = {KeyType.random: bbclib.get_random_value(32)} self.include_admin_info(dat, admin_info, self.node_keypair) return self._send_msg(dat)
def get_forwarding_list(self): """Get forwarding_list of the domain in the core node Returns: bytes: query_id """ dat = self._make_message_structure(MsgType.REQUEST_GET_FORWARDING_LIST) admin_info = {KeyType.random: bbclib.get_random_value(32)} self.include_admin_info(dat, admin_info, self.node_keypair) return self._send_msg(dat)
def get_user_list(self): """Get user_ids in the domain that are connecting to the core node Returns: bytes: query_id """ dat = self._make_message_structure(MsgType.REQUEST_GET_USERS) admin_info = {KeyType.random: bbclib.get_random_value(32)} self.include_admin_info(dat, admin_info, self.node_keypair) return self._send_msg(dat)
def get_domain_list(self): """Get domain_id list in bbc_core Returns: bytes: query_id """ dat = self._make_message_structure(MsgType.REQUEST_GET_DOMAINLIST) admin_info = {KeyType.random: bbclib.get_random_value(32)} self.include_admin_info(dat, admin_info, self.node_keypair) return self._send_msg(dat)
def get_notification_list(self): """Get notification_list of the core node Returns: bytes: query_id """ dat = self._make_message_structure( MsgType.REQUEST_GET_NOTIFICATION_LIST) admin_info = {KeyType.random: bbclib.get_random_value(32)} self.include_admin_info(dat, admin_info, self.node_keypair) return self._send_msg(dat)
def notify_domain_key_update(self): """Notify update of bbc_core This method should be used by a system administrator. Returns: bytes: query_id """ dat = self._make_message_structure(MsgType.NOTIFY_DOMAIN_KEY_UPDATE) admin_info = {KeyType.random: bbclib.get_random_value(32)} self.include_admin_info(dat, admin_info, self.node_keypair) return self._send_msg(dat)
def get_bbc_config(self): """Get config file of bbc_core This method should be used by a system administrator. Returns: bytes: query_id """ dat = self._make_message_structure(MsgType.REQUEST_GET_CONFIG) admin_info = {KeyType.random: bbclib.get_random_value(32)} self.include_admin_info(dat, admin_info, self.node_keypair) return self._send_msg(dat)
def _repair_asset_file(self, asset_group_id, asset_id, need_check=True): """Repair forged asset_file by getting legitimate one from other nodes Args: asset_group_id (bytes): asset_group_id of the asset asset_id (bytes): asset_id of the asset need_check (bool): If True, check the digest of the asset file """ #print("_repair_asset_file:") if self.data_handler.use_external_storage: return if need_check: asset_file = self.data_handler.get_in_storage( asset_group_id, asset_id) if asset_file is not None and asset_id == hashlib.sha256( asset_file).digest(): return random_nonce = bbclib.get_random_value(4) while random_nonce in self.requesting_list: random_nonce = bbclib.get_random_value(4) self.requesting_list[random_nonce] = { "asset_group_id": asset_group_id.hex(), "asset_id": asset_id.hex(), "request_at": int(time.time()) } msg = { KeyType.domain_id: self.domain_id, KeyType.infra_msg_type: InfraMessageCategory.CATEGORY_DATA, KeyType.infra_command: DataHandler.REPAIR_TRANSACTION_DATA, KeyType.command: RepairManager.REQUEST_TO_SEND_ASSET_FILE, KeyType.asset_group_id: asset_group_id, KeyType.asset_id: asset_id, KeyType.nonce: random_nonce, } self.network.broadcast_message_in_network( domain_id=self.domain_id, payload_type=PayloadType.Type_any, msg=msg)
def get_domain_neighborlist(self, domain_id): """Get peer list of the domain from the core node This method should be used by a system administrator. Args: domain_id (bytes): domain_id of the neighbor list Returns: bytes: query_id """ dat = self._make_message_structure(MsgType.REQUEST_GET_NEIGHBORLIST) dat[KeyType.domain_id] = domain_id admin_info = {KeyType.random: bbclib.get_random_value(32)} self.include_admin_info(dat, admin_info, self.node_keypair) return self._send_msg(dat)
def remove_domain(self, domain_id=ZEROS): """Leave the domain and remove it Args: domain_id (bytes): target domain_id to remove Returns: bool: True if successful """ if domain_id not in self.domains: return False self.domains[domain_id]['topology'].stop_all_timers() self.domains[domain_id]['user'].stop_all_timers() self.domains[domain_id]['repair'].exit_loop() for nd in self.domains[domain_id]["neighbor"].nodeinfo_list.values(): nd.key_manager.stop_all_timers() msg = { KeyType.infra_msg_type: InfraMessageCategory.CATEGORY_NETWORK, KeyType.domain_id: domain_id, KeyType.command: BBcNetwork.NOTIFY_LEAVE, } admin_info = { KeyType.source_node_id: self.domains[domain_id]["neighbor"].my_node_id, KeyType.nonce: bbclib.get_random_value(32) # just for randomization } self.include_admin_info_into_message_if_needed(domain_id, msg, admin_info) self.broadcast_message_in_network(domain_id=domain_id, msg=msg) if domain_id == ZEROS: self.domain0manager.stop_all_timers() for dm in self.domains.keys(): if dm != ZEROS: self.domains[dm]['neighbor'].my_info.update(domain0=False) self.domains[dm]['topology'].update_refresh_timer_entry(1) del self.domains[domain_id] if self.domain0manager is not None: self.domain0manager.update_domain_belong_to() self.config.remove_domain_config(domain_id) self.stats.update_stats_decrement("network", "num_domains", 1) self.logger.info("Domain %s is removed" % (domain_id.hex())) return True
def manipulate_ledger_subsystem(self, enable=False, domain_id=None): """Start/stop ledger_subsystem on the bbc_core This method should be used by a system administrator. Args: enable (bool): True->start, False->stop domain_id (bytes): target domain_id to enable/disable ledger_subsystem Returns: bytes: query_id """ dat = self._make_message_structure(MsgType.REQUEST_MANIP_LEDGER_SUBSYS) dat[KeyType.domain_id] = domain_id admin_info = { KeyType.ledger_subsys_manip: enable, KeyType.random: bbclib.get_random_value(32) } self.include_admin_info(dat, admin_info, self.node_keypair) return self._send_msg(dat)
def domain_close(self, domain_id=None): """Close domain leading to remove_domain in the core Args: domain_id (bytes): domain_id to delete Returns: bytes: query_id """ if domain_id is None and self.domain_id is not None: domain_id = self.domain_id if domain_id is None: return None dat = self._make_message_structure(MsgType.REQUEST_CLOSE_DOMAIN) admin_info = { KeyType.domain_id: domain_id, KeyType.random: bbclib.get_random_value(32) } self.include_admin_info(dat, admin_info, self.node_keypair) return self._send_msg(dat)
def domain_setup(self, domain_id, config=None): """Set up domain with the specified network module and storage This method should be used by a system administrator. Args: domain_id (bytes): domain_id to create config (str): system config in json format Returns: bytes: query_id """ dat = self._make_message_structure(MsgType.REQUEST_SETUP_DOMAIN) admin_info = { KeyType.domain_id: domain_id, KeyType.random: bbclib.get_random_value(32) } if config is not None: admin_info[KeyType.bbc_configuration] = config self.include_admin_info(dat, admin_info, self.node_keypair) return self._send_msg(dat)