def test_add_two_identical_from_different_senders_message_to_manager(self): replies_manager = rsi.RepliesManager() packed = self._build_msg(b'hello') rsi_reply = rsi.MsgWithReplicaSpecificInfo(packed, sender_id=0) rsi_reply2 = rsi.MsgWithReplicaSpecificInfo(packed, sender_id=1) num_of_replies = replies_manager.add_reply(rsi_reply) self.assertEqual(num_of_replies, 1) num_of_replies = replies_manager.add_reply(rsi_reply2) self.assertEqual(num_of_replies, 2)
def test_add_same_message_twice_to_manager(self): replies_manager = rsi.RepliesManager() packed = self._build_msg(b'hello') rsi_reply = rsi.MsgWithReplicaSpecificInfo(packed, sender_id=0) num_of_replies = replies_manager.add_reply(rsi_reply) self.assertEqual(num_of_replies, 1) num_of_replies = replies_manager.add_reply(rsi_reply) self.assertEqual(num_of_replies, 1)
def test_create_empty_rsi_message(self): msg = b'hello' primary_id = 0 req_seq_num = 1 packed = bft_msgs.pack_reply(primary_id, req_seq_num, msg, 0, 0) rsi_reply = rsi.MsgWithReplicaSpecificInfo(packed, 0) self.assertEqual(rsi_reply.sender_id, 0) common_header, common_data = rsi_reply.get_common_reply() self.assertEqual(req_seq_num, common_header.req_seq_num) self.assertEqual(common_data, b'hello')
def test_add_two_message_with_different_rsi_to_manager(self): replies_manager = rsi.RepliesManager() packed = self._build_msg(b'hello0', rsi_length=1) rsi_reply = rsi.MsgWithReplicaSpecificInfo(packed, sender_id=1) num_of_replies = replies_manager.add_reply(rsi_reply) self.assertEqual(num_of_replies, 1) packed2 = self._build_msg(b'hello1', rsi_length=1) rsi_reply2 = rsi.MsgWithReplicaSpecificInfo(packed2, sender_id=0) num_of_replies = replies_manager.add_reply(rsi_reply2) self.assertEqual(num_of_replies, 2) common_key = rsi_reply.get_matched_reply_key() rsi_replies = replies_manager.pop(common_key) self.assertEqual(replies_manager.num_matching_replies(common_key), 0) self.assertEqual(common_key.header.req_seq_num, 1) self.assertEqual(common_key.data, b'hello') self.assertEqual(b'1', rsi_replies[0].get_rsi_data()) self.assertTrue(b'0', rsi_replies[1].get_rsi_data())
def _process_received_msg(self, data, sender, replicas_addr, required_replies, cancel_scope): """Called by child class to process a received message. At this point it's unknown if message is valid""" rsi_msg = rsi.MsgWithReplicaSpecificInfo(data, sender) header, reply = rsi_msg.get_common_reply() if self._valid_reply(header, rsi_msg.get_sender_id(), replicas_addr): self.replies_manager.add_reply(rsi_msg) if self.replies_manager.has_quorum_on_all(required_replies): self.replies = self.replies_manager.get_all_replies() self.rsi_replies = self.replies_manager.get_rsi_replies( rsi_msg.get_matched_reply_key()) self.primary = self.replicas[header.primary_id] cancel_scope.cancel()
def test_add_message_with_two_seq_num_to_manager(self): replies_manager = rsi.RepliesManager() packed = self._build_msg(b'hello') rsi_reply = rsi.MsgWithReplicaSpecificInfo(packed, sender_id=0) num_of_replies = replies_manager.add_reply(rsi_reply) self.assertEqual(num_of_replies, 1) packed2 = self._build_msg(b'hello', req_seq_num=2) rsi_reply2 = rsi.MsgWithReplicaSpecificInfo(packed2, 0) num_of_replies = replies_manager.add_reply(rsi_reply2) self.assertEqual(num_of_replies, 1) self.assertEqual(replies_manager.num_distinct_replies(), 2) key1 = rsi_reply.get_matched_reply_key() replies_manager.pop(key1) self.assertEqual(replies_manager.num_matching_replies(key1), 0) self.assertEqual(replies_manager.num_distinct_replies(), 1) key2 = rsi_reply2.get_matched_reply_key() replies_manager.pop(key2) self.assertEqual(replies_manager.num_matching_replies(key2), 0) self.assertEqual(replies_manager.num_distinct_replies(), 0)
async def recv(self, required_replies, dest_replicas, cancel_scope): """ Receive reply messages until a quorum is achieved or the enclosing cancel_scope times out. """ replicas_ids = [(r.ip, r.port) for r in dest_replicas] while True: data, sender = await self.sock.recvfrom(self.config.max_msg_size) rsi_msg = rsi.MsgWithReplicaSpecificInfo(data, sender) header, reply = rsi_msg.get_common_reply() if self.valid_reply(header, rsi_msg.get_sender_id(), replicas_ids): quorum_size = self.replies_manager.add_reply(rsi_msg) if quorum_size == required_replies: self.reply = reply self.rsi_replies = self.replies_manager.get_rsi_replies(rsi_msg.get_matched_reply_key()) self.primary = self.replicas[header.primary_id] cancel_scope.cancel()