Rust Elective 02: ブロックチェーン - 分散台帳の基礎

課題説明

概要

ブロックチェーンは、暗号技術を用いた分散型の改ざん防止台帳システムです。本課題では、Rustを使って基本的なブロックチェーンを実装し、暗号学的ハッシュ、Proof of Work、P2Pネットワークの基礎を学びます。

背景と動機

ブロックチェーンの重要性:

  • 信頼の分散化: 中央機関なしでデータの整合性を保証
  • 改ざん耐性: 暗号学的ハッシュチェーンによる保護
  • 透明性: すべての取引履歴が公開され検証可能
  • 分散合意: ネットワーク参加者間での合意形成

Rustの優位性:

  • メモリ安全性(セキュリティが重要)
  • 高性能な暗号演算
  • 並行処理に強い(P2Pネットワーク)
  • 型システムによるバグ防止
  • 課題要件

    以下の機能を持つブロックチェーンシステムを実装してください:

  • ブロック構造:
- インデックス、タイムスタンプ、データ、前のハッシュ - ナンス(Proof of Work用) - SHA-256ハッシュの計算 - ブロックの検証

  • チェーン管理:
- ジェネシスブロックの生成 - 新しいブロックの追加 - チェーンの整合性検証 - 最長チェーンの選択

  • Proof of Work:
- マイニングアルゴリズム - 難易度調整 - ナンスの探索 - ハッシュレートの計算

  • トランザクション:
- シンプルな送金トランザクション - トランザクションプール - マークルツリー(オプション) - UTXO(未使用トランザクション出力)モデル

  • P2Pネットワーク基礎:
- ノード間通信(TCP) - ブロックの同期 - トランザクションの伝播 - ピアディスカバリー

制約条件

  • SHA-256ハッシュを使用すること
  • Proof of Workは調整可能な難易度を持つこと
  • チェーンの整合性を常に検証すること
  • エラーハンドリングを適切に実装すること
  • CLIインターフェースを提供すること

---

想定解答

プロジェクト構造

rust_blockchain/
├── Cargo.toml
├── src/
│   ├── main.rs
│   ├── lib.rs
│   ├── block.rs
│   ├── blockchain.rs
│   ├── transaction.rs
│   ├── pow.rs
│   ├── network.rs
│   └── cli.rs
├── tests/
│   ├── block_tests.rs
│   ├── blockchain_tests.rs
│   └── integration_tests.rs
└── README.md

Cargo.toml

[package]
name = "rust_blockchain"
version = "0.1.0"
edition = "2021"

[dependencies]
sha2 = "0.10"
hex = "0.4"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
chrono = "0.4"
clap = { version = "4.0", features = ["derive"] }
tokio = { version = "1.0", features = ["full"] }
bincode = "1.3"

[dev-dependencies]
criterion = "0.5"

[[bench]]
name = "mining_benchmark"
harness = false

src/block.rs

use chrono::Utc;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};

use crate::transaction::Transaction;

/// ブロック構造体
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Block {
    pub index: u64,
    pub timestamp: i64,
    pub transactions: Vec<Transaction>,
    pub previous_hash: String,
    pub hash: String,
    pub nonce: u64,
    pub difficulty: usize,
}

impl Block {
    /// 新しいブロックを作成
    pub fn new(
        index: u64,
        transactions: Vec<Transaction>,
        previous_hash: String,
        difficulty: usize,
    ) -> Self {
        let timestamp = Utc::now().timestamp();

        let mut block = Block {
            index,
            timestamp,
            transactions,
            previous_hash,
            hash: String::new(),
            nonce: 0,
            difficulty,
        };

        // 初期ハッシュを計算(マイニング前)
        block.hash = block.calculate_hash();
        block
    }

    /// ジェネシスブロック(最初のブロック)を作成
    pub fn genesis() -> Self {
        Block::new(
            0,
            vec![Transaction::coinbase("genesis".to_string(), 50.0)],
            "0".repeat(64),
            4,
        )
    }

    /// ブロックのハッシュを計算
    pub fn calculate_hash(&self) -> String {
        let block_data = format!(
            "{}{}{}{}{}{}",
            self.index,
            self.timestamp,
            serde_json::to_string(&self.transactions).unwrap(),
            self.previous_hash,
            self.nonce,
            self.difficulty
        );

        let mut hasher = Sha256::new();
        hasher.update(block_data.as_bytes());
        format!("{:x}", hasher.finalize())
    }

    /// ブロックをマイニング(Proof of Work)
    pub fn mine(&mut self) -> u64 {
        let target = "0".repeat(self.difficulty);
        let start_time = Utc::now().timestamp_millis();

        println!(
            "Mining block {} with difficulty {}...",
            self.index, self.difficulty
        );

        loop {
            self.hash = self.calculate_hash();

            if self.hash.starts_with(&target) {
                let end_time = Utc::now().timestamp_millis();
                let duration = end_time - start_time;

                println!(
                    "Block mined! Hash: {}, Nonce: {}, Time: {}ms",
                    self.hash, self.nonce, duration
                );

                return self.nonce;
            }

            self.nonce += 1;

            // デバッグ用:10万回ごとに進捗表示
            if self.nonce % 100_000 == 0 {
                println!("Tried {} nonces...", self.nonce);
            }
        }
    }

    /// ブロックが有効か検証
    pub fn is_valid(&self) -> bool {
        // ハッシュが正しく計算されているか
        if self.hash != self.calculate_hash() {
            println!("Invalid hash for block {}", self.index);
            return false;
        }

        // Proof of Workの条件を満たしているか
        let target = "0".repeat(self.difficulty);
        if !self.hash.starts_with(&target) {
            println!("Invalid proof of work for block {}", self.index);
            return false;
        }

        // すべてのトランザクションが有効か
        for tx in &self.transactions {
            if !tx.is_valid() {
                println!("Invalid transaction in block {}", self.index);
                return false;
            }
        }

        true
    }

    /// ブロック内のトランザクション数を返す
    pub fn transaction_count(&self) -> usize {
        self.transactions.len()
    }

    /// ブロックのサイズ(バイト)を推定
    pub fn size(&self) -> usize {
        bincode::serialize(self).unwrap().len()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_genesis_block() {
        let genesis = Block::genesis();
        assert_eq!(genesis.index, 0);
        assert_eq!(genesis.previous_hash, "0".repeat(64));
        assert!(genesis.is_valid());
    }

    #[test]
    fn test_hash_calculation() {
        let block = Block::genesis();
        let hash1 = block.calculate_hash();
        let hash2 = block.calculate_hash();

        // 同じブロックは同じハッシュを生成
        assert_eq!(hash1, hash2);

        // ハッシュは64文字の16進数
        assert_eq!(hash1.len(), 64);
    }

    #[test]
    fn test_mining() {
        let mut block = Block::new(
            1,
            vec![Transaction::coinbase("miner".to_string(), 50.0)],
            "previous".to_string(),
            2, // 低い難易度でテスト
        );

        block.mine();

        // マイニング後、ハッシュは "00" で始まるはず
        assert!(block.hash.starts_with("00"));
        assert!(block.is_valid());
    }
}

src/transaction.rs

use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};

/// トランザクション構造体
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Transaction {
    pub from: String,
    pub to: String,
    pub amount: f64,
    pub timestamp: i64,
    pub signature: Option<String>,
}

impl Transaction {
    /// 新しいトランザクションを作成
    pub fn new(from: String, to: String, amount: f64) -> Self {
        Transaction {
            from,
            to,
            amount,
            timestamp: chrono::Utc::now().timestamp(),
            signature: None,
        }
    }

    /// コインベーストランザクション(マイニング報酬)
    pub fn coinbase(to: String, amount: f64) -> Self {
        Transaction {
            from: "COINBASE".to_string(),
            to,
            amount,
            timestamp: chrono::Utc::now().timestamp(),
            signature: None,
        }
    }

    /// トランザクションのハッシュを計算
    pub fn hash(&self) -> String {
        let data = format!(
            "{}{}{}{}",
            self.from, self.to, self.amount, self.timestamp
        );

        let mut hasher = Sha256::new();
        hasher.update(data.as_bytes());
        format!("{:x}", hasher.finalize())
    }

    /// トランザクションが有効か検証
    pub fn is_valid(&self) -> bool {
        // 金額が正か
        if self.amount <= 0.0 {
            return false;
        }

        // コインベーストランザクションは常に有効
        if self.from == "COINBASE" {
            return true;
        }

        // 送信者と受信者が異なるか
        if self.from == self.to {
            return false;
        }

        // 実際のシステムでは署名の検証も行う
        true
    }
}

/// トランザクションプール
#[derive(Debug, Clone)]
pub struct TransactionPool {
    transactions: Vec<Transaction>,
}

impl TransactionPool {
    pub fn new() -> Self {
        TransactionPool {
            transactions: Vec::new(),
        }
    }

    /// トランザクションを追加
    pub fn add_transaction(&mut self, tx: Transaction) -> Result<(), String> {
        if !tx.is_valid() {
            return Err("Invalid transaction".to_string());
        }

        self.transactions.push(tx);
        Ok(())
    }

    /// 保留中のトランザクションを取得
    pub fn get_pending_transactions(&self, limit: usize) -> Vec<Transaction> {
        self.transactions.iter().take(limit).cloned().collect()
    }

    /// トランザクションをクリア
    pub fn clear(&mut self) {
        self.transactions.clear();
    }

    /// プール内のトランザクション数
    pub fn len(&self) -> usize {
        self.transactions.len()
    }

    pub fn is_empty(&self) -> bool {
        self.transactions.is_empty()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_transaction_creation() {
        let tx = Transaction::new("Alice".to_string(), "Bob".to_string(), 10.0);
        assert_eq!(tx.from, "Alice");
        assert_eq!(tx.to, "Bob");
        assert_eq!(tx.amount, 10.0);
    }

    #[test]
    fn test_coinbase_transaction() {
        let tx = Transaction::coinbase("Miner".to_string(), 50.0);
        assert_eq!(tx.from, "COINBASE");
        assert!(tx.is_valid());
    }

    #[test]
    fn test_invalid_transaction() {
        let tx = Transaction::new("Alice".to_string(), "Bob".to_string(), -10.0);
        assert!(!tx.is_valid());
    }

    #[test]
    fn test_transaction_pool() {
        let mut pool = TransactionPool::new();

        let tx1 = Transaction::new("Alice".to_string(), "Bob".to_string(), 10.0);
        let tx2 = Transaction::new("Bob".to_string(), "Charlie".to_string(), 5.0);

        assert!(pool.add_transaction(tx1).is_ok());
        assert!(pool.add_transaction(tx2).is_ok());
        assert_eq!(pool.len(), 2);

        let pending = pool.get_pending_transactions(1);
        assert_eq!(pending.len(), 1);
    }
}

src/blockchain.rs

use crate::block::Block;
use crate::transaction::{Transaction, TransactionPool};
use serde::{Deserialize, Serialize};

/// ブロックチェーン構造体
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Blockchain {
    pub chain: Vec<Block>,
    pub difficulty: usize,
    pub mining_reward: f64,
    #[serde(skip)]
    pub transaction_pool: TransactionPool,
}

impl Blockchain {
    /// 新しいブロックチェーンを作成
    pub fn new(difficulty: usize, mining_reward: f64) -> Self {
        let mut blockchain = Blockchain {
            chain: Vec::new(),
            difficulty,
            mining_reward,
            transaction_pool: TransactionPool::new(),
        };

        // ジェネシスブロックを作成
        let genesis = Block::genesis();
        blockchain.chain.push(genesis);

        blockchain
    }

    /// 最後のブロックを取得
    pub fn last_block(&self) -> &Block {
        self.chain.last().unwrap()
    }

    /// 新しいブロックを追加
    pub fn add_block(&mut self, mut block: Block) -> Result<(), String> {
        // ブロックのインデックスと前のハッシュを検証
        let last_block = self.last_block();

        if block.index != last_block.index + 1 {
            return Err("Invalid block index".to_string());
        }

        if block.previous_hash != last_block.hash {
            return Err("Invalid previous hash".to_string());
        }

        // ブロックをマイニング
        block.mine();

        // ブロックが有効か検証
        if !block.is_valid() {
            return Err("Invalid block".to_string());
        }

        self.chain.push(block);
        Ok(())
    }

    /// 保留中のトランザクションをマイニング
    pub fn mine_pending_transactions(&mut self, mining_reward_address: String) -> Result<(), String> {
        let mut transactions = self.transaction_pool.get_pending_transactions(10);

        // マイニング報酬を追加
        transactions.push(Transaction::coinbase(
            mining_reward_address,
            self.mining_reward,
        ));

        // 新しいブロックを作成
        let last_block = self.last_block();
        let new_block = Block::new(
            last_block.index + 1,
            transactions,
            last_block.hash.clone(),
            self.difficulty,
        );

        // ブロックを追加
        self.add_block(new_block)?;

        // トランザクションプールをクリア
        self.transaction_pool.clear();

        Ok(())
    }

    /// トランザクションを追加
    pub fn add_transaction(&mut self, transaction: Transaction) -> Result<(), String> {
        self.transaction_pool.add_transaction(transaction)
    }

    /// アドレスの残高を取得
    pub fn get_balance(&self, address: &str) -> f64 {
        let mut balance = 0.0;

        for block in &self.chain {
            for tx in &block.transactions {
                if tx.to == address {
                    balance += tx.amount;
                }
                if tx.from == address {
                    balance -= tx.amount;
                }
            }
        }

        balance
    }

    /// チェーン全体の整合性を検証
    pub fn is_valid(&self) -> bool {
        // ジェネシスブロックを検証
        if self.chain.is_empty() {
            return false;
        }

        // 各ブロックを検証
        for i in 1..self.chain.len() {
            let current_block = &self.chain[i];
            let previous_block = &self.chain[i - 1];

            // ブロック自体が有効か
            if !current_block.is_valid() {
                println!("Block {} is invalid", i);
                return false;
            }

            // 前のブロックへの参照が正しいか
            if current_block.previous_hash != previous_block.hash {
                println!("Block {} has invalid previous hash", i);
                return false;
            }

            // インデックスが連続しているか
            if current_block.index != previous_block.index + 1 {
                println!("Block {} has invalid index", i);
                return false;
            }
        }

        true
    }

    /// チェーンの統計情報を取得
    pub fn get_stats(&self) -> BlockchainStats {
        let total_blocks = self.chain.len();
        let total_transactions: usize = self.chain.iter().map(|b| b.transaction_count()).sum();
        let total_size: usize = self.chain.iter().map(|b| b.size()).sum();

        BlockchainStats {
            total_blocks,
            total_transactions,
            total_size,
            difficulty: self.difficulty,
        }
    }

    /// チェーンをJSON形式で保存
    pub fn save_to_file(&self, path: &str) -> std::io::Result<()> {
        let json = serde_json::to_string_pretty(self)?;
        std::fs::write(path, json)?;
        Ok(())
    }

    /// JSON形式からチェーンを読み込み
    pub fn load_from_file(path: &str) -> std::io::Result<Self> {
        let json = std::fs::read_to_string(path)?;
        let blockchain: Blockchain = serde_json::from_str(&json)?;
        Ok(blockchain)
    }
}

#[derive(Debug)]
pub struct BlockchainStats {
    pub total_blocks: usize,
    pub total_transactions: usize,
    pub total_size: usize,
    pub difficulty: usize,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_blockchain_creation() {
        let blockchain = Blockchain::new(2, 50.0);
        assert_eq!(blockchain.chain.len(), 1);
        assert!(blockchain.is_valid());
    }

    #[test]
    fn test_add_transaction_and_mine() {
        let mut blockchain = Blockchain::new(2, 50.0);

        let tx = Transaction::new("Alice".to_string(), "Bob".to_string(), 10.0);
        assert!(blockchain.add_transaction(tx).is_ok());

        assert!(blockchain.mine_pending_transactions("Miner".to_string()).is_ok());
        assert_eq!(blockchain.chain.len(), 2);
        assert!(blockchain.is_valid());
    }

    #[test]
    fn test_balance_calculation() {
        let mut blockchain = Blockchain::new(2, 50.0);

        blockchain
            .add_transaction(Transaction::new(
                "Alice".to_string(),
                "Bob".to_string(),
                10.0,
            ))
            .unwrap();

        blockchain
            .mine_pending_transactions("Miner".to_string())
            .unwrap();

        // Miner should have mining reward
        assert_eq!(blockchain.get_balance("Miner"), 50.0);

        // Bob should have 10
        assert_eq!(blockchain.get_balance("Bob"), 10.0);

        // Alice should have -10 (実際のシステムでは残高チェックが必要)
        assert_eq!(blockchain.get_balance("Alice"), -10.0);
    }

    #[test]
    fn test_chain_validation() {
        let mut blockchain = Blockchain::new(2, 50.0);

        // 正常なブロックを追加
        blockchain
            .add_transaction(Transaction::new(
                "Alice".to_string(),
                "Bob".to_string(),
                5.0,
            ))
            .unwrap();

        blockchain
            .mine_pending_transactions("Miner".to_string())
            .unwrap();

        assert!(blockchain.is_valid());

        // ブロックを改ざん
        blockchain.chain[1].transactions[0].amount = 100.0;
        blockchain.chain[1].hash = blockchain.chain[1].calculate_hash();

        // 改ざんが検出されるはず
        assert!(!blockchain.is_valid());
    }
}

src/main.rs

use clap::{Parser, Subcommand};
use rust_blockchain::{Blockchain, Transaction};
use std::path::Path;

#[derive(Parser)]
#[command(name = "rust_blockchain")]
#[command(about = "A simple blockchain implementation in Rust", long_about = None)]
struct Cli {
    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand)]
enum Commands {
    /// 新しいブロックチェーンを作成
    Init {
        #[arg(short, long, default_value_t = 4)]
        difficulty: usize,

        #[arg(short, long, default_value_t = 50.0)]
        reward: f64,
    },

    /// トランザクションを追加
    Transaction {
        #[arg(short, long)]
        from: String,

        #[arg(short, long)]
        to: String,

        #[arg(short, long)]
        amount: f64,
    },

    /// 保留中のトランザクションをマイニング
    Mine {
        #[arg(short, long)]
        address: String,
    },

    /// アドレスの残高を表示
    Balance {
        #[arg(short, long)]
        address: String,
    },

    /// チェーンの統計情報を表示
    Stats,

    /// チェーン全体を表示
    Print,

    /// チェーンの整合性を検証
    Validate,
}

const BLOCKCHAIN_FILE: &str = "blockchain.json";

fn main() {
    let cli = Cli::parse();

    match cli.command {
        Commands::Init { difficulty, reward } => {
            let blockchain = Blockchain::new(difficulty, reward);
            blockchain.save_to_file(BLOCKCHAIN_FILE).unwrap();
            println!("Blockchain initialized with difficulty {} and reward {}", difficulty, reward);
        }

        Commands::Transaction { from, to, amount } => {
            let mut blockchain = load_blockchain();
            let tx = Transaction::new(from.clone(), to.clone(), amount);

            match blockchain.add_transaction(tx) {
                Ok(_) => {
                    blockchain.save_to_file(BLOCKCHAIN_FILE).unwrap();
                    println!("Transaction added: {} -> {} ({})", from, to, amount);
                }
                Err(e) => println!("Error: {}", e),
            }
        }

        Commands::Mine { address } => {
            let mut blockchain = load_blockchain();

            println!("Mining block...");
            match blockchain.mine_pending_transactions(address.clone()) {
                Ok(_) => {
                    blockchain.save_to_file(BLOCKCHAIN_FILE).unwrap();
                    println!("Block mined successfully! Reward sent to {}", address);
                }
                Err(e) => println!("Error: {}", e),
            }
        }

        Commands::Balance { address } => {
            let blockchain = load_blockchain();
            let balance = blockchain.get_balance(&address);
            println!("Balance of {}: {}", address, balance);
        }

        Commands::Stats => {
            let blockchain = load_blockchain();
            let stats = blockchain.get_stats();

            println!("=== Blockchain Statistics ===");
            println!("Total Blocks: {}", stats.total_blocks);
            println!("Total Transactions: {}", stats.total_transactions);
            println!("Total Size: {} bytes", stats.total_size);
            println!("Difficulty: {}", stats.difficulty);
        }

        Commands::Print => {
            let blockchain = load_blockchain();

            for (i, block) in blockchain.chain.iter().enumerate() {
                println!("\n=== Block {} ===", i);
                println!("Hash: {}", block.hash);
                println!("Previous Hash: {}", block.previous_hash);
                println!("Timestamp: {}", block.timestamp);
                println!("Nonce: {}", block.nonce);
                println!("Transactions: {}", block.transaction_count());

                for (j, tx) in block.transactions.iter().enumerate() {
                    println!("  TX {}: {} -> {} ({})", j, tx.from, tx.to, tx.amount);
                }
            }
        }

        Commands::Validate => {
            let blockchain = load_blockchain();

            if blockchain.is_valid() {
                println!("✓ Blockchain is valid!");
            } else {
                println!("✗ Blockchain is invalid!");
            }
        }
    }
}

fn load_blockchain() -> Blockchain {
    if Path::new(BLOCKCHAIN_FILE).exists() {
        Blockchain::load_from_file(BLOCKCHAIN_FILE).unwrap()
    } else {
        println!("No blockchain found. Initializing new blockchain...");
        let blockchain = Blockchain::new(4, 50.0);
        blockchain.save_to_file(BLOCKCHAIN_FILE).unwrap();
        blockchain
    }
}

---

解説

実装のポイント

1. ブロック構造とハッシュチェーン

改ざん防止の仕組み:

pub fn calculate_hash(&self) -> String {
    let block_data = format!(
        "{}{}{}{}{}{}",
        self.index, self.timestamp, transactions,
        self.previous_hash, self.nonce, self.difficulty
    );
    // SHA-256ハッシュを計算
}

  • 各ブロックは前のブロックのハッシュを含む
  • データを変更するとハッシュが変わり、チェーンが壊れる
  • 過去のブロックを改ざんすると以降すべてのブロックが無効化

2. Proof of Work(作業証明)

マイニングアルゴリズム:

pub fn mine(&mut self) -> u64 {
    let target = "0".repeat(self.difficulty);
    loop {
        self.hash = self.calculate_hash();
        if self.hash.starts_with(&target) {
            return self.nonce;
        }
        self.nonce += 1;
    }
}

  • 特定の条件を満たすハッシュを見つける計算パズル
  • 難易度が高いほど時間がかかる(セキュリティ向上)
  • 見つけるのは困難、検証は容易(非対称性)

3. トランザクション管理

UTXO モデルの簡易版:

pub fn get_balance(&self, address: &str) -> f64 {
    let mut balance = 0.0;
    for block in &self.chain {
        for tx in &block.transactions {
            if tx.to == address { balance += tx.amount; }
            if tx.from == address { balance -= tx.amount; }
        }
    }
    balance
}

  • すべてのトランザクションを走査して残高を計算
  • 実際のビットコインはUTXOセットで効率化
  • 二重支払いの防止が重要

4. チェーンの検証

整合性チェック:

pub fn is_valid(&self) -> bool {
    for i in 1..self.chain.len() {
        let current = &self.chain[i];
        let previous = &self.chain[i - 1];

        if !current.is_valid() { return false; }
        if current.previous_hash != previous.hash { return false; }
    }
    true
}

  • 各ブロックのPoWを検証
  • ハッシュチェーンの連続性を確認
  • トランザクションの妥当性をチェック
  • 設計判断

  • シンプルなトランザクションモデル: デジタル署名やUTXOは省略し、基本概念に集中
  • 調整可能な難易度: 実験とデモのため、難易度を柔軟に設定可能
  • CLIインターフェース: コマンドラインで直感的に操作できる
  • 永続化: JSON形式でチェーンをファイルに保存
  • 代替案

  • デジタル署名: 楕円曲線暗号(ECDSA)による署名検証
  • マークルツリー: トランザクションの効率的な検証
  • PoS(Proof of Stake): エネルギー効率の良い合意アルゴリズム
  • スマートコントラクト: 実行可能なコード on chain
  • ---

    学習の意図

    習得する概念

  • 暗号学的ハッシュ:
- SHA-256の性質(一方向性、衝突耐性) - ハッシュチェーンによる改ざん検出 - マークルツリー

  • 分散合意:
- Proof of Work - 最長チェーン原則 - フォークとチェーン選択

  • トランザクション:
- UTXOモデル - デジタル署名 - 二重支払い防止

  • P2Pネットワーク:
- ノード間通信 - データの伝播 - ネットワークの堅牢性

CSの基礎との関連

データ構造:

  • 連結リスト(ブロックチェーン)
  • ハッシュテーブル(UTXO管理)
  • マークルツリー(バイナリツリー)

アルゴリズム:

  • ハッシュ関数の応用
  • 全数探索(マイニング)
  • 最長パス問題(チェーン選択)

分散システム:

  • CAP定理
  • ビザンチン将軍問題
  • 合意アルゴリズム

暗号学:

  • 公開鍵暗号
  • デジタル署名
  • ハッシュ関数
  • ---

    テスト方法

    単体テスト

    cargo test
    

    統合テスト

    # 1. 新しいチェーンを作成
    cargo run -- init --difficulty 3 --reward 50
    
    # 2. トランザクションを追加
    cargo run -- transaction --from Alice --to Bob --amount 10
    cargo run -- transaction --from Bob --to Charlie --amount 5
    
    # 3. マイニング
    cargo run -- mine --address Miner
    
    # 4. 残高確認
    cargo run -- balance --address Miner
    cargo run -- balance --address Bob
    
    # 5. チェーン表示
    cargo run -- print
    
    # 6. 検証
    cargo run -- validate
    
    # 7. 統計情報
    cargo run -- stats
    

    パフォーマンステスト

    # 異なる難易度でマイニング時間を計測
    time cargo run --release -- init --difficulty 4
    time cargo run --release -- init --difficulty 5
    time cargo run --release -- init --difficulty 6
    

    ---

    評価基準

    必須要件(60点)

  • [ ] ブロック構造が正しく実装されている(10点)
  • [ ] SHA-256ハッシュチェーンが機能する(10点)
  • [ ] Proof of Workが実装されている(15点)
  • [ ] トランザクションの追加と管理ができる(10点)
  • [ ] チェーンの整合性検証が動作する(15点)
  • 標準要件(30点)

  • [ ] CLIが使いやすく実装されている(10点)
  • [ ] 難易度調整が可能(5点)
  • [ ] 残高計算が正しく動作する(10点)
  • [ ] チェーンの永続化(保存/読み込み)(5点)
  • 発展要件(10点)

  • [ ] マークルツリーの実装(3点)
  • [ ] デジタル署名の実装(4点)
  • [ ] P2Pネットワーク通信(3点)
  • ボーナス(+10点)

  • [ ] スマートコントラクト機能(+5点)
  • [ ] WebUIの実装(+5点)
  • ---

    参考資料

    ブロックチェーン基礎

  • Bitcoin Whitepaper
  • Mastering Bitcoin (Andreas M. Antonopoulos)
  • Blockchain Demo (Anders Brownworth)
  • 暗号学

  • Cryptographic Hash Functions
  • Public-key Cryptography
  • Digital Signature
  • Rust実装

  • exonum (Rust blockchain framework)
  • parity-bitcoin