- Rust and Cargo installed on your system
- Familiarity with the Bitcoin Whitepaper to understand the core concepts
- Clone the repository:
git clone https://github.com/ChengHua926/rBTC.git
cd rBTC
- Build the project:
cargo build --release
Open two terminal windows to simulate a small blockchain network:
cargo run -- --name alice open
This command will:
- Create a new blockchain network
- Mine the genesis block
- Generate a ticket for other nodes to join
- Print your wallet address
cargo run -- --name bob join <ticket>
Replace <ticket>
with the ticket string displayed in Terminal 1.
Once running, you can interact with the blockchain using these commands:
Command | Description |
---|---|
tx <address> <amount> |
Create and broadcast a transaction |
mine |
Mine a new block with transactions from the mempool |
chain |
View the current blockchain |
mempool |
View pending transactions |
status |
View node status and your wallet's balance |
wallet |
View your wallet information |
help |
Show all available commands |
The rBTC implementation includes the following core components:
The UTXO model is used to track coin ownership instead of account balances. Each transaction consumes previous transaction outputs (inputs) and creates new outputs.
// Transaction Output represents coins that can be spent
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TransactionOutput {
pub value: f64, // Amount of coins
pub address: String, // Recipient address (public key hash)
}
// Transaction Input references a previous output that is being spent
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TransactionInput {
pub tx_id: Hash, // Transaction ID containing the output
pub output_index: usize, // Index of the output in the referenced transaction
pub address: String, // Address/signature that can spend this output
}
A complete transaction looks like:
// Example transaction:
{
"id": "a7d91760c...", // SHA256 hash of the transaction
"inputs": [
{
"tx_id": "3f4a2bce5...", // Previous transaction ID
"output_index": 0, // Output index in that transaction
"address": "P2P-7a1be4f9c831...c52:e5d71a8c..." // Address + signature
}
],
"outputs": [
{
"value": 5.0, // Amount to send
"address": "P2P-4c8de7b3a6..." // Recipient address
},
{
"value": 3.2, // Change returned to sender
"address": "P2P-7a1be4f9c831..." // Sender address
}
],
"timestamp": 1684389245
}
The UTXO model enables parallel transaction processing and provides better privacy than account-based models.
Merkle trees store transaction hashes in a binary tree structure. They reduce block size by requiring only the block header to be stored while the full transaction data can be pruned.
pub struct MerkleTree {
pub root: Hash, // Root hash for block header
pub leaf_hashes: Vec<Hash>, // Transaction hashes
}
Merkle trees allow for efficient verification of transactions using merkle proofs, which require only log(n) hashes instead of the full transaction list. This enables lightweight clients (SPV nodes) to verify transactions without downloading the entire blockchain.
// Calculating a merkle root
fn calculate_root_from_hashes(hashes: &[Hash]) -> Hash {
// For each pair of transaction hashes:
let mut hasher = Sha256::new();
hasher.update(&left.0); // First transaction hash
hasher.update(&right.0); // Second transaction hash
let result = hasher.finalize();
// Example: SHA256(tx1_hash + tx2_hash) = 6b86b273ff...
}
Wallets use Ed25519 cryptographic key pairs for signing transactions:
pub struct Wallet {
name: String,
#[serde(with = "keypair_serde")]
keypair: Keypair, // Ed25519 keys
}
Address generation via public key hashing:
pub fn get_address(&self) -> String {
let mut hasher = Sha256::new();
hasher.update(self.keypair.public.as_bytes());
let hash = hasher.finalize();
format!("P2P-{}", hex::encode(&hash[0..16]))
// Example: P2P-7a1be4f9c831...
}
Transaction signing:
pub fn sign_transaction(&self, transaction: &mut Transaction) {
// For each input owned by this wallet
for input in &mut transaction.inputs {
if input.address == wallet_address {
// Sign transaction ID
let signature = self.keypair.sign(tx_id_bytes);
// Store address:signature format
input.address = format!("{}:{}", wallet_address, hex::encode(signature.to_bytes()));
}
}
}
Mining implements Proof-of-Work consensus with configurable difficulty:
pub fn mine(&mut self, difficulty: u8) -> bool {
let target = vec![0u8; difficulty as usize];
println!("Mining block with {} transactions", self.transactions.len());
let start_time = SystemTime::now();
while self.header.nonce < 100000000 { // Limit to prevent infinite loops
// Calculate a new hash with the current nonce
self.header.hash = self.calculate_hash();
// Check if hash meets difficulty requirement (starts with N zeros)
if self.header.hash.0.starts_with(&target) {
let duration = SystemTime::now().duration_since(start_time).unwrap();
println!("Block mined: {} in {}s",
self.header.hash.to_short_hex(),
duration.as_secs_f64().round());
return true;
}
self.header.nonce += 1;
}
false
}
A block contains:
pub struct Block {
pub header: BlockHeader,
pub transactions: Vec<Transaction>,
}
pub struct BlockHeader {
pub hash: Hash,
pub previous_hash: Hash,
pub merkle_root: Hash,
pub timestamp: u64,
pub nonce: u64,
pub difficulty: u8,
pub height: u64,
}
The network layer uses iroh-gossip for decentralized peer discovery and communication:
// Message types for network communication
pub enum BlockchainMessage {
// Broadcast a new transaction to mempool
NewTransaction {
from: NodeId,
transaction: Transaction,
},
// Broadcast a newly mined block
NewBlock {
from: NodeId,
block: Block,
},
// Request the current blockchain state
RequestChain {
from: NodeId,
}
// Other message types...
}
The wallet module is responsible for:
- Key Management: Using Ed25519 cryptographic keys for secure signing
- Address Generation: Creating unique addresses from public keys
- Transaction Signing: Cryptographically signing transactions
- Wallet Persistence: Saving/loading wallets to/from disk
The core blockchain implementation contains:
- Block Structure: Defines the fundamental blockchain data structures
- UTXO Set: Tracks unspent transaction outputs for balance calculation
- Transaction Validation: Ensures transaction validity using cryptographic proofs
- Mining Algorithm: Implements Proof-of-Work consensus
- Merkle Trees: Enables efficient transaction verification
- Mempool: Manages pending transactions before they're included in blocks
The networking layer handles:
- Peer Discovery: Finding and connecting to other nodes
- Message Propagation: Broadcasting transactions and blocks
- Gossip Protocol: Ensuring network-wide consensus
- Chain Synchronization: Getting new nodes up to date
- Message Serialization: Converting blockchain data to network packets
The interface provides:
- Command Parsing: Interpreting user commands
- Node Control: Starting/stopping the node
- Transaction Creation: User-friendly transaction creation
- Blockchain Inspection: Viewing the current state
- Mining Initiation: Starting the mining process
- Network Management: Joining/creating a network