Rustが10周年だしRustでpythonブロックチェーンデモのマイニングの速度向上ができるかを試す

スポンサーリンク
Uncategorized

はじめに

Rustが10周年を迎えました。

え?それは2025年のことだって?

「Rust 1.0」が米国時間2015年5月15日ので、Rust10周年は2025年5月15日~2026年5月16日までとなります。

ギリギリまだ10周年です。

で、ここからがこの記事の本題になりますが、マイニングの箇所をRustで書いてみるというのが、この記事で書くことです。

そもそもRustというプログラミング言語は速くて、安全性のあるプログラミング言語です。

前回、pythonのみでビットコインのマイニングまでを検証してみました。

試してみると、difficultyを7にすると、完了までに1562651.72 msかかります。

1562秒。

26分。

結構かかりました。

Rustのコード

では、早速、Rustのコードを書きます。

AIに手伝ってもらって以下のコードをpythonの元のコードに入れました。もともとマイニングをする箇所を差し替えています。(元のコードは以前の記事を参照してみてください)

def mine_block_with_rust(index: int, prev_hash: str, txs: List[Dict[str, Any]], difficulty: int) -> Block | None:
	merkle_root = compute_merkle_root(txs)
	payload = {
		"index": index,
		"prev_hash": prev_hash,
		"difficulty": difficulty,
		"merkle_root": merkle_root,
	}
	try:
		proc = subprocess.run(
			["rust_miner/target/release/rust_miner"],
			input=json.dumps(payload).encode("utf-8"),
			stdout=subprocess.PIPE,
			stderr=subprocess.PIPE,
			check=True,
		)
	except Exception as e:
		log("miner", f"rust miner failed ({e}), falling back to Python miner")
		return mine_block(index, prev_hash, txs, difficulty)

	stdout = proc.stdout.decode("utf-8").strip()
	if not stdout:
		return None

	try:
		res = json.loads(stdout)
	except json.JSONDecodeError as e:
		log("miner", f"failed to decode rust miner output ({e}), falling back to Python miner")
		return mine_block(index, prev_hash, txs, difficulty)

	nonce = int(res["nonce"])
	timestamp = float(res["timestamp"])
	block_hash = str(res["hash"])
	return Block(
		index=index,
		prev_hash=prev_hash,
		timestamp=timestamp,
		nonce=nonce,
		difficulty=difficulty,
		merkle_root=merkle_root,
		txs=txs,
		hash=block_hash,
	)

subprocessで外部のrustを実行していて、その結果をstdout = proc.stdout.decode("utf-8").strip()として取得しています。

また、Rust側のプログラムは以下の様になっています。

use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::error::Error;
use std::io::{self, Read};
use std::time::{SystemTime, UNIX_EPOCH};

#[derive(Deserialize)]
struct MineRequest {
    index: u64,
    prev_hash: String,
    difficulty: u32,
    merkle_root: String,
}

#[derive(Serialize)]
struct MineResponse {
    nonce: u64,
    timestamp: f64,
    hash: String,
}

fn sha256d_hex(data: &[u8]) -> String {
    let first = Sha256::digest(data);
    let second = Sha256::digest(&first);
    hex::encode(second)
}

fn build_header(req: &MineRequest, nonce: u64, timestamp: f64) -> Vec<u8> {
    let s = format!(
        "{}|{}|{}|{}|{}|{}",
        req.index, req.prev_hash, timestamp, nonce, req.difficulty, req.merkle_root
    );
    s.into_bytes()
}

fn main() -> Result<(), Box<dyn Error>> {
    let mut buf = String::new();
    io::stdin().read_to_string(&mut buf)?;
    if buf.trim().is_empty() {
        return Ok(());
    }

    let req: MineRequest = serde_json::from_str(&buf)?;
    let target_prefix = "0".repeat(req.difficulty as usize);

    let mut nonce: u64 = 0;
    loop {
        let now = SystemTime::now().duration_since(UNIX_EPOCH)?;
        let timestamp = now.as_secs_f64();
        let header = build_header(&req, nonce, timestamp);
        let hash_hex = sha256d_hex(&header);
        if hash_hex.starts_with(&target_prefix) {
            let resp = MineResponse {
                nonce,
                timestamp,
                hash: hash_hex,
            };
            let out = serde_json::to_string(&resp)?;
            println!("{}", out);
            break;
        }
        nonce = nonce.wrapping_add(1);
    }

    Ok(())
}

rust_minerフォルダに移動して、

cargo build --release

を実行

その後、pythonで前回と同様にデモを実施しました。

結果なんですが、

355791.75 ms

で完了しました。

355秒。

6分ほどで完了ということで、pythonで実装したときの26分と比べて4分の1以上速くなったことを確認しました。

最後に

めっちゃ速くなりましたね!

Rust凄いっす!

タイトルとURLをコピーしました