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
1 changed files with 55 additions and 40 deletions

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,51 +278,58 @@ 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())
```
We need to make sure that qcs can't be removed from aggQc when going up the tree We need to make sure that qcs can't be removed from aggQc when going up the tree