mirror of
https://github.com/logos-blockchain/logos-blockchain-specs.git
synced 2026-02-08 07:13:28 +00:00
Add is_safe_to_timeout checks on unhappy path methods
This commit is contained in:
parent
0922aec404
commit
1cb757f040
106
carnot/carnot.py
106
carnot/carnot.py
@ -147,20 +147,7 @@ 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
|
||||
|
||||
@abstractmethod
|
||||
def leader(self, view: View) -> Id:
|
||||
"""
|
||||
:param view:
|
||||
@ -202,15 +189,18 @@ class Overlay:
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def leaf_committees(self) -> Set[Committee]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def root_committee(self) -> Committee:
|
||||
"""
|
||||
:return: returns root committee
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def is_child_of_root_committee(self, _id: Id) -> bool:
|
||||
"""
|
||||
:return: returns child committee/s of root committee if present
|
||||
@ -350,49 +340,6 @@ class Carnot:
|
||||
self.increment_voted_view(block.view) # to avoid voting again for this view.
|
||||
self.increment_view_qc(block.qc)
|
||||
|
||||
def approve_new_view(self, timeouts: Set[NewView]):
|
||||
"""
|
||||
This step is very similar to approving a block in the happy path
|
||||
A goal of this process is to guarantee that the high_qc gathered at the top
|
||||
(or a more recent one) has been seen by the supermajority of nodes in the network
|
||||
# TODO: Check comment
|
||||
"""
|
||||
assert len(set(timeout.view for timeout in timeouts)) == 1
|
||||
assert all(timeout.view >= self.current_view for timeout in timeouts)
|
||||
assert all(timeout.view == timeout.timeout_qc.view for timeout in timeouts)
|
||||
assert len(timeouts) == self.overlay.super_majority_threshold(self.id)
|
||||
assert all(self.overlay.is_member_of_child_committee(self.id, timeout.sender) for timeout in timeouts)
|
||||
|
||||
timeouts = list(timeouts)
|
||||
timeout_qc = timeouts[0].timeout_qc
|
||||
new_high_qc = timeout_qc.high_qc
|
||||
|
||||
if new_high_qc.view >= self.local_high_qc.view:
|
||||
self.update_high_qc(new_high_qc)
|
||||
self.update_timeout_qc(timeout_qc)
|
||||
self.increment_view_timeout_qc(timeout_qc)
|
||||
|
||||
if self.overlay.is_member_of_root_committee(self.id):
|
||||
new_view_msg = NewView(
|
||||
view=self.current_view,
|
||||
high_qc=self.local_high_qc,
|
||||
sender=self.id,
|
||||
timeout_qc=timeout_qc, # should we do some aggregation here?
|
||||
)
|
||||
self.send(new_view_msg, self.overlay.leader(self.current_view + 1))
|
||||
else:
|
||||
new_view_msg = NewView(
|
||||
view=self.current_view,
|
||||
high_qc=self.local_high_qc,
|
||||
sender=self.id,
|
||||
timeout_qc=timeout_qc
|
||||
)
|
||||
self.send(new_view_msg, *self.overlay.parent_committee(self.id))
|
||||
self.increment_view_timeout_qc(timeout_qc)
|
||||
# This checks if a not has already incremented its voted view by local_timeout. If not then it should
|
||||
# do it now to avoid voting in this view.
|
||||
self.increment_voted_view(timeout_qc.view)
|
||||
|
||||
def forward_vote(self, vote: Vote):
|
||||
assert vote.block in self.safe_blocks
|
||||
assert self.overlay.is_member_of_child_committee(self.id, vote.voter)
|
||||
@ -435,10 +382,6 @@ class Carnot:
|
||||
|
||||
def is_safe_to_timeout(
|
||||
self,
|
||||
highest_voted_view: View,
|
||||
local_high_qc: Qc,
|
||||
last_timeout_view_qc: TimeoutQc,
|
||||
current_view: View
|
||||
):
|
||||
"""
|
||||
Local timeout is different for the root and its child committees. If other committees timeout, they only
|
||||
@ -458,24 +401,19 @@ class Carnot:
|
||||
# Make sure the node doesn't time out continuously without finishing the step to increment the current view.
|
||||
# Make sure current view is always higher than the local_high_qc so that the node won't timeout unnecessary
|
||||
# for a previous view.
|
||||
assert self.current_view > max(highest_voted_view - 1, local_high_qc.view)
|
||||
assert self.current_view > max(self.highest_voted_view - 1, self.local_high_qc.view)
|
||||
# This condition makes sure a node waits for timeout_qc from root committee to change increment its view with
|
||||
# a view change.
|
||||
# A node must change its view after making sure it has the high_Qc or last_timeout_view_qc
|
||||
# from previous view.
|
||||
return (
|
||||
is_sequential_ascending(current_view, local_high_qc.view) or
|
||||
is_sequential_ascending(current_view, last_timeout_view_qc.view) or
|
||||
(current_view == last_timeout_view_qc.view)
|
||||
is_sequential_ascending(self.current_view, self.local_high_qc.view) or
|
||||
is_sequential_ascending(self.current_view, self.last_timeout_view_qc.view) or
|
||||
(self.current_view == self.last_timeout_view_qc.view)
|
||||
)
|
||||
|
||||
def local_timeout(self):
|
||||
assert self.is_safe_to_timeout(
|
||||
self.highest_voted_view,
|
||||
self.local_high_qc,
|
||||
self.last_timeout_view_qc,
|
||||
self.current_view
|
||||
)
|
||||
assert self.is_safe_to_timeout()
|
||||
|
||||
self.increment_voted_view(self.current_view)
|
||||
|
||||
@ -495,8 +433,6 @@ class Carnot:
|
||||
Root committee detected that supermajority of root + its children has timed out
|
||||
The view has failed and this information is sent to all participants along with the information
|
||||
necessary to reconstruct the new overlay
|
||||
:param msgs:
|
||||
:return:
|
||||
"""
|
||||
assert len(msgs) == self.overlay.leader_super_majority_threshold(self.id)
|
||||
assert all(msg.view >= self.current_view for msg in msgs)
|
||||
@ -507,10 +443,16 @@ class Carnot:
|
||||
timeout_qc = self.build_timeout_qc(msgs)
|
||||
self.update_timeout_qc(timeout_qc)
|
||||
self.update_high_qc(timeout_qc.high_qc)
|
||||
# The view failed and the node timeout. The node cannot timeout itself again until it gets updated
|
||||
# from a higher qc, either from a TimeoutQc or from a Qc coming from a newer proposed block.
|
||||
# In case the node do not get updated because the received qc is not new enough we need to skip
|
||||
# rebuilding the overlay and broadcasting our own qc
|
||||
if not self.is_safe_to_timeout():
|
||||
return
|
||||
self.rebuild_overlay_from_timeout_qc(timeout_qc)
|
||||
self.broadcast(timeout_qc) # we broadcast so all nodes can get ready for voting on a new view
|
||||
|
||||
def gather_new_view(self, timeouts: Set[NewView]):
|
||||
def approve_new_view(self, timeouts: Set[NewView]):
|
||||
assert not self.overlay.is_member_of_leaf_committee(self.id)
|
||||
assert len(set(timeout.view for timeout in timeouts)) == 1
|
||||
assert all(timeout.view >= self.current_view for timeout in timeouts)
|
||||
@ -524,10 +466,18 @@ class Carnot:
|
||||
|
||||
self.rebuild_overlay_from_timeout_qc(timeout_qc)
|
||||
|
||||
if new_high_qc.view >= self.local_high_qc.view:
|
||||
self.update_high_qc(new_high_qc)
|
||||
self.update_timeout_qc(timeout_qc)
|
||||
self.increment_view_timeout_qc(timeout_qc)
|
||||
if new_high_qc.view < self.local_high_qc.view:
|
||||
return
|
||||
|
||||
self.update_high_qc(new_high_qc)
|
||||
self.update_timeout_qc(timeout_qc)
|
||||
self.increment_view_timeout_qc(timeout_qc)
|
||||
# The view failed and the node timeout. The node cannot timeout itself again until it gets updated
|
||||
# from a higher qc, either from a TimeoutQc or from a Qc coming from a newer proposed block.
|
||||
# In case the node do not get updated because the received qc is not new enough we need to skip
|
||||
# rebuilding the overlay and broadcasting our own qc
|
||||
if not self.is_safe_to_timeout():
|
||||
return
|
||||
|
||||
if self.overlay.is_member_of_root_committee(self.id):
|
||||
timeout_msg = NewView(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user