Create your first blockchain using Rust
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.
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 theSha256
hashing algorithm and theDigest
trait, which allows for hashing data. It's used for creating cryptographic hashes, essential for blockchain integrity.chrono::Utc:
This is part of thechrono
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 theDebug
andClone
traits for the Transaction struct.Debug
allows instances ofTransaction
to be formatted using the{:?}
formatter, useful for debugging.Clone
allows for creating a copy of aTransaction
instance, which is important for managing transactions in the blockchain.
Part 1: Detailed Blockchain Structures
Transaction Structure:
struct Transaction { sender: String, recipient: String, amount: f64, }
This structure represents a single transaction in the blockchain.
sender
andrecipient
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.
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.
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)
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.
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)
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).
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.
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)
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.
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:
Creating a new blockchain instance.
Mining multiple blocks, each with:
Calculation of proof-of-work
Addition of new transactions
Creation and addition of the block to the chain
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:
Creating new transactions
Mining new blocks (with a simple Proof of Work algorithm)
Calculating block hashes
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.
Subscribe to my newsletter
Read articles from Manojkumar directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by