def test_calc_batches_takes_prepared_only(builder): cp = Checkpoint(instId=0, viewNo=0, seqNoStart=0, seqNoEnd=0, digest=cp_digest(0, 0)) vc = ViewChange(viewNo=0, stableCheckpoint=0, prepared=[], preprepared=[(0, 1, "digest1"), (0, 2, "digest2"), (0, 3, "digest3"), (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, 0)) vc = ViewChange(viewNo=0, stableCheckpoint=0, prepared=[(0, 1, "digest1"), (0, 2, "digest2")], preprepared=[(0, 1, "digest1"), (0, 2, "digest2"), (0, 3, "digest3"), (0, 4, "digest4")], checkpoints=[cp]) vcs = [vc, vc, vc, vc] assert builder.calc_batches( cp, vcs) == [BatchID(0, 1, "digest1"), BatchID(0, 2, "digest2")]
def test_calc_batches_respects_checkpoint(builder): cp = Checkpoint(instId=0, viewNo=0, seqNoStart=0, seqNoEnd=10, digest=cp_digest(0, 0)) vc = ViewChange(viewNo=0, stableCheckpoint=0, prepared=[(0, 1, "digest1"), (0, 2, "digest2")], preprepared=[(0, 1, "digest1"), (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(0, 0)) vc = ViewChange(viewNo=0, stableCheckpoint=0, prepared=[(0, 10, "digest10"), (0, 11, "digest11"), (1, 12, "digest12")], preprepared=[(0, 10, "digest10"), (0, 11, "digest11"), (1, 12, "digest12")], checkpoints=[cp]) vcs = [vc, vc, vc, vc] assert builder.calc_batches( cp, vcs) == [BatchID(0, 11, "digest11"), BatchID(1, 12, "digest12")]
def test_calc_batches_must_be_in_pre_prepare(builder): cp = Checkpoint(instId=0, viewNo=0, seqNoStart=0, seqNoEnd=0, digest=cp_digest(0, 0)) vc = ViewChange(viewNo=0, stableCheckpoint=0, prepared=[(0, 1, "digest1"), (0, 2, "digest2")], preprepared=[(0, 1, "digest1")], checkpoints=[cp]) vcs = [vc, vc, vc, vc] # all nodes are malicious here since all added (0, 2) into prepared without adding to pre-prepared # so, None here means we can not calculate NewView reliably assert builder.calc_batches(cp, vcs) is None vc1 = ViewChange(viewNo=0, stableCheckpoint=0, prepared=[(0, 1, "digest1"), (0, 2, "digest2")], preprepared=[(0, 1, "digest1")], checkpoints=[cp]) vc2 = ViewChange(viewNo=0, stableCheckpoint=0, prepared=[(0, 1, "digest1")], preprepared=[(0, 1, "digest1")], checkpoints=[cp]) vcs = [vc1, vc2, vc2, vc2] assert builder.calc_batches(cp, vcs) == [BatchID(0, 1, "digest1")]
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, 0)) vc1 = ViewChange(viewNo=0, stableCheckpoint=0, prepared=[(1, 1, "digest1")], preprepared=[(1, 1, "digest1")], checkpoints=[cp]) vc2 = ViewChange(viewNo=0, stableCheckpoint=0, prepared=[(1, 1, "digest1")], preprepared=[(1, 1, "digest1")], checkpoints=[cp]) vc3 = ViewChange(viewNo=0, stableCheckpoint=0, prepared=[(1, 1, "digest2")], preprepared=[(1, 1, "digest2")], checkpoints=[cp]) vc4 = ViewChange(viewNo=0, stableCheckpoint=0, prepared=[], preprepared=[(1, 1, "digest1")], checkpoints=[cp]) vcs = [vc1, vc2, vc3, vc4] assert builder.calc_batches(cp, vcs) == [BatchID(1, 1, "digest1")]
def test_calc_batches_takes_next_view_one_prepared_if_weak_quorum_of_preprepared( builder): cp = Checkpoint(instId=0, viewNo=0, seqNoStart=0, seqNoEnd=0, digest=cp_digest(0, 0)) vc1 = ViewChange(viewNo=0, stableCheckpoint=0, prepared=[(0, 1, "digest1"), (1, 2, "digest2")], preprepared=[(0, 1, "digest1"), (1, 2, "digest2")], checkpoints=[cp]) vc2 = ViewChange(viewNo=0, stableCheckpoint=0, prepared=[(0, 1, "digest1")], preprepared=[(0, 1, "digest1"), (1, 2, "digest2")], checkpoints=[cp]) vc3 = ViewChange(viewNo=0, stableCheckpoint=0, prepared=[(0, 1, "digest1")], preprepared=[(0, 1, "digest1")], checkpoints=[cp]) vc4 = ViewChange(viewNo=0, stableCheckpoint=0, prepared=[(0, 1, "digest1")], preprepared=[(0, 1, "digest1")], checkpoints=[cp]) vcs = [vc1, vc2, vc3, vc4] assert builder.calc_batches( cp, vcs) == [BatchID(0, 1, "digest1"), (1, 2, "digest2")]
def test_calc_batches_same_data(builder): cp = Checkpoint(instId=0, viewNo=0, seqNoStart=0, seqNoEnd=0, digest='empty') vc = ViewChange(viewNo=0, stableCheckpoint=0, prepared=[(0, 1, "digest1"), (0, 2, "digest2")], preprepared=[(0, 1, "digest1"), (0, 2, "digest2")], checkpoints=[cp]) vcs = [vc, vc, vc, vc] assert builder.calc_batches( cp, vcs) == [BatchID(0, 1, "digest1"), BatchID(0, 2, "digest2")]
def test_calc_batches_takes_prepared_if_preprepared_in_next_view(builder): cp = Checkpoint(instId=0, viewNo=0, seqNoStart=0, seqNoEnd=0, digest='empty') vc1 = ViewChange(viewNo=0, stableCheckpoint=0, prepared=[(1, 1, "digest2")], preprepared=[(0, 1, "digest1"), (2, 1, "digest2")], checkpoints=[cp]) vc2 = ViewChange(viewNo=0, stableCheckpoint=0, prepared=[(0, 1, "digest1")], preprepared=[(0, 1, "digest1"), (2, 1, "digest2")], checkpoints=[cp]) vc3 = ViewChange(viewNo=0, stableCheckpoint=0, prepared=[(0, 1, "digest1")], preprepared=[(0, 1, "digest1")], checkpoints=[cp]) vc4 = ViewChange(viewNo=0, stableCheckpoint=0, prepared=[(0, 1, "digest1")], preprepared=[(0, 1, "digest1")], checkpoints=[cp]) vcs = [vc1, vc2, vc3, vc4] assert builder.calc_batches(cp, vcs) == [BatchID(1, 1, "digest2")]
def test_calc_batches_takes_quorum_of_prepared(builder): cp = Checkpoint(instId=0, viewNo=0, seqNoStart=0, seqNoEnd=0, digest=cp_digest(0, 0)) vc1 = ViewChange(viewNo=0, stableCheckpoint=0, prepared=[(0, 1, "digest2")], preprepared=[(0, 1, "digest2")], checkpoints=[cp]) vc2 = ViewChange(viewNo=0, stableCheckpoint=0, prepared=[(0, 1, "digest1")], preprepared=[(0, 1, "digest1")], checkpoints=[cp]) vc3 = ViewChange(viewNo=0, stableCheckpoint=0, prepared=[], preprepared=[(0, 1, "digest1")], checkpoints=[cp]) vcs = [vc1, vc2, vc2, vc2] assert builder.calc_batches(cp, vcs) == [BatchID(0, 1, "digest1")] vcs = [vc3, vc2, vc2, vc2] assert builder.calc_batches(cp, vcs) == [BatchID(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, 1, "digest1")] vcs = [vc2, vc2, vc3, vc3] assert builder.calc_batches(cp, vcs) == [BatchID(0, 1, "digest1")]
def some_pool(random: SimRandom) -> (SimPool, List): pool_size = random.integer(4, 8) pool = SimPool(pool_size, random) # Create simulated history # TODO: Move into helper? faulty = (pool_size - 1) // 3 seq_no_per_cp = 10 max_batches = 50 batches = [BatchID(0, n, random.string(40)) for n in range(1, max_batches)] checkpoints = [ some_checkpoint(random, 0, n) for n in range(0, max_batches, seq_no_per_cp) ] # Preprepares pp_count = [random.integer(0, len(batches)) for _ in range(pool_size)] max_pp = sorted(pp_count)[faulty] # Prepares p_count = [random.integer(0, min(max_pp, pp)) for pp in pp_count] max_p = sorted(p_count)[faulty] # Checkpoints cp_count = [ 1 + random.integer(0, min(max_p, p)) // seq_no_per_cp for p in pp_count ] max_stable_cp_indx = sorted(cp_count)[faulty] - 1 stable_cp = [ checkpoints[random.integer(0, min(max_stable_cp_indx, cp))].seqNoEnd for cp in cp_count ] # Initialize consensus data for i, node in enumerate(pool.nodes): node._data.preprepared = batches[:pp_count[i]] node._data.prepared = batches[:p_count[i]] node._data.checkpoints = checkpoints[:cp_count[i]] node._data.stable_checkpoint = stable_cp[i] committed = [] for i in range(1, max_batches): prepare_count = sum(1 for node in pool.nodes if i <= len(node._data.prepared)) has_prepared_cert = prepare_count >= pool_size - faulty if has_prepared_cert: committed.append(batches[i - 1]) return pool, committed
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[1] == 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 calc_committed(view_changes, max_pp_seq_no, n, f) -> List[BatchID]: def check_in_batch(batch_id, some_batch_id, check_view_no=False): if check_view_no and (batch_id[0] != some_batch_id[0]): return False return batch_id[1] == some_batch_id[1] and batch_id[ 2] == some_batch_id[2] def check_prepared_in_vc(vc, batch_id): # check that (pp_seq_no, digest) is present in VC's prepared and preprepared for p_batch_id in vc.prepared: if not check_in_batch(batch_id, p_batch_id, check_view_no=True): continue for pp_batch_id in vc.preprepared: if check_in_batch(batch_id, pp_batch_id, check_view_no=True): 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[1] != 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