From 4d73ae89c35132e24e2b88654e5120d95adb7205 Mon Sep 17 00:00:00 2001 From: mjalalzai <33738574+MForensic@users.noreply.github.com> Date: Sun, 2 Apr 2023 16:58:37 -0700 Subject: [PATCH] Receive timeout msgs --- carnot/carnot.py | 76 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 69 insertions(+), 7 deletions(-) diff --git a/carnot/carnot.py b/carnot/carnot.py index d8cf332..5fdeb47 100644 --- a/carnot/carnot.py +++ b/carnot/carnot.py @@ -65,13 +65,22 @@ class Vote: @dataclass class TimeoutQc: view: View - high_qc: AggregateQc + high_qc: Qc + qc_views: list[View] + SenderIds: Set[Id] + Sender: Id +# local timeout field is only used by the root committee and its children when they timeout. The timeout_qc is built +# from local_timeouts. Leaf nodes when receive timeout_qc build their timeout msg and includes the timeout_qc in it. +# The timeout_qc is indicator that the root committee and its child committees (if exist) have failed to collect votes. @dataclass class Timeout: view: View high_qc: Qc + sender: Id + timeout_qc: TimeoutQc + local_timeout: bool Quorum: TypeAlias = Set[Vote] | Set[Timeout] @@ -90,6 +99,20 @@ class Overlay: """ pass + def is_child_of_root(self, _id: Id): + """ + :param _id: Node id to be checked + :return: true if node is the member of child of the root committee + """ + pass + + def number_of_committees(self, _ids: set[Id]) -> int: + """ + :param _ids: Set of Node id to be checked + :return: Number of committees in the overlay + """ + pass + def leader(self, view: View) -> Id: """ :param view: @@ -166,6 +189,10 @@ def download(view) -> Block: raise NotImplementedError +def build_timeoutqc(msgs) -> TimeoutQc: + pass + + class Carnot: def __init__(self, _id: Id): self.id: Id = _id @@ -190,6 +217,7 @@ class Carnot: return False return block.view >= self.current_view and block.view == (aggregated.view + 1) + # Ask Dani def update_high_qc(self, qc: Qc): match (self.local_high_qc, qc): case (None, StandardQc() as new_qc): @@ -201,6 +229,13 @@ class Carnot: case (old_qc, AggregateQc() as new_qc) if new_qc.high_qc().view != old_qc.view: self.local_high_qc = new_qc.high_qc() + def update_timeout_qc(self, timeout_qc: TimeoutQc): + match (self.last_timeout_view_qc, timeout_qc): + case (None, timeout_qc): + self.local_high_qc = timeout_qc + case (self.last_timeout_view_qc, timeout_qc) if timeout_qc.view > self.last_timeout_view_qc.view: + self.last_timeout_view_qc = timeout_qc + def receive_block(self, block: Block): assert block.parent() in self.safe_blocks if block.id() in self.safe_blocks or block.view <= self.latest_committed_view: @@ -258,12 +293,32 @@ class Carnot: def local_timeout(self, new_overlay: Overlay): self.last_timeout_view = self.current_view self.increment_voted_view(self.current_view) - self.overlay = new_overlay - if self.overlay.member_of_leaf_committee(self.id): - raise NotImplementedError() + if self.overlay.member_of_leaf_committee(self.id) or self.overlay.is_child_of_root(): + timeout_Msg: Timeout = Timeout( + view=self.current_view, + high_qc=self.local_high_qc, + local_timeout=True, + # local_timeout is only true for the root committee or members of its children + # root committee or its children can trigger the timeout. + timeout_qc=self.last_timeout_view_qc, + sender=self.id() + ) - def timeout(self, view: View, msgs: Set["TimeoutMsg"]): - raise NotImplementedError() + self.broadcast(timeout_Msg) + + def timeout(self, view: View, msgs: Set["Timeout"]): + assert len(msgs) == self.overlay.super_majority_threshold(self.id) + assert all(msg.view == msgs.pop().view for msg in msgs) + assert msgs.pop().view > self.current_view + max_msg = self.get_max_timeout(msgs) + if self.local_high_qc.view < max_msg.high_qc.view: + self.update_high_qc(max_msg.high_qc) + if self.overlay.member_of_root_committee(self.id) and self.overlay.member_of_leaf_committee(self.id): + timeout_qc = build_timeoutqc(msgs) + self.update_timeout_qc(timeout_qc) + else: + self.update_timeout_qc(msgs.pop().timeout_qc) + raise NotImplementedError() def send(self, vote: Vote, *ids: Id): pass @@ -300,7 +355,14 @@ class Carnot: self.current_view = qc.view + 1 return True - def get_max_timeout(timeouts: List[Timeout]) -> Optional[Timeout]: + def increment_view_timeout_qc(self, timeoutqc: TimeoutQc): + if timeoutqc == None or timeoutqc.view < self.current_view: + return + self.last_timeout_view_qc = timeoutqc + self.current_view = self.last_timeout_view_qc.view + 1 + return True + + def get_max_timeout(self, timeouts: Set[Timeout]) -> Optional[Timeout]: if not timeouts: return None return max(timeouts, key=lambda time: time.qc.view)