DEV Community

Gaurav Jadkar
Gaurav Jadkar

Posted on

Consensus Engine

Building a Quorum-Based Consensus Engine in Python: A Beginner's Guide

Learn how validators, voting mechanisms, and quorum thresholds work by implementing a simplified blockchain consensus simulation.

Introduction

How can a group of independent participants agree that a proposed block should be accepted?

This is one of the fundamental questions behind blockchain consensus.

In a decentralized network, participants need a mechanism to agree on decisions without relying entirely on a central authority. Consensus mechanisms address this problem by defining how participants propose, validate, and approve changes to a shared state.

In this tutorial, we'll build a simplified quorum-based consensus engine using Python.

We'll implement a validator registry, collect votes, calculate the required quorum, and determine whether a proposed block should be approved.

This is an educational simulation, not a production blockchain consensus protocol. It does not implement network communication, cryptographic signatures, Byzantine fault tolerance, or fork resolution.

  1. Understanding the consensus model

Before writing code, let's understand the components.

Our simulation contains three main elements:

Validators: Participants responsible for evaluating a proposal and casting votes.

Proposals: The blocks or decisions that validators are asked to approve.

Quorum: The minimum number of affirmative votes required to approve a proposal.

For this example, imagine a network containing six validators.

We require four approvals before accepting a proposed block.

      Proposed Block
            |
    -----------------
    |       |       |
  Voter   Voter   Voter ...
    |       |       |
    -----------------
            |
      Vote Collection
            |
      Quorum Check
            |
    -----------------
    |               |
  Approved        Rejected
Enter fullscreen mode Exit fullscreen mode
  1. Creating the validator registry

Let's begin by creating a class that manages the validators.

class Validators:

def __init__(self):
    self.validators = []

def add_validator(self, validator_id):
    if validator_id in self.validators:
        raise ValueError("Validator already exists")

    self.validators.append(validator_id)

def get_validators(self):
    return self.validators.copy()
Enter fullscreen mode Exit fullscreen mode

The constructor initializes an empty validator list.

The "add_validator()" method registers a validator and prevents duplicate identifiers.

The "get_validators()" method returns a copy of the registered validator list.

Let's create six validators:

registry = Validators()

for validator_id in range(1001, 1007):
registry.add_validator(validator_id)

print(registry.get_validators())

Expected output:

[1001, 1002, 1003, 1004, 1005, 1006]

We now have six registered validators.

  1. Defining the quorum requirement

A quorum represents the minimum number of approvals needed for a proposal to pass.

For a fixed absolute quorum, the calculation is straightforward.

total_validators = 6
required_quorum = 4

Our example requires four affirmative votes out of six validators.

A different consensus model might calculate quorum as a fraction of the total validator population.

For example, a two-thirds threshold for six validators would require:

required_quorum = 4

The exact quorum policy depends on the consensus protocol being designed.

  1. Collecting validator votes

Now let's implement a voting mechanism.

Each validator can approve or reject a proposal.

class Voting:

def __init__(self):
    self.votes = {}

def cast_vote(self, validator_id, decision):
    if decision not in ("yes", "no"):
        raise ValueError("Invalid vote")

    if validator_id in self.votes:
        raise ValueError("Validator has already voted")

    self.votes[validator_id] = decision

def get_approvals(self):
    return sum(
        1 for vote in self.votes.values()
        if vote == "yes"
    )
Enter fullscreen mode Exit fullscreen mode

The "votes" dictionary stores each validator's decision.

For example:

voting = Voting()

voting.cast_vote(1001, "yes")
voting.cast_vote(1002, "yes")
voting.cast_vote(1003, "no")
voting.cast_vote(1004, "yes")
voting.cast_vote(1005, "yes")

We can now calculate the number of affirmative votes.

print(voting.get_approvals())

Output:

4

Four validators have approved the proposal.

  1. Implementing the consensus decision

We now need a function that compares the approval count with the required quorum.

def check_consensus(voting, required_quorum):

approvals = voting.get_approvals()

if approvals >= required_quorum:
    return "Quorum achieved"

return "Quorum not achieved"
Enter fullscreen mode Exit fullscreen mode

Let's execute it:

result = check_consensus(voting, required_quorum)

print(result)

Output:

Quorum achieved

Our proposal has received the required number of approvals.

If the proposal receives fewer than four approvals, it will not pass the quorum check.

Notice that this is only a simplified approval rule. A production protocol also needs rules for validator eligibility, vote authenticity, conflicting proposals, timeouts, and finality.

  1. Connecting the components

We have now implemented three important components:

  • Validator registration.
  • Vote collection.
  • Quorum verification.

Their relationship can be represented as:

Validator Registry
|
v
Proposal Created
|
v
Votes Collected
|
v
Approval Count
|
v
Quorum Verification
|
-----------
| |
Pass Fail
| |
Accept Reject

The key design principle is separation of responsibilities.

The validator registry manages participants, the voting component manages decisions, and the consensus function evaluates the final approval condition.

This makes the system easier to extend and test.

  1. Testing different scenarios

A useful way to evaluate the implementation is to test different voting combinations.

Yes votes| Required quorum| Result
6| 4| Approved
4| 4| Approved
3| 4| Not approved
2| 4| Not approved

These tests verify that the decision logic behaves as expected.

Additional tests should cover duplicate votes, invalid validator IDs, invalid decisions, and missing votes.

  1. What would be required for a real blockchain?

A simplified Python simulation is a useful starting point, but it should not be confused with a complete consensus protocol.

A production implementation would require several additional mechanisms:

Network communication: Validators need to exchange proposals and votes.

Cryptographic verification: Votes must be authenticated to prevent impersonation.

Fault tolerance: The protocol must define how it behaves when validators fail or act maliciously.

Finality: The system needs clear rules for when a block becomes irreversible under the protocol's assumptions.

State management: Accepted blocks must be connected to a consistent chain or replicated state.

These are substantial engineering challenges beyond simple quorum counting.

Conclusion

Building a quorum-based consensus simulation provides a practical way to understand how distributed decision-making works.

By implementing validator registration, vote collection, and quorum verification in Python, we have explored the basic structure of a consensus engine.

The next challenge is moving from a local simulation to a distributed environment where multiple independent nodes communicate, verify decisions, and maintain a consistent state.

That is where consensus engineering becomes particularly interesting.

Technologies: Python | Object-Oriented Programming | Consensus Fundamentals

Project extension: Explore proposer selection, block validation, cryptographic signatures, and distributed validator communication.

Top comments (0)