示例#1
0
    def test_unpack_request(self):
        client_id = 4
        req_seq_num = 1
        read_only = True
        timeout_milli = 5000
        span_context = b'span context'
        msg = b'hello'
        cid = str(req_seq_num)

        packed = bft_msgs.pack_request(client_id,
                                       req_seq_num,
                                       read_only,
                                       timeout_milli,
                                       cid,
                                       msg,
                                       pre_process=False,
                                       span_context=span_context)
        header, unpacked_span_context, unpacked_msg, unpacked_cid = bft_msgs.unpack_request(
            packed)

        self.assertEqual(len(span_context), header.span_context_size)
        self.assertEqual(client_id, header.client_id)
        self.assertEqual(1, header.flags)  # read_only = True
        self.assertEqual(req_seq_num, header.req_seq_num)
        self.assertEqual(len(msg), header.length)
        self.assertEqual(timeout_milli, header.timeout_milli)
        self.assertEqual(len(cid), header.cid)

        self.assertEqual(span_context, unpacked_span_context)
        self.assertEqual(msg, unpacked_msg)
        self.assertEqual(cid, unpacked_cid)
示例#2
0
    async def sendSync(self, msg, read_only):
        """
        Send a client request and wait for a quorum (2F+C+1) of replies.

        Return a single reply message if a quorum of replies matches.
        Otherwise, raise a trio.TooSlowError indicating the request timed out.

        Retry Strategy:
            If the request is a write and the primary is known then send only to
            the primary on the first attempt. Otherwise, if the request is read
            only or the primary is unknown, then send to all replicas on the
            first attempt.

            After `config.retry_timeout_milli` without receiving a quorum of
            identical replies, then clear the replies and send to all replicas.
            Continue this strategy every `retry_timeout_milli` until
            `config.req_timeout_milli` elapses. If `config.req_timeout_milli`
            elapses then a trio.TooSlowError is raised.

         Note that this method also binds the socket to an appropriate port if
         not already bound.
        """
        if not self.sock_bound:
            await self.bind()

        seq = self.req_seq_num.next()
        data = bft_msgs.pack_request(self.client_id, seq, read_only, msg)
        # Raise a trio.TooSlowError exception if a quorum of replies
        with trio.fail_after(self.config.req_timeout_milli / 1000):
            self.reset_on_new_request()
            self.retries = 0
            return await self.send_loop(data, read_only)
示例#3
0
    async def sendSync(self, msg, read_only, seq_num=None, cid=None, pre_process=False, m_of_n_quorum=None, \
        reconfiguration=False, include_ro=False, corrupt_params={}, no_retries=False):
        """
        Send a client request and wait for a m_of_n_quorum (if None, it will set to 2F+C+1 quorum) of replies.

        Return a single reply message if a quorum of replies matches.
        Otherwise, raise a trio.TooSlowError indicating the request timed out.

        Retry Strategy:
            If the request is a write and the primary is known then send only to
            the primary on the first attempt. Otherwise, if the request is read
            only or the primary is unknown, then send to all replicas on the
            first attempt.

            After `config.retry_timeout_milli` without receiving a quorum of
            identical replies, then clear the replies and send to all replicas.
            Continue this strategy every `retry_timeout_milli` until
            `config.req_timeout_milli` elapses. If `config.req_timeout_milli`
            elapses then a trio.TooSlowError is raised.

         Note that this method also binds the socket to an appropriate port if
         not already bound.
        """
        # Call an abstract function to allow each client type to set-up its communication before starting
        if not self.comm_prepared:
            await self._comm_prepare()

        if seq_num is None:
            seq_num = self.req_seq_num.next()

        if cid is None:
            cid = str(seq_num)

        signature = b''
        client_id = self.client_id
        if self.signing_key:
            h = SHA256.new(msg)
            signature = pkcs1_15.new(self.signing_key).sign(h)
            if corrupt_params:
                msg, signature, client_id = self._corrupt_signing_params(msg, signature, client_id, corrupt_params)

        data = bft_msgs.pack_request(client_id, seq_num, read_only, self.config.req_timeout_milli, cid, msg,
                                    pre_process, reconfiguration=reconfiguration, signature=signature)

        if m_of_n_quorum is None:
            m_of_n_quorum = MofNQuorum.LinearizableQuorum(self.config, [r.id for r in self.replicas])

        # Raise a trio.TooSlowError exception if a quorum of replies
        try:
            with trio.fail_after(self.config.req_timeout_milli / 1000):
                self._reset_on_new_request([seq_num])
                replies = await self._send_receive_loop(
                    data, read_only, m_of_n_quorum, include_ro=include_ro, no_retries=no_retries)
                return next(iter(self.replies.values())).get_common_data() if replies else None
        except trio.TooSlowError:
            print("TooSlowError thrown from client_id", self.client_id, "for seq_num", seq_num)
            raise trio.TooSlowError
        finally:
            pass
示例#4
0
 def test_unpack_request(self):
     msg = b'hello'
     client_id = 4
     req_seq_num = 1
     read_only = True
     packed = bft_msgs.pack_request(client_id, req_seq_num, read_only, msg)
     (header, unpacked_msg) = bft_msgs.unpack_request(packed)
     self.assertEqual(client_id, header.client_id)
     self.assertEqual(1, header.flags) # read_only = True
     self.assertEqual(req_seq_num, header.req_seq_num)
     self.assertEqual(msg, unpacked_msg)
示例#5
0
    async def write_batch(self,
                          msg_batch,
                          batch_seq_nums=None,
                          m_of_n_quorum=None):
        if not self.comm_prepared:
            await self._comm_prepare()

        cid = str(self.req_seq_num.next())
        batch_size = len(msg_batch)

        if batch_seq_nums is None:
            batch_seq_nums = []
            for n in range(batch_size):
                batch_seq_nums.append(self.req_seq_num.next())

        msg_data = b''
        for n in range(batch_size):
            msg = msg_batch[n]
            msg_seq_num = batch_seq_nums[n]
            msg_cid = str(msg_seq_num)
            msg_data = b''.join([
                msg_data,
                bft_msgs.pack_request(self.client_id, msg_seq_num, False,
                                      self.config.req_timeout_milli, msg_cid,
                                      msg, True)
            ])

        data = bft_msgs.pack_batch_request(self.client_id, batch_size,
                                           msg_data, cid)

        if m_of_n_quorum is None:
            m_of_n_quorum = MofNQuorum.LinearizableQuorum(
                self.config, [r.id for r in self.replicas])

        # Raise a trio.TooSlowError exception if a quorum of replies
        try:
            with trio.fail_after(batch_size * self.config.req_timeout_milli /
                                 1000):
                self._reset_on_new_request(batch_seq_nums)
                return await self._send_receive_loop(
                    data, False, m_of_n_quorum,
                    batch_size * self.config.retry_timeout_milli / 1000)
        except trio.TooSlowError:
            print(
                f"TooSlowError thrown from client_id {self.client_id}, for batch msg {cid} {batch_seq_nums}"
            )
            raise trio.TooSlowError
        finally:
            pass
示例#6
0
    async def write_batch(self, msg_batch, batch_seq_nums=None, m_of_n_quorum=None, corrupt_params=None, no_retries=False):
        if not self.comm_prepared:
            await self._comm_prepare()

        cid = str(self.req_seq_num.next())
        batch_size = len(msg_batch)

        if batch_seq_nums is None:
            batch_seq_nums = []
            for n in range(batch_size):
                batch_seq_nums.append(self.req_seq_num.next())

        msg_data = b''
        req_index_to_corrupt = random.randint(1, batch_size-1) # don't corrupt the 1st
        for n in range(batch_size):
            msg = msg_batch[n]
            msg_seq_num = batch_seq_nums[n]
            msg_cid = str(msg_seq_num)

            signature = b''
            client_id = self.client_id
            if self.signing_key:
                h = SHA256.new(msg)
                signature = pkcs1_15.new(self.signing_key).sign(h)
                if corrupt_params and (req_index_to_corrupt == n):
                    msg, signature, client_id = self._corrupt_signing_params(msg, signature, client_id, corrupt_params)

            msg_data = b''.join([msg_data, bft_msgs.pack_request(
                self.client_id, msg_seq_num, False, self.config.req_timeout_milli, msg_cid, msg, True,
                reconfiguration=False, span_context=b'', signature=signature)])

        data = bft_msgs.pack_batch_request(
            self.client_id, batch_size, msg_data, cid)

        if m_of_n_quorum is None:
            m_of_n_quorum = MofNQuorum.LinearizableQuorum(self.config, [r.id for r in self.replicas])

        # Raise a trio.TooSlowError exception if a quorum of replies
        try:
            with trio.fail_after(batch_size * self.config.req_timeout_milli / 1000):
                self._reset_on_new_request(batch_seq_nums)
                return await self._send_receive_loop(data, False, m_of_n_quorum,
                    batch_size * self.config.retry_timeout_milli / 1000, no_retries=no_retries)
        except trio.TooSlowError:
            print(f"TooSlowError thrown from client_id {self.client_id}, for batch msg {cid} {batch_seq_nums}")
            raise trio.TooSlowError
        finally:
            pass
示例#7
0
    async def sendSync(self, msg, read_only, seq_num=None, cid=None, pre_process=False, m_of_n_quorum=None):
        """
        Send a client request and wait for a m_of_n_quorum (if None, it will set to 2F+C+1 quorum) of replies.

        Return a single reply message if a quorum of replies matches.
        Otherwise, raise a trio.TooSlowError indicating the request timed out.

        Retry Strategy:
            If the request is a write and the primary is known then send only to
            the primary on the first attempt. Otherwise, if the request is read
            only or the primary is unknown, then send to all replicas on the
            first attempt.

            After `config.retry_timeout_milli` without receiving a quorum of
            identical replies, then clear the replies and send to all replicas.
            Continue this strategy every `retry_timeout_milli` until
            `config.req_timeout_milli` elapses. If `config.req_timeout_milli`
            elapses then a trio.TooSlowError is raised.

         Note that this method also binds the socket to an appropriate port if
         not already bound.
        """
        if not self.sock_bound:
            await self.bind()

        if seq_num is None:
            seq_num = self.req_seq_num.next()

        if cid is None:
            cid = str(seq_num)
        data = bft_msgs.pack_request(
            self.client_id, seq_num, read_only, self.config.req_timeout_milli, cid, msg, pre_process)

        if m_of_n_quorum is None:
            m_of_n_quorum = MofNQuorum.LinearizableQuorum(self.config, [r.id for r in self.replicas])

        # Raise a trio.TooSlowError exception if a quorum of replies
        try:
            with trio.fail_after(self.config.req_timeout_milli / 1000):
                self.reset_on_new_request()
                self.retries = 0
                return await self.send_loop(data, read_only, m_of_n_quorum)
        except trio.TooSlowError:
            print("TooSlowError thrown from client_id", self.client_id, "for seq_num", seq_num)
            raise trio.TooSlowError
        finally:
            pass