Skip to content

Commit 99db13b

Browse files
bors[bot]Alexander Hentscheldurkmurder
authored
Merge #4114
4114: extend documentation of pending tree r=AlexHentschel a=AlexHentschel This PR aims to extend and polish the documentation of `PendingTree`. Particularly, I am trying to add more details to address this comment in a related PR: #4094 (comment) **GoDoc revisions only; no algorithmic changes** Co-authored-by: Alexander Hentschel <[email protected]> Co-authored-by: Yurii Oleksyshyn <[email protected]>
2 parents a5f683f + 3c3f3de commit 99db13b

File tree

1 file changed

+48
-20
lines changed

1 file changed

+48
-20
lines changed

engine/common/follower/pending_tree/pending_tree.go

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,25 @@ func (v *PendingBlockVertex) Parent() (flow.Identifier, uint64) {
6363
// As soon as a valid fork of certified blocks descending from the latest finalized block is observed,
6464
// we pass this information to caller. Internally, the mempool utilizes the LevelledForest.
6565
// PendingTree is NOT safe to use in concurrent environment.
66-
// NOTE: PendingTree relies on notion of `CertifiedBlock` which is a valid block accompanied by a certifying QC (proving block validity).
67-
// This works well for consensus follower as it is designed to work with certified blocks. To use this structure for consensus
68-
// participant we can abstract out CertifiedBlock or replace it with a generic argument that satisfies some contract(returns View, Height, BlockID).
69-
// With this change this structure can be used by consensus participant for tracking connection to the finalized state even without
70-
// having QC but relying on payload validation.
66+
// Note:
67+
// - The ability to skip ahead is irrelevant for staked nodes, which continuously follow the chain.
68+
// However, light clients don't necessarily follow the chain block by block. Assume a light client
69+
// that knows the EpochCommit event, i.e. the consensus committee authorized to certify blocks. A
70+
// staked node can easily ship a proof of finalization for a block within that epoch to such a
71+
// light client. This would be much cheaper for the light client than downloading the headers for
72+
// all blocks in the epoch.
73+
// - The pending tree supports skipping ahead, as this is a more general and simpler algorithm.
74+
// Removing the ability to skip ahead would restrict the PendingTree's domain of potential
75+
// applications _and_ would require additional code and additional tests making it more complex.
76+
//
77+
// Outlook:
78+
// - At the moment, PendingTree relies on notion of a `Certified Block` which is a valid block accompanied
79+
// by a certifying QC (proving block validity). This works well for consensus follower, as it is designed
80+
// to work with certified blocks.
81+
// - In the future, we could use the PendingTree also for consensus participants. Therefore, we would need
82+
// to abstract out CertifiedBlock or replace it with a generic argument that satisfies some contract
83+
// (returns View, Height, BlockID). Then, consensus participants could use the Pending Tree without
84+
// QCs and instead fully validate inbound blocks (incl. payloads) to guarantee block validity.
7185
type PendingTree struct {
7286
forest *forest.LevelledForest
7387
lastFinalizedID flow.Identifier
@@ -81,20 +95,24 @@ func NewPendingTree(finalized *flow.Header) *PendingTree {
8195
}
8296
}
8397

84-
// AddBlocks accepts a batch of certified blocks, adds them to the tree of pending blocks and finds blocks connected to the finalized state.
85-
// This function performs processing of incoming certified blocks, implementation is split into a few different sections
86-
// but tries to be optimal in terms of performance to avoid doing extra work as much as possible.
87-
// This function proceeds as follows:
88-
// 1. Sorts incoming batch by height. Since blocks can be submitted in random order we need to find blocks with
89-
// the lowest height since they are candidates for being connected to the finalized state.
90-
// 2. Filters out blocks that are already finalized.
91-
// 3. Deduplicates incoming blocks. We don't store additional vertices in tree if we have that block already stored.
92-
// 4. Checks for exceeding byzantine threshold. Only one certified block per view is allowed.
93-
// 5. Finally, blocks with the lowest height from incoming batch that connect to the finalized state we will
94-
// mark all descendants as connected, collect them and return as result of invocation.
98+
// AddBlocks accepts a batch of certified blocks, adds them to the tree of pending blocks and finds blocks connected to
99+
// the finalized state.
100+
//
101+
// Details:
102+
// Adding blocks might result in additional blocks now being connected to the latest finalized block. The returned
103+
// slice contains:
104+
// 1. the subset of `certifiedBlocks` that are connected to the finalized block
105+
// - excluding any blocks whose view is smaller or equal to the finalized block
106+
// - if a block `B ∈ certifiedBlocks` is already known to the PendingTree and connected,
107+
// `B` and all its connected descendants will be in the returned list
108+
// 2. additionally, all of the _connected_ descendants of the blocks from step 1.
109+
//
110+
// PendingTree treats its input as a potentially repetitive stream of information: repeated inputs are already
111+
// consistent with the current state. While repetitive inputs might cause repetitive outputs, the implementation
112+
// has some general heuristics to avoid extra work:
113+
// - It drops blocks whose view is smaller or equal to the finalized block
114+
// - It deduplicates incoming blocks. We don't store additional vertices in tree if we have that block already stored.
95115
//
96-
// This function is designed to perform resolution of connected blocks(resolved block is the one that connects to the finalized state)
97-
// using incoming batch. Each block that was connected to the finalized state is reported once.
98116
// Expected errors during normal operations:
99117
// - model.ByzantineThresholdExceededError - detected two certified blocks at the same view
100118
//
@@ -157,7 +175,7 @@ func (t *PendingTree) connectsToFinalizedBlock(block CertifiedBlock) bool {
157175
// inputs might cause repetitive outputs.
158176
// When a block is finalized we don't care for any blocks below it, since they were already finalized.
159177
// Finalizing a block might causes the pending PendingTree to detect _additional_ blocks as now
160-
// being connected to the latest finalized block. This happens of some connecting blocks are missing
178+
// being connected to the latest finalized block. This happens if some connecting blocks are missing
161179
// and then a block higher than the missing blocks is finalized.
162180
// In the following example, B is the last finalized block known to the PendingTree
163181
//
@@ -167,8 +185,18 @@ func (t *PendingTree) connectsToFinalizedBlock(block CertifiedBlock) bool {
167185
// by '←-?-?-?--' have not been received by our PendingTree. Therefore, we still consider X,Y,Z
168186
// as disconnected. If the PendingTree tree is now informed that X is finalized, it can fast-
169187
// forward to the respective state, as it anyway would prune all the blocks below X.
188+
// Note:
189+
// - The ability to skip ahead is irrelevant for staked nodes, which continuously follows the chain.
190+
// However, light clients don't necessarily follow the chain block by block. Assume a light client
191+
// that knows the EpochCommit event, i.e. the consensus committee authorized to certify blocks. A
192+
// staked node can easily ship a proof of finalization for a block within that epoch to such a
193+
// light client. This would be much cheaper for the light client than downloading the headers for
194+
// all blocks in the epoch.
195+
// - The pending tree supports skipping ahead, as this is a more general and simpler algorithm.
196+
// Removing the ability to skip ahead would restrict the PendingTree's its domain of potential
197+
// applications _and_ would require additional code and additional tests making it more complex.
170198
//
171-
// If the PendingTree detect additional blocks as descending from the latest finalized block, it
199+
// If the PendingTree detects additional blocks as descending from the latest finalized block, it
172200
// returns these blocks. Returned blocks are ordered such that parents appear before their children.
173201
//
174202
// No errors are expected during normal operation.

0 commit comments

Comments
 (0)