use coarse grained events
This commit is contained in:
parent
26501440b1
commit
0adbf47306
148
carnot/spec.md
148
carnot/spec.md
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue