diff --git a/carnot/carnot.py b/carnot/carnot.py index de2d7ab..e3279b9 100644 --- a/carnot/carnot.py +++ b/carnot/carnot.py @@ -158,6 +158,7 @@ class Carnot: def __init__(self, _id: Id): self.id: Id = _id self.current_view: View = 0 + self.highest_voted_view:View=0 self.local_high_qc: Optional[Qc] = None self.latest_committed_view: View = 0 self.safe_blocks: Dict[Id, Block] = dict() @@ -203,14 +204,13 @@ class Carnot: self.safe_blocks[block.id()] = block self.update_high_qc(block.qc) self.try_commit_grand_parent(block) - self.increment_view_qc(block.qc) def vote(self, block: Block, votes: Set[Vote]): assert block.id() in self.safe_blocks assert len(votes) == self.overlay.super_majority_threshold(self.id) assert all(self.overlay.child_committee(self.id, vote.voter) for vote in votes) assert all(vote.block == block.id() for vote in votes) - + assert (block.view>highest_voted_view) if self.overlay.member_of_root_com(self.id): vote: Vote = Vote( block=block.id(), @@ -227,6 +227,8 @@ class Carnot: qc=None ) self.send(vote, *self.overlay.parent_committee(self.id)) + self.increment_voted_view(block.view) + self.increment_view_qc(block.qc) def forward_vote(self, vote: Vote): assert vote.block in self.safe_blocks @@ -248,6 +250,7 @@ class Carnot: def local_timeout(self, new_overlay: Overlay): self.last_timeout_view = self.current_view + increment_voted_view(self.current_view) # to avoid voting again for this view. self.overlay = new_overlay if self.overlay.member_of_leaf_committee(self.id): raise NotImplementedError() @@ -275,6 +278,8 @@ class Carnot: ) if can_commit: self.committed_blocks[block.id()] = block + def increment_voted_view(self,view: View): + highest_voted_view =max(view,highest_voted_view) def increment_view_qc(self, qc: Qc) -> bool: if qc.view < self.current_view: diff --git a/carnot/test_happy_path.py b/carnot/test_happy_path.py index 015daa7..57bd28a 100644 --- a/carnot/test_happy_path.py +++ b/carnot/test_happy_path.py @@ -36,9 +36,9 @@ class TestCarnotHappyPath(TestCase): carnot.receive_block(block4) # This test seem to fail because safe_block dict checks for id of - #the block and we can receive blocks with different ids for the same view. + #the block, and we can receive blocks with different ids for the same view. # There must be only one block per view at most. - # May be we have a dict with view as key and dict[block.id()]block as value? + # Maybe we have a dict with view as key and dict[block.id()]block as value? block5 = Block(view=4, qc=StandardQc(block=block3.id(), view=3)) carnot.receive_block(block5) @@ -62,17 +62,13 @@ class TestCarnotHappyPath(TestCase): block4 = Block(view=4, qc=StandardQc(block=block3.id(), view=3)) carnot.receive_block(block4) - # 5 This is the old standard qc of block number 3. For standarnd QC we must always have qc.view==block.view-1. # This block should be rejected based on the condition below in block_is_safe(). # block.view >= self.latest_committed_view and block.view == (standard.view + 1) # block_is_safe() should return false. block5 = Block(view=3, qc=StandardQc(block=block4.id(), view=4)) carnot.receive_block(block5) - - - - def test_receive_block_has_an_old_qc(self): +def test_receive_block_has_an_old_qc(self): carnot = Carnot(int_to_id(0)) genesis_block = self.add_genesis_block(carnot) # 1 @@ -95,4 +91,62 @@ class TestCarnotHappyPath(TestCase): # block.view >= self.latest_committed_view and block.view == (standard.view + 1) # block_is_safe() should return false. block5 = Block(view=5, qc=StandardQc(block=block3.id(), view=3)) - carnot.receive_block(block5) \ No newline at end of file + carnot.receive_block(block5) + + + + + #Any block with block.view < 4 must be committed + def test_receive_block_and_commit_its_grand_parent_chain(self): + carnot = Carnot(int_to_id(0)) + genesis_block = self.add_genesis_block(carnot) + # 1 + block1 = Block(view=1, qc=StandardQc(block=genesis_block.id(), view=0)) + carnot.receive_block(block1) + + # 2 + block2 = Block(view=2, qc=StandardQc(block=block1.id(), view=1)) + carnot.receive_block(block2) + + # 3 + block3 = Block(view=3, qc=StandardQc(block=block2.id(), view=2)) + carnot.receive_block(block3) + # 4 + block4 = Block(view=4, qc=StandardQc(block=block3.id(), view=3)) + carnot.receive_block(block4) + + block5 = Block(view=5, qc=StandardQc(block=block3.id(), view=3)) + carnot.receive_block(block5) + + + + +# Block3 must be committed as it is the grandparent of block5. Hence, it should not be possible +#to avert it. + def test_receive_block_has_an_old_qc_and_tries_to_revert_a_committed_block(self): + carnot = Carnot(int_to_id(0)) + genesis_block = self.add_genesis_block(carnot) + # 1 + block1 = Block(view=1, qc=StandardQc(block=genesis_block.id(), view=0)) + carnot.receive_block(block1) + + # 2 + block2 = Block(view=2, qc=StandardQc(block=block1.id(), view=1)) + carnot.receive_block(block2) + + # 3 + block3 = Block(view=3, qc=StandardQc(block=block2.id(), view=2)) + carnot.receive_block(block3) + # 4 + block4 = Block(view=4, qc=StandardQc(block=block3.id(), view=3)) + carnot.receive_block(block4) + + # 5 This is the old standard qc of block number 2. By using the QC for block2, block5 tries to form a fork + # to avert block3 and block b4. Block3 is a committed block + # block_is_safe() should return false. + block5 = Block(view=5, qc=StandardQc(block=block2.id(), view=2)) + carnot.receive_block(block5) + + + +