use coarse grained events

This commit is contained in:
Giacomo Pasini 2023-03-28 12:14:37 +02:00
parent 26501440b1
commit 0adbf47306
No known key found for this signature in database
GPG Key ID: FC08489D2D895D4B
1 changed files with 66 additions and 86 deletions

View File

@ -6,6 +6,10 @@ In addition, all types can be expected to have their invariants checked by the t
'Q:' is used to indicate unresolved questions.
Notation is loosely based on CDDL.
Similar to the Carnot algorithm, this specification will be event-based, prescribing the actions to perform in response to relevant events in the Carnot consensus.
Events should be processed one at a time, picking any from the available ones.
As for ordering between events, there are some constraints (e.g. can't process a proposal before it's parent) which will likely form a DAG of relations. The expectation is that an implementation will respect these requirements by processing only events which have all preconditions satisfied.
## Messages
A critical piece in the protocol, these are the different kind of messages used by participants during the protocol execution.
* `Block`: propose a new block
@ -106,6 +110,7 @@ CURRENT_VIEW: View
LOCAL_HIGH_QC: Qc
LATEST_COMMITTED_VIEW: View
COLLECTION: Q?
SAFE_BLOCKS: Set[Block]
```
@ -140,38 +145,37 @@ The following functions are expected to be available to participants during the
## Core functions
## Core events
These are the core events necessary for the Carnot consensus protocol. In response to such events a participant is expected to execute the corresponding handler action.
* receive block b -> `receive_block(b)`
Preconditions:
* `b.parent() in SAFE_BLOCKS`
* receive a supermajority of votes for block b -> `vote(b, votes)`
Preconditions:
* `b in SAFE_BLOCKS`
* receive a vote v for block b when a supermajority of votes already exists -> `forward_votes(b, v)`
Preconditions:
* `b in SAFE_BLOCKS`
* `vote(b, some_votes)` already called and `v not in some_votes`
* `current_time() - time(last view update) > TIMEOUT` -> `timeout(last view)`
* leader for view v and leader supermajority for previous proposal -> `propose_block(v, votes)`
These are the core functions necessary for the Carnot consensus protocol, to be executed in response of incoming messages, except for `timeout` which is triggered by a participant configurable timer.
### Receive block
```python3
def receive_block(block: Block):
if block.id() is known or block.view <=LATEST_COMMITTED_VIEW:
# checking preconditions
assert block.parent() in SAFE_BLOCKS
if block.id() in SAFE_BLOCKS or block.view <= LATEST_COMMITTED_VIEW:
return
# Recursively make sure that we process blocks in order
if block.parent() is missing:
parent: Block = download(block.parent())
receive(parent)
if safe_block(block):
# This is not in the original spec, but
# let's validate I have this clear.
assert block.view == current_view
SAFE_BLOCKS.add(block)
update_high_qc(block.qc)
vote = create_vote()
if member_of_leaf_committee():
if member_of_root_committee():
send(vote, leader(current_view + 1))
else:
send(vote, parent_commitee())
current_view += 1
reset_timer()
try_to_commit_grandparent(block)
```
##### Auxiliary functions
@ -231,78 +235,54 @@ def update_high_qc(qc: Qc):
# Q: same thing about missing views
```
### Receive Vote
Q: this whole function needs to be revised
### Vote
```python
def vote(block: Block, votes: Set[Vote]):
# check preconditions
assert block in SAFE_BLOCKS
assert supermajority(votes) # should also check votes are from children only
def receive_vote(vote: Vote):
if vote.block is missing:
block = download(vote.block)
receive(block)
# Q: we should probably return if we already received this vote
if member_of_internal_com() and not_member_of_root():
if child_commitee(vote.voter):
COLLECTION[vote.block].append(vote)
else:
# Q: not returning here would mean it's extremely easy to
# trigger building a new vote in the following branches
return
if supermajority(COLLECTION[vote.block]):
# Q: should we send it to everyone in the committee?
self_vote = build_vote()
send(self_vote, parent_committee)
# Q: why here?
current_view += 1
reset_timer()
# Q: why do we do this here?
try_to_commit_grand_parent(block)
vote = create_vote(votes)
if member_of_root():
if child_commitee(vote.voter):
COLLECTION[vote.block].append(vote)
else:
# Q: not returning here would mean it's extremely easy to
# trigger building a new vote in the following branches
return
vote.qc = build_qc(collection[vote.block])
send(vote, leader(CURRENT_VIEW + 1))
else:
send(vote, parent_committee())
if supermajority(COLLECTION[vote.block]):
# Q: The vote to send is not the one received but
# the one build by this participant, right?
self_vote = build_vote();
qc = build_qc(collection[vote.block])
self_vote.qc=qc
send(self_vote, leader(current_view + 1))
# Q: why here?
current_view += 1
reset_timer()
# Q: why here?
try_to_commit_grandparent(block)
current_view += 1
reset_timer()
try_to_commit_grandparent(block)
```
# Q: this means that we send a message for every incoming
# message after the threshold has been reached, i.e. a vote
# from a node in the leaf committee can trigger
# at least height(tree) messages.
if morethanSsupermajority(collection[vote.block]):
# just forward the vote to the leader
# Q: But then childcommitte(vote.voter) would return false
# in the leader, as it's a granchild, not a child
send(vote, leader(current_view + 1))
### Forward vote
```python
# Q: how can you reach 2/3 of total votes if you're missing the qc
# from the other root committe
def forward_vote(block: Block, vote: Vote):
assert block in SAFE_BLOCKS
assert child_committe(vote.id)
# already supermajority
if leader(view): # Q? Which view? CURRENT_VIEW or vote.view?
if vote.view < CURRENT_VIEW - 1:
return
if member_of_root():
# just forward the vote to the leader
# Q: But then childcommitte(vote.voter) would return false
# in the leader, as it's a granchild, not a child
send(vote, leader(current_view + 1))
```
# Q: No filtering? I can just create a key and vote?
COLLECTION[vote.block].append(vote)
if supermajority(collection[vote.block]):
qc = build_qc(collection[vote.block])
block = build_block(qc)
broadcast(block)
### Propose block
```python
def propose_block(view: View, votes: Set[Vote]):
assert leader(view)
assert leader_supermajority(votes)
assert all(vote.view == view - 1 for vote in votes)
qc = build_qc(votes)
block = build_block(qc)
broadcast(block)
```
### Receive Timeout