Proof of Work (PoW)
Understanding and implementing Proof of Work consensus mechanism.
Proof of Work (PoW)
Proof of Work is a consensus mechanism where miners compete to solve a cryptographic puzzle. The first to solve it gets to create the next block.
How PoW Works
- Mining: Miners try different nonce values
- Hashing: Calculate hash of (block data + nonce)
- Difficulty: Hash must be below target (start with zeros)
- Success: When hash meets difficulty, block is valid
- Reward: Miner receives block reward
Difficulty Target
The difficulty determines how many leading zeros the hash must have:
RUST// Hash must start with N zeros // Difficulty 1: hash starts with "0" // Difficulty 4: hash starts with "0000"
Mining Process
RUSTstruct Block { index: u64, data: String, previous_hash: String, nonce: u64, // Number used once hash: String, } fn mine_block(mut block: Block, difficulty: usize) -> Block { let prefix = "0".repeat(difficulty); loop { block.nonce += 1; block.hash = calculate_hash(&block); if block.hash.starts_with(&prefix) { return block; // Found valid hash! } } }
Bitcoin's PoW
- Uses SHA-256 double hashing
- Difficulty adjusts every 2016 blocks
- Target: ~10 minutes per block
- Energy intensive but secure
Advantages
- Security: Expensive to attack (51% attack)
- Decentralized: Anyone can mine
- Proven: Bitcoin has been secure for years
Disadvantages
- Energy consumption: Very high
- Centralization: Mining pools dominate
- Slow: 10 minutes per block (Bitcoin)
Alternative: Proof of Stake
Many newer blockchains use PoS instead of PoW for better efficiency.
Code Examples
Mining Process
Simple Proof of Work mining
struct Block {
index: u64,
data: String,
previous_hash: String,
nonce: u64,
hash: String,
}
fn calculate_hash(block: &Block) -> String {
let data = format!("{}{}{}{}",
block.index,
block.data,
block.previous_hash,
block.nonce);
format!("hash_{}", data)
}
fn mine_block(mut block: Block, difficulty: usize) -> Block {
let prefix = "0".repeat(difficulty);
println!("Mining block... (difficulty: {} zeros)", difficulty);
loop {
block.nonce += 1;
block.hash = calculate_hash(&block);
if block.hash.starts_with(&prefix) {
println!("Block mined! Nonce: {}, Hash: {}", block.nonce, block.hash);
return block;
}
if block.nonce > 10000 {
println!("Stopping after 10000 attempts");
break;
}
}
block
}
fn main() {
let block = Block {
index: 1,
data: String::from("Transaction data"),
previous_hash: String::from("prev_hash"),
nonce: 0,
hash: String::from(""),
};
let mined = mine_block(block, 2);
println!("Final hash: {}", mined.hash);
}Explanation:
Mining involves trying different nonce values until the hash meets the difficulty requirement. This requires computational work, hence 'Proof of Work'.
Difficulty Adjustment
Understanding difficulty in PoW
fn check_difficulty(hash: &str, difficulty: usize) -> bool {
let prefix = "0".repeat(difficulty);
hash.starts_with(&prefix)
}
fn main() {
let hashes = vec![
"0abc123",
"00def456",
"000ghi789",
"abc123",
];
for hash in hashes {
println!("Hash: {}", hash);
println!(" Difficulty 1: {}", check_difficulty(hash, 1));
println!(" Difficulty 2: {}", check_difficulty(hash, 2));
println!(" Difficulty 3: {}", check_difficulty(hash, 3));
println!();
}
}Explanation:
Difficulty determines how many leading zeros the hash must have. Higher difficulty = more zeros = harder to find = more secure but slower.
Exercises
Mine a Block
Implement a simple mining function that finds a nonce!
Starter Code:
struct Block {
data: String,
nonce: u64,
hash: String,
}
fn calculate_hash(data: &str, nonce: u64) -> String {
format!("hash_{}_{}", data, nonce)
}
fn main() {
let mut block = Block {
data: String::from("test"),
nonce: 0,
hash: String::from(""),
};
}