def run_protocol(self, protocol, arguments=(), blocking=False): """ Run the protocol *protocol*. Args: protocol (function): The protocol that the host should run. arguments (tuple): The set of (ordered) arguments for the protocol blocking (bool): Wait for thread to stop before proceeding """ arguments = (self, ) + arguments if blocking: DaemonThread(protocol, args=arguments).join() else: DaemonThread(protocol, args=arguments)
def get_data_qubit(self, partner_id, q_id=None, wait=-1): """ Gets the data qubit received from another host in the network. If qubit ID is specified, qubit with that ID is returned, else, the last qubit received is returned. Args: partner_id (string): The ID of the host that data qubit to be returned is received from. q_id (string): The qubit ID of the data qubit to get. wait (float): The amount of time to wait for the a qubit to arrive Returns: Qubit: Qubit received from the host with *partner_id* and *q_id*. """ if not isinstance(wait, float) and not isinstance(wait, int): raise Exception('wait parameter should be a number') def _wait(): nonlocal q nonlocal wait wait_start_time = time.time() while time.time() - wait_start_time < wait and q is None: q = _get_qubit(self._data_qubit_store, partner_id, q_id) return q if wait > 0: q = None DaemonThread(_wait).join() return q else: return _get_qubit(self._data_qubit_store, partner_id, q_id)
def get_epr(self, partner_id, q_id=None, wait=-1): """ Gets the EPR that is entangled with another host in the network. If qubit ID is specified, EPR with that ID is returned, else, the last EPR added is returned. Args: partner_id (string): The ID of the host that returned EPR is entangled to. q_id (string): The qubit ID of the EPR to get. wait (float): the amount of time to wait Returns: Qubit: Qubit shared with the host with *partner_id* and *q_id*. """ if not isinstance(wait, float) and not isinstance(wait, int): raise Exception('wait parameter should be a number') def _wait(): nonlocal q nonlocal wait wait_start_time = time.time() while time.time() - wait_start_time < wait and q is None: q = _get_qubit(self._EPR_store, partner_id, q_id) return q if wait > 0: q = None DaemonThread(_wait).join() return q else: return _get_qubit(self._EPR_store, partner_id, q_id)
def get_classical(self, partner_id, wait=-1): """ Get the classical messages from partner host *partner_id*. Args: partner_id (string): The ID of the partner who sent the clasical messages wait (float): How long in seconds to wait for the messages if none are set. Returns: A list of classical messages from Host with ID *partner_id*. """ if not isinstance(wait, float) and not isinstance(wait, int): raise Exception('wait parameter should be a number') def process_messages(): nonlocal cla cla = self._classical_messages.get_all_from_sender(partner_id) def _wait(): nonlocal cla nonlocal wait wait_start_time = time.time() while time.time() - wait_start_time < wait and len(cla) == 0: process_messages() return cla if wait > 0: cla = [] DaemonThread(_wait).join() return sorted(cla, key=lambda x: x.seq_num, reverse=True) else: cla = [] process_messages() return sorted(cla, key=lambda x: x.seq_num, reverse=True)
def get_message_w_seq_num(self, sender_id, seq_num, wait=-1): """ Get a message from a sender with a specific sequence number. Args: sender_id (str): The ID of the sender seq_num (int): The sequence number wait (int): Returns: """ def _wait(): nonlocal m nonlocal wait wait_start_time = time.time() while time.time() - wait_start_time < wait and m is None: filter_messages() def filter_messages(): nonlocal m for message in self.classical: if message.sender == sender_id and message.seq_num == seq_num: m = message m = None if wait > 0: DaemonThread(_wait).join() return m else: filter_messages() return m
def start(self, nodes=None, backend=None): """ Starts the network. """ if backend is None: self._backend = EQSNBackend() else: self._backend = backend if nodes is not None: self._backend.start(nodes=nodes) self._queue_processor_thread = DaemonThread(target=self._process_queue)
def _process_queue(self): """ Runs a thread for processing the packets in the packet queue. """ self.logger.log('Host ' + self.host_id + ' started processing') while True: if self._stop_thread: break time.sleep(self.delay) if not self._packet_queue.empty(): packet = self._packet_queue.get() if not packet: raise Exception('empty message') DaemonThread(self._process_packet, args=(packet, ))
def main(): # Initialize a network network = Network.get_instance() # Define the host IDs in the network nodes = ['Alice', 'Bob', 'Eve'] network.delay = 0.0 # Start the network with the defined hosts network.start(nodes) # Initialize the host Alice host_alice = Host('Alice') # Add a one-way connection (classical and quantum) to Bob host_alice.add_connection('Bob') # Start listening host_alice.start() host_bob = Host('Bob') # Bob adds his own one-way connection to Alice and Eve host_bob.add_connection('Alice') host_bob.add_connection('Eve') host_bob.start() host_eve = Host('Eve') host_eve.add_connection('Bob') host_eve.start() # Add the hosts to the network # The network is: Alice <--> Bob <--> Eve network.add_host(host_alice) network.add_host(host_bob) network.add_host(host_eve) # Generate random key key_size = 8 # the size of the key in bit secret_key = np.random.randint(2, size=key_size) hosts = {'Alice': host_alice, 'Bob': host_bob, 'Eve': host_eve} # Concatentate functions def Alice_func(alice=host_alice): msg_buff = [] Alice_qkd(alice, msg_buff, secret_key, hosts) Alice_send_message(alice, msg_buff, secret_key, hosts) def Eve_func(eve=host_eve): msg_buff = [] eve_key = Eve_qkd(eve, msg_buff, key_size, hosts) Eve_receive_message(eve, msg_buff, eve_key, hosts) # Run Bob and Alice thread_alice = DaemonThread(Alice_func) thread_eve = DaemonThread(Eve_func) thread_alice.join() thread_eve.join() for h in hosts.values(): h.stop() network.stop()
def _process_queue(self): """ Runs a thread for processing the packets in the packet queue. """ while True: if self._stop_thread: break if not self._packet_queue.empty(): # Artificially delay the network if self.delay > 0: time.sleep(self.delay) packet = self._packet_queue.get() # Simulate packet loss packet_drop_var = random.random() if packet_drop_var > (1 - self.packet_drop_rate): Logger.get_instance().log("PACKET DROPPED") if packet.payload_type == protocols.QUANTUM: packet.payload.release() continue sender, receiver = packet.sender, packet.receiver if packet.payload_type == protocols.QUANTUM: self._route_quantum_info(sender, receiver, [packet.payload]) try: if packet.protocol == protocols.RELAY and not self.use_hop_by_hop: full_route = packet.route route = full_route[full_route.index(sender):] else: if packet.protocol == protocols.REC_EPR: route = self.get_classical_route(sender, receiver) else: route = self.get_classical_route(sender, receiver) if len(route) < 2: raise Exception('No route exists') elif len(route) == 2: if packet.protocol != protocols.RELAY: if packet.protocol == protocols.REC_EPR: host_sender = self.get_host(sender) q = host_sender \ .backend \ .create_EPR(host_sender.host_id, receiver, q_id=packet.payload['q_id'], block=packet.payload['blocked']) host_sender.add_epr(receiver, q) self.ARP[receiver].rec_packet(packet) else: self.ARP[receiver].rec_packet(packet.payload) else: if packet.protocol == protocols.REC_EPR: q_id = packet.payload['q_id'] blocked = packet.payload['blocked'] q_route = self.get_quantum_route(sender, receiver) if self.use_ent_swap: DaemonThread(self._entanglement_swap, args=(sender, receiver, q_route, q_id, packet.seq_num, blocked)) else: DaemonThread(self._establish_epr, args=(sender, receiver, q_id, packet.seq_num, blocked)) else: network_packet = self._encode(route, packet) self.ARP[route[1]].rec_packet(network_packet) except nx.NodeNotFound: Logger.get_instance().error( "route couldn't be calculated, node doesn't exist") except ValueError: Logger.get_instance().error( "route couldn't be calculated, value error") except Exception as e: Logger.get_instance().error('Error in network: ' + str(e))
def _entanglement_swap(self, sender, receiver, route, q_id, o_seq_num, blocked): """ Performs a chain of entanglement swaps with the hosts between sender and receiver to create a shared EPR pair between sender and receiver. Args: sender (Host): Sender of the EPR pair receiver (Host): Receiver of the EPR pair route (list): Route between the sender and receiver q_id (str): Qubit ID of the sent EPR pair o_seq_num (int): The original sequence number blocked (bool): If the pair being distributed is blocked or not """ host_sender = self.get_host(sender) def establish_epr(net, s, r): if not net.shares_epr(s, r): self.get_host(s).send_epr(r, q_id, await_ack=True) else: old_id = self.get_host(s).change_epr_qubit_id(r, q_id) net.get_host(r).change_epr_qubit_id(route[i], q_id, old_id) # Create EPR pairs on the route, where all EPR qubits have the id q_id threads = [] for i in range(len(route) - 1): threads.append( DaemonThread(establish_epr, args=(self, route[i], route[i + 1]))) for t in threads: t.join() for i in range(len(route) - 2): host = self.get_host(route[i + 1]) q = host.get_epr(route[0], q_id, wait=10) if q is None: print("Host is %s" % host.host_id) print("Search host is %s" % route[0]) print("Search id is %s" % q_id) print("EPR storage is") print(host.EPR_store) Logger.get_instance().error('Entanglement swap failed') return data = { 'q': q, 'eq_id': q_id, 'node': sender, 'o_seq_num': o_seq_num, 'type': protocols.EPR } if route[i + 2] == route[-1]: data = { 'q': q, 'eq_id': q_id, 'node': sender, 'ack': True, 'o_seq_num': o_seq_num, 'type': protocols.EPR } host.send_teleport(route[i + 2], None, await_ack=True, payload=data, generate_epr_if_none=False) # Change in the storage that the EPR qubit is shared with the receiver q2 = host_sender.get_epr(route[1], q_id=q_id) host_sender.add_epr(receiver, q2, q_id, blocked) Logger.get_instance().log( 'Entanglement swap was successful for pair with id ' + q_id + ' between ' + sender + ' and ' + receiver)
def start(self): """ Starts the host. """ self._queue_processor_thread = DaemonThread(target=self._process_queue)