【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っぽいけど、問題は読み込みやな。

【Rust】txtファイルを読み込む

use serde::{Serialize, Deserialize};
use std::fs::File;
use std::io::prelude::*;

fn main() -> std::io::Result<()>{
    let mut file = File::open("foo.txt")?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    println!("{}", contents);
    assert_eq!(contents, "hello, world!");
    Ok(())
}

$ cargo run
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.49s
Running `target/debug/sample`
bytes bytes bytes 8bytes
asdfghjkl
1234567890
thread ‘main’ panicked at src/main.rs:11:5:
assertion `left == right` failed
left: “bytes bytes bytes 8bytes\nasdfghjkl\n1234567890”
right: “hello, world!”
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

これも同じ

fn main() -> std::io::Result<()>{
    let file = File::open("foo.txt")?;
    let mut buf_reader = BufReader::new(file);
    let mut contents = String::new();
    buf_reader.read_to_string(&mut contents)?;
    println!("{}", contents);
    assert_eq!(contents, "hello, world!");
    Ok(())
}

### 1行ずつ読み取る

use std::fs::File;
use std::io::{BufReader, BufRead, Error};

fn main() -> Result<(), Error>{
    let path = "foo.txt";
    let input = File::open(path)?;
    let buffered = BufReader::new(input);

    for line in buffered.lines() {
        println!("{}", line?);
    }

    Ok(())
}

### ファイルの書き込み

fn main() -> std::io::Result<()>{
    let mut buffer = File::create("hoge.txt")?;

    buffer.write_all(b"some bytes\n hogehoge")?;
    Ok(())
}

### 最終行に書き込み

use std::fs::OpenOptions;
use std::io::Write;

fn main() -> std::io::Result<()>{
    let mut fileRef = OpenOptions::new()
                        .append(true)
                        .open("hoge.txt")
                        .expect("Unable to open file");

    fileRef.write_all(b"last line\n").expect("write failed");
    Ok(())
}

改行コードを入れないと、同じ列になってしまう。
さて、Structでやりたい。

【Rust】jsonファイルの書き換え

jsonデータを読み込んで、書き換え

use serde::{Serialize, Deserialize};
use std::fs;
use std::fs::File;
use std::io::prelude::*;

#[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 input_fn = fs::read_to_string("test_input.json")
        .expect("JSON Read Failed.");
    
    let mut param_list: Vec<CharaParam> = serde_json::from_str(&input_fn).unwrap();

    let chara00 = CharaParam{Name:String::from("Apple"), Id:0x01, ItemIdList:vec![1000, 1001], RotData:Rotator{Pitch:0.0, Roll:0.0, Yaw:32.0} };
    let chara01 = CharaParam{Name:String::from("Banana"), Id:0x02, ItemIdList:vec![1002, 1003], RotData:Rotator{Pitch:0.0, Roll:-70.0, Yaw:66.0}};

    param_list.push(chara00);
    param_list.push(chara01);

    let output_fn = String::from("test_output.json");
    let result = output_json(&output_fn, param_list);
    match result {
        Ok(..) => { println!("Json output finished.【{}】", output_fn) }
        Err(err) => { println!("Error: {}", err)}
    }

}

fn output_json(output_fn: &str, charaparam_list: Vec<CharaParam>) -> std::io::Result<()>{

    let serialized: String = serde_json::to_string(&charaparam_list).unwrap();

    let mut file = File::create(output_fn)?;
    file.write_all(serialized.as_bytes())?;
    Ok(())
}

[{“Name”:”Kiwi”,”Id”:3,”ItemIdList”:[2000,2001],”RotData”:{“Pitch”:0.0,”Roll”:0.0,”Yaw”:0.0}},{“Name”:”Orange”,”Id”:4,”ItemIdList”:[2002,2003],”RotData”:{“Pitch”:1.0,”Roll”:50.2,”Yaw”:66.5}},{“Name”:”Apple”,”Id”:1,”ItemIdList”:[1000,1001],”RotData”:{“Pitch”:0.0,”Roll”:0.0,”Yaw”:32.0}},{“Name”:”Banana”,”Id”:2,”ItemIdList”:[1002,1003],”RotData”:{“Pitch”:0.0,”Roll”:-70.0,”Yaw”:66.0}}]

書き込みは出来ていますが、処理量が増えてくると処理が冗長になるように思います。

【Rust】文字列の繰り返し

文字列の繰り返しは repeat() を使用する

fn main() {
    let mut difficulty = 4;
    let str: String = "0".to_string();
    let target = str.repeat(difficulty);
    println!("{}", target);
}

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

リファクタリングする。

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);

    let mut difficulty = 4;
    let str: String = "0".to_string();
    let difficulty_str = str.repeat(difficulty);

    while target != difficulty_str {
        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: 60678 0000b49915dc1fbb5fc8660d285674dbbe9e8d06f31fc5040b6044b4fb3da86a
2.082390487s

問題ありません。なるほどね。これで、difficultyの値を自動調整できるようになりました。

【Rust】jsonの読み書き

### Jsonの読み込み

use serde::{Serialize, Deserialize};
use std::fs;
use std::io::prelude::*;

#[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 input_fn = fs::read_to_string("test_input.json")
        .expect("JSON Read Failed.");
    let deserialized: Vec<CharaParam> = serde_json::from_str(&input_fn).unwrap();

    for data in &deserialized {
        println!("{:?}", data);
    }
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.67s
Running `target/debug/sample`
CharaParam { Name: “Kiwi”, Id: 3, ItemIdList: [2000, 2001], RotData: Rotator { Pitch: 0.0, Roll: 0.0, Yaw: 0.0 } }
CharaParam { Name: “Orange”, Id: 4, ItemIdList: [2002, 2003], RotData: Rotator { Pitch: 1.0, Roll: 50.2, Yaw: 66.5 } }

### Jsonの書き込み

use serde::{Serialize, Deserialize};
use std::fs::File;
use std::io::prelude::*;

#[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 chara00 = CharaParam{Name:String::from("Apple"), Id:0x01, ItemIdList:vec![1000, 1001], RotData:Rotator{Pitch:0.0, Roll:0.0, Yaw:32.0} };
    let chara01 = CharaParam{Name:String::from("Banana"), Id:0x02, ItemIdList:vec![1002, 1003], RotData:Rotator{Pitch:0.0, Roll:-70.0, Yaw:66.0}};

    let mut param_list:Vec<CharaParam> = Vec::new();
    param_list.push(chara00);
    param_list.push(chara01);

    let output_fn = String::from("test_output.json");
    let result = output_json(&output_fn, param_list);
    match result {
        Ok(..) => { println!("Json output finished.【{}】", output_fn) }
        Err(err) => { println!("Error: {}", err)}
    }

}

fn output_json(output_fn: &str, charaparam_list: Vec<CharaParam>) -> std::io::Result<()>{

    let serialized: String = serde_json::to_string(&charaparam_list).unwrap();

    let mut file = File::create(output_fn)?;
    file.write_all(serialized.as_bytes())?;
    Ok(())
}

読み込み、書き込みはわかったが、書き込み時に、最終行にappendとしたい。。果たして出来るんか??
単純にjsonファイルを読み取って、vectorにデータを追加してjsonファイルに保存で良い?

【Rust】PoWをしてBlockを生成しよう

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);

    let t = vec!["sender".to_string(), "receiver".to_string(), "amount".to_string()];
    let utc_datetime: DateTime<Utc> = Utc::now();
    let b = Block{time:utc_datetime.to_string(), transactions: t, hash: hex::encode(header).to_string(), nonce:num.to_string()};
    println!("{:?}", b);
}

$ cargo run

Block { time: “2025-01-04 00:22:15.386703002 UTC”, transactions: [“sender”, “receiver”, “amount”], hash: “0000cef5d82d7d11f5e90aab7439a128e5877ee6e6b8da31301a6359d74b5503”, nonce: “494683” }

なるほど、こういう仕組みなのか!
うん、悪くないかも
これで、トランザクションの箇所を、Postされてきたデータの構造体に置き換えると…

#[derive(Debug, Serialize, Clone, Deserialize)]
struct Block {
    time: String,
    transactions: Vec<SignedTransaction>,
    hash: String,
    nonce: String,
}

$ cargo run

Block { time: “2025-01-04 00:31:53.015599503 UTC”, transactions: [SignedTransaction { time: “2024-12-25 22:53:36.824066840 UTC”, sender: “5bac6cb0f4ad6397752c3d73b88c5c86e3d88ac695118494a1732e2abd16c76acad3d6586c37c8db7e69c2f812f99275198936957d72c38d71981991123”, receiver: “4bac6cb0f4ad6397752c3d73b88c5c86e3d88ac695118494a1732e2abd16c76acad3d6586c37c8db7e69c2f812f99275198936957d72c38d71981991124”, amount: 10, signature: “8000E340A55A517D0F27F3A63FBE39ED576BA491DFAC89B44654AB147EC66B206B054BAAF53E318EB2721DC892B4736630F400547989AE9F7C069034ECB4DF98” }, SignedTransaction { time: “2024-12-25 22:53:36.824066840 UTC”, sender: “4bac6cb0f4ad6397752c3d73b88c5c86e3d88ac695118494a1732e2abd16c76acad3d6586c37c8db7e69c2f812f99275198936957d72c38d71981991123”, receiver: “4bac6cb0f4ad6397752c3d73b88c5c86e3d88ac695118494a1732e2abd16c76acad3d6586c37c8db7e69c2f812f99275198936957d72c38d71981991124”, amount: 10, signature: “8000E340A55A517D0F27F3A63FBE39ED576BA491DFAC89B44654AB147EC66B206B054BAAF53E318EB2721DC892B4736630F400547989AE9F7C069034ECB4DF98” }], hash: “0000f5b116872033b276499b3e039b03fc4753aaa4e400f2cce856c358d2d51f”, nonce: “4961” }
blockを作成しました。

SignedTransactionがvectorとしてblockの中に入ります。
おおおおおおおおおおおおおお
基本的な形はできたやん
次の課題として、
– blockをjsonデータとして保存する
– genesis blockと生成したblockを繋げていく処理を検討する

【Rust】構造体(struct)の要素にvector

#[derive(Debug, Serialize, Clone, Deserialize)]
struct Name {
    family: String,
    first: String,
    age: u32,
    hobby: Vec<String>,
}

fn main(){
    let n1 = Name {family: "yamada".to_string(), first: "taro".to_string(), age: 20, hobby: vec!["サッカー".to_string(), "旅行".to_string()]};

    println!("{:?}", n1);
}

$ 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`
Name { family: “yamada”, first: “taro”, age: 20, hobby: [“サッカー”, “旅行”] }

VecのところをVec<(構造体名)>でもできる。ちょっとややこしいけど…

use serde::{Serialize, Deserialize};
use once_cell::sync::Lazy;
use std::sync::Mutex;
use std::ops::DerefMut;


#[derive(Debug, Serialize, Clone, Deserialize)]
struct Name {
    family: String,
    first: String,
    age: u32,
    note: String,
}

#[derive(Debug, Serialize, Clone, Deserialize)]
struct list {
    family: String,
    first: String,
    age: u32,
    note: Vec<Name>,
}

static VECT: Lazy<Mutex<Vec<Name>>> = Lazy::new(|| Mutex::new(vec![]));

fn push_name() {
    let n1 = Name {family: "yamada".to_string(), first: "taro".to_string(), age: 20, note: "AAA".to_string()};
    VECT.lock().unwrap().push(n1);
    let n2 = Name {family: "tanaka".to_string(), first: "kazuo".to_string(), age: 18, note: "BBB".to_string()};
    VECT.lock().unwrap().push(n2);
    let n3 = Name {family: "sato".to_string(), first: "hanako".to_string(), age: 22, note: "CCC".to_string()};
    VECT.lock().unwrap().push(n3);
}

fn main(){
    push_name();

    let mut binding = VECT.lock().unwrap();
    let objs = binding.deref_mut();

    let l1 = list {family: "yamada".to_string(), first: "taro".to_string(), age: 20, note: objs.to_vec()};

    println!("{:?}", l1);
}

$ cargo run
Compiling sample v0.1.0 (/home/vagrant/dev/rust/sample)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.28s
Running `target/debug/sample`
Name2 { family: “yamada”, first: “taro”, age: 20, note: [Name { family: “yamada”, first: “taro”, age: 20, note: “AAA” }, Name { family: “tanaka”, first: “kazuo”, age: 18, note: “BBB” }, Name { family: “sato”, first: “hanako”, age: 22, note: “CCC” }] }

うむ、普通に出来ますね。これをマイニング終了のタイミングでBlockとトランザクションで作りたい。

use serde::{Serialize, Deserialize};

#[derive(Debug, Serialize, Clone, Deserialize)]
struct Block {
    time: String,
    transactions: Vec<String>,
    hash: String,
    nonce: String,
}

fn main(){
    let t = vec!["sender".to_string(), "receiver".to_string(), "amount".to_string()];
    let b = Block{time:"a".to_string(), transactions: t, hash:"aaa".to_string(), nonce:"aaa".to_string()};
    println!("{:?}", b);
}

$ cargo run
Compiling sample v0.1.0 (/home/vagrant/dev/rust/sample)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.31s
Running `target/debug/sample`
Block { time: “a”, transactions: [“sender”, “receiver”, “amount”], hash: “aaa”, nonce: “aaa” }

これを作っていきます!

【C++】構造体の中にvectorを定義

### 構造体(struct)の中にVectorを入れようとすると

#include <iostream>
using namespace std;

struct PERSON {
    int age;
    float weight;
    vector<int> id;
} family_member;

int main() {

    PERSON brother;
    brother.age = 20;
    brother.weight = 60.5;
    brother.id =  {7, 4, 0, 8};

    cout << brother.id[0] << endl;
}

$ g++ -o test test.cpp && ./test
test.cpp:7:5: error: ‘vector’ does not name a type
7 | vector id;
| ^~~~~~
test.cpp: In function ‘int main()’:

書き方が間違っていたみたい。以下のように書くと、、、

#include <iostream>
#include <vector>
using namespace std;

struct PERSON {
    int age;
    float weight;
    std::vector<int> id;
} family_member;

int main() {

    PERSON brother;
    brother.age = 20;
    brother.weight = 60.5;
    brother.id =  {7, 4, 0, 8};

    cout << brother.id[0] << endl;
}

$ g++ -o test test.cpp && ./test
7

うん、出来ますね♪ これをRustで書いていきたい。