Exemple #1
0
    def assert_and_kill_receiver(self, assertions, receiver: MessageReceiver):

        try:

            assertions()

        except AssertionError as e:

            self.fail(e)

        finally:

            receiver.kill()
Exemple #2
0
    def test_kill_receiver(self):
        """
        Tests that a receiver can be open and appropriately killed.
        """
        pub, priv = generate_contact_key_pair()

        receiver = MessageReceiver(self.port_range_min,
                                   private_key=priv,
                                   contacts=[])

        time.sleep(0.1)

        receiver.kill()
Exemple #3
0
    def test_messaging_single_channel(self) -> None:
        """
        Basic messaging test.
        """

        receiver_public, receiver_private = generate_contact_key_pair()
        sender_public, sender_private = generate_contact_key_pair()

        sender_contact = Contact(id="sender",
                                 public_key=sender_public,
                                 host=self.localhost,
                                 port=self.port_range_min)

        receiver_contact = Contact(id="receiver",
                                   public_key=receiver_public,
                                   host=self.localhost,
                                   port=self.port_range_min)

        channel = "channel1"

        sender = MessageSender(receiver_contact)

        consumer = DebugConsumer()

        receiver = MessageReceiver(port=self.port_range_min,
                                   private_key=receiver_private,
                                   contacts=[sender_contact],
                                   notify_interval=self.notify_interval)
        receiver.register_consumer(channel, consumer)

        message = Message(channel, "test command", "test data")

        sender.send_message(message=message,
                            sender_contact_id=sender_contact.id,
                            private_key=sender_private)

        time.sleep(self.notify_interval * 2)

        def assertions():

            assert len(consumer.messages) == 1

            received_message_sender_id, received_message = consumer.messages[0]

            assert received_message_sender_id == sender_contact.id
            assert received_message == message

        self.assert_and_kill_receiver(assertions, receiver)
Exemple #4
0
    def test_wrong_receiver_public(self):
        """
        Tests that sending message with wrong key results in MessageDeliveryError.
        """

        receiver_public, receiver_private = generate_contact_key_pair()
        sender_public, sender_private = generate_contact_key_pair()

        wrong_public, wrong_private = generate_contact_key_pair()

        sender_contact = Contact(id="sender",
                                 public_key=sender_public,
                                 host=self.localhost,
                                 port=self.port_range_min)

        receiver_contact = Contact(id="receiver",
                                   public_key=wrong_public,
                                   host=self.localhost,
                                   port=self.port_range_min)

        channel = "channel1"

        sender = MessageSender(receiver_contact)

        consumer = DebugConsumer()

        receiver = MessageReceiver(port=self.port_range_min,
                                   private_key=receiver_private,
                                   contacts=[sender_contact],
                                   notify_interval=self.notify_interval)
        receiver.register_consumer(channel, consumer)

        message = Message(channel, "test command", "test data")

        sender.send_message(message=message,
                            sender_contact_id=sender_contact.id,
                            private_key=sender_private)

        time.sleep(self.notify_interval * 2)

        def assertions():

            assert len(consumer.messages) == 0

        self.assert_and_kill_receiver(assertions, receiver)
Exemple #5
0
    def __init__(
            self,
            self_contact: Contact,
            private_key: rsa.PrivateKey,
            contacts=None,
            receiver_notify_interval=1.0,
            contact_restore_timeout=3600,
            inactive_nodes_ping_interval=1799
    ):
        """
        Initializes a new address book.
        :param self_contact: contact of the owner of the address book
        :param private_key: private key of the message receiver
        :param contacts: list of known contacts
        :param receiver_notify_interval: interval at which the message receiver notifies of new messages
        :param contact_restore_timeout: timeout of pinging of inactive nodes before deletion
        :param inactive_nodes_ping_interval: interval for pinging inactive nodes
        """

        if contacts is None:
            contacts = []

        self.contacts = deepcopy(contacts)
        self._private_key = private_key

        self.receiver = MessageReceiver(
            port=self_contact.port,
            private_key=self._private_key,
            contacts=self.contacts,
            notify_interval=receiver_notify_interval
        )

        self.receiver.register_consumer(channel=self._messaging_channel, message_consumer=self)

        self.self_contact = self_contact
        self._contact_restore_timeout = contact_restore_timeout
        self._inactive_nodes_ping_interval = inactive_nodes_ping_interval
        
        thread = threading.Thread(target=self._start_pinging_inactive_nodes)
        thread.daemon = True
        thread.start()
Exemple #6
0
class AddressBook(MessageConsumer):
    """
    Node address book, responsible for sharing new contacts and deleting inactive ones.
    """
    _messaging_channel = 'network'

    def __init__(self,
                 self_contact: Contact,
                 private_key: rsa.PrivateKey,
                 contacts=None,
                 receiver_notify_interval=1.0,
                 contact_restore_timeout=3600,
                 inactive_nodes_ping_interval=1799):
        """
        Initializes a new address book.
        :param self_contact: contact of the owner of the address book
        :param private_key: private key of the message receiver
        :param contacts: list of known contacts
        :param receiver_notify_interval: interval at which the message receiver notifies of new messages
        :param contact_restore_timeout: timeout of pinging of inactive nodes before deletion
        :param inactive_nodes_ping_interval: interval for pinging inactive nodes
        """

        if contacts is None:
            contacts = []

        self.contacts = deepcopy(contacts)
        self._private_key = private_key

        self.receiver = MessageReceiver(
            port=self_contact.port,
            private_key=self._private_key,
            contacts=self.contacts,
            notify_interval=receiver_notify_interval)

        self.receiver.register_consumer(channel=self._messaging_channel,
                                        message_consumer=self)

        self.self_contact = self_contact
        self._contact_restore_timeout = contact_restore_timeout
        self._inactive_nodes_ping_interval = inactive_nodes_ping_interval

        threading.Thread(target=self._start_pinging_inactive_nodes).start()

    def kill(self):

        self.receiver.kill()

    def _generate_add_contact_message(self, contact: Contact) -> Message:
        """
        Generates an "add-contact" message.
        :param contact: contact to add
        """

        return Message(channel=self._messaging_channel,
                       command='add-contact',
                       data=contact)

    def _add_contact(self, contact: Contact) -> None:
        """
        Handles incoming "add-contacts" commands.
        :param contact: contact to add
        """

        self.create_new_distributed_contact(contact)

    def _forward_contact(self, contact: Contact) -> None:
        """
        Forwards a contact to all other known contacts.
        :param contact: contact to forward
        """

        message = self._generate_add_contact_message(contact)

        for known_contact in self.contacts:

            # Prevent notifying a contact of themselves
            if known_contact.id == contact.id:
                continue

            # Prevent node notifying itself
            if known_contact.id == self.self_contact.id:
                continue

            self.send_message_to_contact(known_contact, message)

    def send_message_to_contact(self, recipient: Contact,
                                message: Message) -> bool:
        """
        Sends a message to a contact, and marks the link to the recipient as either up or down.
        :param recipient: recipient node's contact or recipient node's contact id
        :param message: message to send
        :return: True iff the delivery of the message was successful
        """

        try:

            sender = MessageSender(recipient)
            sender.send_message(message, self.self_contact.id,
                                self._private_key)

            self._set_link_state(True, recipient)

            return True

        except MessageDeliveryError:

            self._set_link_state(False, recipient)

            return False

    def _set_link_state(self, link_up: bool, contact: Contact) -> None:
        """
        Sets the link with a node's state.
        :param link_up: state to set
        :param contact: contact to set the link's state of
        """

        for known_contact in self.contacts:

            if known_contact.id == contact.id:

                if link_up:

                    known_contact.link_up()

                else:

                    known_contact.link_down()

    def _delete_contact(self, contact: Contact) -> None:
        """
        Deletes a contact from the contact list.
        :param contact: contact to delete
        :return:
        """

        for known_contact in self.contacts:

            if known_contact.id == contact.id:
                self.contacts.remove(known_contact)

                return

    def _generate_ping_message(self) -> Message:
        """
        Generates a ping message
        :return: the generated ping message
        """

        return Message(channel=self._messaging_channel, command="ping")

    def _start_pinging_inactive_nodes(self) -> None:
        """
        Starts periodically pinging inactive nodes.
        """

        while not self.receiver.kill_flag:

            time.sleep(self._inactive_nodes_ping_interval)

            if self.receiver.kill_flag:
                return

            for contact in self.contacts:

                if not contact.is_active():

                    ping_message = self._generate_ping_message()

                    if not self.send_message_to_contact(contact, ping_message):

                        current_timestamp = now()

                        if current_timestamp - contact.first_failure > self._contact_restore_timeout:
                            self._delete_contact(contact)

    def notify(self, message: Message, sender_id) -> None:
        """
        Handles incoming messages.
        :param sender_id: id of the message sender
        :param message: message to handle
        """

        if message.channel == self._messaging_channel:

            if message.command == 'add-contact':
                self._add_contact(message.data)

    def create_new_distributed_contact(self, contact: Contact) -> None:
        """
        Adds new contact and notifies the network.
        :param contact: new contact
        """

        if self._append_contact(contact):
            self._forward_contact(contact)

    def _append_contact(self, contact: Contact) -> bool:
        """
        Appends a contact to the contacts list.
        :param contact: contact to append to the contacts list
        :return: true iff the contact list has changed as a result of the operation
        """

        if contact.id == self.self_contact.id:
            return False

        for known_contact in self.contacts:

            if known_contact.id == contact.id:
                return False

        self.contacts.append(contact)

        return True
Exemple #7
0
    def test_messaging_multiple_channels(self):
        """
        Messaging test with multiple channels.
        """

        receiver_public, receiver_private = generate_contact_key_pair()
        sender_public, sender_private = generate_contact_key_pair()

        sender_contact = Contact(id="sender",
                                 public_key=sender_public,
                                 host=self.localhost,
                                 port=self.port_range_min)

        receiver_contact = Contact(id="receiver",
                                   public_key=receiver_public,
                                   host=self.localhost,
                                   port=self.port_range_min)

        channel1 = "channel1"
        channel2 = "channel2"

        sender = MessageSender(receiver_contact)

        consumer1 = DebugConsumer()
        consumer2 = DebugConsumer()

        receiver = MessageReceiver(port=self.port_range_min,
                                   private_key=receiver_private,
                                   contacts=[sender_contact],
                                   notify_interval=self.notify_interval)
        receiver.register_consumer(channel1, consumer1)
        receiver.register_consumer(channel2, consumer2)

        message1 = Message(channel1, "test command", "message 1")
        message2 = Message(channel2, "test command", "message 2")

        sender.send_message(message=message1,
                            sender_contact_id=sender_contact.id,
                            private_key=sender_private)

        sender.send_message(message=message2,
                            sender_contact_id=sender_contact.id,
                            private_key=sender_private)

        time.sleep(self.notify_interval * 2)

        def assertions():

            assert len(consumer1.messages) == 1
            assert len(consumer2.messages) == 1

            received_message_sender_id1, received_message1 = consumer1.messages[
                0]
            received_message_sender_id2, received_message2 = consumer2.messages[
                0]

            assert received_message_sender_id1 == sender_contact.id
            assert received_message_sender_id2 == sender_contact.id
            assert received_message1 == message1
            assert received_message2 == message2

        self.assert_and_kill_receiver(assertions, receiver)