def test_new_view_message_is_not_sent_by_non_primary_when_view_change_certificate_is_reached( internal_bus, external_bus, validators, primary, view_change_service_builder, initial_view_no, some_item, is_master): # TODO: Need to decide on how we handle this case if not is_master: return next_view_no = initial_view_no + 1 primary_name = primary(next_view_no) non_primary_name = some_item(validators, exclude=[primary_name]) service = view_change_service_builder(non_primary_name) # start view change internal_bus.send(NeedViewChange()) external_bus.sent_messages.clear() # receive quorum of ViewChanges and ViewChangeAcks non_primaries = [item for item in validators if item != primary_name] vc = create_view_change(initial_view_no) for vc_frm in non_primaries: external_bus.process_incoming( vc, generateName(vc_frm, service._data.inst_id)) for ack, ack_frm in create_view_change_acks(vc, vc_frm, non_primaries): external_bus.process_incoming( ack, generateName(ack_frm, service._data.inst_id)) # check that NewView hasn't been sent assert all(not isinstance(msg, NewView) for msg in external_bus.sent_messages)
def test_new_view_message_is_sent_by_primary_when_view_change_certificate_is_reached( internal_bus, external_bus, validators, primary, view_change_service_builder, initial_view_no, view_change_acks, is_master): # TODO: Need to decide on how we handle this case if not is_master: return primary_name = primary(initial_view_no + 1) service = view_change_service_builder(primary_name) # start view change internal_bus.send(NeedViewChange()) external_bus.sent_messages.clear() # receive quorum of ViewChanges and ViewChangeAcks non_primaries = [item for item in validators if item != primary_name] vc = create_view_change(initial_view_no) for vc_frm in non_primaries: external_bus.process_incoming( vc, generateName(vc_frm, service._data.inst_id)) for ack, ack_frm in view_change_acks(vc, vc_frm, primary_name, len(validators) - 2): external_bus.process_incoming( ack, generateName(ack_frm, service._data.inst_id)) # check that NewView has been sent assert len(external_bus.sent_messages) == 1 msg, dst = external_bus.sent_messages[0] assert dst is None # message was broadcast assert isinstance(msg, NewView) assert msg.viewNo == initial_view_no + 1
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)
def test_new_view_message_is_sent_by_primary_when_view_change_certificate_is_reached( internal_bus, validators, primary, view_change_service_builder, initial_view_no, view_change_acks): primary_name = primary(initial_view_no + 1) service = view_change_service_builder(primary_name) # start view change internal_bus.send(NeedViewChange()) service._network.sent_messages.clear() # receive quorum of ViewChanges and ViewChangeAcks non_primaries = [item for item in validators if item != primary_name] vc = create_view_change(initial_view_no) for vc_frm in non_primaries: service._network.process_incoming(vc, vc_frm) for ack, ack_frm in view_change_acks(vc, vc_frm, primary_name, len(validators) - 2): service._network.process_incoming(ack, ack_frm) # check that NewView has been sent assert len(service._network.sent_messages) == 1 msg, dst = service._network.sent_messages[0] assert dst is None # message was broadcast assert isinstance(msg, NewView) assert msg.viewNo == initial_view_no + 1
def test_view_change_finished_is_sent_by_non_primary_once_view_change_certificate_is_reached_and_new_view_from_primary( internal_bus, external_bus, validators, primary, view_change_service_builder, initial_view_no, some_item, is_master): # TODO: Need to decide on how we handle this case if not is_master: return handler = Mock() internal_bus.subscribe(NewViewAccepted, handler) next_view_no = initial_view_no + 1 primary_name = primary(next_view_no) non_primary_name = some_item(validators, exclude=[primary_name]) service = view_change_service_builder(non_primary_name) vc = create_view_change(initial_view_no) service._data.preprepared = vc.preprepared service._data.prepared = vc.prepared service._data.stable_checkpoint = vc.stableCheckpoint service._data.checkpoints = vc.checkpoints old_data = copy_shared_data(service._data) # start view change internal_bus.send(NeedViewChange()) external_bus.sent_messages.clear() # receive quorum of ViewChanges and ViewChangeAcks non_primaries = [item for item in validators if item != primary_name] non_primaries = random.sample(non_primaries, service._data.quorums.view_change.value) new_view = create_new_view_from_vc(vc, non_primaries) for vc_frm in non_primaries: external_bus.process_incoming( vc, generateName(vc_frm, service._data.inst_id)) for ack, ack_frm in create_view_change_acks(vc, vc_frm, non_primaries): external_bus.process_incoming( ack, generateName(ack_frm, service._data.inst_id)) # check that NewViewAccepted hasn't been sent if NewView is from non-primary external_bus.process_incoming( new_view, generateName(non_primary_name, service._data.inst_id)) handler.assert_not_called() assert service._data.view_no == initial_view_no + 1 assert service._data.waiting_for_new_view # check that NewViewAccepted has been sent if NewView is from primary external_bus.process_incoming( new_view, generateName(primary_name, service._data.inst_id)) expected_finish_vc = NewViewAccepted(view_no=initial_view_no + 1, view_changes=new_view.viewChanges, checkpoint=new_view.checkpoint, batches=new_view.batches) handler.assert_called_with(expected_finish_vc) # check that shared data is updated new_data = copy_shared_data(service._data) check_service_changed_only_owned_fields_in_shared_data( ViewChangeService, old_data, new_data) assert service._data.view_no == initial_view_no + 1 assert not service._data.waiting_for_new_view
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 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_primary_doesnt_respond_to_view_change_message( some_item, validators, primary, view_change_service_builder, initial_view_no, view_change_message): name = primary(initial_view_no + 1) service = view_change_service_builder(name) vc = create_view_change(initial_view_no) frm = some_item(validators, exclude=[name]) service._network.process_incoming(vc, frm) assert len(service._network.sent_messages) == 0
def test_send_instance_change_on_new_view_with_incorrect_checkpoint( internal_bus, external_bus, validators, primary, view_change_service_builder, initial_view_no, some_item, is_master): # TODO: Need to decide on how we handle this case if not is_master: return next_view_no = initial_view_no + 1 primary_name = primary(next_view_no) non_primary_name = some_item(validators, exclude=[primary_name]) service = view_change_service_builder(non_primary_name) vc = create_view_change(initial_view_no) service._data.preprepared = vc.preprepared service._data.prepared = vc.prepared service._data.stable_checkpoint = vc.stableCheckpoint service._data.checkpoints = vc.checkpoints # start view change internal_bus.send(NeedViewChange()) external_bus.sent_messages.clear() # receive quorum of ViewChanges and ViewChangeAcks non_primaries = [item for item in validators if item != primary_name] non_primaries = random.sample(non_primaries, service._data.quorums.view_change.value) for vc_frm in non_primaries: external_bus.process_incoming( vc, generateName(vc_frm, service._data.inst_id)) for ack, ack_frm in create_view_change_acks(vc, vc_frm, non_primaries): external_bus.process_incoming( ack, generateName(ack_frm, service._data.inst_id)) cp = Checkpoint(instId=0, viewNo=initial_view_no, seqNoStart=0, seqNoEnd=1000, digest=cp_digest(1000)) new_view = create_new_view_from_vc(vc, non_primaries, checkpoint=cp) # send NewView by Primary init_network_msg_count = len(external_bus.sent_messages) external_bus.process_incoming( new_view, generateName(primary_name, service._data.inst_id)) # we don't go to new view, just send Instance Change assert service._data.view_no == initial_view_no + 1 assert init_network_msg_count + 1 == len(external_bus.sent_messages) msg, dst = external_bus.sent_messages[-1] assert dst is None # broadcast assert isinstance(msg, InstanceChange) assert msg.viewNo == initial_view_no + 2 assert msg.reason == Suspicions.NEW_VIEW_INVALID_CHECKPOINTS.code
def test_primary_doesnt_respond_to_view_change_message( some_item, validators, primary, external_bus, view_change_service_builder, initial_view_no, view_change_message, is_master): # TODO: Need to decide on how we handle this case if not is_master: return name = primary(initial_view_no + 1) service = view_change_service_builder(name) vc = create_view_change(initial_view_no) frm = some_item(validators, exclude=[name]) external_bus.process_incoming(vc, generateName(frm, service._data.inst_id)) assert len(external_bus.sent_messages) == 0
def test_do_not_send_instance_change_on_timeout_when_multiple_view_change_finished_on_time(internal_bus, external_bus, validators, primary, view_change_service_builder, timer, initial_view_no, is_master): # TODO: Need to decide on how we handle this case if not is_master: return primary_name = primary(initial_view_no + 2) service = view_change_service_builder(primary_name) # start first view change internal_bus.send(NeedViewChange()) # start second view change internal_bus.send(NeedViewChange()) external_bus.sent_messages.clear() # receive quorum of ViewChanges and ViewChangeAcks non_primaries = [item for item in validators if item != primary_name] vc = create_view_change(initial_view_no + 1) service._data.checkpoints.append(Checkpoint(instId=0, viewNo=initial_view_no + 1, seqNoStart=0, seqNoEnd=DEFAULT_STABLE_CHKP, digest=cp_digest(DEFAULT_STABLE_CHKP))) for vc_frm in non_primaries: external_bus.process_incoming(vc, generateName(vc_frm, service._data.inst_id)) for ack, ack_frm in create_view_change_acks(vc, vc_frm, non_primaries): external_bus.process_incoming(ack, generateName(ack_frm, service._data.inst_id)) # check that view change is finished assert service._data.view_no == initial_view_no + 2 assert not service._data.waiting_for_new_view assert len(external_bus.sent_messages) == 1 msg, dst = external_bus.sent_messages[0] assert isinstance(msg, NewView) # make sure view change hasn't been started again timer.sleep(service._config.NEW_VIEW_TIMEOUT + 1) assert service._data.view_no == initial_view_no + 2 assert len(external_bus.sent_messages) == 1 msg, dst = external_bus.sent_messages[0] assert isinstance(msg, NewView)
def test_new_view_incorrect_checkpoint(internal_bus, validators, primary, view_change_service_builder, initial_view_no, some_item): next_view_no = initial_view_no + 1 primary_name = primary(next_view_no) non_primary_name = some_item(validators, exclude=[primary_name]) service = view_change_service_builder(non_primary_name) vc = create_view_change(initial_view_no) service._data.preprepared = vc.preprepared service._data.prepared = vc.prepared service._data.stable_checkpoint = vc.stableCheckpoint service._data.checkpoints = vc.checkpoints # start view change internal_bus.send(NeedViewChange()) service._network.sent_messages.clear() handler = Mock() internal_bus.subscribe(NeedViewChange, handler) # receive quorum of ViewChanges and ViewChangeAcks non_primaries = [item for item in validators if item != primary_name] non_primaries = random.sample(non_primaries, service._data.quorums.view_change.value) for vc_frm in non_primaries: service._network.process_incoming(vc, vc_frm) for ack, ack_frm in create_view_change_acks(vc, vc_frm, non_primaries): service._network.process_incoming(ack, ack_frm) cp = Checkpoint(instId=0, viewNo=initial_view_no, seqNoStart=0, seqNoEnd=1000, digest=cp_digest(1000)) new_view = create_new_view_from_vc(vc, non_primaries, checkpoint=cp) # send NewView by Primary service._network.process_incoming(new_view, primary_name) # make sure that NeedViewChange is called handler.assert_called_with(NeedViewChange()) # make sure that we get to the next view assert service._data.view_no == initial_view_no + 2 assert service._data.waiting_for_new_view
def test_non_primary_responds_to_view_change_message_with_view_change_ack_to_new_primary( internal_bus, some_item, other_item, validators, primary, view_change_service_builder, initial_view_no): 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()) service._network.sent_messages.clear() vc = create_view_change(initial_view_no) frm = other_item(validators, exclude=[non_primary_name]) service._network.process_incoming(vc, frm) assert len(service._network.sent_messages) == 1 msg, dst = service._network.sent_messages[0] assert dst == service._data.primary_name assert isinstance(msg, ViewChangeAck) assert msg.viewNo == vc.viewNo assert msg.name == frm assert msg.digest == view_change_digest(vc)
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
def test_view_change_finished_is_sent_by_primary_once_view_change_certificate_is_reached( internal_bus, validators, primary, view_change_service_builder, initial_view_no): handler = Mock() internal_bus.subscribe(NewViewAccepted, handler) primary_name = primary(initial_view_no + 1) service = view_change_service_builder(primary_name) # start view change internal_bus.send(NeedViewChange()) service._network.sent_messages.clear() old_data = copy_shared_data(service._data) # receive quorum of ViewChanges and ViewChangeAcks non_primaries = [item for item in validators if item != primary_name] non_primaries = random.sample(non_primaries, service._data.quorums.view_change.value) vc = create_view_change(initial_view_no) new_view = create_new_view_from_vc(vc, non_primaries) for vc_frm in non_primaries: service._network.process_incoming(vc, vc_frm) for ack, ack_frm in create_view_change_acks(vc, vc_frm, non_primaries): service._network.process_incoming(ack, ack_frm) # check that NewViewAccepted has been sent expected_finish_vc = NewViewAccepted(view_no=initial_view_no + 1, view_changes=new_view.viewChanges, checkpoint=new_view.checkpoint, batches=new_view.batches) handler.assert_called_with(expected_finish_vc) # check that shared data is updated new_data = copy_shared_data(service._data) check_service_changed_only_owned_fields_in_shared_data( ViewChangeService, old_data, new_data) assert service._data.view_no == initial_view_no + 1 assert not service._data.waiting_for_new_view
def test_new_view_message_is_not_sent_by_non_primary_when_view_change_certificate_is_reached( internal_bus, validators, primary, view_change_service_builder, initial_view_no, some_item): next_view_no = initial_view_no + 1 primary_name = primary(next_view_no) non_primary_name = some_item(validators, exclude=[primary_name]) service = view_change_service_builder(non_primary_name) # start view change internal_bus.send(NeedViewChange()) service._network.sent_messages.clear() # receive quorum of ViewChanges and ViewChangeAcks non_primaries = [item for item in validators if item != primary_name] vc = create_view_change(initial_view_no) for vc_frm in non_primaries: service._network.process_incoming(vc, vc_frm) for ack, ack_frm in create_view_change_acks(vc, vc_frm, non_primaries): service._network.process_incoming(ack, ack_frm) # check that NewView hasn't been sent assert all(not isinstance(msg, NewView) for msg in service._network.sent_messages)
def view_change_message(data): return create_view_change(data.view_no)