Skip to content

Instantly share code, notes, and snippets.

@brunopgalvao
Created June 9, 2025 10:10
Show Gist options
  • Save brunopgalvao/f3c6d4330e1a4ed2e2edbc05d216923a to your computer and use it in GitHub Desktop.
Save brunopgalvao/f3c6d4330e1a4ed2e2edbc05d216923a to your computer and use it in GitHub Desktop.
ProofOfStakeBlockchain example
use sha2::{Sha256, Digest};
// A single block in our proof-of-stake blockchain
#[derive(Debug, Clone)]
struct Block {
index: u64, // Position in the chain (0, 1, 2, ...)
data: String, // The actual data/transactions
previous_hash: String, // Hash of the previous block
hash: String, // This block's unique identifier
validator: String, // Address of the validator who created this block
stake_proof: u64, // Proof that validator had required stake
}
impl Block {
// Create a new block (constructor) - validator creates without mining
fn new(index: u64, data: String, previous_hash: String, validator: String, stake_proof: u64) -> Block {
let mut block = Block {
index,
data,
previous_hash,
hash: String::new(),
validator,
stake_proof,
};
// Calculate the hash for this block - no mining required!
block.hash = block.calculate_hash();
block
}
}
use std::collections::HashMap;
// The proof-of-stake blockchain - manages both blocks and validators
struct ProofOfStakeBlockchain {
chain: Vec<Block>,
validators: HashMap<String, Validator>,
minimum_stake: u64, // Minimum stake required to become a validator
current_epoch: u64, // Current time period for validator selection
}
impl ProofOfStakeBlockchain {
// Create a new proof-of-stake blockchain with genesis block
fn new(minimum_stake: u64) -> ProofOfStakeBlockchain {
let mut blockchain = ProofOfStakeBlockchain {
chain: Vec::new(),
validators: HashMap::new(),
minimum_stake,
current_epoch: 0,
};
// Create the genesis block with a bootstrap validator
blockchain.create_genesis_block();
blockchain
}
fn create_genesis_block(&mut self) {
// Bootstrap validator for the genesis block
let genesis_validator = "genesis_validator".to_string();
self.validators.insert(genesis_validator.clone(), Validator {
address: genesis_validator.clone(),
stake: self.minimum_stake,
is_active: true,
});
let genesis = Block::new(
0,
"Genesis Block".to_string(),
"0".to_string(),
genesis_validator,
self.minimum_stake
);
self.chain.push(genesis);
}
// Register a new validator with their stake
fn register_validator(&mut self, address: String, stake_amount: u64) -> Result<(), String> {
if stake_amount < self.minimum_stake {
return Err(format!("Minimum stake required: {}", self.minimum_stake));
}
let validator = Validator {
address: address.clone(),
stake: stake_amount,
is_active: true,
};
self.validators.insert(address, validator);
Ok(())
}
// Create and add a new block using proof of stake consensus
fn create_block(&mut self, data: String) -> Result<(), String> {
// Select validator for this block based on their stake
let selected_validator = select_validator(&self.validators)
.ok_or("No eligible validators available")?;
let previous_block = self.chain.last().unwrap();
let validator_stake = self.validators[&selected_validator].stake;
let new_block = Block::new(
previous_block.index + 1,
data,
previous_block.hash.clone(),
selected_validator.clone(),
validator_stake
);
// Verify the block meets proof of stake requirements
if new_block.validate_stake_proof(&self.validators) {
self.chain.push(new_block);
println!("Block created by validator: {}", selected_validator);
Ok(())
} else {
Err("Invalid stake proof for selected validator".to_string())
}
}
// Verify the entire blockchain's integrity
fn is_chain_valid(&self) -> bool {
for i in 1..self.chain.len() {
let current_block = &self.chain[i];
let previous_block = &self.chain[i - 1];
// Check if the current block's hash is valid
if current_block.hash != current_block.calculate_hash() {
return false;
}
// Check if the current block properly references the previous block
if current_block.previous_hash != previous_block.hash {
return false;
}
// Check if the validator had sufficient stake when creating the block
if !current_block.validate_stake_proof(&self.validators) {
return false;
}
}
true
}
}
use std::collections::HashMap;
use rand::Rng;
// Validator information in our proof-of-stake system
#[derive(Debug, Clone)]
struct Validator {
address: String, // Unique identifier for this validator
stake: u64, // Amount of cryptocurrency they've staked
is_active: bool, // Whether they're eligible to validate
}
impl Block {
// Calculate the cryptographic hash for this block
fn calculate_hash(&self) -> String {
let input = format!(
"{}{}{}{}{}",
self.index,
&self.data,
&self.previous_hash,
&self.validator,
self.stake_proof
);
// Use SHA-256 to create a unique hash - no mining loops needed
let mut hasher = Sha256::new();
hasher.update(input.as_bytes());
format!("{:x}", hasher.finalize())
}
// Validate that the block creator had sufficient stake
fn validate_stake_proof(&self, validators: &HashMap<String, Validator>) -> bool {
if let Some(validator) = validators.get(&self.validator) {
// Check if validator is active and has minimum required stake
validator.is_active && validator.stake >= self.stake_proof
} else {
false // Validator not found in the registry
}
}
}
// Function to select the next validator (simplified selection algorithm)
fn select_validator(validators: &HashMap<String, Validator>) -> Option<String> {
let active_validators: Vec<_> = validators
.iter()
.filter(|(_, v)| v.is_active && v.stake > 0)
.collect();
if active_validators.is_empty() {
return None;
}
// Simple weighted random selection based on stake
let total_stake: u64 = active_validators.iter().map(|(_, v)| v.stake).sum();
let mut rng = rand::thread_rng();
let random_point = rng.gen_range(0..total_stake);
let mut cumulative_stake = 0;
for (address, validator) in active_validators {
cumulative_stake += validator.stake;
if random_point < cumulative_stake {
return Some(address.clone());
}
}
None // This should never happen if logic is correct
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment