You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: node/bft/README.md
+38-20Lines changed: 38 additions & 20 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -10,26 +10,44 @@ The `snarkos-node-bft` crate provides a node implementation for a BFT-based memo
10
10
11
11
The primary is the coordinator, responsible for advancing rounds and broadcasting the anchor.
12
12
13
-
#### Triggering Round Advancement
14
-
15
-
Each round runs until one of two conditions is met:
16
-
1. The coinbase target has been reached, or
17
-
2. The round has reached its timeout (currently set to 10 seconds)
18
-
19
-
#### Advancing Rounds
20
-
21
-
As described in the paper [Bullshark: The Partially Synchronous Version](https://arxiv.org/abs/2209.05633),
22
-
the BFT generally advances rounds when `n − f` vertices are delivered, however:
23
-
```
24
-
The problem in advancing rounds whenever n − f vertices are delivered is that parties
25
-
might not vote for the anchor even if the party that broadcast it is just slightly slower
26
-
than the fastest n − f parties. To deal with this, the BFT integrates timeouts into
27
-
the DAG construction. If the first n − f vertices a party p gets in an even-numbered round r
28
-
do not include the anchor of round r, then p sets a timer and waits for the anchor
29
-
until the timer expires. Similarly, in an odd-numbered round, parties wait for either
30
-
f + 1 vertices that vote for the anchor, or 2f + 1 vertices that do not, or a timeout.
31
-
```
32
-
Note that in this quote `2f + 1` should really be `n - f`.
13
+
#### Round Advancement
14
+
15
+
A round advances once a quorum (`n - f`) of validators have submitted certificates for that round
16
+
and the following round-type-specific conditions are met:
17
+
18
+
-**Even rounds**: the elected leader's certificate is present among the quorum, confirming the
19
+
leader was reachable. If the leader's certificate is absent, the node waits up to
20
+
`MAX_LEADER_CERTIFICATE_DELAY` before advancing without it.
21
+
-**Odd rounds**: at least `f + 1` certificates from the current round reference the previous
22
+
even round's leader certificate (availability threshold), or `n - f` do not (non-leader
23
+
quorum). If neither threshold is reached, the node again falls back to the timeout.
24
+
25
+
In both cases the timeout is `MAX_LEADER_CERTIFICATE_DELAY` (currently 5 seconds), reset at the
26
+
start of each round. This follows the [Bullshark](https://arxiv.org/abs/2209.05633) protocol.
27
+
28
+
#### Batch Proposal
29
+
30
+
Batch proposals are driven by a dedicated **`ProposalTask`** that runs in a loop and is the only place that calls `Primary::propose_batch()`.
31
+
This keeps proposal on a single execution path and avoids concurrent proposal attempts. Each loop iteration covers one full round and proceeds through three stages:
32
+
33
+
**Stage 1 — Wait until ready to propose**
34
+
35
+
The task blocks until all of the following conditions are satisfied:
36
+
1. The node is synced. If it is currently syncing, the task waits via `wait_for_synced_if_syncing()` before continuing.
37
+
2.`MIN_BATCH_DELAY` has elapsed since the start of the round, enforcing a minimum inter-proposal interval.
38
+
3. One of two events fires:
39
+
-**Ready signal** — `ProposalTask::signal()` is called from `try_increment_to_the_next_round()` when the primary successfully advances to a new round (e.g. after a leader certificate is committed). This is delivered via a `watch` channel.
40
+
-**`MAX_BATCH_DELAY` timeout** — If no signal arrives within `MAX_BATCH_DELAY` of the round start, the task proceeds anyway. This handles the case where the elected leader's certificate never arrives.
41
+
42
+
A short `CREATE_BATCH_INTERVAL` heartbeat keeps the round-change check alive while waiting.
43
+
44
+
**Stage 2 — Propose**
45
+
46
+
The task calls `propose_batch()` in a loop until it returns `Ok(true)` (batch submitted). On `Ok(false)` or a transient error it retries every `CREATE_BATCH_INTERVAL`. If the round advances during retries, the task restarts from Stage 1.
47
+
48
+
**Stage 3 — Wait for signatures**
49
+
50
+
Once the batch is broadcast, the task periodically calls `propose_batch()` every `MAX_BATCH_DELAY` to rebroadcast to any validators that have not yet signed. It exits this stage as soon as the round advances (detected either via the ready signal or by polling `current_round()`).
0 commit comments