【Rust】Rustでマークルツリーを作りたい

use sha2::{Digest, Sha256};

#[derive(Debug, Clone)]
enum Position {
    Right,
    Left,
}

#[derive(Debug, Clone)]
struct Node {
    left: String,
    right: String,
    parent: String,
    sibling: String,
    position: Position,
    data: String,
}

fn hash(data: String) -> String {
    let h = Sha256::digest(data.clone());
    hex::encode(h).to_string()
}

fn sha256hash(right_data: String, left_data: String) -> String {
        let s = format!("{}{}", right_data, left_data);
        let h = Sha256::digest(s);
        hex::encode(h).to_string()
}

#[derive(Debug, Clone)]
struct Tree {
    leaves: Vec<Node>,
    layer: Vec<Node>,
    root: String,
}

impl Tree {
    fn build_layer(&mut self) {
        let mut new_layer: Vec<Node> = Vec::new();

        if self.layer.len() % 2 == 1 {
            self.layer.push(self.layer[self.layer.len() - 1].clone());
        }

        for i in (0..self.layer.len()).step_by(2) {
            let parent_data = sha256hash(self.layer[i].data.clone(), self.layer[i+1].data.clone());
            let left = Node {left: "".to_string(), right: "".to_string(), parent: parent_data.clone(), sibling: self.layer[i+1].data.clone(), position: Position::Left, data: self.layer[i].data.clone()};
            let right = Node {left: "".to_string(), right: "".to_string(), parent: parent_data.clone(), sibling: self.layer[i].data.clone(), position: Position::Right, data: self.layer[i+1].data.clone()};
            let parent = Node {left: self.layer[i].data.clone(), right: self.layer[i+1].data.clone(), parent: "".to_string(), sibling: self.layer[i].data.clone(), position: Position::Left, data: parent_data.clone()};
            new_layer.push(parent.clone());
            self.leaves.push(left.clone());
            self.leaves.push(right.clone());
            self.leaves.push(parent.clone());
        }
        self.layer = new_layer;
    }

    fn build_tree(&mut self) {
        while self.layer.len() > 1 {
            let _ = self.build_layer();
        }
        self.root = self.layer[0].data.clone();
    }

    fn search(&mut self, data: String) -> Node {
        let hash_value = data.clone();
        let mut target = Node {left: "".to_string(), right: "".to_string(), parent: "".to_string(), sibling: "".to_string(), position: Position::Left, data: "".to_string()};
        for node in &self.leaves {
            if node.data == hash_value {
                let target: Node = node.clone();
            } 
        }
        return target
    }

    fn get_pass(&mut self, data: String) -> Vec<String>{
        let mut target = self.search(hash(data.clone()));
        let mut markle_pass: Vec<String> = Vec::new();
        markle_pass.push(target.data);
        while target.parent != "" {
            markle_pass.push(target.sibling);
            target = self.search(target.parent);
        }       
        return markle_pass
    }
}

fn merkle_root(v: Vec<&str>) -> String {
    let mut t = Tree {leaves: [].to_vec(), layer: [].to_vec(), root: "".to_string()};
    for n in v {
        let mut s =  Node {left: "".to_string(), right: "".to_string(), parent: "".to_string(), sibling: "".to_string(), position: Position::Left, data: hash(n.to_string())};
        t.layer.push(s.clone());
    }
    t.build_tree();
    t.root
}

fn main(){
    let v= vec!["aaa", "bbb", "ccc", "ddd", "eee", "fff", "gggg"];
    let m = merkle_root(v);
    println!("{}", m);
}

うーん….

【Rust】Rustで for i in range(0, 10, 2) をやりたい

step_by(n) が使える模様
ただし、これだと、iがイテレータになってしまうので、pythonでいう for i in range(0, 10, 2) にならない

fn main(){
    let v= vec![1, 2, 3, 4, 5, 6, 7];
    for i in v.into_iter().step_by(2) {
        println!("{}", i);
    }   
}

$ cargo run
Compiling sample v0.1.0 (/home/vagrant/dev/rust/sample)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.34s
Running `target/debug/sample`
1
3
5
7

正確にはこう↓

fn main(){
    let v= vec![1, 2, 3, 4, 5, 6, 7];
    for n in (0..v.len()).step_by(2) {
        println!("{}", v[n]);
    }   
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.36s
Running `target/debug/sample`
1
3
5
7

なるほど、勉強になるね〜

【Rust】Vecへの要素の追加

appendではなく、pushを使う

fn main(){
    let mut layer: Vec<String> = vec!["hoge".to_string(), "fuga".to_string()];
    layer.push("aaa".to_string());
    println!("{:?}", layer);
}

[“hoge”, “fuga”, “aaa”]

    let mut layer: Vec<String> = vec!["hoge".to_string(), "fuga".to_string()];
    layer.push("aaa".to_string());
    println!("{:?}", layer[layer.len() - 1]);

“aaa”

これを応用して

#[derive(Debug, Clone)]
struct Tree {
    leaves: Vec<String>,
    layer: Vec<String>,
    root: String,
}

impl Tree {
    fn build_layer(&mut self) {
        let new_layer: Vec<String> = Vec::new();

        if self.layer.len() % 2 == 1 {
            self.layer.push(self.layer[self.layer.len() - 1].clone());
        }
    }
}

appendだと上手くいきませんね。

【Rust】structにenumとimplを使用したい

use sha2::{Digest, Sha256};

#[derive(Debug)]
enum Position {
    Right,
    Left,
}

#[derive(Debug)]
struct Node {
    left: String,
    right: String,
    sibling: String,
    position: Position,
    data: String,
}

impl Node {
    fn hash(&self) -> String {
        let h = Sha256::digest(self.data.clone());
        hex::encode(h).to_string()
    }
}

fn main(){
    let d = Node { left:"aaa".to_string(), right:"aaa".to_string(), sibling:"aaa".to_string(), position: Position::Left, data:"aaa".to_string()};
    println!("{:?}", d);
    println!("{:?}", d.hash());
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.24s
Running `target/debug/sample`
Node { left: “aaa”, right: “aaa”, sibling: “aaa”, position: Left, data: “aaa” }
“9834876dcfb05cb167a5c24953eba58c4ac89b1adf57f28f2f9d09af107ee8f0”

これで良いのか? なんか違うような気もするが、これでmerkle treeを作っていきたい。。。

【Rust】ファイルの有無によって処理を分けたい

use std::{fs::File, io::ErrorKind};

fn main(){
    let f = File::open("secret.pem");
    let f = match f {
        Ok(_) => (),
        Err(ref error) if error.kind() == ErrorKind::NotFound => println!("Not found"),
        Err(error) => {
            panic!("There was a problem opening the file: {:?}", error);
        } 
    };
    println!("OK");
}

何もしない場合は、Ok(_) => () と書いた。

【Rust】writeln!で後ろに改行が入った時に削除

writeln!でpemファイルを作成します。

env::set_var("RUST_BACKTRACE", "1");
    let secret_key = SigningKey::random(&mut OsRng);
    let secret_key_serialized = secret_key
        .to_pkcs8_pem(Default::default())
        .unwrap()
        .to_string();
    println!("Secret Key: \n{}", secret_key_serialized);

    let mut file = File::create("secret.pem").expect("file not found.");

    writeln!(file, "{}", secret_key_serialized).expect("can not write.");

すると、最後尾に2行改行が入ってしまいます。

-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgO7s4/UmqJ5+UWIIU
oL4XIxslh+htWtvTY7wZPp+usEKhRANCAAS/iV6WinhhKw8M/tkGNNwf2W+Vt+cd
d0hLjWQ9iZhjP7NxlPLoUjWERctvft3zPOktCedW5rzLIhVtj7rX2F4j
-----END PRIVATE KEY-----


これをstd::fs::read_to_stringで読み取ってパースしても、改行が入っているためエラーになってしまいます。
改行を指定しての文字列削除や文字列の置き換え(s.replace)だとうまくいかないのですが、
trim()だと、いい具合に最後2行の改行を削除してくれます。

    let mut file = File::create("secret.pem").expect("file not found.");

    writeln!(file, "{}", secret_key_serialized).expect("can not write.");

    let contents = std::fs::read_to_string("secret.pem")
        .expect("something went wrong reading the file");
    let secret_pem = contents.trim();
    println!("{}", secret_pem);

Secret Key:
—–BEGIN PRIVATE KEY—–
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgNMDcEubwpsVrs/TJ
YgiL/HFyb3hvOgF/QI3AwXWBFKmhRANCAARyuwFIbkPm5Q1zbd6DZbNMG35s7NmU
6QduJGRjofpwCrVaOorsjZASpG546WgoTof9eONpXYY92NY5hCvhPJrU
—–END PRIVATE KEY—–

—–BEGIN PRIVATE KEY—–
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgNMDcEubwpsVrs/TJ
YgiL/HFyb3hvOgF/QI3AwXWBFKmhRANCAARyuwFIbkPm5Q1zbd6DZbNMG35s7NmU
6QduJGRjofpwCrVaOorsjZASpG546WgoTof9eONpXYY92NY5hCvhPJrU
—–END PRIVATE KEY—–

素晴らしいですね。これ解決するのに半日かかりました。

【Rust】p256(ecdsa)でpemファイルを扱う

use p256::{
    ecdsa::{
        signature::{Signer, Verifier},
        SigningKey, VerifyingKey,
    },
    pkcs8::EncodePrivateKey,
    PublicKey, SecretKey,
};
use rand_core::OsRng;
use std::fs::OpenOptions;
use std::io::Write;

fn main(){
    let secret_key = SigningKey::random(&mut OsRng);
    let secret_key_serialized = secret_key
        .to_pkcs8_pem(Default::default())
        .unwrap()
        .to_string();
    println!("Secret Key: \n{}", secret_key_serialized);
    let secret_key = secret_key_serialized.parse::<SecretKey>().unwrap();

    let public_key = secret_key.public_key();
    let public_key_serialized = public_key.to_string();
    println!("Public Key: \n{}", public_key_serialized);
    let public_key = public_key_serialized.parse::<PublicKey>().unwrap();

    let signing_key: SigningKey = secret_key.into();
    let message = b"ECDSA proves knowledge of a secret number in the context of a single message";
    let signature = signing_key.sign(message);

    let verifying_key: VerifyingKey = public_key.into();
    assert!(verifying_key.verify(message, &signature).is_ok());
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 5.89s
Running `target/debug/sample`
Secret Key:
—–BEGIN PRIVATE KEY—–
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgqmVTGsuHMDgaN3TG
Jvsem2P1dA3l/wnRsxPfN8PTnUqhRANCAASv58iralPg2mOjuf28sSC8UuxqR4kD
u9tATiYXDvaU6BfmuI0tl0JsrZ2Brf1BkXtzbwBbiM2+h6+I6J55TU2p
—–END PRIVATE KEY—–

Public Key:
—–BEGIN PUBLIC KEY—–
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEr+fIq2pT4Npjo7n9vLEgvFLsakeJ
A7vbQE4mFw72lOgX5riNLZdCbK2dga39QZF7c28AW4jNvoeviOieeU1NqQ==
—–END PUBLIC KEY—–

p256とk256は微妙に違うのね。。これをpemファイルとして保存したい。

【Shell】ファイルを生成

ジェネシスブロックを生成するシェルを作ります。

#!/bin/bash

echo "{\"time\":\"0000-00-00 00:00:00.000000000 UTC\",\"transactions\":[],\"hash\":\"genesisblockhash\",\"nonce\":\"0\"}" > data/blocks.txt

$ ./setup.sh

// blockを生成

data/blocks.txt

{"time":"0000-00-00 00:00:00.000000000 UTC","transactions":[],"hash":"genesisblockhash","nonce":"0"}
{"time":"2025-01-05 01:43:43.528815824 UTC","transactions":[{"time":"2024-12-25 22:53:36.824066840 UTC","sender":"5bac6cb0f4ad6397752c3d73b88c5c86e3d88ac695118494a1732e2abd16c76acad3d6586c37c8db7e69c2f812f99275198936957d72c38d71981991123","receiver":"4bac6cb0f4ad6397752c3d73b88c5c86e3d88ac695118494a1732e2abd16c76acad3d6586c37c8db7e69c2f812f99275198936957d72c38d71981991124","amount":10,"signature":"8000E340A55A517D0F27F3A63FBE39ED576BA491DFAC89B44654AB147EC66B206B054BAAF53E318EB2721DC892B4736630F400547989AE9F7C069034ECB4DF98"},{"time":"2024-12-25 22:53:36.824066840 UTC","sender":"4bac6cb0f4ad6397752c3d73b88c5c86e3d88ac695118494a1732e2abd16c76acad3d6586c37c8db7e69c2f812f99275198936957d72c38d71981991123","receiver":"4bac6cb0f4ad6397752c3d73b88c5c86e3d88ac695118494a1732e2abd16c76acad3d6586c37c8db7e69c2f812f99275198936957d72c38d71981991124","amount":10,"signature":"8000E340A55A517D0F27F3A63FBE39ED576BA491DFAC89B44654AB147EC66B206B054BAAF53E318EB2721DC892B4736630F400547989AE9F7C069034ECB4DF98"}],"hash":"00006059ac7bd5ea2ece1428c90c402c602cf402b0483e7063dfa4f3e58c7ae4","nonce":"215835"}

ちゃんと改行されていますね。echoのテキストの最後に\nを入れなくても大丈夫でした。
genesis blockとblockの生成までできました。
続いて、verify chainの関数を作って、ブロックが正しく生成されているか確認した上で、minerへの報酬トランザクションを生成する関数を作りたい。

【Rust】txtの最終行を読み込んで、構造体に変換する

まず、txtファイルに書き込まれているデータがあります。

hoge.txt

{"Name":"Apple","Id":1,"ItemIdList":[1000,1001],"RotData":{"Pitch":0.0,"Roll":0.0,"Yaw":32.0}}
{"Name":"Apple","Id":1,"ItemIdList":[1000,1001],"RotData":{"Pitch":0.0,"Roll":0.0,"Yaw":32.0}}

上記の最終行を読み込んで、その中のデータの一つから処理を行いたい。

#[derive(Serialize, Deserialize, Debug)]
#[allow(non_snake_case)]
struct Rotator {
    Pitch:f32,
    Roll:f32,
    Yaw:f32,
}

#[derive(Serialize, Deserialize, Debug)]
#[allow(non_snake_case)]
struct CharaParam {
    Name: String,
    Id: i32,
    ItemIdList: Vec<u32>,
    RotData: Rotator,
}

fn main(){

    let f = File::open("hoge.txt").unwrap();
    let reader = BufReader::new(f);
    let lines = reader.lines();

    let input_fn = lines.last().unwrap_or(Ok("".to_string())).unwrap();
    let param:CharaParam = serde_json::from_str(&input_fn).unwrap();

    println!("{:?}", param.Name);
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.24s
Running `target/debug/sample`
“Apple”

おおおおおおおお、やりたいことは出来ているっぽい。ここが出来ると、あらゆることが可能になる!

【Rust】txtの最終行にvectorを書き込んでいく

#[derive(Serialize, Deserialize, Debug)]
#[allow(non_snake_case)]
struct Rotator {
    Pitch:f32,
    Roll:f32,
    Yaw:f32,
}

#[derive(Serialize, Deserialize, Debug)]
#[allow(non_snake_case)]
struct CharaParam {
    Name: String,
    Id: i32,
    ItemIdList: Vec<u32>,
    RotData: Rotator,
}

fn main() -> std::io::Result<()>{

    let mut param =  CharaParam{Name:String::from("Apple"), Id:0x01, ItemIdList:vec![1000, 1001], RotData:Rotator{Pitch:0.0, Roll:0.0, Yaw:32.0} };
 
    let serialized: Vec<u8> = serde_json::to_vec(&param).unwrap();
    let mut fileRef = OpenOptions::new()
                        .append(true)
                        .open("hoge.txt")
                        .expect("Unable to open file");

    fileRef.write_all(&serialized).expect("write failed");
    fileRef.write_all(b"\n").expect("write failed");
    Ok(())
}

hoge.txt

{"Name":"Apple","Id":1,"ItemIdList":[1000,1001],"RotData":{"Pitch":0.0,"Roll":0.0,"Yaw":32.0}}
{"Name":"Apple","Id":1,"ItemIdList":[1000,1001],"RotData":{"Pitch":0.0,"Roll":0.0,"Yaw":32.0}}

書き込みはなんかこれでOKっぽいけど、問題は読み込みやな。