Integrate bank-forks proposal into the book

This commit is contained in:
Greg Fitzgerald 2019-03-04 17:50:36 -07:00
parent 63477dabcd
commit 799ed24113
3 changed files with 64 additions and 61 deletions

View File

@ -15,6 +15,7 @@
- [Synchronization](synchronization.md)
- [Leader Rotation](leader-rotation.md)
- [Fork Generation](fork-generation.md)
- [Managing Forks](managing-forks.md)
- [Data Plane Fanout](data-plane-fanout.md)
- [Ledger Replication](ledger-replication.md)
- [Secure Vote Signing](vote-signing.md)
@ -39,7 +40,6 @@
- [Staking Rewards](staking-rewards.md)
- [Fork Selection](fork-selection.md)
- [Reliable Vote Transmission](reliable-vote-transmission.md)
- [Bank Forks](bank-forks.md)
- [Persistent Account Storage](persistent-account-storage.md)
- [Leader to Leader Transition](leader-leader-transition.md)
- [Cluster Economics](ed_overview.md)

View File

@ -1,60 +0,0 @@
# Bank Forks
This design describes a way to checkpoint the bank state such that it can track
multiple forks without duplicating data. It addresses the following
challenges:
* Forks are potentially created at every slot boundary.
* Forks can be based on any previously produced block.
* Forks are eventually finalized such that rollback is impossible.
* Unreachable forks need to be pruned.
## Architecture
The basic design idea is to maintain a DAG of Banks checkpointed at different
slot heights. The DAG is initialized with a *root* slot height. Each
subsequent fork must descend from the root.
## Active Forks
An *active fork* is a direct list of connected forks that descend from the
current root to a specific fork without any descendants.
For example:
<img alt="Forks" src="img/forks.svg" class="center"/>
The following *active forks* are in the forks DAG
* {4, 2, 1}
* {5, 2, 1}
* {6, 3, 1}
* {7, 3, 1}
## Squashing
A validator votes for a frozen fork. The active fork connecting the fork to
the root is *squashed*. If the active fork is longer than
`Forks::ROLLBACK_DEPTH` the oldest two forks are squashed. The oldest fork in
the active fork is the current root, so the second oldest is a direct
descendant of the root fork. Once squashed, the current root is updated to the
root descendant. Any forks that are not descendants from the new root are
pruned since they are no longer reachable.
Starting from the example above, consider a vote on 5 versus a vote on 6:
<img alt="Forks" src="img/forks.svg" class="center"/>
* ROLLBACK\_DEPTH=2, vote=5, *active fork*={5, 2, 1}
<img alt="Forks after pruning" src="img/forks-pruned.svg" class="center"/>
The new root is 2, and any active forks that are not descendants from 2 are
pruned.
* ROLLBACK\_DEPTH=2, vote=6, *active fork*={6, 3, 1}
<img alt="Forks" src="img/forks-pruned2.svg" class="center"/>
The tree remains with `root=1`, since the active fork starting at 6 is only 2
forks long.

View File

@ -0,0 +1,63 @@
# Managing Forks in the Ledger
The ledger is permitted to fork at slot boundaries. The resulting data
structure forms a tree called a *blocktree*. When the fullnode interprets the
blocktree, it must maintain state for each fork in the chain. We call each
instance an *active fork*. It is the responsibility of a fullnode to weigh
those forks, such that it may eventually select a fork.
A fullnode selects a fork by submiting a vote to a slot leader on that fork.
The vote commits the fullnode for a duration of time called a *lockout period*.
The fullnode is not permitted to vote on a different fork until that lockout
period expires. Each subsequent vote on the same fork doubles the length of the
lockout period. After some cluster-configured number of votes (currently 32),
the length of the lockout period reaches what's called *max lockout*. Until the
max lockout is reached, the fullnode has the option to wait until the lockout
period is over and then vote on another fork. When it votes on another fork, it
performs a operation called *rollback*, whereby the state rolls back in time to
a shared checkpoint and then jumps forward to the tip of the fork that it just
voted on. The maximum distance that a fork may roll back is called the
*rollback depth*. Rollback depth is the number of votes required to achieve
max lockout. Whenever a fullnode votes, any checkpoints beyond the rollback
depth become unreachable. That is, there is no scenario in which the fullnode
will need to roll back beyond rollback depth. It therefore may safely *prune*
unreachable forks and *squash* all checkpoints beyond rollback depth into the
root checkpoint.
## Active Forks
An active fork is as a sequence of checkpoints that has a length at least one
longer than the rollback depth. The shortest fork will have a length exactly
one longer than the rollback depth. For example:
<img alt="Forks" src="img/forks.svg" class="center"/>
The following sequences are *active forks*:
* {4, 2, 1}
* {5, 2, 1}
* {6, 3, 1}
* {7, 3, 1}
## Pruning and Squashing
A fullnode may vote on any checkpoint in the tree. In the diagram above,
that's every node except the leaves of the tree. After voting, the fullnode
prunes nodes that fork from a distance farther than the rollback depth and then
takes the opportunity to minimize its memory usage by squashing any nodes it
can into the root.
Starting from the example above, wth a rollback depth of 2, consider a vote on
5 versus a vote on 6. First, a vote on 5:
<img alt="Forks after pruning" src="img/forks-pruned.svg" class="center"/>
The new root is 2, and any active forks that are not descendants from 2 are
pruned.
Alternatively, a vote on 6:
<img alt="Forks" src="img/forks-pruned2.svg" class="center"/>
The tree remains with a root of 1, since the active fork starting at 6 is only
2 checkpoints from the root.