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 = qrllegacy_pb2.MRData(type=qrllegacy_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()
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 = qrllegacy_pb2.MRData(type=qrllegacy_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()
def test_broadcast_does_not_broadcast_to_peers_known_to_have_the_mr(self, m_reactor, m_logger): message_request = MessageRequest() message_request.peers_connection_list = [self.channel_2, self.channel_3] self.factory.master_mr.requested_hash[b'1234'] = message_request self.factory.broadcast(qrllegacy_pb2.LegacyMessage.SL, b'1234') self.channel_1.send.assert_called_once() self.channel_2.send.assert_not_called() self.channel_3.send.assert_not_called()
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 = qrllegacy_pb2.MRData(type=qrllegacy_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'))
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 = qrllegacy_pb2.MRData(type=qrllegacy_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()
def add_peer(self, msg_hash: bytes, msg_type, peer, data=None): # Filter if msg_type not in self.allowed_types: return # Limit amount if len(self.requested_hash) >= config.dev.message_q_size: self.__remove__(self.requested_hash) if msg_hash not in self.requested_hash: self.requested_hash[msg_hash] = MessageRequest() self.requested_hash[msg_hash].add_peer(msg_type, peer, data)
def test_request_full_message_we_already_have_this_message(self, m_reactor, m_logger): # The local node already has this message! mrData = qrllegacy_pb2.MRData(type=qrllegacy_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'))
class TestMessageRequest(TestCase): def setUp(self): self.mr = MessageRequest() self.test_data = {"camel": "animal", "bitcoin": "cryptocoin"} def test_validate(self): # MessageRequest.validate() simply make sure self.params and an arg are the same dict. # MessageRequest.params is None result = self.mr.validate(self.test_data) self.assertFalse(result) # MessageRequest.params is the same as the argument self.mr.params = self.test_data result = self.mr.validate(self.test_data) self.assertTrue(result) # MessageRequest.params is missing a key compared to the argument self.mr.params = {"bitcoin": "cryptocoin"} result = self.mr.validate(self.test_data) self.assertTrue(result) self.mr.params = self.test_data # the argument is missing a key that MessageRequest.params has result = self.mr.validate({}) self.assertFalse(result) # the argument has different data from MessageRequest.params result = self.mr.validate({"camel": "cryptocoin", "bitcoin": "animal"}) self.assertFalse(result) def test_add_peer(self): msg_type = Mock(name='mock Message Type') peer = Mock(name='mock P2PProtocol') self.mr.add_peer(msg_type, peer, params=self.test_data) self.assertEqual(self.mr.params, self.test_data) self.assertEqual(self.mr.msg_type, msg_type) self.assertEqual(self.mr.peers_connection_list, [peer])
def setUp(self): self.mr = MessageRequest() self.test_data = {"camel": "animal", "bitcoin": "cryptocoin"}