Implementing Consensus

Building a complete consensus mechanism for a blockchain.

Advanced⏱️ 55 min📚 Prerequisites: 1

Implementing Consensus

Consensus ensures all nodes agree on the valid state of the blockchain. Let's implement a simple consensus mechanism.

Consensus Requirements

  1. Agreement: All honest nodes agree on valid blocks
  2. Validity: Only valid blocks are accepted
  3. Termination: Consensus eventually reaches agreement
  4. Integrity: Blocks cannot be tampered with

Block Validation

Before accepting a block, nodes must validate:

RUST
fn validate_block(block: &Block, previous_block: &Block) -> bool {
    // Check index is sequential
    if block.index != previous_block.index + 1 {
        return false;
    }
    
    // Check previous hash matches
    if block.previous_hash != previous_block.hash {
        return false;
    }
    
    // Check block hash is valid
    if block.hash != calculate_block_hash(block) {
        return false;
    }
    
    // Check transactions are valid
    for tx in &block.transactions {
        if !validate_transaction(tx) {
            return false;
        }
    }
    
    true
}

Longest Chain Rule

In PoW blockchains, the longest valid chain wins:

RUST
fn select_best_chain(chains: Vec<Blockchain>) -> Blockchain {
    chains.into_iter()
        .max_by_key(|chain| chain.blocks.len())
        .unwrap()
}

Fork Resolution

When forks occur:

  1. Temporary fork: Two blocks at same height
  2. Wait: Wait for next block
  3. Choose: Longest chain becomes canonical
  4. Reorganize: Orphaned blocks are discarded

Finality

  • PoW: Probabilistic finality (more confirmations = more secure)
  • PoS: Faster finality, sometimes instant
  • BFT: Byzantine Fault Tolerance provides immediate finality

Consensus Implementation

RUST
struct ConsensusEngine {
    blockchain: Blockchain,
    pending_blocks: Vec<Block>,
}

impl ConsensusEngine {
    fn add_block(&mut self, block: Block) -> Result<(), String> {
        // Validate block
        if !self.validate(&block) {
            return Err(String::from("Invalid block"));
        }
        
        // Add to chain
        self.blockchain.add_block(block);
        Ok(())
    }
}

Code Examples

Block Validation

Validating blocks before adding to chain

RUST
struct Block {
    index: u64,
    data: String,
    previous_hash: String,
    hash: String,
}
fn calculate_hash(block: &Block) -> String {
    format!("hash_{}{}{}", block.index, block.data, block.previous_hash)
}
fn validate_block(block: &Block, previous: &Block) -> bool {
    if block.index != previous.index + 1 {
        println!("Invalid index");
        return false;
    }
    if block.previous_hash != previous.hash {
        println!("Invalid previous hash link");
        return false;
    }
    let calculated_hash = calculate_hash(block);
    if block.hash != calculated_hash {
        println!("Invalid hash");
        return false;
    }
    true
}
fn main() {
    let block0 = Block {
        index: 0,
        data: String::from("Genesis"),
        previous_hash: String::from("0"),
        hash: String::from("hash_0Genesis0"),
    };
    let block1 = Block {
        index: 1,
        data: String::from("Block1"),
        previous_hash: String::from("hash_0Genesis0"),
        hash: String::from("hash_1Block1hash_0Genesis0"),
    };
    println!("Block valid: {}", validate_block(&block1, &block0));
}

Explanation:

Block validation ensures only valid blocks are added to the chain. This maintains blockchain integrity and prevents invalid states.

Chain Selection

Selecting the best chain when forks occur

RUST
struct Blockchain {
    blocks: Vec<Block>,
}
struct Block {
    index: u64,
    hash: String,
}
impl Blockchain {
    fn length(&self) -> usize {
        self.blocks.len()
    }
}
fn select_longest_chain(chains: Vec<Blockchain>) -> Blockchain {
    chains.into_iter()
        .max_by_key(|chain| chain.length())
        .unwrap()
}
fn main() {
    let chain1 = Blockchain {
        blocks: vec![
            Block { index: 0, hash: String::from("hash0") },
            Block { index: 1, hash: String::from("hash1") },
        ],
    };
    let chain2 = Blockchain {
        blocks: vec![
            Block { index: 0, hash: String::from("hash0") },
            Block { index: 1, hash: String::from("hash1") },
            Block { index: 2, hash: String::from("hash2") },
        ],
    };
    let best_chain = select_longest_chain(vec![chain1, chain2]);
    println!("Best chain length: {}", best_chain.length());
}

Explanation:

When forks occur, the longest valid chain is selected. This ensures all nodes eventually agree on the canonical chain.

Exercises

Validate Block

Create a function that validates a block!

Medium

Starter Code:

RUST
struct Block {
    index: u64,
    previous_hash: String,
    hash: String,
}
fn main() {
    let prev = Block {
        index: 0,
        previous_hash: String::from("0"),
        hash: String::from("hash0"),
    };
    let block = Block {
        index: 1,
        previous_hash: String::from("hash0"),
        hash: String::from("hash1"),
    };
}