【Rust】RustでbitcoinのProof of work(PoW)を実装したい

まず、previous hashとnonceを足して、sha256で暗号化します。

use sha2::{Digest, Sha256};
use rand::Rng;

fn main() {
    let previous_hash = "b9b9ee9ffc95fa4956b63b6043a99d0a8f04e0e52e687fc1958d3c6dff885f01";
    let num = rand::thread_rng().gen_range(0..1000000);
    let hash_num = format!("{}{}", previous_hash, num.to_string());
    let header = Sha256::digest(hash_num);
    println!("{:x}", header);
}

$ cargo run
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.01s
Running `target/debug/crypt`
07b9391fce0c6299097b36a06392a6a6b6245ee59ca816de22aac6dbaf7419af

うむ、やりたいことはできている。ここから、hash値先頭が0000… のheaderになるようなnonceの値を探す計算処理を行う

use sha2::{Digest, Sha256};
use rand::Rng;

fn main() {
    let previous_hash = "b9b9ee9ffc95fa4956b63b6043a99d0a8f04e0e52e687fc1958d3c6dff885f01";
    let mut num = rand::thread_rng().gen_range(0..1000000);
    let mut hash_num = format!("{}{}", previous_hash, num.to_string());
    let mut header = Sha256::digest(hash_num);
    let mut target: String  = (&hex::encode(header)[..4]).to_string();
    
    let mut cnt = 1;
    println!("count: {} {:x}", cnt, header);

    while target != "0000" {
        println!("count: {} {:x}", cnt, header);
        num = rand::thread_rng().gen_range(0..1000000);
        hash_num = format!("{}{}", previous_hash, num.to_string());
        header = Sha256::digest(hash_num);
        target = (&hex::encode(header)[..4]).to_string();
        cnt += 1;
    }
    println!("count: {} {:x}", cnt, header);
}

$ cargo run

count: 37455 4b1d6582d1fed66a34041346d0f43cc7b6c2a803588da8c5d515a813c2dcff7a
count: 37456 34b478ac26947b93e63a747be64a06b904fe98953a8db9d4d3f773fdadf5abba
count: 37457 0000bdebe741af3994f4a2160b4480a23ca137aaf0ac51b10fe574f04afc7be4

凄い簡単なコードなんだけど、これ作るのに結構時間かかった…

あとは計算時間を計測してDifficultyの調整機能を作りたい

use sha2::{Digest, Sha256};
use rand::Rng;
use std::{thread, time};

fn main() {
    let now = time::Instant::now();
    let previous_hash = "b9b9ee9ffc95fa4956b63b6043a99d0a8f04e0e52e687fc1958d3c6dff885f01";
    let mut num = rand::thread_rng().gen_range(0..1000000);
    let mut hash_num = format!("{}{}", previous_hash, num.to_string());
    let mut header = Sha256::digest(hash_num);
    let mut target: String  = (&hex::encode(header)[..4]).to_string();
    
    let mut cnt = 1;
    println!("count: {} {:x}", cnt, header);

    while target != "0000" {
        println!("count: {} {:x}", cnt, header);
        num = rand::thread_rng().gen_range(0..1000000);
        hash_num = format!("{}{}", previous_hash, num.to_string());
        header = Sha256::digest(hash_num);
        target = (&hex::encode(header)[..4]).to_string();
        cnt += 1;
    }
    println!("count: {} {:x}", cnt, header);
    println!("{:?}", now.elapsed());
}

$ cargo run

count: 70736 000074213e839089c9bd8e446dd5835d537cd7037cdf193bf9881df44d2a55b4
1.818378391s

これをmainに取り込む。powはminingとして別ファイルにして、ブロック作成時に読み込むようにする。

mod mining;

fn make_block (){
    mining::proof_of_work();
    println!("blockを作成しました。");
    Pool.lock().unwrap().clear();
}

$ cargo run

count: 16990 0000ea27e22db290e4f2163f968bfaf3ff7d58ccf1cd4ab43b3fbc4326c0eb4a
428.967757ms
blockを作成しました。
8000E340A55A517D0F27F3A63FBE39ED576BA491DFAC89B44654AB147EC66B206B054BAAF53E318EB2721DC892B4736630F400547989AE9F7C069034ECB4DF98

### 課題
– トランザクションプールが出来た後のblock作成のロジックを詰める必要がある。(merkletree, serialize等)
– genesis block, minerへのコイン分配なども考える必要あり。
– トランザクションスピードを上げるために並列処理を導入する場合、どこを並列処理にするのか考える必要あり。