-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
103 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
**DIP Number:** 2 | ||
**Title:** Rolling Finality | ||
**Author(s):** @fed-franz, @herr-seppia, @autholykos | ||
**Status:** Final | ||
**Category/Type:** Core (Consensus) | ||
**Creation Date:** 20-09-2023 | ||
|
||
--- | ||
|
||
## Abstract | ||
This DIP proposes to remove instant finality and introduce a finality system based on the block iteration number. | ||
|
||
## Motivation | ||
Currently, an accepted block is marked as *final* (i.e. it cannot be reverted) if it's a first-iteration candidate. | ||
This behavior is problematic since it can produce an unrecoverable split in some cases. | ||
|
||
For instance, consider the following case: at height H, node A accepts a block B with iteration 0, while node B accepts block B' with iteration 1; then both nodes accept a valid successor with iteration 0. At this point, both nodes have finalized their block at height H, which means the two nodes are now irrevertibly on separate branches. | ||
|
||
An occurrence of this situation has been shown in Issue [#1093](https://github.com/dusk-network/rusk/issues/1093) ("*Unrecoverable consensus state after a split event*"). | ||
|
||
To solve this issue, finalization logic must be updated taking forks into account. | ||
|
||
## Technical Specification | ||
This DIP introduces the definition of *Attestation*, *Consensus Label*, and new *Finality Rules*, which in turn introduce the definition of *Instant Finality*, and *Rolling Finality*. | ||
|
||
### Attestations | ||
An *Attestation* is a proof of a quorum reached in a specific iteration. It consists of two aggregated signatures (one for the Validation step and one for the Ratification step) and the bitsets of the votes with respect to the corresponding committees. | ||
|
||
Depending on wether the result of the iteration was Success (Agreement) or Fail (Nil), the Attestation can be a *Success Attestation* or a *Fail Attestation*, respectively. | ||
|
||
### Failed Iterations field | ||
A $FailedIterations$ field is added to the block header. | ||
The field is an array of Attestations, with each position corresponding to an iteration. | ||
|
||
An empty position implies there is no information on whether a quorum was reached for the corresponding iteration. | ||
|
||
### Consensus State Labels | ||
Each block is locally labeled by the node to indicate its state with respect to consensus. | ||
The following labels are introduced: | ||
|
||
- *Accepted*: the block has been accepted (i.e., it has a Success Attestation) but it has iteration $I>0$ and some of the lower iterations have an *Unknown* result (i.e., no known quorum has been reached); | ||
- *Attested*: the block has been accepted at iteration $0$ or it has a valid Fail Attestation for all previous iterations; | ||
- *Final*: the block is *Attested*, and all previous blocks are labeled as *Final*. | ||
|
||
### Finality Rules | ||
Each block can be finalized in two ways: *instant finality* and *rolling finality*. | ||
|
||
An accepted block B has instant finality if (1) it's labeled as *Attested* and (2) its parent block is labeled as *Final*. | ||
|
||
Blocks labeled as *Accepted* can be finalized with the so-called rolling finality. | ||
Rolling finality refers to the finalization of a block by means of accepting other blocks on top of it (this is similar to the concept of *confirmation* in other blockchains). | ||
|
||
Specifically, an *Accepted* block is labeled as *Final* if it's followed by $5$ consecutive *Attested* blocks. | ||
If a block is finalized in this way, all previous non-final blocks are also finalized. | ||
|
||
|
||
## Rationale | ||
There are two reasons a block can be reverted: (1) there is a lower-iteration candidate for the same round that reached a Success Quorum, or (2) a previous block is reverted. | ||
The finalization process proposed by this DIP hinges on the probability of these two events to occur for a given block. | ||
In particular, it aims at finalizing a block only if its probability of being reverted is negligible. | ||
|
||
With respect to (1), it can be noted that a block at iteration $I=0$ cannot be reverted by any lower-iteration block. Nonetheless, this is also true for blocks with iteration $I>0$ when all previous iterations have reached a Fail Quorum. The *Attested* label represents both such cases. | ||
In this respect, the introduction of Attestations, and particularly Fail Attestations, is aimed at tracking the result of previous iterations (w.r.t. the candidate iteration) in a provable manner. By including Fail Attestations in the block header, it is possible to label a new block in a deterministc way. | ||
|
||
On the other hand, labeling a block as *Accepted* represents a situation in which there *might* be (since the Attestationis missing, the iteration result is unknown) a lower-iteration block that reached a Success Quorum; since lower-iteration blocks have higher priority, receiving such a block would replace the Accepted one. | ||
|
||
A block is marked as *Final* if it's first-iteration and its parent block is also Final. In fact, such a block cannot possibly be reverted, since its predecessor cannot be reverted and there are no lower-iteration blocks in the same round. | ||
|
||
The finalization of Accepted blocks rely on *confirmations* from successive blocks. This is based on the observation that each block added on top of an Accepted one inherently proves that the network is building on top it. | ||
In particular, each successor of an Accepted block: | ||
- increases the set of provisioners that included the block in its chain; | ||
- increases the set of provisioners working on the branch to which the Accepted block belong; | ||
- decreases the set of provisioners that might have accepted a better block than the Accepted one. | ||
Therefore, each successor reduces the probability of reverting the Accepted block. | ||
|
||
The concept of *Rolling Finality* is based on the above observations, but only leverages consecutive Attested blocks. In fact, Accepted blocks cannot be used to mark a block as Final. To understand this, consider the following example: a block B is marked as Final after accepting 5 successors, the first of which is marked as Accepted; then this Accepted block is reverted (along with all other successors); at this point, B's label has to change back to Accepted (which contradicts the fact of it being Finalized, that is, not revertable). | ||
|
||
The threshold of 5 consecutive blocks has been chosen to be big enough to be considered as stable (although more precise probability calculations are due to properly choose this value). | ||
|
||
Finally, note that only Attestations included in the block header are considered, making the finalization process deterministic. | ||
Due to this, nodes with the same blocks will have the same consensus state labels (and hence the same finalized blocks). | ||
|
||
|
||
## Backwards Compatibility | ||
While State Labels and Finality Rules are local changes, and hence backward compatible, the introduction of the $FailedIterations$ field in the block header makes this DIP a breaking change. | ||
|
||
## Implementation | ||
- https://github.com/dusk-network/rusk/issues/1116: Add failed_iterations fields | ||
- https://github.com/dusk-network/rusk/issues/1158: Introduce block "Consensus State" labels | ||
- https://github.com/dusk-network/rusk/issues/1118: Change block's finality definition | ||
|
||
|
||
## Security Considerations | ||
N/A | ||
|
||
## References | ||
N/A | ||
|
||
--- | ||
|
||
## Updates | ||
- In accordance with the new finality definition, lower-round blocks can now be reverted (see [Allow fallback to lower-round blocks](https://github.com/dusk-network/rusk/issues/1168)) | ||
- Finality rules have been modified by the implementation of [Incremental Rolling Finality](https://github.com/dusk-network/dips/issues/11) |