超シンプルなブロックチェーンを実装する

## はじめに
以下の記事を元に超シンプルなブロックチェーンを実装します。
https://blog.logrocket.com/how-to-build-a-blockchain-in-rust/

実装は元記事同様Rustですが、細かいところがわからなくても読めるように書いたので、最後まで読んでいただけると嬉しいです。

## 実装
まず、データが格納されるブロックとブロックをつなげたチェーンの2つの構造を定義します。
```
pub struct Blockchain {
    pub chain: Vec<Block>,
}

#[derive(Debug, Clone)]
pub struct Block {
    pub index: u64,
    pub hash: String,
    pub previous_hash: String,
    pub timestamp: u128,
    pub data: String,
}
```

BlockchainはBlockを単純につなげるベクトルとします。

Blockの各フィールドは以下のように利用します。
* `index`: Blockの番号。今回は0, 1, 2, ...と連番で利用
* `hash`: Block全体をsha256でハッシュした値
* `previous_hash`: 1つ前のBlock全体をsha256でハッシュした値
* `timestamp`: Blockを作成した時刻
* `data`: Blockに格納されているデータ

### Block::new()メソッド

データ構造を定義したら、Blockを生成するためのメソッドを定義します。
```
impl Block {
    pub fn new(index: u64, previous_hash: String, data: String) -> Self {
        let timestamp = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .expect("The time is something wrong")
            .as_millis();

        // ハッシュ値の計算
        let hash = Self::calculate_hash(index, &previous_hash, &data, timestamp);

        Block {
            index,
            hash,
            previous_hash,
            timestamp,
            data,
        }
    }

    fn calculate_hash(index: u64, previous_hash: &str, data: &str, timestamp: u128) -> String {
        let input = format!("{index}{previous_hash}{data}{timestamp}");
        let hash = format!("{:x}", Sha256::digest(input.as_bytes()));
        hash
    }
}
```

newメソッドの内容はシンプルで、現在のtimestampを取得し、{index}{previous_hash}{data}{timestamp}の4つをハッシュしているだけです。

### Blockchain::new()メソッド

Blockchain側でも初回Blockを追加できるようにnewメソッドを作成します。

```
impl Blockchain {
    pub fn new() -> Self {
        let genesis_block = Block::new(0, String::new(), "Genesis Block".to_string());
        Blockchain {
            chain: vec![genesis_block],
        }
    }
}
```

Block::newメソッドを使ってVec型データの最初の値にしているだけです。最初のBlockであるため、previous_hashは空文字列とします。ちなみにブロックチェーンではよく最初のブロックをGenesis Blockと呼びます。ここでも最初のブロックのdata値にGenesis Blockと入れることにします。

### Blockchain::add_block()メソッド

Blockchainに初回のBlockを作成するメソッド（new()メソッド）を作成したら、2つ目以降のBlockを追加できるようにメソッドを定義します。

```
impl Blockchain {
    ...

    pub fn add_block(&mut self, data: String) {
        let previous_block = self.chain.last().unwrap();
        let new_block = Block::new(previous_block.index + 1, previous_block.hash.clone(), data);

        if new_block.is_valid(previous_block) {
            self.chain.push(new_block);
        } else {
            panic!("Failed to add block: Block is invalid.");
        }
    }

    pub fn is_valid(&self, previous_block: &Block) -> bool {
        self.previous_hash == previous_block.hash
            && self.hash
                == Self::calculate_hash(self.index, &self.previous_hash, &self.data, self.timestamp)
    }
}
```

新しいBlockの作成には、Block::new()メソッドを使用します。このメソッドの重要なポイントは、直前のBlockのハッシュ値をprevious_hashとして利用している点です。この仕組みにより、ブロック同士がチェーンのように繋がった構造をになり、データの改ざんを難しくします。
また、実際にBlockChainに追加する前に追加するBlockが正しいことを確認（Block::is_validメソッド）します。

### Blockchain::is_chain_valid()メソッド

最後にBlockChain全体が正しいかどうかを確認するメソッドを作成します。
"Genesis Block"から順にハッシュ値を検証し、ブロックチェーンに問題がないことを確認します。

```
impl Blockchain {
    ...
    pub 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];

            if !current_block.is_valid(previous_block) {
                return false;
            }
        }
        true
    }
}
``` 

以上で実装したいデータ構造、メソッドが完成しました。

### main()関数

実際に作成したコードを利用してみます。
今まで実装してきたコードを整理すると以下のようになります。

``` block.rs
use sha2::{Digest, Sha256};
use std::time::{SystemTime, UNIX_EPOCH};

#[derive(Debug, Clone)]
pub struct Block {
    pub index: u64,
    pub hash: String,
    pub previous_hash: String,
    pub timestamp: u128,
    pub data: String,
}

impl Block {
    pub fn new(index: u64, previous_hash: String, data: String) -> Self {
        let timestamp = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .expect("The time is something wrong")
            .as_millis();

        let hash = Self::calculate_hash(index, &previous_hash, &data, timestamp);

        Block {
            index,
            hash,
            previous_hash,
            timestamp,
            data,
        }
    }

    fn calculate_hash(index: u64, previous_hash: &str, data: &str, timestamp: u128) -> String {
        let input = format!("{index}{previous_hash}{data}{timestamp}");
        let hash = format!("{:x}", Sha256::digest(input.as_bytes()));
        hash
    }

    pub fn is_valid(&self, previous_block: &Block) -> bool {
        self.previous_hash == previous_block.hash
            && self.hash
                == Self::calculate_hash(self.index, &self.previous_hash, &self.data, self.timestamp)
    }
}

```

``` blockchain.rs
use crate::block::Block;

pub struct Blockchain {
    pub chain: Vec<Block>,
}

impl Blockchain {
    pub fn new() -> Self {
        let genesis_block = Block::new(0, String::new(), "Genesis Block".to_string());
        Blockchain {
            chain: vec![genesis_block],
        }
    }

    pub fn add_block(&mut self, data: String) {
        let previous_block = self.chain.last().unwrap();
        let new_block = Block::new(previous_block.index + 1, previous_block.hash.clone(), data);

        if new_block.is_valid(previous_block) {
            self.chain.push(new_block);
        } else {
            panic!("Failed to add block: Block is invalid.");
        }
    }

    pub 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];

            if !current_block.is_valid(previous_block) {
                return false;
            }
        }
        true
    }
}
```

``` main.rs
mod block;
mod blockchain;

use blockchain::Blockchain;

fn main() {
    pretty_env_logger::init();

    // create a new blockchain
    let mut blockchain = Blockchain::new();
    log::info!("{:?}", blockchain.chain[0]);

    // Add blocks
    blockchain.add_block("First block".to_string());
    blockchain.add_block("Second block".to_string());
    blockchain.add_block("Third block".to_string());

    // print the blockchain
    for block in &blockchain.chain {
        println!("{:?}", block);
    }

    // Validate the blockchain
    println!(
        "Check if the blockchain is valid: {}",
        blockchain.is_chain_valid()
    );
}
```

実行結果
```
cargo run
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.07s
     Running `target/debug/rust_simple_blockchain`
Block { index: 0, hash: "ab793e5f08ef2d0c3200999a7a9e92789c131451152603552e5050f8b9df93d2", previous_hash: "", timestamp: 1732669772313, data: "Genesis Block" }
Block { index: 1, hash: "8266fcc02a8428e30fb98d5f127c1157088305650801df0b2e38349d32ce665b", previous_hash: "ab793e5f08ef2d0c3200999a7a9e92789c131451152603552e5050f8b9df93d2", timestamp: 1732669772315, data: "First block" }
Block { index: 2, hash: "f896bdbd7011d8147103f704979e6c201de7b37aa7987ee6e26779d8b9df125b", previous_hash: "8266fcc02a8428e30fb98d5f127c1157088305650801df0b2e38349d32ce665b", timestamp: 1732669772316, data: "Second block" }
Block { index: 3, hash: "043062a264ffec3a78d166de40525b2d2d6e0dd05d2b8dd3cb521953bc110b60", previous_hash: "f896bdbd7011d8147103f704979e6c201de7b37aa7987ee6e26779d8b9df125b", timestamp: 1732669772316, data: "Third block" }
Check if the blockchain is valid: true
```

## さいごに

上記の実装は以下リポジトリに置いています。
https://github.com/hsmto25519/rust-simple-blockchain

最後までお読みいただきありがとうございました。