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
- Agreement: All honest nodes agree on valid blocks
- Validity: Only valid blocks are accepted
- Termination: Consensus eventually reaches agreement
- Integrity: Blocks cannot be tampered with
Block Validation
Before accepting a block, nodes must validate:
RUSTfn 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:
RUSTfn select_best_chain(chains: Vec<Blockchain>) -> Blockchain { chains.into_iter() .max_by_key(|chain| chain.blocks.len()) .unwrap() }
Fork Resolution
When forks occur:
- Temporary fork: Two blocks at same height
- Wait: Wait for next block
- Choose: Longest chain becomes canonical
- 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
RUSTstruct 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!
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"),
};
}