Add timeout handler

This commit is contained in:
Giacomo Pasini 2023-03-30 10:54:07 +02:00
parent 0adbf47306
commit 0603b98076
No known key found for this signature in database
GPG Key ID: FC08489D2D895D4B

View File

@ -97,7 +97,6 @@ class Timeout:
view: View view: View
high_qc: AggregateQc high_qc: AggregateQc
``` ```
## Local Variables ## Local Variables
Participants in the protocol are expected to mainting the following data in addition to the DAG of received proposal: Participants in the protocol are expected to mainting the following data in addition to the DAG of received proposal:
* `current_view` * `current_view`
@ -109,8 +108,8 @@ Participants in the protocol are expected to mainting the following data in addi
CURRENT_VIEW: View CURRENT_VIEW: View
LOCAL_HIGH_QC: Qc LOCAL_HIGH_QC: Qc
LATEST_COMMITTED_VIEW: View LATEST_COMMITTED_VIEW: View
COLLECTION: Q?
SAFE_BLOCKS: Set[Block] SAFE_BLOCKS: Set[Block]
LAST_VIEW_TIMEOUT_QC: TimeoutQc
``` ```
@ -155,12 +154,17 @@ These are the core events necessary for the Carnot consensus protocol. In respon
* receive a supermajority of votes for block b -> `vote(b, votes)` * receive a supermajority of votes for block b -> `vote(b, votes)`
Preconditions: Preconditions:
* `b in SAFE_BLOCKS` * `b in SAFE_BLOCKS`
* `local_timeout(b.view)` never called
* receive a vote v for block b when a supermajority of votes already exists -> `forward_votes(b, v)` * receive a vote v for block b when a supermajority of votes already exists -> `forward_votes(b, v)`
Preconditions: Preconditions:
* `b in SAFE_BLOCKS` * `b in SAFE_BLOCKS`
* `vote(b, some_votes)` already called and `v not in some_votes` * `vote(b, some_votes)` already called and `v not in some_votes`
* `current_time() - time(last view update) > TIMEOUT` -> `timeout(last view)` * `local_timeout(b.view)` never called
* `current_time() - time(last view update) > TIMEOUT` and received new overlay -> `local_timeout(last view, new_overlay)`
* leader for view v and leader supermajority for previous proposal -> `propose_block(v, votes)` * leader for view v and leader supermajority for previous proposal -> `propose_block(v, votes)`
* receive a supermajority of timeouts for view v -> `timeout(v, timeouts)`
Preconditions:
* `local_timeout(v)` already called
### Receive block ### Receive block
@ -202,6 +206,8 @@ def safe_block(block: Block):
``` ```
```python ```python
# FIX_ME: Don't think we need to specify this as a function if we don't use
# LAST_COMMITTED_VIEW
# Commit a grand parent if the grandparent and # Commit a grand parent if the grandparent and
# the parent have been added during two consecutive views. # the parent have been added during two consecutive views.
def try_to_commit_grand_parent(block: Block): def try_to_commit_grand_parent(block: Block):
@ -241,16 +247,20 @@ def update_high_qc(qc: Qc):
def vote(block: Block, votes: Set[Vote]): def vote(block: Block, votes: Set[Vote]):
# check preconditions # check preconditions
assert block in SAFE_BLOCKS assert block in SAFE_BLOCKS
assert supermajority(votes) # should also check votes are from children only assert supermajority(votes)
assert all(child_committee(vote.id) for vote in votes)
assert all(vote.block == block for vote in votes)
vote = create_vote(votes) vote = create_vote(votes)
if member_of_root(): if member_of_root():
vote.qc = build_qc(collection[vote.block]) vote.qc = build_qc(votes)
send(vote, leader(CURRENT_VIEW + 1)) send(vote, leader(CURRENT_VIEW + 1))
else: else:
send(vote, parent_committee()) send(vote, parent_committee())
# Q: what about a node that is joining later and does not
# have access to votes? how does it commit blocks?
current_view += 1 current_view += 1
reset_timer() reset_timer()
try_to_commit_grandparent(block) try_to_commit_grandparent(block)
@ -259,10 +269,8 @@ def vote(block: Block, votes: Set[Vote]):
### Forward vote ### Forward vote
```python ```python
# Q: how can you reach 2/3 of total votes if you're missing the qc def forward_vote(vote: Vote):
# from the other root committe assert vote.block in SAFE_BLOCKS
def forward_vote(block: Block, vote: Vote):
assert block in SAFE_BLOCKS
assert child_committe(vote.id) assert child_committe(vote.id)
# already supermajority # already supermajority
@ -270,50 +278,57 @@ def forward_vote(block: Block, vote: Vote):
# just forward the vote to the leader # just forward the vote to the leader
# Q: But then childcommitte(vote.voter) would return false # Q: But then childcommitte(vote.voter) would return false
# in the leader, as it's a granchild, not a child # in the leader, as it's a granchild, not a child
send(vote, leader(current_view + 1)) send(vote, leader(vote.block.view + 1))
``` ```
### Propose block ### Propose block
```python ```python
def propose_block(view: View, votes: Set[Vote]): def propose_block(view: View, quorum: Set[Vote] | Set[TimeoutMsg]):
assert leader(view) assert leader(view)
assert leader_supermajority(votes) assert leader_supermajority(quorum)
assert all(vote.view == view - 1 for vote in votes)
qc = build_qc(votes) qc = build_qc(votes)
block = build_block(qc) block = build_block(qc)
broadcast(block) broadcast(block)
``` ```
### Receive Timeout
```python
# Failure Case
def receive(timeout: Timeout):
# download the missing block
if timeout.high_qc().block is missing:
block = download(timeout.high_qc.block)
receive(block)
# It's an old message. Ignore it.
if timeout.view < CURRENT_VIEW:
return
update_high_qc(timeout.high_qc())
if member_of_internal_com():
COLLECTION[timeout.view].append(timeout)
if supermajority(timeout.view):
new_view_qc = build_qc(COLLECTION[timeout.view])
if member_of_root():
send(new_view_qc, leader(CURRENT_VIEW +1))
CURRENT_VIEW += 1
else:
send(new_view_qc, parent_committee())
```
### Timeout ### Timeout
```python ```python
def timeout(): def local_timeout(new_overlay: Overlay):
raise NotImplementedError # make it so we don't vote or forward any more vote after this
LAST_TIMEOUT_VIEW = CURRENT_VIEW
# TODO: change overlay
if member_of_leaf():
timeout_msg = create_timeout(CURRENT_VIEW, LOCAL_HIGH_QC, LAST_TIMEOUT_VIEW_QC)
send(timeout_msg, parent_committee())
```
### Receive
this is called *after* local_timeout
```python
def timeout(view: View, msgs: Set[TimeoutMsg]):
assert supermajority(msgs)
assert all(child_committee(msg.id) for msg in msgs)
assert all(timeout.view == view for timeout in msgs)
if CURRENT_VIEW > view:
return
if view <= LAST_VIEW_TIMEOUT_QC.view:
return
if view > LOCAL_HIGH_QC.view:
LOCAL_HIGH_QC = timeout_Msg.high_qc
timeout_qc = create_timeout_qc(msgs)increment_view_timeout_qc(timeout_qc.view)
LAST_VIEW_TIMEOUT_QC = timeout_qc
send(timeout_qc, own_committee()) ####helps nodes to sync quicker but not required
if member_of_root():
send(timeout_qc, leader(view+1))
else:
send(timeout_qc, parent_committee())
``` ```