Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
341 changes: 341 additions & 0 deletions BEPs/BEP-684.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,341 @@
<pre>
BEP: 684
Title: Header-Only Voting for Fast Finality
Status: Draft
Type: Standards
Created: 2026-04-23
Description: Decouple consensus voting from block execution by allowing validators to vote upon header verification, removing execution from the critical voting path.
</pre>

# BEP-684: Header-Only Voting for Fast Finality

- [BEP-684: Header-Only Voting for Fast Finality](#bep-684-header-only-voting-for-fast-finality)
- [1. Summary](#1-summary)
- [2. Abstract](#2-abstract)
- [3. Motivation](#3-motivation)
- [4. Specification](#4-specification)
- [4.1 Header-Only Voting Protocol](#41-header-only-voting-protocol)
- [4.2 Header Verification](#42-header-verification)
- [4.3 Import-Gated Finality](#43-import-gated-finality)
- [4.4 Lazy Execution Pipeline](#44-lazy-execution-pipeline)
- [4.5 Invalid Block Recovery](#45-invalid-block-recovery)
- [5. Safety Analysis](#5-safety-analysis)
- [5.1 Theorem 1 — Accountable Safety](#51-theorem-1--accountable-safety)
- [5.2 Theorem 2 — No Premature Finality](#52-theorem-2--no-premature-finality)
- [5.3 Theorem 3 — Fast Node Safety Under Invalid Successor](#53-theorem-3--fast-node-safety-under-invalid-successor)
- [5.4 Theorem 4 — Fork Consistency](#54-theorem-4--fork-consistency)
- [6. Liveness Analysis](#6-liveness-analysis)
- [6.1 Theorem 5 — Normal Liveness](#61-theorem-5--normal-liveness)
- [6.2 Theorem 6 — Invalid Block Liveness Bound](#62-theorem-6--invalid-block-liveness-bound)
- [6.3 Degradation Hierarchy](#63-degradation-hierarchy)
- [7. Related Work](#7-related-work)
- [8. Backward Compatibility](#8-backward-compatibility)
- [9. Security Considerations](#9-security-considerations)
- [10. License](#10-license)

## 1. Summary

This BEP allows validators to cast Fast Finality votes upon verifying the block header alone, without waiting for full block import (execution), removing execution time from the critical voting path.

## 2. Abstract

Under the current Fast Finality mechanism ([BEP-126](./BEP126.md)), a validator must fully import a block — including transaction execution and state root verification — before voting. This places execution time on the critical path of vote propagation, imposing a hard lower bound on block interval:

```
BlockInterval > 2 × OWD + T_import
```

This BEP decouples voting from execution: validators verify only the block header (parent hash, height, proposer, signature, timestamp) and vote immediately. Full block import proceeds asynchronously, and finality is still gated on successful import. This removes `T_import` from the critical voting path, relaxing the constraint to:

```
BlockInterval > 2 × OWD + T_header_verify
```

where `T_header_verify ≈ 2ms` versus `T_import ≈ 100–300ms`, significantly lowering the minimum achievable block interval while maintaining the safety and liveness guarantees of BEP-126.

**Key assumption**: BSC has no light clients — all participants are full nodes that eventually execute every block.

## 3. Motivation

The current voting pipeline requires execution in the critical path:

```
|<------------ BlockInterval ------------>|
| δ_net | T_import | δ_net | δ_agg |idle|
| 75ms | 100-300ms| 75ms | 10ms | |

B_h arrives → execute → vote → votes arrive at P_{h+1}
```

Execution (`T_import`) consumes the majority of the time budget. As block intervals decrease, execution becomes the dominant bottleneck — the constant `2 × OWD` overhead plus `T_import` leaves diminishing room for further reduction.

With Header-Only Voting, execution is removed from the critical path:

```
|<------------ BlockInterval ------------>|
| δ_net | δ_hdr| δ_net | δ_agg | |
| 75ms | 2ms | 75ms | 10ms | |

header arrives → verify → vote → votes arrive at P_{h+1}
↓ (async)
import B_h in background
```

The critical path shrinks from `2 × OWD + T_import ≈ 312ms` to `2 × OWD + T_header ≈ 152ms`, unlocking further block interval reductions that are infeasible under the current protocol.

## 4. Specification

### 4.1 Header-Only Voting Protocol

The BEP-126 vote rules (Rule 1: no double voting at same height; Rule 2: no surround voting; Rule 3: vote for canonical chain tip) remain unchanged. The only change is the **vote trigger condition**:

```
Current (BEP-126): vote for B_h iff imported[h] == B_h
Header-Only Voting: vote for B_h iff header_verified[h] == B_h
```

Where `header_verified[h]` means the header of `B_h` has passed the checks defined in §4.2.

### 4.2 Header Verification

A validator may vote for block `B_h` after verifying the following header fields:

| Check | Description |
|-------|-------------|
| Parent link | `header.parent_hash == canonical_tip_hash()` |
| Height | `header.height == canonical_tip_height() + 1` |
| Proposer | `header.proposer ∈ {in_turn(h), backup(h)}` |
| Signature | `verify_signature(header, header.proposer) == true` |
| Timestamp | `parent.timestamp < header.timestamp ≤ now() + CLOCK_DRIFT` |

The following fields are **not** verified before voting (deferred to import):
- `state_root`, `tx_root`, `receipt_root`
- Transaction validity, gas usage, execution results

### 4.3 Import-Gated Finality

Voting is decoupled from execution, but **finality is not**. The BEP-126 finalization rule (two consecutive justified blocks → the first is finalized) applies only after both blocks have been successfully imported:

```
finalized(B_h) iff:
justified(B_h) ∧ justified(B_{h+1}) ∧ imported(B_h) ∧ imported(B_{h+1})
```

This guarantees that no block is finalized without its execution result being verified by all honest validators.

### 4.4 Lazy Execution Pipeline

With voting decoupled from execution, blocks are imported asynchronously. Define execution debt `D(h)` as the number of blocks that have been voted on but not yet imported at height `h`:

```
D(h) = h - max{h' : imported(B_{h'}) == true}
```

Stability requires `E[T_import] < BlockInterval`, so execution debt remains bounded in expectation. When debt accumulates due to heavy blocks, the pipeline absorbs transient spikes:

```
EXECUTION_DEBT_MAX = 2 # maximum pipeline depth
```

If `D(h) > EXECUTION_DEBT_MAX`, the validator pauses voting until execution catches up.

### 4.5 Invalid Block Recovery

A Byzantine proposer may produce a block `B_h*` with a valid header but invalid body (e.g., incorrect state root). Under Header-Only Voting, honest validators will vote for `B_h*` based on the header, but import will fail.

**Invalid Block Skip Rule**: if a full node fails to import `B_h` within `IMPORT_TIMEOUT` (= 3 × BlockInterval):

1. Mark height `h` as execution-invalid.
2. Do not finalize any block through `B_h`.
3. Continue processing consensus at subsequent heights.
4. When a valid block at height `h' > h` forms a new justified chain, resume finalization.

Stall bound: a single Byzantine proposer causes at most one stalled height. With 6/21 Byzantine validators, the probability of ≥3 consecutive Byzantine proposers is `(6/21)³ ≈ 2.3%`. Single stall duration ≤ `IMPORT_TIMEOUT + BlockInterval ≈ 4 × BlockInterval`.

## 5. Safety Analysis

### 5.1 Theorem 1 — Accountable Safety

If two conflicting blocks `B_h` and `B_h'` are both finalized, then ≥ ⅓ of validators violated BEP-126 vote rules.

```
Proof:
Finality requires justified(B) ∧ imported(B).
Header-Only Voting changes when votes are cast, not the vote rules themselves.
BEP-126 Rule 1 (no double vote) and Rule 2 (no surround vote) still hold.
The safety proof of BEP-126 Theorem 1 applies without modification. ∎
```

### 5.2 Theorem 2 — No Premature Finality

No block is finalized without successful import by all honest validators.

```
Proof:
By §4.3, finalized(B_h) requires imported(B_h).
Imported(B_h) means the full block has been executed and the state root verified.
A block with an invalid body will fail import → never satisfy finalized().
Therefore, finality implies execution correctness. ∎
```

### 5.3 Theorem 3 — Fast Node Safety Under Invalid Successor

It is safe for a fast node to finalize `B_h` when both `B_h` and `B_{h+1}` are justified, even if `B_{h+1}`'s body is invalid.

Combined with [BEP-648](./BEP-648.md) (in-memory vote pool finality), a fast node can determine finality without waiting for attestations to appear in block headers. The full finality path becomes:

```
BEP-648: justified(B) when ≥14 votes observed in vote pool (no header inclusion needed)
BEP-684: votes cast upon header verification (no import needed)
Combined: fast node finalizes B_3 when vote pool shows ≥14 votes for both B_3 and B_4
— before B_4 is included in any header, and before B_4 is imported.
```

```
Scenario:
B_3: valid header, valid body → justified (≥14 votes in pool, per BEP-648)
B_4: valid header, INVALID body → justified (≥14 header votes in pool, per BEP-648)

Full node: imported(B_3)=true, imported(B_4)=false → finalized(B_3)=false
Fast node: does not verify state_root → considers both "imported"
→ finalized(B_3)=true (earliest, via BEP-648 vote pool)

Question: Is the fast node's finalization of B_3 correct?

Proof — Safety (no conflicting finality):
Finalizing B_3 means: no conflicting B_3' at height 3 can also be finalized.
This guarantee depends solely on BEP-126 vote rules:
Rule 1: no validator votes for two different blocks at the same height.
Rule 2: no surround voting.
These rules are unchanged by HOV or BEP-648 — votes are still per-height.
→ A conflicting B_3' requires ≥7 validators violating Rule 1.
→ Safety holds regardless of B_4's body validity. ✓

Proof — Correctness (B_3 has been execution-verified by supermajority):
B_4 receiving ≥14 votes means ≥14 validators accepted B_4's header.
B_4.parent_hash = hash(B_3), so these validators have B_3 in their canonical chain.
Under the current protocol (pre-HOV): voting requires import, so ≥14 validators
have already fully imported and executed B_3 — its state root is verified by
a supermajority.
Under HOV: voting only requires header verification, but the lazy execution
pipeline imports blocks asynchronously. With EXECUTION_DEBT_MAX = 2,
B_3 is imported before or shortly after B_4 arrives. Even in the worst case,
the ≥14 validators will eventually import B_3 and detect any invalidity.
In both cases: ≥14 honest validators have (or will have) verified B_3's execution.
B_3's state = f(state(B_2), txs(B_3)) — independent of B_4's body.
Since B_3 is valid, the fast node's state at B_3 is correct. ✓

Proof — Consistency (fast node does not diverge from full nodes):
Full nodes do not finalize B_3 via the B_3→B_4 path (imported(B_4) fails).
But B_3 IS on the canonical chain — honest full nodes successfully import B_3.
The chain will eventually recover from B_4's invalidity (§4.5):
An honest proposer produces B_4' or B_5 on a valid fork.
B_3 gets finalized through a later justified pair.
→ Fast node and full nodes converge on the same finalized B_3. ✓

Conclusion:
The fast node finalizes B_3 earlier than full nodes (because it doesn't
gate on imported(B_4)), but this is safe because:
1. B_3 itself is valid — verified by ≥14 validators who voted for B_4.
2. No conflicting B_3' can be finalized (vote rule guarantee).
3. Full nodes will eventually finalize the same B_3 through recovery.
BEP-648 accelerates when the fast node observes justification (vote pool
vs header inclusion), but does not change what justification means.
The fast node's existing trust model (trust consensus, skip execution)
is preserved — neither HOV nor BEP-648 weakens it. ∎
```

### 5.4 Theorem 4 — Fork Consistency

Header-Only Voting does not introduce new fork scenarios beyond BEP-126.

```
Proof:
A fork requires two justified blocks at the same height.
Justification requires ≥ 14/21 votes.
BEP-126 Rule 1 prevents any validator from voting for two blocks at the same height.
This rule is unchanged — votes are still per-height, only the trigger changes.
→ Fork requires ≥ 7 validators violating Rule 1, same as BEP-126. ∎
```

## 6. Liveness Analysis

### 6.1 Theorem 5 — Normal Liveness

After GST, if the proposer at height `h` is honest, `B_h` is finalized within `2 × BlockInterval + T_import`.

```
Proof:
t=0: P_h broadcasts valid B_h
t ≤ δ_net: honest validators receive header → cast header vote
t ≤ 2×δ_net: P_{h+1} receives ≥15 votes → constructs B_{h+1}
t ≤ 2×δ_net: honest validators receive body(B_h) → begin import
t ≤ BlockInterval: B_{h+1} broadcast
t ≤ BlockInterval + δ_net: honest validators receive header(B_{h+1}) → vote
t ≤ 2×BlockInterval: both B_h and B_{h+1} justified
t ≤ 2×BlockInterval + T_import: both imported

Finality ≤ 2 × BlockInterval + T_import ∎
```

### 6.2 Theorem 6 — Invalid Block Liveness Bound

A Byzantine proposer producing an invalid block delays finality by at most `O(BlockInterval)`, and does not permanently stall the chain.

```
Proof:
After B_h* fails import:
Invalid Block Skip Rule triggers within IMPORT_TIMEOUT = 3 × BlockInterval.
Next honest proposer builds on B_{h-1}.
Validators who have not voted at height h can vote for the replacement.
Stall ≤ IMPORT_TIMEOUT + BlockInterval ≈ 4 × BlockInterval.
With 15/21 honest validators, next proposer is honest with probability 71%. ∎
```

### 6.3 Degradation Hierarchy

| Level | Condition | Finality Latency |
|-------|-----------|-----------------|
| BEP-126 + HOV | ≥14 header votes + import OK | ~2 × BlockInterval + T_import |
| Parlia fallback | ≥11/21 online | Probabilistic finality |

## 7. Related Work

| | Ethereum | Solana (Alpenglow) | BSC (this BEP) |
|---|---|---|---|
| Execution timing | Synchronous | Synchronous (lazy planned) | Asynchronous (lazy) |
| Vote prerequisite | Full attestation | Block received | Header only |
| Finality prerequisite | Epoch boundary | 1–2 rounds | Import confirmed |
| Invalid block handling | Slashing | Slashing | Existing bad block mechanism |
| Light client | Yes | Yes | No (all full nodes) |

**vs Ethereum ePBS**: ePBS separates builder from proposer, requiring fraud/validity proofs for dishonest builders. BSC Header-Only Voting separates voting from execution — invalid bodies are caught by the existing bad block mechanism, no new proof system needed.

**vs Solana Lazy Execution**: Solana plans to reach consensus on transaction ordering only, with state commitment deferred to later blocks. BSC Header-Only Voting is more conservative — the proposer still commits to `state_root` in the header, and finality still requires import verification.

## 8. Backward Compatibility

This BEP is **not backward compatible**. It changes the vote trigger condition from full import to header verification, requiring a hard fork with coordinated client upgrades across all validators and full nodes.

Dependencies:
- [BEP-126](./BEP126.md): Fast Finality Mechanism (base protocol)
- [BEP-590](./BEP-590.md): Extended Voting Rules (vote aggregation flexibility)
- [BEP-648](./BEP-648.md): Enhanced Fast Finality via In-Memory Voting Pool (vote pool finality)

## 9. Security Considerations

| Threat | Impact | Mitigation |
|--------|--------|-----------|
| Byzantine proposer: invalid body | Single-height stall | Existing bad block mechanism + Invalid Block Skip Rule (§4.5) |
| Byzantine proposer: missing body | Votes cast but import impossible | Import timeout → skip |
| Network partition after header vote | Finality stall | Resumes after partition heals |
| Execution slower than block rate | Execution debt growth | Gas limit tuning + `EXECUTION_DEBT_MAX` backpressure |

Core invariants:
1. **Vote-Finality gap**: header votes may exist without finality. Finality always requires import.
2. **No light client leakage**: no component acts on header-vote finality alone.
3. **Bad block handling unchanged**: detection and existing mechanisms are preserved; no new slashing conditions.

## 10. License

The content is licensed under [CC0](https://creativecommons.org/publicdomain/zero/1.0/).