mirror of
https://github.com/logos-co/nomos-specs.git
synced 2025-02-12 23:36:29 +00:00
refactor
This commit is contained in:
parent
af685e62d8
commit
541eb2f1b7
166
carnot/carnot.py
166
carnot/carnot.py
@ -74,19 +74,24 @@ class TimeoutQc:
|
|||||||
sender: Id
|
sender: Id
|
||||||
|
|
||||||
|
|
||||||
# local timeout field is only used by the root committee and its children when they timeout. The timeout_qc is built
|
# Timeout in the root or direct children committees
|
||||||
# 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
|
@dataclass
|
||||||
class Timeout:
|
class Timeout:
|
||||||
view: View
|
view: View
|
||||||
high_qc: Qc
|
high_qc: Qc
|
||||||
sender: Id
|
sender: Id
|
||||||
timeout_qc: TimeoutQc
|
timeout_qc: TimeoutQc
|
||||||
local_timeout: bool
|
|
||||||
|
# Timeout has been detected, nodes agree on it and gather high qc
|
||||||
|
@dataclass
|
||||||
|
class NewView:
|
||||||
|
view: View
|
||||||
|
high_qc: Qc
|
||||||
|
sender: Id
|
||||||
|
timeout_qc: TimeoutQc
|
||||||
|
|
||||||
|
|
||||||
Quorum: TypeAlias = Set[Vote] | Set[Timeout]
|
Quorum: TypeAlias = Set[Vote] | Set[NewView]
|
||||||
|
|
||||||
|
|
||||||
class Overlay:
|
class Overlay:
|
||||||
@ -284,7 +289,13 @@ class Carnot:
|
|||||||
self.update_high_qc(block.qc)
|
self.update_high_qc(block.qc)
|
||||||
self.try_commit_grand_parent(block)
|
self.try_commit_grand_parent(block)
|
||||||
|
|
||||||
def vote(self, block: Block, votes: Set[Vote]):
|
def receive_timeout_qc(self, timeout_qc: TimeoutQc):
|
||||||
|
# TODO: we should be more strict with views in the sense that we should not
|
||||||
|
# accept 'future' events
|
||||||
|
assert timeout_qc.view >= self.current_view
|
||||||
|
self.rebuild_overlay_from_timeout_qc(timeout_qc)
|
||||||
|
|
||||||
|
def approve_block(self, block: Block, votes: Set[Vote]):
|
||||||
assert block.id() in self.safe_blocks
|
assert block.id() in self.safe_blocks
|
||||||
assert len(votes) == self.overlay.super_majority_threshold(self.id)
|
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(self.overlay.child_committee(self.id, vote.voter) for vote in votes)
|
||||||
@ -310,13 +321,61 @@ class Carnot:
|
|||||||
self.increment_voted_view(block.view) # to avoid voting again for this view.
|
self.increment_voted_view(block.view) # to avoid voting again for this view.
|
||||||
self.increment_view_qc(block.qc)
|
self.increment_view_qc(block.qc)
|
||||||
|
|
||||||
def forward_vote(self, vote: Vote):
|
# 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
|
||||||
|
def approve_new_view(self, timeouts: Set[NewView]):
|
||||||
|
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.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.member_of_root_com(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, msg: Vote):
|
||||||
assert vote.block in self.safe_blocks
|
assert vote.block in self.safe_blocks
|
||||||
assert self.overlay.child_committee(self.id, vote.voter)
|
assert self.overlay.child_committee(self.id, vote.voter)
|
||||||
|
|
||||||
if self.overlay.member_of_root_com(self.id):
|
if self.overlay.member_of_root_com(self.id):
|
||||||
self.send(vote, self.overlay.leader(self.current_view + 1))
|
self.send(vote, self.overlay.leader(self.current_view + 1))
|
||||||
|
|
||||||
|
def forward_new_view(self, msg: NewView):
|
||||||
|
assert msg.view == self.current_view
|
||||||
|
assert self.overlay.child_committee(self.id, vote.voter)
|
||||||
|
|
||||||
|
if self.overlay.member_of_root_com(self.id):
|
||||||
|
self.send(vote, self.overlay.leader(self.current_view + 1))
|
||||||
|
|
||||||
def build_qc(self, quorum: Quorum) -> Qc:
|
def build_qc(self, quorum: Quorum) -> Qc:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -328,7 +387,7 @@ class Carnot:
|
|||||||
block = Block(view=view, qc=qc)
|
block = Block(view=view, qc=qc)
|
||||||
self.broadcast(block)
|
self.broadcast(block)
|
||||||
|
|
||||||
def local_timeout(self, new_overlay: Overlay):
|
def local_timeout(self):
|
||||||
# This condition makes sure a node waits for timeout_qc from root committee to change increment its view with
|
# This condition makes sure a node waits for timeout_qc from root committee to change increment its view with
|
||||||
# a view change.
|
# a view change.
|
||||||
# A node must change its view after making sure it has the high_Qc or last_timeout_view_qc
|
# A node must change its view after making sure it has the high_Qc or last_timeout_view_qc
|
||||||
@ -349,102 +408,23 @@ class Carnot:
|
|||||||
)
|
)
|
||||||
self.send(timeout_msg, *self.overlay.root_committee())
|
self.send(timeout_msg, *self.overlay.root_committee())
|
||||||
|
|
||||||
def timeout(self, msgs: Set[Timeout]):
|
# Root committee detected that supermajority of root + its children has timed out
|
||||||
assert len(msgs) == self.overlay.super_majority_threshold(self.id)
|
# The view has failed and this information is sent to all participants along with the information
|
||||||
assert all(msg.view >= self.current_view for msg in msgs)
|
# necessary to reconstruct the new overlay
|
||||||
assert len(set(msg.view for msg in msgs)) == 1
|
def timeout_detected(self, msgs: Set[Timeout]):
|
||||||
|
|
||||||
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) or self.overlay.child_of_root_committee(self.id):
|
|
||||||
timeout_qc = self.build_timeout_qc(msgs)
|
|
||||||
self.update_timeout_qc(timeout_qc)
|
|
||||||
else:
|
|
||||||
self.update_timeout_qc(max_msg.timeout_qc)
|
|
||||||
|
|
||||||
def detected_timeout(self, msgs: Set[Timeout]):
|
|
||||||
assert len(msgs) == self.overlay.leader_super_majority_threshold(self.id)
|
assert len(msgs) == self.overlay.leader_super_majority_threshold(self.id)
|
||||||
assert all(msg.view >= self.current_view for msg in msgs)
|
assert all(msg.view >= self.current_view for msg in msgs)
|
||||||
assert len(set(msg.view for msg in msgs)) == 1
|
assert len(set(msg.view for msg in msgs)) == 1
|
||||||
assert all(msg.local_timeout for msg in msgs)
|
assert all(msg.local_timeout for msg in msgs)
|
||||||
assert self.overlay.member_of_root_committee(self.id) or self.overlay.child_of_root_committee(self.id)
|
assert self.overlay.member_of_root_committee(self.id)
|
||||||
|
|
||||||
timeout_qc = self.build_timeout_qc(msgs)
|
timeout_qc = self.build_timeout_qc(msgs)
|
||||||
self.update_timeout_qc(timeout_qc)
|
self.update_timeout_qc(timeout_qc)
|
||||||
self.update_high_qc(timeout_qc.high_qc)
|
self.update_high_qc(timeout_qc.high_qc)
|
||||||
self.rebuild_overlay_from_timeout_qc(timeout_qc)
|
self.rebuild_overlay_from_timeout_qc(timeout_qc)
|
||||||
self.send(timeout_qc, *self.overlay.leaf_committees()) # should be sent only to the leafs
|
self.broadcast(timeout_qc) # can be sent only to the leafs
|
||||||
|
|
||||||
def gather_timeouts(self, timeouts: Set[Timeout]):
|
|
||||||
assert not self.overlay.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)
|
|
||||||
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.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
|
|
||||||
|
|
||||||
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 self.overlay.member_of_root_com(self.id):
|
|
||||||
timeout_msg = Timeout(
|
|
||||||
view=self.current_view,
|
|
||||||
high_qc=self.local_high_qc,
|
|
||||||
sender=self.id,
|
|
||||||
timeout_qc=timeout_qc,
|
|
||||||
local_timeout=False,
|
|
||||||
)
|
|
||||||
self.send(timeout_msg, self.overlay.leader(self.current_view + 1))
|
|
||||||
else:
|
|
||||||
timeout_msg = Timeout(
|
|
||||||
view=self.current_view,
|
|
||||||
high_qc=self.local_high_qc,
|
|
||||||
sender=self.id,
|
|
||||||
timeout_qc=timeout_qc,
|
|
||||||
local_timeout=False,
|
|
||||||
)
|
|
||||||
self.send(timeout_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.
|
|
||||||
if self.highest_voted_view < self.current_view:
|
|
||||||
self.increment_voted_view(timeout_qc.view)
|
|
||||||
|
|
||||||
def received_timeout_qc(self, timeout_qc: TimeoutQc):
|
|
||||||
assert timeout_qc.view >= self.current_view
|
|
||||||
self.rebuild_overlay_from_timeout_qc(timeout_qc)
|
|
||||||
|
|
||||||
if self.overlay.member_of_leaf_committee(self.id):
|
|
||||||
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)
|
|
||||||
timeout_msg = Timeout(
|
|
||||||
view=self.current_view,
|
|
||||||
high_qc=self.local_high_qc,
|
|
||||||
sender=self.id,
|
|
||||||
timeout_qc=timeout_qc,
|
|
||||||
local_timeout=False,
|
|
||||||
)
|
|
||||||
self.send(timeout_msg, *self.overlay.parent_committee(self.id))
|
|
||||||
# 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.
|
|
||||||
if self.highest_voted_view < self.current_view:
|
|
||||||
self.increment_voted_view(timeout_qc.view)
|
|
||||||
|
|
||||||
def rebuild_overlay_from_timeout_qc(self, timeout_qc: TimeoutQc):
|
def rebuild_overlay_from_timeout_qc(self, timeout_qc: TimeoutQc):
|
||||||
assert timeout_qc.view >= self.current_view
|
|
||||||
self.overlay = Overlay()
|
self.overlay = Overlay()
|
||||||
|
|
||||||
def build_timeout_qc(self, msgs: Set[Timeout]) -> TimeoutQc:
|
def build_timeout_qc(self, msgs: Set[Timeout]) -> TimeoutQc:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user