Exemplo n.º 1
0
    def test_request_full_message(self, m_reactor, m_logger):
        """
        MessageReceipt is only for Blocks and Transactions, both of which have hashes.
        When you receive the MessageReceipt for them, if you find that you don't have that message corresponding to the
        hash, this is when you call request_full_message()

        If the local node already has the Message, its hash will be in self.master_mr._hash_msg (perhaps it got it
        from another peer). So in that case we check if we requested this hash from any other peer.
        if yes then delete that request and then we go through peer list, who broadcasted this same MR.

        and we request from any one of those peer, and we add that peer into our list, that we already have requested
        from this peer.

        We also make the callLater to the request_full_message, so that in a given interval of time,
        if peer doesn't respond, then we can request to next peer otherwise we can delete all the request, if we
        received it from the peer
        """
        # The local node does not already have this message.
        mrData = xrdlegacy_pb2.MRData(type=xrdlegacy_pb2.LegacyMessage.SL, hash=b'1234')

        # You can get this Message from channel_1. No, we have not requested this Message from channel_1 yet.
        message_request = MessageRequest()
        message_request.peers_connection_list.append(self.channel_1)
        message_request.already_requested_peers = []
        self.factory.master_mr.requested_hash[b'1234'] = message_request

        self.factory.request_full_message(mrData)

        self.channel_1.send.assert_called_once()

        # We use reactor.callLater() to schedule a call to request_full_message() again in the future.
        # So if this peer doesn't respond, when this function is next called again, it will ignore this peer
        # because of already_requested_peers and use the next one in peers_list.
        m_reactor.callLater.assert_called_once()
Exemplo n.º 2
0
    def broadcast_block(self, block: Block):
        # logger.info('<<<Transmitting block: ', block.headerhash)
        data = xrdlegacy_pb2.MRData()
        data.stake_selector = block.transactions[0].public_key
        data.block_number = block.block_number
        data.prev_headerhash = bytes(block.prev_headerhash)

        self.register_and_broadcast(xrdlegacy_pb2.LegacyMessage.BK,
                                    block.headerhash, block.pbdata, data)
Exemplo n.º 3
0
    def test_request_full_message_we_have_already_forgotten_about_this_hash(self, m_reactor, m_logger):
        """
        If we couldn't download the full Message from any peer, then we would've forgotten about this MessageReceipt
        and its hash.
        The next time request_full_message() is called, we find that we have forgotten about the MR and thus do nothing.
        """
        mrData = xrdlegacy_pb2.MRData(type=xrdlegacy_pb2.LegacyMessage.SL, hash=b'1234')

        self.factory.request_full_message(mrData)

        self.channel_1.send.assert_not_called()
        m_reactor.callLater.assert_not_called()
Exemplo n.º 4
0
 def test_handle_block_bad_block(self, m_Block, m_logger):
     """
     If the block couldn't be constructed from the message, the function should return without doing
     anything else.
     :return:
     """
     m_Block.side_effect = Exception
     msg = make_message(func_name=xrdlegacy_pb2.LegacyMessage.BK,
                        block=xrd_pb2.Block(),
                        mrData=xrdlegacy_pb2.MRData())
     self.manager.handle_block(self.channel, msg)
     self.channel.factory.master_mr.register.assert_not_called()
     self.channel.factory.pow.pre_block_logic.assert_not_called()
Exemplo n.º 5
0
    def test_request_full_message_we_already_have_this_message(self, m_reactor, m_logger):
        # The local node already has this message!
        mrData = xrdlegacy_pb2.MRData(type=xrdlegacy_pb2.LegacyMessage.SL, hash=b'1234')
        self.factory.master_mr._hash_msg[b'1234'] = Mock(autospec=Message)
        message_request = MessageRequest()
        message_request.peers_connection_list.append(self.channel_1)
        self.factory.master_mr.requested_hash[b'1234'] = message_request

        self.factory.request_full_message(mrData)

        # Because we already have this message, channel_1 is left alone.
        self.channel_1.send.assert_not_called()
        # Also, this hash should no longer appear in the Master MessageReceipt.
        self.assertIsNone(self.factory.master_mr.requested_hash.get(b'1234'))
Exemplo n.º 6
0
    def request_full_message(self, mr_data: xrdlegacy_pb2.MRData):
        """
        Request Full Message
        This function request for the full message against,
        the Message Receipt received.
        :return:
        """

        # FIXME: Again, breaking encasulation
        # FIXME: Huge amount of lookups in dictionaries
        msg_hash = mr_data.hash

        if msg_hash in self.master_mr._hash_msg:
            if msg_hash in self.master_mr.requested_hash:
                del self.master_mr.requested_hash[msg_hash]
            return

        if msg_hash not in self.master_mr.requested_hash:
            return

        peers_list = self.master_mr.requested_hash[
            msg_hash].peers_connection_list
        message_request = self.master_mr.requested_hash[msg_hash]
        for peer in peers_list:
            if peer in message_request.already_requested_peers:
                continue
            message_request.already_requested_peers.append(peer)

            msg = xrdlegacy_pb2.LegacyMessage(
                func_name=xrdlegacy_pb2.LegacyMessage.SFM,
                mrData=xrdlegacy_pb2.MRData(hash=mr_data.hash,
                                            type=mr_data.type))

            peer.send(msg)

            call_later_obj = reactor.callLater(
                config.dev.message_receipt_timeout, self.request_full_message,
                mr_data)

            message_request.callLater = call_later_obj
            return

        # If execution reach to this line, then it means no peer was able to provide
        # Full message for this hash thus the hash has to be deleted.
        # Moreover, negative points could be added to the peers, for this behavior
        if msg_hash in self.master_mr.requested_hash:
            del self.master_mr.requested_hash[msg_hash]
Exemplo n.º 7
0
    def test_request_full_message_no_peer_could_provide_full_message(self, m_reactor, m_logger):
        """
        If this happens, we should forget about the MessageReceipt and its hash completely.
        Optionally punish peers.
        """
        # The local node does not already have this message.
        mrData = xrdlegacy_pb2.MRData(type=xrdlegacy_pb2.LegacyMessage.SL, hash=b'1234')

        # No idea where we can get this Message from. We haven't requested this Message from anybody.
        message_request = MessageRequest()
        message_request.peers_connection_list = []
        message_request.already_requested_peers = []
        self.factory.master_mr.requested_hash[b'1234'] = message_request

        self.factory.request_full_message(mrData)

        self.channel_1.send.assert_not_called()
        self.assertIsNone(self.factory.master_mr.requested_hash.get(b'1234'))
Exemplo n.º 8
0
    def test_handle_block(self, m_logger):
        """
        1. A peer has found a new block. It broadcasts the MessageReceipt for that block.
        2. This node finds that it hasn't got that block yet, so it requests that block.
        3. The peer sends the new block, which is handled by this function.
        :return:
        """
        msg = make_message(func_name=xrdlegacy_pb2.LegacyMessage.BK,
                           block=xrd_pb2.Block(),
                           mrData=xrdlegacy_pb2.MRData())
        self.manager.handle_block(self.channel, msg)
        self.channel.factory.master_mr.register.assert_called()

        # But if we didn't request this block, we shouldn't process it.
        self.channel.factory.master_mr.register.reset_mock()
        self.channel.factory.master_mr.isRequested.return_value = False

        self.manager.handle_block(self.channel, msg)
        self.channel.factory.master_mr.register.assert_not_called()
Exemplo n.º 9
0
    def test_request_full_message_already_requested_this_message_from_same_peer(self, m_reactor, m_logger):
        # The local node does not already have this message.
        mrData = xrdlegacy_pb2.MRData(type=xrdlegacy_pb2.LegacyMessage.SL, hash=b'1234')

        # You can get this Message from channel_1 and channel_2. Also, we already requested this Message from channel_1.
        message_request = MessageRequest()
        message_request.peers_connection_list = [self.channel_1, self.channel_2]
        message_request.already_requested_peers = [self.channel_1]
        self.factory.master_mr.requested_hash[b'1234'] = message_request

        self.factory.request_full_message(mrData)

        # We should leave channel_1 alone, but we ask channel_2 for the Message.
        self.channel_1.send.assert_not_called()
        self.channel_2.send.assert_called_once()

        # We use reactor.callLater() to schedule a call to request_full_message() again in the future.
        # So if this peer doesn't respond, when this function is next called again, it will ignore this peer
        # because of already_requested_peers and use the next one in peers_list.
        m_reactor.callLater.assert_called_once()
Exemplo n.º 10
0
    def broadcast(self, msg_type, msg_hash: bytes, mr_data=None):
        """
        Broadcast
        This function sends the Message Receipt to all connected peers.
        :return:
        """
        ignore_peers = []
        if msg_hash in self.master_mr.requested_hash:
            ignore_peers = self.master_mr.requested_hash[
                msg_hash].peers_connection_list

        if not mr_data:
            mr_data = xrdlegacy_pb2.MRData()

        mr_data.hash = msg_hash
        mr_data.type = msg_type
        data = xrdlegacy_pb2.LegacyMessage(
            func_name=xrdlegacy_pb2.LegacyMessage.MR, mrData=mr_data)

        for peer in self._peer_connections:
            if peer not in ignore_peers:
                peer.send(data)
Exemplo n.º 11
0
    def test_request_full_message_already_requested_this_message_from_another_peer(self, m_reactor, m_logger):
        """
        If we have already requested this Message (from another peer), this function should still go ahead
        and request it from the peer we are currently dealing with.
        """
        # The local node does not already have this message.
        mrData = xrdlegacy_pb2.MRData(type=xrdlegacy_pb2.LegacyMessage.SL, hash=b'1234')

        # You can get this Message from channel_1. Also, we already requested this Message from channel_2.
        message_request = MessageRequest()
        message_request.peers_connection_list.append(self.channel_1)
        message_request.already_requested_peers = [self.channel_2]
        self.factory.master_mr.requested_hash[b'1234'] = message_request

        self.factory.request_full_message(mrData)

        # But, the code should ignore channel_2 and ask channel_1 anyway.
        self.channel_1.send.assert_called_once()

        # We use reactor.callLater() to schedule a call to request_full_message() again in the future.
        # So if this peer doesn't respond, when this function is next called again, it will ignore this peer
        # because of already_requested_peers and use the next one in peers_list.
        m_reactor.callLater.assert_called_once()