Create your first blockchain using Rust

ManojkumarManojkumar
8 min read

Rust is one of the most important programming languages in the modern programming world because it runs blazingly fast, prevents segfaults, and guarantees thread safety. It achieves memory safety without garbage collection and is known for its rich type system and ownership model, guaranteeing memory safety and thread safety.

Unit Testing Rust Smart Contracts on NEAR Blockchain | Udemy

As the blockchain space grows in complexity and scale, Rust’s advantages are likely to become even more pronounced. Especially, rust based core developers are huge in demand in the blockchain space. In this guide, we’ll build a very simple blockchain application from scratch using Rust.

Here, I provided a more detailed breakdown of the Rust code, offering deeper insights into each component and its role in the blockchain implementation.

This starts with the imports sha2 , chrono::Utc: and Transaction Struct

use sha2::{Digest, Sha256};
use chrono::Utc;
  • sha2: This crate provides the Sha256 hashing algorithm and the Digest trait, which allows for hashing data. It's used for creating cryptographic hashes, essential for blockchain integrity.

  • chrono::Utc: This is part of the chrono crate, which provides date and time functionality. Utc represents the Coordinated Universal Time, used for timestamps in the blockchain.

The next shows the Transaction struct, representing a transaction within the blockchain.

#[allow(dead_code)]
#[derive(Debug, Clone)]
  • #[allow(dead_code)]: This attribute suppresses warnings about the struct being defined but not used, which can be useful during development.

  • #[derive(Debug, Clone)]: This automatically implements the Debug and Clone traits for the Transaction struct.

  • Debug allows instances of Transaction to be formatted using the {:?} formatter, useful for debugging.

  • Clone allows for creating a copy of a Transaction instance, which is important for managing transactions in the blockchain.

Part 1: Detailed Blockchain Structures

  1. Transaction Structure:

     struct Transaction {
         sender: String,
         recipient: String,
         amount: f64,
     }
    
    • This structure represents a single transaction in the blockchain.

    • sender and recipient are String types, likely representing wallet addresses or user identifiers.

    • amount is a f64 (64-bit floating-point) type, representing the value transferred.

    • The #[allow(dead_code)] attribute suggests these fields might not be directly accessed in this example.

  2. Block Structure:

     struct Block {
         index: u64,
         timestamp: i64,
         transactions: Vec<Transaction>,
         proof: u64,
         previous_hash: String,
     }
    
    • index: A u64 (unsigned 64-bit integer) representing the block's position in the chain.

    • timestamp: An i64 (signed 64-bit integer) storing the block's creation time.

    • transactions: A vector of Transaction structures, allowing multiple transactions per block.

    • proof: A u64 representing the proof-of-work solution.

    • previous_hash: A String containing the hash of the previous block, crucial for maintaining the chain's integrity.

  3. Blockchain Structure:

     struct Blockchain {
         chain: Vec<Block>,
         current_transactions: Vec<Transaction>,
     }
    
    • chain: A vector of Block structures, representing the entire blockchain.

    • current_transactions: A vector of pending transactions not yet included in a block.

Part 2: Block Creation and Hashing (In-Depth)

  1. Block Creation:

     fn new(index: u64, transactions: Vec<Transaction>, proof: u64, previous_hash: String) -> Self {
         Block {
             index,
             timestamp: Utc::now().timestamp(),
             transactions,
             proof,
             previous_hash,
         }
     }
    
    • This function creates a new Block with the given parameters.

    • It uses Utc::now().timestamp() to set the current time as the block's timestamp.

  2. Hash Calculation:

     fn calculate_hash(&self) -> String {
         let mut hasher = Sha256::new();
         let data = format!("{}{}{:?}{}{}", self.index, self.timestamp, self.transactions, self.proof, self.previous_hash);
         hasher.update(data);
         format!("{:x}", hasher.finalize())
     }
    
    • This method calculates the SHA-256 hash of the block.

    • It concatenates all block data into a single string before hashing.

    • The result is formatted as a hexadecimal string.

Part 3: Blockchain Operations (Detailed)

  1. Blockchain Initialization:

     fn new() -> Self {
         let mut chain = Vec::new();
         chain.push(Block::new(0, Vec::new(), 100, String::from("0")));
         Blockchain {
             chain,
             current_transactions: Vec::new(),
         }
     }
    
    • Creates a new blockchain with a genesis block (index 0, no transactions, arbitrary proof, and "0" as previous hash).
  2. Adding New Transactions:

     fn new_transaction(&mut self, sender: String, recipient: String, amount: f64) -> usize {
         self.current_transactions.push(Transaction { sender, recipient, amount });
         self.last_block().index as usize + 1
     }
    
    • Adds a new transaction to the list of current transactions.

    • Returns the index of the block that will contain this transaction.

  3. Creating New Blocks:

     fn new_block(&mut self, proof: u64) -> Block {
         let previous_hash = self.last_block().calculate_hash();
         let block = Block::new(
             self.chain.len() as u64,
             std::mem::take(&mut self.current_transactions),
             proof,
             previous_hash,
         );
         self.chain.push(block.clone());
         block
     }
    
    • Creates a new block with the current transactions and given proof.

    • Uses std::mem::take to efficiently move current transactions into the new block.

    • Adds the new block to the chain and returns it.

Part 4: Proof of Work (Detailed Explanation)

  1. Proof of Work Algorithm:

     fn proof_of_work(&self, last_proof: u64) -> u64 {
         let mut proof = 0;
         while !self.valid_proof(last_proof, proof) {
             proof += 1;
         }
         proof
     }
    
    • Implements a simple proof-of-work algorithm.

    • Incrementally tries different proof values until a valid one is found.

  2. Proof Validation:

     fn valid_proof(&self, last_proof: u64, proof: u64) -> bool {
         let guess = format!("{}{}", last_proof, proof);
         let guess_hash = Sha256::digest(guess.as_bytes());
         let result = format!("{:x}", guess_hash);
         result.starts_with("0000")
     }
    
    • Validates a proof by checking if the hash of the last proof and current proof starts with "0000".

    • This difficulty can be adjusted by changing the required prefix.

Part 5: Simulating Blockchain Operations (Extended)

The main function demonstrates:

  1. Creating a new blockchain instance.

  2. Mining multiple blocks, each with:

    • Calculation of proof-of-work

    • Addition of new transactions

    • Creation and addition of the block to the chain

  3. Displaying each newly forged block, showing:

    • Block index

    • Timestamp

    • Transactions

    • Proof

    • Previous block's hash

fn main() {
    // Create a new blockchain
    let mut blockchain = Blockchain::new();

    // Mine the first block
    println!("Mining first block...");
    let last_proof = blockchain.last_block().proof;
    let proof = blockchain.proof_of_work(last_proof);
    blockchain.new_transaction(String::from("0"), String::from("Alice"), 1.0);
    let block = blockchain.new_block(proof);
    println!("New block forged: {:?}", block);

    // Mine the second block
    println!("Mining second block...");
    let last_proof = blockchain.last_block().proof;
    let proof = blockchain.proof_of_work(last_proof);
    blockchain.new_transaction(String::from("Alice"), String::from("Bob"), 0.5);
    blockchain.new_transaction(String::from("Alice"), String::from("Charlie"), 0.3);
    let block = blockchain.new_block(proof);
    println!("New block forged: {:?}", block);
    // Mine the third block
    println!("Mining third block...");
    let last_proof = blockchain.last_block().proof;
    let proof = blockchain.proof_of_work(last_proof);
    blockchain.new_transaction(String::from("Bob"), String::from("David"), 0.2);
    blockchain.new_transaction(String::from("Charlie"), String::from("Eve"), 0.1);
    let block = blockchain.new_block(proof);
    println!("New block forged: {:?}", block);

println!("Blockchain: {:?}", blockchain.chain);
}

This simulation provides a comprehensive view of how transactions are processed, blocks are created and added, and how the proof-of-work system maintains consensus in the blockchain.

Make sure to check the dependencies in cargo.toml

[dependencies]
sha2 = "0.10.6"
chrono = "0.4.23"

Here, I will share the final code here.

use sha2::{Digest, Sha256};
use chrono::Utc;

/// Represents a transaction in the blockchain
#[allow(dead_code)]
#[derive(Debug, Clone)]
struct Transaction {
    sender: String,
    recipient: String,
    amount: f64,
}

/// Represents a block in the blockchain
#[derive(Debug, Clone)]
struct Block {
    index: u64,
    timestamp: i64,
    transactions: Vec<Transaction>,
    proof: u64,
    previous_hash: String,
}

impl Block {
    /// Creates a new block
    fn new(index: u64, transactions: Vec<Transaction>, proof: u64, previous_hash: String) -> Self {
        Block {
            index,
            timestamp: Utc::now().timestamp(),
            transactions,
            proof,
            previous_hash,
        }
    }

    /// Calculates the hash of the block
    fn calculate_hash(&self) -> String {
        let mut hasher = Sha256::new();
        let data = format!("{}{}{:?}{}{}", self.index, self.timestamp, self.transactions, self.proof, self.previous_hash);
        hasher.update(data);
        format!("{:x}", hasher.finalize())
    }
}

/// Represents the blockchain
struct Blockchain {
    chain: Vec<Block>,
    current_transactions: Vec<Transaction>,
}

impl Blockchain {
    /// Creates a new blockchain with a genesis block
    fn new() -> Self {
        let mut chain = Vec::new();
        chain.push(Block::new(0, Vec::new(), 100, String::from("0")));
        Blockchain {
            chain,
            current_transactions: Vec::new(),
        }
    }

    /// Adds a new transaction to the list of current transactions
    fn new_transaction(&mut self, sender: String, recipient: String, amount: f64) -> usize {
        self.current_transactions.push(Transaction { sender, recipient, amount });
        self.last_block().index as usize + 1
    }

    /// Creates a new block and adds it to the chain
    fn new_block(&mut self, proof: u64) -> Block {
        let previous_hash = self.last_block().calculate_hash();
        let block = Block::new(
            self.chain.len() as u64,
            std::mem::take(&mut self.current_transactions),
            proof,
            previous_hash,
        );
        self.chain.push(block.clone());
        block
    }

    /// Returns a reference to the last block in the chain
    fn last_block(&self) -> &Block {
        self.chain.last().unwrap()
    }

    /// Implements a simple proof-of-work algorithm
    fn proof_of_work(&self, last_proof: u64) -> u64 {
        let mut proof = 0;
        while !self.valid_proof(last_proof, proof) {
            proof += 1;
        }
        proof
    }

    /// Validates the proof: does hash(last_proof, proof) contain 4 leading zeroes?
    fn valid_proof(&self, last_proof: u64, proof: u64) -> bool {
        let guess = format!("{}{}", last_proof, proof);
        let guess_hash = Sha256::digest(guess.as_bytes());
        let result = format!("{:x}", guess_hash);
        result.starts_with("0000")
    }
}

fn main() {
    // Create a new blockchain
    let mut blockchain = Blockchain::new();

    // Mine the first block
    println!("Mining first block...");
    let last_proof = blockchain.last_block().proof;
    let proof = blockchain.proof_of_work(last_proof);
    blockchain.new_transaction(String::from("0"), String::from("Alice"), 1.0);
    let block = blockchain.new_block(proof);
    println!("New block forged: {:?}", block);

    // Mine the second block
    println!("Mining second block...");
    let last_proof = blockchain.last_block().proof;
    let proof = blockchain.proof_of_work(last_proof);
    blockchain.new_transaction(String::from("Alice"), String::from("Bob"), 0.5);
    blockchain.new_transaction(String::from("Alice"), String::from("Charlie"), 0.3);
    let block = blockchain.new_block(proof);
    println!("New block forged: {:?}", block);
    // Mine the third block
    println!("Mining third block...");
    let last_proof = blockchain.last_block().proof;
    let proof = blockchain.proof_of_work(last_proof);
    blockchain.new_transaction(String::from("Bob"), String::from("David"), 0.2);
    blockchain.new_transaction(String::from("Charlie"), String::from("Eve"), 0.1);
    let block = blockchain.new_block(proof);
    println!("New block forged: {:?}", block);

    // Display the entire blockchain
    println!("Blockchain: {:?}", blockchain.chain);
}

The main features implemented are:

  1. Creating new transactions

  2. Mining new blocks (with a simple Proof of Work algorithm)

  3. Calculating block hashes

  4. Maintaining the blockchain

The Output :

The first mined block (index 1) has one transaction: 1.0 coins sent from "0" (likely representing newly minted coins) to "Alice". The second mined block (index 2) has two transactions: Alice sending 0.5 to Bob and 0.3 to Charlie.

Block Details: Each block contains:

Index: The position of the block in the chain (0, 1, 2)

Timestamp: Unix timestamp when the block was created transactions.

Proof: A number used in the proof-of-work system

Previous hash: Hash of the previous block, ensuring chain integrity

Final Blockchain State: The output shows the entire blockchain, including the genesis block (index 0) and the two mined blocks.

Conclusion:

This output demonstrates that your cryptocurrency implementation is working as intended. It's creating blocks, processing transactions, and maintaining the blockchain structure. The proof-of-work system also functions, as evidenced by the different proof values for each block.

This implementation is simplified and not secure enough for real-world use, but it demonstrates the core concepts of a blockchain-based cryptocurrency.

0
Subscribe to my newsletter

Read articles from Manojkumar directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Manojkumar
Manojkumar