# MonadBFT Explained Part 1: A Brief Introduction to Two-Phase BFT Protocols

By [Cryptolytic](https://paragraph.com/@cryptolytic) · 2025-08-06

---

Introduction
============

Monad recently released the MonadBFT paper, along with a series of blog posts explaining its consensus design and block propagation. With major updates to their documentation as well, I think it’s a great time to dig in and explore the protocol in more depth.

In this blog series, I’ll explain the core ideas behind MonadBFT and the problems it aims to solve. The main sources are the MonadBFT paper \[1\], along with Monad’s blog posts and updated documentation. I’ll break things down so general readers can follow along, without getting lost in the technical details. If you’re looking for formal proofs or want to dive into the pseudocode, you’ll find that in the original paper \[1\].

In this first part, I’ll walk you through the concept of two-phase pipelined BFT protocols. In fact, there are many variations in this family of consensus protocols. Some focus on efficiency, others on robustness, and some try to strike a balance between the two. Here, I’ll focus only on giving you a high-level picture of how these protocols work in the happy path (i.e., when all nodes and the system behave correctly), so we can build a foundation for understanding MonadBFT.

We'll start with the non-pipelined version and gradually evolve it into the pipelined one. Along the way, you’ll learn some useful jargon that will help you better understand MonadBFT in the next blog. We’ll also look at the locking mechanism used in traditional two-phase BFT protocols, which is important because Monad uses a slightly different version—something we'll explore in the next part.

If you’re already familiar with these concepts, feel free to skip ahead. Otherwise, let’s dive in.

Concepts and Notations
======================

First things first, let’s go over some technical terms that will come up in this blog (and likely in future ones too). To be clear, the actual MonadBFT paper includes more jargon than what I’ll cover here. But as I said, I want to keep things simple, so I’ll only introduce the necessary ones.

My goal is to give you just enough background to follow along with this series. And if you want to dive deeper into the paper later, I hope this will give you the foundation you need to read it more efficiently.

n = 3f + 1
----------

We assume there are $$n = 3f + 1$$ validators (sometimes I may refer to a validator as a node) in the network, where up to f of them can be malicious. That means roughly one-third of the validators can behave arbitrarily (Byzantine faults), and the protocol should still work correctly.

In the examples, I’ll use $$n = 4$$, which implies $$f = 1$$. So we assume that, in the worst case, 3 validators are honest and 1 is malicious. If you’re curious why we need to assume $$n = 3f + 1$$, I’ve included a sketch proof in this article \[5\].

Round and Block Height
----------------------

In pipelined BFT protocols like HotStuff and MonadBFT, each round (also called a view) assigns a leader who proposes (or re-proposes) a block. Validators then vote to reach consensus on whether the proposed block should be committed.

Block height refers to the position of a block in the chain. For example, the genesis block has height 0, the next has height 1, and so on. The height only increases when a block is actually committed.

Rounds, on the other hand, increase continuously and never reset, even if no block is committed. This is different from non-pipelined BFT protocols, where the round number might reset after each committed block.

The reason for this difference is pipelining: consensus can work on multiple blocks at different stages (e.g., preparing one while committing another). This is why round $$\\neq$$ block height, and why rounds can advance even when no block is committed. This will become clearer in the next section.

General Flow of Two-Phase BFT Protocols
=======================================

This section will walk you through the typical flow of the happy path, where the network is functioning properly and validators behave honestly. The goal is to give a quick refresher for those who might have forgotten how this works, and to build a foundation for new readers before we move on to more advanced topics like the unhappy path and how MonadBFT handles it in the next blog.

The following examples will help you better understand the difference between round and block height, and also how a (partially synchronous) two-phase BFT protocol works in general.

In two-phase BFT protocols, there are usually four steps: propose, prevote, precommit, and commit. Commit happens locally and is just a consequence of the precommit phase, but I separate it out here to make the flow clearer. The reason it is called “two-phase” is that there are two voting steps: prevote and precommit. Assume there are 4 validators in the network. We’ll begin with the non-pipelined, leader-centric version. This is easier to grasp and can be generalized later to the pipelined version. Let’s say we’re at the beginning of block height 10. Validators are trying to commit a block at that height. Suppose validator 1 is the leader. This height begins with round 0.

Non-Pipelined Version
---------------------

At the beginning of the round, the leader (validator 1) proposes a block, called a proposal, and sends it to the other validators. This marks the end of the propose phase.

After receiving the proposal, the prevote phase begins. Non-leader validators check the proposal and vote for it by sending their (pre)votes (signatures) back to the leader. The leader aggregates $$2f + 1$$ prevote messages (including its own vote) to form a quorum certificate (QC) for prevotes. In our case, $$2f + 1 = 3$$. The leader then sends the prevote QC to all validators, marking the end of the prevote phase.

A validator moves to the precommit phase when it receives the prevote QC. It verifies the QC and responds to the leader with a precommit message. The leader aggregates $$2f + 1$$ precommit messages into a precommit QC and sends it back to the validators. At this point, when a validator receives the precommit QC, it can commit the proposal and add it to the chain at height = $$10$$, then move on to the next height, starting a new round 0.

Note: in this non-pipelined version, the round is reset at the beginning of every new height. Later, when we upgrade to the pipelined version, rounds will no longer reset.

Now you can see this approach is not very efficient, as we need to wait for at least 2.5 message round-trip times (RTT) just to commit a single block:

1.  Propose – 0.5 RTT The leader proposes a block to validators
    
2.  Prevote – 1.0 RTT
    
    1.  0.5 RTT: Validators send prevotes to the leader
        
    2.  0.5 RTT: Leader sends the prevote QC back to validators
        
3.  Precommit – 1.0 RTT
    
    1.  0.5 RTT: Validators send precommits to the leader
        
    2.  0.5 RTT: Leader sends the precommit QC back to validators
        
4.  Commit – 0 RTTValidators commit the block locally upon receiving the precommit QC
    

There are also protocols with higher latency, like HotStuff’s 3-phase BFT, which takes around 3.5 RTT. That’s outside the scope of this blog, but if you're interested, I recommend this article \[4\].

![Figure 1: Two-Phase BFT without Pipelining](https://storage.googleapis.com/papyrus_images/0924ae73980878509669e71713fd93d0e5d85411b178f3a0bc2c7041bc5c7acc.png)

Figure 1: Two-Phase BFT without Pipelining

Pipelined Version
-----------------

But we can do better. With pipelining, although the latency to commit each block remains at 2.5 RTT, the latency between two consecutive blocks can be reduced to just 1 RTT.

This is possible because the flow of messages from the leader (Proposal, PrevoteQC, and PrecommitQC) and from the validators (Prevote and Precommit) follows the same communication pattern across rounds. Instead of sending them separately, we can bundle messages from different block heights that follow the same path.

For example, leader V3 can send the PrecommitQC of B10, which would normally come from V1, the PrevoteQC of B11 from V2, and its own proposal for B12, all together in a single message. This bundling reduces the message cost to just 0.5 RTT. Similarly, validators can bundle their Prevote for B12 and Precommit for B11 into one response and send it to the next leader.

The figure titled "Message Bundling Across Block Heights" shows the basic idea. It illustrates how messages from different heights can align and piggyback on one another, but each round is still viewed separately. The second figure, "Pipelined Architecture in Two-Phase BFT", builds on this by showing how these phases are interleaved in a real pipelined setting, allowing multiple blocks to make progress at the same time across consecutive rounds.

![Figure 2: Repeating Patterns in BFT Rounds](https://storage.googleapis.com/papyrus_images/79cec8f23d8308102d6fcc4ea50565ebb2bcea421e3725cbff7a23095ca43465.png)

Figure 2: Repeating Patterns in BFT Rounds

This becomes a bit confusing if we try to reset the round number every time a block is committed, because in the pipelined version, different block heights are interleaved and piggybacked together. While it’s still manageable on the happy path, where each block starts and ends in a single round (usually round 0), things get trickier in the unhappy path, where some block heights may take multiple rounds before getting committed.

To make things easier, we can simply avoid resetting the round number and let it keep increasing globally. A round R starts when a validator receives a Proposal, PrevoteQC, and PrecommitQC, and responds by sending its votes to the next validator. That round ends when the next validator aggregates those votes into QCs and broadcasts the QCs along with a new proposal. When validators receive those new QCs and the new proposal, the next round begins.

![Figure 3: Two-Phase BFT with Pipelining](https://storage.googleapis.com/papyrus_images/5b30a1cffc28f3960505656a7fcdb36ad919b01556d761630742f1fc7d7405e2.png)

Figure 3: Two-Phase BFT with Pipelining

Let’s summarize the flow. Assume we are at the beginning of the propose phase for height 10, where V1 is the leader and proposes block B10. Here's how the pipelined flow proceeds:

1.  Leader V1 aggregates Prevote messages for B9 and Precommit messages for B8 into QCs — namely, PrevoteQC(B9) and PrecommitQC(B8). Then, V1 creates a new proposal Proposal(B10) for height 10 and broadcasts all three messages: PrecommitQC(B8), PrevoteQC(B9), and Proposal(B10) to the other validators.
    
2.  Once a validator receives this message (this marks the start of round 10), it does the following:
    
    1.  For height 8: it can commit block B8, since PrecommitQC(B8) is now available.
        
    2.  For height 9: it enters the precommit phase and sends a Precommit(B9) to the next leader.
        
    3.  For height 10: it enters the prevote phase and sends a Prevote(B10) to the next leader.
        
    
    So each validator sends Precommit(B9) and Prevote(B10) to the next leader, V2.
    
3.  Leader V2 performs the same steps as V1. It aggregates QCs to form PrecommitQC(B9) and PrevoteQC(B10), creates a new proposal Proposal(B11) for height 11, and broadcasts these three messages: PrecommitQC(B9), PrevoteQC(B10), and Proposal(B11) to all validators.
    
4.  As before, validators begin round 11. They:
    
    1.  Commit B9 at height 9
        
    2.  Send Precommit(B10) and Prevote(B11) to the next leader, V3
        
5.  Leader V3 creates a new proposal Proposal(B12) for height 12, along with PrecommitQC(B10) and PrevoteQC(B11), and sends them to the validators.
    
6.  And so on.
    

Locking Mechanism
-----------------

Now that we’ve covered the general flow of a two-phase BFT protocol, we’re almost ready to dive into MonadBFT. But before we explore what problem Monad is trying to solve in pipelined two-phase BFT, let’s take a look at the locking mechanism used in standard two-phase protocols. I didn’t explain this earlier when walking through the happy path, but locking plays a crucial role in ensuring safety, especially during the unhappy path.

A locking mechanism forces a validator to “lock” on a specific block. Once a validator is locked on a block, it can only vote for that block. It will only unlock and vote for a different one if certain conditions are met, such as seeing a new PrevoteQC for a different block.

To understand this better, let’s go back to the non-pipelined version. Imagine we have 4 validators, where V4 is malicious, and V1, V2, and V3 are honest. Assume there is no locking mechanism in place yet.

Let V1 be the leader. It proposes a block B1 at height H. Validators V2, V3, and V4 all send prevote and precommit messages for this block. V1 collects these votes and constructs both PrevoteQC and PrecommitQC, then broadcasts them to the other validators.

However, due to network delays, validators V2, V3, and V4 only receive the PrevoteQC in time. They don’t receive the PrecommitQC, so the round ends with only V1 committing block B1 at height H, because it’s the leader and the first to see the PrecommitQC.

Now the protocol moves on. Since V2, V3, and V4 didn’t commit anything at height H, they stay at the same height but advance to the next round. Meanwhile, V1 goes offline, and the network needs to elect a new leader to make progress. This situation highlights how, in the unhappy path, a single block height can span multiple rounds before a commit occurs.

Suppose V4 becomes the new leader. Instead of reproposing block B1, it proposes a new block B2 to the network. Since there are enough votes from V2, V3, and V4, it’s possible to construct both PrevoteQC and PrecommitQC for B2. That means V2, V3, and V4 can commit block B2 at the same height (H) as block B1. As a result, we now have a fork: V1 has B1 as the tip of the chain at height H, while V2, V3, and V4 have B2 at the same height. What makes this worse is that V4 didn’t even break any rules. It followed the protocol correctly by proposing a new block in a new round. From the protocol’s perspective, V4 might not even be considered malicious.

This is exactly why we need a locking mechanism. The idea is simple. A validator locks on a block when it sees a PrevoteQC for that block. Once locked, it can only vote for that block, and it will remain locked unless it sees a new PrevoteQC for a different block. This simple rule fixes the fork problem.

In our example, during the first round, V2, V3, and V4 all see the PrevoteQC for block B1, so they lock on B1. Later, V1 creates the PrecommitQC, commits B1, and then goes offline. However, due to network delay, V2, V3, and V4 never receive the PrecommitQC. When the protocol moves to the next round and V4 becomes the new leader, it proposes a new block B2. But even if V4 is malicious, V2 and V3 won't vote for B2, because they are still locked on B1. As a result, V4 won’t be able to create a fork.

Summary
=======

In this first blog, I explained the fundamental ideas behind how two-phase BFT protocols work in general. My aim is for this post to serve both as a quick refresher for those who may have forgotten the basics and as a foundation for readers new to the concept, so you'll be better prepared for the next blog, which will cover MonadBFT.

I introduced important terms such as round, height, QC, prevote, precommit, and more. In the next post, we’ll explore MonadBFT by first looking at the limitations of traditional two-phase BFT, specifically the tail-forking problem, and then see how Monad addresses it.

References
==========

\[1\] [https://arxiv.org/pdf/2502.20692](https://arxiv.org/pdf/2502.20692)

\[2\] [https://www.category.xyz/blogs/monadbft-fast-responsive-fork-resistant-streamlined-consensus](https://www.category.xyz/blogs/monadbft-fast-responsive-fork-resistant-streamlined-consensus)

\[3\] [https://mirror.xyz/contributiondaoblog.eth/5nbATpfI0G-ryvhCAexc0\_bCdVAk1AQbqC0HZ8hTNgc](https://mirror.xyz/contributiondaoblog.eth/5nbATpfI0G-ryvhCAexc0_bCdVAk1AQbqC0HZ8hTNgc)

\[4\] [https://decentralizedthoughts.github.io/2023-04-01-hotstuff-2/](https://decentralizedthoughts.github.io/2023-04-01-hotstuff-2/)

\[5\] [https://mirror.xyz/contributiondaoblog.eth/-TLP4crabITcCutzGLstUp7OEcNxfA5UHb1gqMGnJ4g](https://mirror.xyz/contributiondaoblog.eth/-TLP4crabITcCutzGLstUp7OEcNxfA5UHb1gqMGnJ4g)

---

*Originally published on [Cryptolytic](https://paragraph.com/@cryptolytic/monadbft-explained-part-1-a-brief-introduction-to-two-phase-bft-protocols)*
