Proof of Stake (PoS)

Understanding Proof of Stake consensus mechanism used by modern blockchains.

Advanced⏱️ 50 min📚 Prerequisites: 1

Proof of Stake (PoS)

Proof of Stake is a consensus mechanism where validators are chosen based on the amount of cryptocurrency they "stake" (lock up) rather than computational work.

How PoS Works

  1. Staking: Validators lock up coins as stake
  2. Selection: Validator chosen based on stake amount and randomness
  3. Validation: Selected validator creates and validates block
  4. Rewards: Validator receives transaction fees
  5. Slashing: Malicious validators lose stake

Advantages over PoW

  • Energy efficient: No mining required
  • Faster: Blocks can be created quickly
  • Scalable: Higher transaction throughput
  • Lower barriers: No expensive mining hardware

Staking Structure

RUST
struct Validator {
    address: String,
    stake: u64,  // Amount staked
    is_active: bool,
}

struct StakingPool {
    validators: Vec<Validator>,
    total_stake: u64,
}

Validator Selection

Validators are typically selected based on:

  • Stake amount: More stake = higher chance
  • Randomness: Prevents predictability
  • Age: Some systems consider staking duration

Slashing

Validators who misbehave lose their stake:

  • Double signing: Signing two conflicting blocks
  • Downtime: Being offline too often
  • Malicious behavior: Attempting attacks

PoS Variants

  • Delegated PoS (DPoS): Stakeholders vote for delegates
  • Liquid PoS: Staked tokens remain liquid
  • Hybrid PoS: Combines PoS with other mechanisms

Ethereum 2.0

Ethereum moved from PoW to PoS:

  • 32 ETH minimum stake
  • Validators earn rewards
  • More energy efficient
  • Faster finality

Code Examples

Validator Structure

Creating validators and staking pool

RUST
struct Validator {
    address: String,
    stake: u64,
    is_active: bool,
}
struct StakingPool {
    validators: Vec<Validator>,
    total_stake: u64,
}
impl StakingPool {
    fn new() -> Self {
        StakingPool {
            validators: Vec::new(),
            total_stake: 0,
        }
    }
    fn add_validator(&mut self, validator: Validator) {
        self.total_stake += validator.stake;
        self.validators.push(validator);
    }
    fn select_validator(&self) -> Option<&Validator> {
        self.validators.iter()
            .filter(|v| v.is_active)
            .max_by_key(|v| v.stake)
    }
}
fn main() {
    let mut pool = StakingPool::new();
    pool.add_validator(Validator {
        address: String::from("0xAlice"),
        stake: 1000,
        is_active: true,
    });
    pool.add_validator(Validator {
        address: String::from("0xBob"),
        stake: 2000,
        is_active: true,
    });
    if let Some(validator) = pool.select_validator() {
        println!("Selected validator: {} (stake: {})",
                validator.address, validator.stake);
    }
}

Explanation:

In PoS, validators are selected based on their stake. Higher stake typically means higher chance of being selected, but randomness is also important.

Staking Mechanism

Staking and unstaking tokens

RUST
struct Validator {
    address: String,
    staked_amount: u64,
    total_rewards: u64,
}
impl Validator {
    fn stake(&mut self, amount: u64) {
        self.staked_amount += amount;
        println!("Staked {} tokens. Total stake: {}", amount, self.staked_amount);
    }
    fn unstake(&mut self, amount: u64) -> Result<(), String> {
        if amount > self.staked_amount {
            return Err(String::from("Cannot unstake more than staked"));
        }
        self.staked_amount -= amount;
        println!("Unstaked {} tokens. Remaining stake: {}", amount, self.staked_amount);
        Ok(())
    }
    fn add_reward(&mut self, reward: u64) {
        self.total_rewards += reward;
    }
}
fn main() {
    let mut validator = Validator {
        address: String::from("0xValidator1"),
        staked_amount: 0,
        total_rewards: 0,
    };
    validator.stake(1000);
    validator.stake(500);
    validator.add_reward(10);
    validator.unstake(300).unwrap();
    println!("Final stake: {}, Total rewards: {}",
            validator.staked_amount, validator.total_rewards);
}

Explanation:

Validators stake tokens to participate. They earn rewards for validating blocks and can unstake (after a waiting period in real systems).

Exercises

Create Validator

Create a validator and implement staking!

Medium

Starter Code:

RUST
struct Validator {
    address: String,
    stake: u64,
}
fn main() {
    let mut validator = Validator {
        address: String::from("0xMyValidator"),
        stake: 0,
    };
}