def test_calc_checkpoints_empty(builder): vc = ViewChange(viewNo=2, stableCheckpoint=0, prepared=[BatchID(1, 1, 1, "digest1")], preprepared=[BatchID(1, 1, 1, "digest1")], checkpoints=[]) vcs = [vc, vc, vc, vc] assert builder.calc_checkpoint(vcs) is None
def test_view_change_data(view_change_service, data): data.view_no = 1 data.checkpoints.clear() cp = Checkpoint(instId=0, viewNo=1, seqNoStart=0, seqNoEnd=10, digest=cp_digest(10)) data.checkpoints.add(cp) data.stable_checkpoint = 10 data.prepared = [BatchID(0, 0, 1, "digest1"), BatchID(0, 0, 2, "digest2")] data.preprepared = [ BatchID(0, 0, 1, "digest1"), BatchID(0, 0, 2, "digest2"), BatchID(0, 0, 3, "digest3") ] view_change_service._bus.send(NeedViewChange()) assert data.view_no == 2 msg = get_view_change(view_change_service) assert msg.viewNo == 2 assert msg.prepared == [(0, 0, 1, "digest1"), (0, 0, 2, "digest2")] assert msg.preprepared == [(0, 0, 1, "digest1"), (0, 0, 2, "digest2"), (0, 0, 3, "digest3")] assert msg.stableCheckpoint == 10 assert msg.checkpoints == [cp]
def test_view_change_data_multiple(view_change_service, data): # view 0 -> 1 data.view_no = 0 cp1 = Checkpoint(instId=0, viewNo=0, seqNoStart=0, seqNoEnd=10, digest=cp_digest(10)) data.checkpoints.add(cp1) data.stable_checkpoint = 0 data.prepared = [BatchID(0, 0, 1, "digest1"), BatchID(0, 0, 2, "digest2")] data.preprepared = [ BatchID(0, 0, 1, "digest1"), BatchID(0, 0, 2, "digest2"), BatchID(0, 0, 3, "digest3") ] view_change_service._bus.send(NeedViewChange()) assert data.view_no == 1 msg = get_view_change(view_change_service) assert msg.viewNo == 1 assert msg.prepared == [(0, 0, 1, "digest1"), (0, 0, 2, "digest2")] assert msg.preprepared == [(0, 0, 1, "digest1"), (0, 0, 2, "digest2"), (0, 0, 3, "digest3")] assert msg.stableCheckpoint == 0 assert msg.checkpoints == [data.initial_checkpoint, cp1] # view 1 -> 2 data.view_no = 1 cp2 = Checkpoint(instId=0, viewNo=1, seqNoStart=0, seqNoEnd=20, digest=cp_digest(20)) data.checkpoints.add(cp2) data.stable_checkpoint = 0 data.prepared = [ BatchID(1, 1, 11, "digest11"), BatchID(1, 1, 12, "digest12") ] data.preprepared = [ BatchID(1, 1, 11, "digest11"), BatchID(1, 1, 12, "digest12"), BatchID(1, 1, 13, "digest13") ] view_change_service._bus.send(NeedViewChange()) assert data.view_no == 2 msg = get_view_change(view_change_service) assert msg.viewNo == 2 assert msg.prepared == [(0, 0, 1, "digest1"), (0, 0, 2, "digest2"), (1, 1, 11, "digest11"), (1, 1, 12, "digest12")] assert msg.preprepared == [(0, 0, 1, "digest1"), (0, 0, 2, "digest2"), (0, 0, 3, "digest3"), (1, 1, 11, "digest11"), (1, 1, 12, "digest12"), (1, 1, 13, "digest13")] assert msg.stableCheckpoint == 0 assert msg.checkpoints == [data.initial_checkpoint, cp1, cp2]
def test_calc_checkpoints_equal_stable(builder): cp = Checkpoint(instId=0, viewNo=0, seqNoStart=0, seqNoEnd=10, digest=cp_digest(0)) vc = ViewChange(viewNo=2, stableCheckpoint=10, prepared=[BatchID(1, 1, 1, "digest1")], preprepared=[BatchID(1, 1, 1, "digest1")], checkpoints=[cp]) vcs = [vc, vc, vc, vc] assert builder.calc_checkpoint(vcs) == cp
def test_calc_batches_diff_pp_viewno_in_prepare_and_preprepare(builder): cp = Checkpoint(instId=0, viewNo=0, seqNoStart=0, seqNoEnd=0, digest=cp_digest(0)) vc = ViewChange(viewNo=2, stableCheckpoint=0, prepared=[BatchID(1, 0, 1, "digest1"), BatchID(1, 0, 2, "digest2")], preprepared=[BatchID(1, 1, 1, "digest1"), BatchID(1, 1, 2, "digest2")], checkpoints=[cp]) vcs = [vc, vc, vc, vc] assert builder.calc_batches(cp, vcs) is None
def test_calc_checkpoints_selects_max(builder): cp1 = Checkpoint(instId=0, viewNo=0, seqNoStart=0, seqNoEnd=0, digest=cp_digest(0)) cp2 = Checkpoint(instId=0, viewNo=0, seqNoStart=0, seqNoEnd=10, digest=cp_digest(10)) cp3 = Checkpoint(instId=0, viewNo=0, seqNoStart=0, seqNoEnd=20, digest=cp_digest(20)) vc1 = ViewChange(viewNo=2, stableCheckpoint=0, prepared=[BatchID(1, 1, 1, "digest1")], preprepared=[BatchID(1, 1, 1, "digest1")], checkpoints=[cp1]) vc2_not_stable = ViewChange(viewNo=2, stableCheckpoint=0, prepared=[BatchID(1, 1, 1, "digest1")], preprepared=[BatchID(1, 1, 1, "digest1")], checkpoints=[cp2]) vc2_stable = ViewChange(viewNo=2, stableCheckpoint=10, prepared=[BatchID(1, 1, 1, "digest1")], preprepared=[BatchID(1, 1, 1, "digest1")], checkpoints=[cp2]) vc3_not_stable = ViewChange(viewNo=2, stableCheckpoint=10, prepared=[BatchID(1, 1, 1, "digest1")], preprepared=[BatchID(1, 1, 1, "digest1")], checkpoints=[cp3]) vc3_stable = ViewChange(viewNo=2, stableCheckpoint=10, prepared=[BatchID(1, 1, 1, "digest1")], preprepared=[BatchID(1, 1, 1, "digest1")], checkpoints=[cp3]) for vc3 in (vc3_not_stable, vc3_stable): for vc2 in (vc2_not_stable, vc2_stable): vcs = [vc1, vc2, vc3, vc3] assert builder.calc_checkpoint(vcs) == cp3 vcs = [vc1, vc3, vc3, vc3] assert builder.calc_checkpoint(vcs) == cp3 vcs = [vc2, vc3, vc3, vc3] assert builder.calc_checkpoint(vcs) == cp3 vcs = [vc2, vc2, vc3, vc3] assert builder.calc_checkpoint(vcs) == cp3 vcs = [vc1, vc1, vc3, vc3] assert builder.calc_checkpoint(vcs) == cp3 vcs = [vc1, vc1, vc1, vc3] assert builder.calc_checkpoint(vcs) == cp1 vcs = [vc1, vc1, vc2, vc2] assert builder.calc_checkpoint(vcs) == cp2 vcs = [vc2, vc2, vc2, vc3] assert builder.calc_checkpoint(vcs) == cp2
def test_calc_batches_takes_quorum_of_prepared(builder): cp = Checkpoint(instId=0, viewNo=0, seqNoStart=0, seqNoEnd=0, digest=cp_digest(0)) vc1 = ViewChange(viewNo=1, stableCheckpoint=0, prepared=[BatchID(0, 0, 1, "digest2")], preprepared=[BatchID(0, 0, 1, "digest2")], checkpoints=[cp]) vc2 = ViewChange(viewNo=1, stableCheckpoint=0, prepared=[BatchID(0, 0, 1, "digest1")], preprepared=[BatchID(0, 0, 1, "digest1")], checkpoints=[cp]) vc3 = ViewChange(viewNo=1, stableCheckpoint=0, prepared=[], preprepared=[BatchID(0, 0, 1, "digest1")], checkpoints=[cp]) vcs = [vc1, vc2, vc2, vc2] assert builder.calc_batches(cp, vcs) == [BatchID(0, 0, 1, "digest1")] vcs = [vc3, vc2, vc2, vc2] assert builder.calc_batches(cp, vcs) == [BatchID(0, 0, 1, "digest1")] vcs = [vc3, vc3, vc3, vc3] assert builder.calc_batches(cp, vcs) == [] vcs = [vc1, vc1, vc2, vc2] assert builder.calc_batches(cp, vcs) is None # since we have enough pre-prepares vcs = [vc2, vc3, vc3, vc3] assert builder.calc_batches(cp, vcs) == [BatchID(0, 0, 1, "digest1")] vcs = [vc2, vc2, vc3, vc3] assert builder.calc_batches(cp, vcs) == [BatchID(0, 0, 1, "digest1")]
def test_calc_batches_takes_max_view_same_pp_view(builder): cp = Checkpoint(instId=0, viewNo=0, seqNoStart=0, seqNoEnd=0, digest=cp_digest(0)) vc = ViewChange(viewNo=2, stableCheckpoint=0, prepared=[BatchID(0, 0, 1, "digest1"), BatchID(0, 0, 2, "digest2"), BatchID(1, 1, 1, "digest1"), BatchID(1, 1, 2, "digest2")], preprepared=[BatchID(0, 0, 1, "digest1"), BatchID(0, 0, 2, "digest2"), BatchID(1, 1, 1, "digest1"), BatchID(1, 1, 2, "digest2")], checkpoints=[cp]) vcs = [vc, vc, vc, vc] assert builder.calc_batches(cp, vcs) == [BatchID(1, 1, 1, "digest1"), BatchID(1, 1, 2, "digest2")]
def test_calc_batches_quorum(builder): cp = Checkpoint(instId=0, viewNo=0, seqNoStart=0, seqNoEnd=0, digest=cp_digest(0)) vc = ViewChange(viewNo=1, stableCheckpoint=0, prepared=[BatchID(0, 0, 1, "digest1"), BatchID(0, 0, 2, "digest2")], preprepared=[BatchID(0, 0, 1, "digest1"), BatchID(0, 0, 2, "digest2"), BatchID(0, 0, 3, "digest3")], checkpoints=[cp]) vcs = [vc] assert builder.calc_batches(cp, vcs) is None vcs.append(vc) assert builder.calc_batches(cp, vcs) is None vcs.append(vc) assert builder.calc_batches(cp, vcs)
def calc_committed(view_changes, max_pp_seq_no, n, f) -> List[BatchID]: def check_prepared_in_vc(vc, batch_id): # check that batch_id is present in VC's prepared and preprepared for p_batch_id in vc.prepared: if batch_id != p_batch_id: continue for pp_batch_id in vc.preprepared: if batch_id == pp_batch_id: return True return False def find_batch_id(pp_seq_no): for vc in view_changes: for batch_id in vc.prepared: if batch_id[2] != pp_seq_no: continue prepared_count = sum(1 for vc in view_changes if check_prepared_in_vc(vc, batch_id)) if prepared_count < n - f: continue return batch_id return None committed = [] for pp_seq_no in range(1, max_pp_seq_no): batch_id = find_batch_id(pp_seq_no) if batch_id is not None: committed.append(BatchID(*batch_id)) return committed
def update_shared_data(node, msg: NewViewCheckpointsApplied): x = [ BatchID(view_no=msg.view_no, pp_view_no=batch_id.pp_view_no, pp_seq_no=batch_id.pp_seq_no, pp_digest=batch_id.pp_digest) for batch_id in msg.batches ] node._orderer._data.preprepared = x
def check(vc: ViewChange): if pp_seq_no <= vc.stableCheckpoint: return False for _some_bid in vc.prepared: some_bid = BatchID(*_some_bid) if some_bid.pp_seq_no == pp_seq_no: return False return True
def test_calc_batches_respects_checkpoint(builder): cp = Checkpoint(instId=0, viewNo=0, seqNoStart=0, seqNoEnd=10, digest=cp_digest(10)) vc = ViewChange(viewNo=1, stableCheckpoint=0, prepared=[BatchID(0, 0, 1, "digest1"), BatchID(0, 0, 2, "digest2")], preprepared=[BatchID(0, 0, 1, "digest1"), BatchID(0, 0, 2, "digest2")], checkpoints=[cp]) vcs = [vc, vc, vc, vc] assert builder.calc_batches(cp, vcs) == [] cp = Checkpoint(instId=0, viewNo=0, seqNoStart=0, seqNoEnd=10, digest=cp_digest(10)) vc = ViewChange(viewNo=2, stableCheckpoint=0, prepared=[BatchID(0, 0, 10, "digest10"), BatchID(0, 0, 11, "digest11"), BatchID(1, 0, 12, "digest12")], preprepared=[BatchID(0, 0, 10, "digest10"), BatchID(0, 0, 11, "digest11"), BatchID(1, 0, 12, "digest12")], checkpoints=[cp]) vcs = [vc, vc, vc, vc] assert builder.calc_batches(cp, vcs) == [BatchID(0, 0, 11, "digest11"), BatchID(1, 0, 12, "digest12")]
def test_calc_batches_takes_prepared_only(builder): cp = Checkpoint(instId=0, viewNo=0, seqNoStart=0, seqNoEnd=0, digest=cp_digest(0)) vc = ViewChange(viewNo=1, stableCheckpoint=0, prepared=[], preprepared=[BatchID(0, 0, 1, "digest1"), BatchID(0, 0, 2, "digest2"), BatchID(0, 0, 3, "digest3"), BatchID(0, 0, 4, "digest4")], checkpoints=[cp]) vcs = [vc, vc, vc, vc] assert builder.calc_batches(cp, vcs) == [] cp = Checkpoint(instId=0, viewNo=0, seqNoStart=0, seqNoEnd=0, digest=cp_digest(0)) vc = ViewChange(viewNo=1, stableCheckpoint=0, prepared=[BatchID(0, 0, 1, "digest1"), BatchID(0, 0, 2, "digest2")], preprepared=[BatchID(0, 0, 1, "digest1"), BatchID(0, 0, 2, "digest2"), BatchID(0, 0, 3, "digest3"), BatchID(0, 0, 4, "digest4")], checkpoints=[cp]) vcs = [vc, vc, vc, vc] assert builder.calc_batches(cp, vcs) == [BatchID(0, 0, 1, "digest1"), BatchID(0, 0, 2, "digest2")]
def test_process_preprepare_on_old_view_pre_prepares_reply( external_bus, internal_bus, orderer, is_primary, initial_view_no, pre_prepares): # !!!SETUP!!! orderer._data.view_no = initial_view_no + 1 new_view = create_new_view( initial_view_no=initial_view_no, stable_cp=200, batches=create_batches_from_preprepares(pre_prepares)) orderer._data.new_view_votes.add_new_view(new_view, orderer._data.primary_name) orderer._data.prev_view_prepare_cert = new_view.batches[-1].pp_seq_no # !!!EXECUTE!!! rep = OldViewPrePrepareReply(0, [pp._asdict() for pp in pre_prepares]) orderer._network.process_incoming( rep, generateName("node1", orderer._data.inst_id)) # !!!CHECK!!! if not orderer.is_master: # no re-ordering is expected on non-master assert orderer._data.preprepared == [] assert orderer._data.prepared == [] return # check that PPs were added assert orderer._data.preprepared == [ BatchID(view_no=initial_view_no + 1, pp_view_no=pp.viewNo, pp_seq_no=pp.ppSeqNo, pp_digest=pp.digest) for pp in pre_prepares ] # check that sent_preprepares is updated in case of Primary and prePrepares in case of non-primary updated_prepares_collection = orderer.prePrepares if not is_primary else orderer.sent_preprepares non_updated_prepares_collection = orderer.sent_preprepares if not is_primary else orderer.prePrepares for pp in pre_prepares: new_pp = updateNamedTuple(pp, viewNo=initial_view_no + 1, originalViewNo=pp.viewNo) assert (initial_view_no + 1, new_pp.ppSeqNo) in updated_prepares_collection assert updated_prepares_collection[(initial_view_no + 1, new_pp.ppSeqNo)] == new_pp assert not non_updated_prepares_collection # check that Prepare is sent in case of non primary if not is_primary: check_prepares_sent(external_bus, pre_prepares, initial_view_no + 1) else: assert len(external_bus.sent_messages) == 0 # we don't have a quorum of Prepares yet assert orderer._data.prepared == []
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if isinstance(self.checkpoint, dict): self.checkpoint = Checkpoint(**self.checkpoint) # The field `batches` can to be a list of BatchIDs or of dicts. # If it's not a list of dicts then we don't need to deserialize it. if not self.batches or not isinstance(self.batches[0], dict): return self.batches = [ BatchID(**bid) for bid in self.batches if isinstance(bid, dict) ]
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) checkpoints = [] for chk in self.checkpoints: if isinstance(chk, dict): checkpoints.append(Checkpoint(**chk)) if checkpoints: self.checkpoints = checkpoints # The field `prepared` can to be a list of BatchIDs or of dicts. # If its a list of dicts then we need to deserialize it. if self.prepared and isinstance(self.prepared[0], dict): self.prepared = [BatchID(**bid) for bid in self.prepared if isinstance(bid, dict)] # The field `preprepared` can to be a list of BatchIDs or of dicts. # If its a list of dicts then we need to deserialize it. if self.preprepared and isinstance(self.preprepared[0], dict): self.preprepared = [BatchID(**bid) for bid in self.preprepared if isinstance(bid, dict)]
def _specific_validation(self, val): if len(val) != 4: return 'should have size of 4' if isinstance(val, dict): if any(key not in BatchID._fields for key in val.keys()): return 'incorrect list of fields' bid = BatchID(**val) else: bid = BatchID(*val) for validate, value in ((self._validate_non_negative_number_field, bid.view_no), (self._validate_non_negative_number_field, bid.pp_view_no), (self._validate_non_negative_number_field, bid.pp_seq_no), (self._validate_non_empty_string_field, bid.pp_digest)): err = validate(value) if err: return err
def _specific_validation(self, val): if len(val) != 4: return 'should have size of 4' if isinstance(val, dict): if any(key not in BatchID._fields for key in val.keys()): return 'incorrect list of fields' bid = BatchID(**val) else: bid = BatchID(*val) for validator, value in ((NonNegativeNumberField().validate, bid.view_no), (NonNegativeNumberField().validate, bid.pp_view_no), (NonNegativeNumberField().validate, bid.pp_seq_no), (NonEmptyStringField().validate, bid.pp_digest)): err = validator(value) if err: return err
def test_view_change_replaces_prepare(view_change_service, data): data.view_no = 0 data.prepared = [BatchID(0, 0, 1, "digest1"), BatchID(0, 0, 2, "digest2")] # view no 0->1 view_change_service._bus.send(NeedViewChange()) assert data.view_no == 1 msg = get_view_change(view_change_service) assert msg.viewNo == 1 assert msg.prepared == [(0, 0, 1, "digest1"), (0, 0, 2, "digest2")] # view no 1->2 # replace by different viewNo and digest data.prepared = [ BatchID(1, 1, 1, "digest11"), BatchID(1, 1, 2, "digest22") ] view_change_service._bus.send(NeedViewChange()) assert data.view_no == 2 msg = get_view_change(view_change_service) assert msg.viewNo == 2 assert msg.prepared == [(1, 1, 1, "digest11"), (1, 1, 2, "digest22")] # view no 2->3 # replace by different viewNo only data.prepared = [BatchID(2, 2, 2, "digest22"), BatchID(2, 2, 3, "digest3")] view_change_service._bus.send(NeedViewChange()) assert data.view_no == 3 msg = get_view_change(view_change_service) assert msg.viewNo == 3 assert msg.prepared == [(1, 1, 1, "digest11"), (2, 2, 2, "digest22"), (2, 2, 3, "digest3")]
def check(vc: ViewChange): for _some_bid in vc.preprepared: some_bid = BatchID(*_some_bid) if some_bid.pp_seq_no != bid.pp_seq_no: continue if some_bid.pp_view_no != bid.pp_view_no: continue if some_bid.pp_digest != bid.pp_digest: continue if some_bid.view_no >= bid.view_no: return True return False
def _try_find_batch_for_pp_seq_no(self, vcs, pp_seq_no): for vc in vcs: for _bid in vc.prepared: bid = BatchID(*_bid) if bid.pp_seq_no != pp_seq_no: continue if not self._is_batch_prepared(bid, vcs): continue if not self._is_batch_preprepared(bid, vcs): continue return bid return None
def check(vc: ViewChange): if bid.pp_seq_no <= vc.stableCheckpoint: return False for _some_bid in vc.prepared: some_bid = BatchID(*_some_bid) if some_bid.pp_seq_no != bid.pp_seq_no: continue # not ( (v' < v) OR (v'==v and d'==d and pp_view_no'==pp_view_no) ) if some_bid.view_no > bid.view_no: return False if some_bid.view_no >= bid.view_no and some_bid.pp_digest != bid.pp_digest: return False if some_bid.view_no >= bid.view_no and some_bid.pp_view_no != bid.pp_view_no: return False return True
def calc_committed(view_changes): committed = [] for pp_seq_no in range(1, 50): batch_id = None for vc in view_changes: # pp_seq_no must be present in all PrePrepares for pp in vc.preprepared: if pp[2] == pp_seq_no: if batch_id is None: batch_id = pp assert batch_id == pp break # pp_seq_no must be present in all Prepares if batch_id not in vc.prepared: return committed committed.append(BatchID(*batch_id)) return committed
def test_calc_checkpoints_quorum(builder): cp1 = Checkpoint(instId=0, viewNo=0, seqNoStart=0, seqNoEnd=0, digest=cp_digest(0)) cp2 = Checkpoint(instId=0, viewNo=0, seqNoStart=0, seqNoEnd=10, digest=cp_digest(10)) vc1 = ViewChange(viewNo=2, stableCheckpoint=0, prepared=[BatchID(1, 1, 1, "digest1")], preprepared=[BatchID(1, 1, 1, "digest1")], checkpoints=[cp1]) vc2 = ViewChange(viewNo=2, stableCheckpoint=0, prepared=[BatchID(1, 1, 1, "digest1")], preprepared=[BatchID(1, 1, 1, "digest1")], checkpoints=[cp2]) vc2_stable = ViewChange(viewNo=2, stableCheckpoint=10, prepared=[BatchID(1, 1, 1, "digest1")], preprepared=[BatchID(1, 1, 1, "digest1")], checkpoints=[cp2]) vcs = [vc1, vc1, vc1, vc1] assert builder.calc_checkpoint(vcs) == cp1 vcs = [vc2, vc2, vc2, vc2] assert builder.calc_checkpoint(vcs) == cp2 vcs = [vc2_stable, vc2_stable, vc2_stable, vc2_stable] assert builder.calc_checkpoint(vcs) == cp2 vcs = [vc2, vc1, vc1, vc1] assert builder.calc_checkpoint(vcs) == cp1 vcs = [vc2, vc2, vc1, vc1] assert builder.calc_checkpoint(vcs) == cp2 vcs = [vc2, vc2, vc2, vc1] assert builder.calc_checkpoint(vcs) == cp2 vcs = [vc2_stable, vc1, vc1, vc1] assert builder.calc_checkpoint(vcs) == cp1 vcs = [vc2_stable, vc2_stable, vc1, vc1] assert builder.calc_checkpoint(vcs) == cp2 vcs = [vc2_stable, vc2_stable, vc2_stable, vc1] assert builder.calc_checkpoint(vcs) == cp2 vcs = [vc2_stable, vc2, vc2, vc2] assert builder.calc_checkpoint(vcs) == cp2 vcs = [vc2_stable, vc2_stable, vc2, vc2] assert builder.calc_checkpoint(vcs) == cp2 vcs = [vc2_stable, vc2_stable, vc2_stable, vc2] assert builder.calc_checkpoint(vcs) == cp2
def test_calc_batches_takes_next_view_prepared_if_old_view_prepared(builder): cp = Checkpoint(instId=0, viewNo=0, seqNoStart=0, seqNoEnd=0, digest=cp_digest(0)) vc1 = ViewChange(viewNo=2, stableCheckpoint=0, prepared=[(1, 1, 1, "digest2")], preprepared=[(0, 0, 1, "digest1"), (1, 1, 1, "digest2")], checkpoints=[cp]) vc2 = ViewChange(viewNo=0, stableCheckpoint=0, prepared=[BatchID(0, 0, 1, "digest1")], preprepared=[BatchID(0, 0, 1, "digest1"), BatchID(1, 1, 1, "digest2")], checkpoints=[cp]) vc3 = ViewChange(viewNo=2, stableCheckpoint=0, prepared=[BatchID(0, 0, 1, "digest1")], preprepared=[BatchID(0, 0, 1, "digest1")], checkpoints=[cp]) vc4 = ViewChange(viewNo=2, stableCheckpoint=0, prepared=[BatchID(0, 0, 1, "digest1")], preprepared=[BatchID(0, 0, 1, "digest1")], checkpoints=[cp]) vcs = [vc1, vc2, vc3, vc4] assert builder.calc_batches(cp, vcs) == [BatchID(1, 1, 1, "digest2")]
def test_calc_batches_takes_prepared_with_same_batchid_only(builder): cp = Checkpoint(instId=0, viewNo=0, seqNoStart=0, seqNoEnd=0, digest=cp_digest(0)) vc1 = ViewChange(viewNo=2, stableCheckpoint=0, prepared=[BatchID(1, 1, 1, "digest1")], preprepared=[BatchID(1, 1, 1, "digest1")], checkpoints=[cp]) vc2 = ViewChange(viewNo=2, stableCheckpoint=0, prepared=[BatchID(1, 1, 1, "digest1")], preprepared=[BatchID(1, 1, 1, "digest1")], checkpoints=[cp]) vc3 = ViewChange(viewNo=2, stableCheckpoint=0, prepared=[BatchID(1, 1, 1, "digest2")], preprepared=[BatchID(1, 1, 1, "digest2")], checkpoints=[cp]) vc4 = ViewChange(viewNo=2, stableCheckpoint=0, prepared=[], preprepared=[BatchID(1, 1, 1, "digest1")], checkpoints=[cp]) vcs = [vc1, vc2, vc3, vc4] assert builder.calc_batches(cp, vcs) == [BatchID(1, 1, 1, "digest1")]
def test_calc_checkpoints_digest(builder): d1 = cp_digest(0) d2 = cp_digest(10) cp1_d1 = Checkpoint(instId=0, viewNo=0, seqNoStart=0, seqNoEnd=0, digest=d1) cp2_d2 = Checkpoint(instId=0, viewNo=0, seqNoStart=0, seqNoEnd=10, digest=d2) cp2_d1 = Checkpoint(instId=0, viewNo=0, seqNoStart=0, seqNoEnd=10, digest=d1) vc1_d1 = ViewChange(viewNo=2, stableCheckpoint=0, prepared=[BatchID(1, 1, 1, "digest1")], preprepared=[BatchID(1, 1, 1, "digest1")], checkpoints=[cp1_d1]) vc2_d2 = ViewChange(viewNo=2, stableCheckpoint=0, prepared=[BatchID(1, 1, 1, "digest1")], preprepared=[BatchID(1, 1, 1, "digest1")], checkpoints=[cp2_d2]) vc2_d1 = ViewChange(viewNo=2, stableCheckpoint=0, prepared=[BatchID(1, 1, 1, "digest1")], preprepared=[BatchID(1, 1, 1, "digest1")], checkpoints=[cp2_d1]) vcs = [vc1_d1, vc1_d1, vc2_d1, vc2_d2] assert builder.calc_checkpoint(vcs) == cp1_d1 vcs = [vc1_d1, vc2_d1, vc2_d2, vc2_d2] assert builder.calc_checkpoint(vcs) == cp2_d2 vcs = [vc1_d1, vc2_d2, vc2_d1, vc2_d1] assert builder.calc_checkpoint(vcs) == cp2_d1 # Here we have 2 nodes malicious (f=1), but calc_checkpoint returns a value depending on the order # Is it OK, or calc_checkpoint should return None (indicating that there is no valid quorum)? vcs = [vc2_d1, vc2_d1, vc2_d2, vc2_d2] assert builder.calc_checkpoint(vcs) == cp2_d1 vcs = [vc2_d2, vc2_d2, vc2_d1, vc2_d1] assert builder.calc_checkpoint(vcs) == cp2_d2
def create_batches(view_no): return [ BatchID(view_no, view_no, 11, "d1"), BatchID(view_no, view_no, 12, "d2"), BatchID(view_no, view_no, 13, "d3") ]
def preprepare_to_batch_id(pre_prepare: PrePrepare) -> BatchID: pp_view_no = get_original_viewno(pre_prepare) return BatchID(pre_prepare.viewNo, pp_view_no, pre_prepare.ppSeqNo, pre_prepare.digest)