def test_03_send_ping(self): print("\n-----", sys._getframe().f_code.co_name, "-----") for i in range(1, core_nodes): print("node=%d: ping_to:%s" % (i, binascii.b2a_hex(nodes[0]))) query_entry = query_management.QueryEntry( expire_after=2, callback_expire=get_test_func_failure, data={}, retry_count=1) query_entry.update(2, callback=get_test_func_success) ret = cores[i].networking.domains[domain_id].send_ping( nodes[0], query_entry.nonce) assert ret print("wait queue: 9") total = wait_results(9) assert total == 9 cores[0].networking.domains[domain_id].print_peerlist() query_entry = query_management.QueryEntry( expire_after=2, callback_expire=get_test_func_failure, data={}, retry_count=1) query_entry.update(2, callback=get_test_func_success) ret = cores[2].networking.domains[domain_id].send_ping( nodes[3], query_entry.nonce) assert not ret print("wait queue: 1 (** should fail to send)") total = wait_results(1) assert total == 0
def alive_check(self): query_entry = query_management.QueryEntry(expire_after=15, callback_expire=self.broadcast_peerlist, data={KeyType.peer_info: [], 'number_of_ping': len(self.id_ip_mapping)}, retry_count=0) for nd in self.id_ip_mapping.keys(): query_entry2 = query_management.QueryEntry(expire_after=14, callback_expire=None, callback=self.add_advertise_list, callback_error=self.ping_with_retry, interval=3, data={KeyType.node_id: nd, 'parent_nonce': query_entry.nonce}) self.ping_with_retry(query_entry2)
def send_domain_ping(self, domain_id, ipv4, ipv6, port, is_static=False): """ (internal use) Send raw message to the specified node :param domain_id: :param ipv4: :param ipv6: :param port: :param is_static: :return: """ if domain_id not in self.domains: return False if ipv4 is None and ipv6 is None: return False node_id = self.domains[domain_id]['neighbor'].my_node_id nodeinfo = NodeInfo(ipv4=ipv4, ipv6=ipv6, port=port, is_static=is_static) query_entry = query_management.QueryEntry( expire_after=10, callback_error=self.domain_ping, callback_expire=self.invalidate_neighbor, data={ KeyType.domain_id: domain_id, KeyType.node_id: node_id, KeyType.node_info: nodeinfo }, retry_count=3) self.domain_ping(query_entry) return True
def _update_cross_ref_timer_entry(self): """Update cross_ref timer""" rand_interval = random.randint(int(Domain0Manager.DOMAIN_ACCEPTANCE_RECOVER_INTERVAL * 5 / 6), int(Domain0Manager.DOMAIN_ACCEPTANCE_RECOVER_INTERVAL * 7 / 6)) self.logger.debug("update_cross_ref_timer_entry: %d" % rand_interval) self.cross_ref_timer_entry = query_management.QueryEntry( expire_after=rand_interval, callback_expire=self._purge_left_cross_ref, retry_count=0)
def _update_advertise_timer_entry(self): """Update advertisement timer""" rand_interval = random.randint(int(Domain0Manager.DOMAIN_INFO_ADVERTISE_INTERVAL * 5 / 6), int(Domain0Manager.DOMAIN_INFO_ADVERTISE_INTERVAL * 7 / 6)) self.logger.debug("_update_advertise_timer_entry: %d" % rand_interval) self.advertise_timer_entry = query_management.QueryEntry( expire_after=rand_interval, callback_expire=self._advertise_domain_info, retry_count=0)
def test_03_callback_expire(self): print("\n-----", sys._getframe().f_code.co_name, "-----") entry = query_management.QueryEntry(expire_after=5, callback_expire=callback_expire, data=[4, 4, 4]) total = wait_results(1) assert total == -1
def _add_user_for_forwarding(self, user_id, node_id, permanent=False): """Register user to forwarding list Args: user_id (bytes): target user_id node_id (bytes): node_id which the client with the user_id connects to parmanent (bool): If True, the entry won't expire """ self.forwarding_entries.setdefault(user_id, dict()) if not permanent: if 'refresh' not in self.forwarding_entries[user_id]: query_entry = query_management.QueryEntry( expire_after=UserMessageRouting. REFRESH_FORWARDING_LIST_INTERVAL, callback_expire=self._remove_user_from_forwarding, data={ KeyType.user_id: user_id, }, retry_count=0) self.forwarding_entries[user_id]['refresh'] = query_entry else: self.forwarding_entries[user_id]['refresh'].update( fire_after=UserMessageRouting. REFRESH_FORWARDING_LIST_INTERVAL) self.forwarding_entries[user_id].setdefault('nodes', set()) self.forwarding_entries[user_id]['nodes'].add(node_id) self.stats.update_stats("user_message", "registered_users_in_forwarding_list", len(self.forwarding_entries))
def _resolve_accommodating_core_node(self, dst_user_id, src_user_id, orig_msg=None): """Resolve which node the user connects to Find the node that accommodates the user_id first, and then, send the message to the node. Args: dst_user_id (bytes): destination user_id src_user_id (bytes): source user_id orig_msg (dict): message to send """ if orig_msg is not None: query_entry = query_management.QueryEntry( expire_after=UserMessageRouting.RESOLVE_TIMEOUT, callback_expire=self._resolve_failure, callback=self._resolve_success, data={ KeyType.message: orig_msg, }, retry_count=0) self.on_going_timers.add(query_entry.nonce) msg = { KeyType.infra_msg_type: InfraMessageCategory.CATEGORY_USER, KeyType.domain_id: self.domain_id, KeyType.infra_command: UserMessageRouting.RESOLVE_USER_LOCATION, KeyType.destination_user_id: dst_user_id, } if orig_msg is not None: msg[KeyType.nonce] = query_entry.nonce if src_user_id is not None: msg[KeyType.source_user_id] = src_user_id self.networking.broadcast_message_in_network(domain_id=self.domain_id, msg=msg)
def send_raw_message(self, domain_id, ipv4, ipv6, port): """ (internal use) Send raw message to the specified node :param domain_id: :param ipv4: :param ipv6: :param port: :return: """ if domain_id not in self.domains: return False node_id = self.domains[domain_id].node_id nodeinfo = NodeInfo(ipv4=ipv4, ipv6=ipv6, port=port) query_entry = query_management.QueryEntry(expire_after=10, callback_error=self.raw_ping, data={ KeyType.domain_id: domain_id, KeyType.node_id: node_id, KeyType.peer_info: nodeinfo }, retry_count=3) self.raw_ping(query_entry) return True
def get_domain_keypair(self, domain_id): """Get domain_keys (private key and public key) Args: domain_id (bytes): target domain_id """ keyconfig = self.config.get_config().get('domain_key', None) if keyconfig is None: self.domains[domain_id]['keypair'] = None return if 'use' not in keyconfig or not keyconfig['use']: return if 'directory' not in keyconfig or not os.path.exists(keyconfig['directory']): self.domains[domain_id]['keypair'] = None return domain_id_str = domain_id.hex() keypair = bbclib.KeyPair() try: with open(os.path.join(keyconfig['directory'], domain_id_str+".pem"), "r") as f: keypair.mk_keyobj_from_private_key_pem(f.read()) except: self.domains[domain_id]['keypair'] = None return self.domains[domain_id].setdefault('keypair', dict()) self.domains[domain_id]['keypair'].setdefault('keys', list()) self.domains[domain_id]['keypair']['keys'].insert(0, keypair) timer = self.domains[domain_id]['keypair'].setdefault('timer', None) if timer is None or not timer.active: self.domains[domain_id]['keypair']['timer'] = query_management.QueryEntry( expire_after=keyconfig['obsolete_timeout'], data={KeyType.domain_id: domain_id}, callback_expire=self._delete_obsoleted_domain_keys) else: timer.update_expiration_time(keyconfig['obsolete_timeout']) self.domains[domain_id]['keypair']['keys'].insert(0, keypair)
def send_domain_ping(self, domain_id, ipv4, ipv6, port, is_static=False): """Send domain ping to the specified node Args: domain_id (bytes): target domain_id ipv4 (str): IPv4 address of the node ipv6 (str): IPv6 address of the node port (int): Port number is_static (bool): If true, the entry is treated as static one and will be saved in config.json Returns: bool: True if successful """ if domain_id not in self.domains: return False if ipv4 is None and ipv6 is None: return False node_id = self.domains[domain_id]['neighbor'].my_node_id nodeinfo = NodeInfo(ipv4=ipv4, ipv6=ipv6, port=port, is_static=is_static) query_entry = query_management.QueryEntry( expire_after=10, callback_error=self._domain_ping, callback_expire=self._invalidate_neighbor, data={ KeyType.domain_id: domain_id, KeyType.node_id: node_id, KeyType.node_info: nodeinfo }, retry_count=3) self._domain_ping(query_entry) return True
def test_06_multiple_entries_normal_error_expire(self): print("\n-----", sys._getframe().f_code.co_name, "-----") query_entries = [] for i in range(10): entry = query_management.QueryEntry( expire_after=5, callback_expire=callback_expire, callback=callback_normal, callback_error=callback_error, data=[i, 0, 6]) entry.update(2) query_entries.append(entry.nonce) time.sleep(1) # -- normal for i in range(10): entry = ticker.get_entry(query_entries[i]) entry.callback() total = wait_results(10) assert total == 10 # -- error for i in range(10): entry = ticker.get_entry(query_entries[i]) entry.update(1) total = wait_results(10) assert total == 0 # -- finally time.sleep(5) total = wait_results(10) assert total == -10
def test_04_alive_check(self): print("\n-----", sys._getframe().f_code.co_name, "-----") for i in range(core_nodes): cores[i].networking.domains[domain_id].print_peerlist() cores[0].networking.domains[domain_id].alive_check() print("** wait 5 sec to finish alive_check") time.sleep(5) assert len( cores[1].networking.domains[domain_id].id_ip_mapping) == 10 - 1 query_entry = query_management.QueryEntry( expire_after=2, callback_expire=get_test_func_failure, data={}, retry_count=1) query_entry.update(2, callback=get_test_func_success) ret = cores[2].networking.domains[domain_id].send_ping( nodes[3], query_entry.nonce) assert ret print("wait queue: 1") total = wait_results(1) assert total == 1 for i in range(core_nodes): cores[i].networking.domains[domain_id].print_peerlist()
def purge(self, query_entry): for node_id in list(self.nodeinfo_list.keys()): if not self.nodeinfo_list[node_id].is_alive: self.nodeinfo_list.pop(node_id, None) self.purge_timer = query_management.QueryEntry( expire_after=NeighborInfo.PURGE_INTERVAL_SEC, callback_expire=self.purge, retry_count=3)
def purge(self, query_entry): """Purge obsoleted entry in nodeinfo_list""" for node_id in list(self.nodeinfo_list.keys()): if not self.nodeinfo_list[node_id].is_alive or self.nodeinfo_list[node_id].updated_at + \ NeighborInfo.NODEINFO_LIFETIME < time.time(): self.nodeinfo_list.pop(node_id, None) self.purge_timer = query_management.QueryEntry(expire_after=NeighborInfo.PURGE_INTERVAL_SEC, callback_expire=self.purge, retry_count=3)
def _set_delete_timer(self, key_name, timeout): """Set timer for key revocation""" if key_name is not None: #print("(%d) _set_delete_timer:" % int(time.time()), key_name.hex()[:10], timeout) query_management.QueryEntry(expire_after=timeout, callback_expire=remove_old_key, data={KeyType.hint: key_name}, retry_count=0)
def add_node(self, node_id): if node_id not in self.asset_groups: self.asset_groups[node_id] = query_management.QueryEntry(expire_after=ASSET_GROUP_INFO_LIFETIME, callback_expire=self.remove_entry, data={KeyType.node_id: node_id}, retry_count=0) else: self.asset_groups[node_id].update_expiration_time(ASSET_GROUP_INFO_LIFETIME)
def set_invoke_timer(self, timeout, retry_entry=False): if self.timer_entry is not None and self.timer_entry.active: self.timer_entry.deactivate() #print("(%d) set_invoke_timer:" % int(time.time()), timeout) self.timer_entry = query_management.QueryEntry(expire_after=timeout, callback_expire=self.perform_key_exchange, retry_count=0) if retry_entry: self.timer_entry.data[KeyType.retry_timer] = True
def __init__(self, network=None, domain_id=None, node_id=None, my_info=None): self.networking = network self.domain_id = domain_id self.my_node_id = node_id self.my_info = my_info self.admin_sequence_number = 0 self.nodeinfo_list = dict() self.purge_timer = query_management.QueryEntry(expire_after=NeighborInfo.PURGE_INTERVAL_SEC, callback_expire=self.purge, retry_count=3)
def test_02_normal_callback(self): print("\n-----", sys._getframe().f_code.co_name, "-----") entry = query_management.QueryEntry(expire_after=2, callback_expire=callback_expire, callback=callback_normal, callback_error=callback_error, data=[2, 2, 2]) entry.update(fire_after=1.5) time.sleep(1) entry.callback() # entry is deactivated in the callback total = wait_results(1) assert total == 1
def test_04_expire_callback_reschedule(self): print("\n-----", sys._getframe().f_code.co_name, "-----") entry = query_management.QueryEntry(expire_after=2, callback_expire=callback_expire, callback=callback_normal, callback_error=callback_error, data=[3, 3, 3]) entry.update(expire_after=1.5) time.sleep(1.6) entry.deactivate() total = wait_results(1) assert total == -1
def test_02_deactivate(self): print("\n-----", sys._getframe().f_code.co_name, "-----") entry = query_management.QueryEntry(expire_after=3, callback_expire=callback_expire, callback=callback_normal, callback_error=callback_error, interval=2, data=[2.5, 2.5, 2.5], retry_count=1) print("**sleep 2.5 sec") time.sleep(2.5) entry.deactivate()
def put_resource(self, asset_group_id, resource_id, resource_type, resource): for nd in self.get_neighbor_nodes(): entry = query_management.QueryEntry(expire_after=30, callback_expire=None, callback_error=self.resend_resource, data={'target_id': nd, KeyType.asset_group_id: asset_group_id, KeyType.resource_id: resource_id, KeyType.resource: resource, KeyType.resource_type: resource_type}, retry_count=2) entry.update(INTERVAL_RETRY) self.send_store(nd, entry.nonce, asset_group_id, resource_id, resource, resource_type)
def route_message(self, domain_id=ZEROS, asset_group_id=None, dst_user_id=None, src_user_id=None, msg_to_send=None, payload_type=PayloadType.Type_msgpack): """ Find the destination host and send it :param domain_id: :param asset_group_id: :param src_user_id: source user :param dst_user_id: destination user :param msg_to_send: content to send :param payload_type: PayloadType value :return: """ if domain_id not in self.domains: return False self.logger.debug("route_message to dst_user_id:%s" % (binascii.b2a_hex(dst_user_id[:2]))) if self.domains[domain_id].is_registered_user(asset_group_id, dst_user_id): self.logger.debug(" -> directly to the app") self.core.send_message(msg_to_send) return True query_entry = query_management.QueryEntry( expire_after=DURATION_GIVEUP_PUT, callback_expire=self.callback_route_failure, callback=self.forward_message, callback_error=self.domains[domain_id].send_p2p_message, interval=INTERVAL_RETRY, data={ KeyType.domain_id: domain_id, KeyType.asset_group_id: asset_group_id, KeyType.source_node_id: src_user_id, KeyType.resource_id: dst_user_id, 'payload_type': payload_type, 'msg_to_send': msg_to_send }, retry_count=ROUTE_RETRY_COUNT) self.domains[domain_id].send_p2p_message(query_entry) return True
def test_12_get(self): print("-----", sys._getframe().f_code.co_name, "-----") query_entry = query_management.QueryEntry( expire_after=10, callback_expire=get_test_func_failure, data={ KeyType.domain_id: domain_id, KeyType.asset_group_id: asset_group_id, KeyType.resource_id: sample_resource_id, KeyType.resource_type: ResourceType.Transaction_data }, retry_count=3) query_entry.update(2, callback=get_test_func_success) networkings[1].get(query_entry) print("wait queue: 1") total = wait_results(1) assert total == 1
def test_05_multiple_entries_normal(self): print("\n-----", sys._getframe().f_code.co_name, "-----") query_entries = [] for i in range(10): entry = query_management.QueryEntry( expire_after=4, callback_expire=callback_expire, callback=callback_normal, callback_error=callback_error, data=[i, 0, 5]) entry.update(fire_after=2) query_entries.append(entry.nonce) time.sleep(1) for i in range(10): entry = ticker.get_entry(query_entries[i]) entry.callback() ticker.del_entry(query_entries[i]) total = wait_results(10) assert total == 10
def ping_with_retry(self, query_entry=None, node_id=None, retry_count=3): """ Retry ping if response is not received within a given time :param query_entry: :param node_id: target node_id (need for first trial) :param retry_count: :return: """ if node_id is not None: query_entry = query_management.QueryEntry( expire_after=ALIVE_CHECK_PING_WAIT, callback_error=self.ping_with_retry, interval=1, data={KeyType.node_id: node_id}, retry_count=retry_count) else: node_id = query_entry.data[KeyType.node_id] query_entry.update() self.send_ping(node_id, nonce=query_entry.nonce)
def update_refresh_timer_entry(self, new_entry=True, force_refresh_time=None): if force_refresh_time is None: rand_interval = random.randint( int(TopologyManagerBase.NEIGHBOR_LIST_REFRESH_INTERVAL * 2 / 3), int(TopologyManagerBase.NEIGHBOR_LIST_REFRESH_INTERVAL * 4 / 3)) else: rand_interval = force_refresh_time self.logger.debug("update_refresh_timer_entry: %d" % rand_interval) if new_entry: self.neighbor_refresh_timer_entry = query_management.QueryEntry( expire_after=rand_interval, data={"is_refresh": True}, callback_expire=self.advertise_neighbor_info, retry_count=0) else: self.neighbor_refresh_timer_entry.update_expiration_time( rand_interval)
def add_peer_node_ip46(self, node_id, ipv4, ipv6, port): """ Add as a peer node (with ipv4 and ipv6 address) :param node_id: :param ipv4: :param ipv6: :param port: :return: """ self.logger.debug( "[%s] add_peer_node_ip46: nodeid=%s, port=%d" % (self.shortname, binascii.b2a_hex(node_id[:2]), port)) self.id_ip_mapping[node_id] = NodeInfo(node_id=node_id, ipv4=ipv4, ipv6=ipv6, port=port) query_entry = query_management.QueryEntry( expire_after=ALIVE_CHECK_PING_WAIT, callback_expire=self.ping_response_check, data={KeyType.node_id: node_id}, retry_count=0) self.ping_with_retry(node_id=node_id, retry_count=3)
def notify_neighbor_update(self, node_id, is_new=True): """Update expiration timer for the notified node_id Args: node_id (bytes): target node_id is_new (bool): If True, this node is a new comer node """ if node_id is not None: self.logger.debug( "[%s] notify_neighbor_update: node_id=%s, is_new=%s" % (self.my_node_id.hex()[:4], node_id.hex()[:4], is_new)) else: self.logger.debug("[%s] notify_neighbor_update" % self.my_node_id.hex()[:4]) rand_time = random.uniform( 0.5, 1) * 5 / (len(self.neighbors.nodeinfo_list) + 1) if self.advertise_wait_entry is None: self.advertise_wait_entry = query_management.QueryEntry( expire_after=rand_time, callback_expire=self._advertise_neighbor_info, retry_count=0) else: self.advertise_wait_entry.update_expiration_time(rand_time)