def process_view_change_message(self, msg: ViewChange, frm: str):
        result = self._validate(msg, frm)
        if result == STASH_WAITING_VIEW_CHANGE:
            self._stashed_vc_msgs.setdefault(msg.viewNo, 0)
            self._stashed_vc_msgs[msg.viewNo] += 1
            if self._data.quorums.view_change.is_reached(self._stashed_vc_msgs[msg.viewNo]) and \
                    not self._data.waiting_for_new_view:
                self._bus.send(StartViewChange(msg.viewNo))
        if result != PROCESS:
            return result, None

        self._logger.info("{} processing {} from {}".format(self, msg, frm))

        self.view_change_votes.add_view_change(msg, frm)

        vca = ViewChangeAck(viewNo=msg.viewNo,
                            name=getNodeName(frm),
                            digest=view_change_digest(msg))
        self.view_change_votes.add_view_change_ack(
            vca, getNodeName(self._data.name))

        if self._data.is_primary:
            self._send_new_view_if_needed()
            return PROCESS, None

        primary_node_name = getNodeName(self._data.primary_name)
        self._logger.info("{} sending {}".format(self, vca))
        self._network.send(vca, [primary_node_name])

        self._finish_view_change_if_needed()
        return PROCESS, None
def test_non_primary_responds_to_view_change_message_with_view_change_ack_to_new_primary(
        internal_bus, external_bus, some_item, other_item, validators, primary,
        view_change_service_builder, initial_view_no, is_master):
    # TODO: Need to decide on how we handle this case
    if not is_master:
        return

    next_view_no = initial_view_no + 1
    non_primary_name = some_item(validators, exclude=[primary(next_view_no)])
    service = view_change_service_builder(non_primary_name)

    internal_bus.send(NeedViewChange())
    external_bus.sent_messages.clear()

    vc = create_view_change(initial_view_no)
    frm = other_item(validators, exclude=[non_primary_name])
    external_bus.process_incoming(vc, generateName(frm, service._data.inst_id))

    assert len(external_bus.sent_messages) == 1
    msg, dst = external_bus.sent_messages[0]
    assert dst == [getNodeName(service._data.primary_name)]
    assert isinstance(msg, ViewChangeAck)
    assert msg.viewNo == vc.viewNo
    assert msg.name == frm
    assert msg.digest == view_change_digest(vc)
Beispiel #3
0
def create_new_view_from_vc(vc, validators, checkpoint=None, batches=None):
    vc_digest = view_change_digest(vc)
    vcs = [[node_name, vc_digest] for node_name in validators]
    checkpoint = checkpoint or vc.checkpoints[0]
    batches = batches or vc.prepared
    return NewView(vc.viewNo, sorted(vcs, key=itemgetter(0)), checkpoint,
                   batches)
    def process_view_change_message(self, msg: ViewChange, frm: str):
        result = self._validate(msg, frm)
        if result != PROCESS:
            return result, None

        self._logger.info("{} processing {} from {}".format(self, msg, frm))

        self.view_change_votes.add_view_change(msg, frm)

        vca = ViewChangeAck(viewNo=msg.viewNo,
                            name=getNodeName(frm),
                            digest=view_change_digest(msg))
        self.view_change_votes.add_view_change_ack(
            vca, getNodeName(self._data.name))

        if self._data.is_primary:
            self._send_new_view_if_needed()
            return PROCESS, None

        primary_node_name = getNodeName(self._data.primary_name)
        self._logger.info("{} sending {}".format(self, vca))
        self._network.send(vca, [primary_node_name])

        self._finish_view_change_if_needed()
        return PROCESS, None
def test_view_change_digest_is_256_bit_hexdigest(random):
    digest = view_change_digest(
        create_view_change(initial_view_no=0,
                           stable_cp=random.integer(0, 10000),
                           batches=create_batches(view_no=0)))
    assert isinstance(digest, str)
    assert len(digest) == 64
    assert all(v in string.hexdigits for v in digest)
 def _view_change_acks(vc, vc_frm, primary, count):
     digest = view_change_digest(vc)
     non_senders = [
         name for name in validators if name not in [vc_frm, primary]
     ]
     ack_frms = random.sample(non_senders, count)
     return [(ViewChangeAck(viewNo=vc.viewNo, name=vc_frm,
                            digest=digest), ack_frm)
             for ack_frm in ack_frms]
def test_different_view_change_messages_have_different_digests(random):
    batches = create_batches(view_no=0)
    assert view_change_digest(create_view_change(initial_view_no=0, stable_cp=100, batches=batches)) != \
           view_change_digest(create_view_change(initial_view_no=1, stable_cp=100, batches=batches))
    assert view_change_digest(create_view_change(initial_view_no=1, stable_cp=100, batches=batches)) != \
           view_change_digest(create_view_change(initial_view_no=1, stable_cp=101, batches=batches))
    assert view_change_digest(
        create_view_change(initial_view_no=1, stable_cp=100, batches=create_batches(view_no=0))) != \
           view_change_digest(create_view_change(initial_view_no=1, stable_cp=100, batches=create_batches(view_no=1)))
    assert view_change_digest(create_view_change(initial_view_no=0, stable_cp=100, batches=batches)) == \
           view_change_digest(create_view_change(initial_view_no=0, stable_cp=100, batches=batches))
def test_process_missing_message_view_change(message_req_service: MessageReqService, external_bus, data,
                                             internal_bus, view_change_message: ViewChange):
    frm = "frm"
    confused_node = "confused_node"
    inst_id = data.inst_id
    digest = view_change_digest(view_change_message)
    missing_msg = MissingMessage(msg_type=VIEW_CHANGE,
                                 key=(frm, digest),
                                 inst_id=inst_id,
                                 dst=[confused_node],
                                 stash_data=None)
    internal_bus.send(missing_msg)
    assert len(external_bus.sent_messages) == 1
    assert external_bus.sent_messages[0] == (MessageReq(VIEW_CHANGE,
                                                        {f.INST_ID.nm: inst_id,
                                                         f.DIGEST.nm: digest,
                                                         f.NAME.nm: frm}),
                                             [confused_node])
def test_process_message_req_view_change(message_req_service: MessageReqService,
                                         external_bus, data: ConsensusSharedData,
                                         view_change_message: ViewChange):
    frm = "frm"
    digest = view_change_digest(view_change_message)
    message_req = MessageReq(**{
        f.MSG_TYPE.nm: VIEW_CHANGE,
        f.PARAMS.nm: {f.INST_ID.nm: data.inst_id,
                      f.DIGEST.nm: digest,
                      f.NAME.nm: frm},
    })
    data.view_change_votes.add_view_change(view_change_message, frm)
    external_bus.process_incoming(message_req, frm)
    assert len(external_bus.sent_messages) == 1

    assert external_bus.sent_messages[0] == (MessageRep(message_req.msg_type,
                                                        message_req.params,
                                                        view_change_message._asdict()),
                                             [frm])
Beispiel #10
0
    def _validate_view_change(self, msg: ViewChange, frm: str, params):
        if msg is None:
            raise IncorrectMessageForHandlingException(msg,
                                                       reason='received null',
                                                       log_method=self._logger.debug)

        key = (params["name"], view_change_digest(msg))
        if key not in self.requested_messages:
            raise IncorrectMessageForHandlingException(msg,
                                                       reason='Had either not requested this msg or already '
                                                              'received the msg for {}'.format(key),
                                                       log_method=self._logger.debug)
        self._received_vc.setdefault(key, set())
        self._received_vc[key].add(frm)
        if not self._data.quorums.weak.is_reached(len(self._received_vc[key])) and frm != params["name"]:
            raise IncorrectMessageForHandlingException(msg,
                                                       reason='Count of VIEW_CHANGE messages {} '
                                                              'is not enough for quorum.'.format(msg),
                                                       log_method=self._logger.trace)
def test_process_message_rep_view_change_from_one(message_req_service: MessageReqService, external_bus, data,
                                                  view_change_message: ViewChange):
    frm = "frm"
    inst_id = data.inst_id
    digest = view_change_digest(view_change_message)
    key = (frm, digest)
    message_req_service.handlers[VIEW_CHANGE].requested_messages[key] = None
    message_rep_from_primary = MessageRep(**{
        f.MSG_TYPE.nm: VIEW_CHANGE,
        f.PARAMS.nm: {f.INST_ID.nm: inst_id,
                      f.DIGEST.nm: digest,
                      f.NAME.nm: frm},
        f.MSG.nm: dict(view_change_message.items())
    })
    frm = "frm"
    network_handler = Mock()
    external_bus.subscribe(ViewChange, network_handler)
    network_handler.assert_not_called()
    message_req_service.process_message_rep(message_rep_from_primary, frm)
    network_handler.assert_called_once_with(view_change_message, frm)
def test_new_view_from_malicious(view_change_service_builder, primary, initial_view_no, validators):
    """
    This test shows situation, when there is quorum of correct NEW_VIEW msgs
    and NEW_VIEW msg from malicious primary.
    In this case, view_change will be completed by quorum of the same NEW_VIEW msgs
    not by NEW_VIEW from malicious
    """

    proposed_view_no = initial_view_no + 1
    primary_name = primary(proposed_view_no)
    without_primary = [v for v in validators if v != primary_name]
    vcs_name = without_primary[0]

    vcs = view_change_service_builder(vcs_name)
    vcs._data.is_master = True
    vcs.process_need_view_change(NeedViewChange(view_no=proposed_view_no))

    vc_not_malicious = vcs.view_change_votes._get_vote(vcs_name).view_change
    not_malicious_nv = create_new_view_from_vc(vc_not_malicious, without_primary, checkpoint=vc_not_malicious.checkpoints[-1])

    vc_from_malicious = create_view_change(initial_view_no, stable_cp=20, batches=[])

    for i in range(0, len(without_primary)):
        vcs.view_change_votes.add_view_change(vc_not_malicious, without_primary[i])
        vcs._data.new_view_votes.add_new_view(not_malicious_nv, without_primary[i])

    vcs.view_change_votes.add_view_change(vc_from_malicious, primary_name)

    malicious_nv = NewView(viewNo=proposed_view_no,
                           viewChanges=[[primary_name, view_change_digest(vc_from_malicious)]],
                           checkpoint=Checkpoint(instId=0,
                                                 viewNo=initial_view_no,
                                                 seqNoStart=10,
                                                 seqNoEnd=20,
                                                 digest=cp_digest(20)),
                           batches=[])

    vcs.process_new_view_message(malicious_nv, "{}:{}".format(primary_name, 0))
    assert not vcs._data.waiting_for_new_view
Beispiel #13
0
def create_view_change_acks(vc, vc_frm, senders):
    digest = view_change_digest(vc)
    senders = [name for name in senders if name != vc_frm]
    return [(ViewChangeAck(viewNo=vc.viewNo, name=vc_frm,
                           digest=digest), ack_frm) for ack_frm in senders]
Beispiel #14
0
 def _create(self, msg: Dict, **kwargs):
     message = super()._create(msg)
     if view_change_digest(message) != kwargs[f.DIGEST.nm]:
         raise MismatchedMessageReplyException
     return message