【Rust】シャミアの秘密計算(SSS)をRustで動かしてみる

private keyを複数のシェアに分割して、それをrestoreして元のprivate keyに戻すということは割と簡単にできそうではある。

shamirsecretsharing = “0.1.5”

use shamirsecretsharing::*;

async fn main()  {

    let private_key = "i3HdSBYb5fmuD4RxAf77VnYbyAy6b8ab5LEddfQLM2VaYrUUFNNp6Pk22dsTAibj".to_string();
    let data = private_key.into_bytes();
    println!("{:?}", data);

    let count = 3;
    let treshold = 2;
    let mut shares = create_shares(&data, count, treshold).unwrap();
    
    for share in shares.clone() {
        print_base_encode(share);
    }

    let restored = combine_shares(&shares.clone()).unwrap();
    println!("{:?}", String::from_utf8(restored.unwrap()).unwrap());
}

fn print_base_encode(s: Vec<u8>) {
    // println!("{:?}", s.clone());
    let base64_encoded = base64::encode(&s);
    println!("base64 encoded: {}", base64_encoded);
    // println!("{:?}", BASE64_STANDARD.decode(base64_encoded).unwrap());
}

base64 encoded: AZv7eBLYhiBxgjpD3kI0YbLnnC88DnTWPghszvPWHLt4D+dFwsC4CE7R2mlSAlDsuituyPlTHSzE7rSNJylQvj1kn7sHEhEDqxC5b0qEj0SjfkVoaPcgdXdHOmX8rhOjO+xy9n5qlHpAcLJDHLcj2hA=

base64 encoded: AhM+urNxZnzJsekDjygSpCqresvZz1Kg/zFE8/zdbLkWD+dFwsC4CE7R2mlSAlDsuituyPlTHSzE7rSNJylQvj1kn7sHEhEDqxC5b0qEj0SjfkVoaPcgdXdHOmX8rhOjO+xy9n5qlHpAcLJDHLcj2hA=

base64 encoded: A2t9DSUWz0ihoFHKSQ7556tm0Zdzebl7SSZcEfkttU7FD+dFwsC4CE7R2mlSAlDsuituyPlTHSzE7rSNJylQvj1kn7sHEhEDqxC5b0qEj0SjfkVoaPcgdXdHOmX8rhOjO+xy9n5qlHpAcLJDHLcj2hA=
“i3HdSBYb5fmuD4RxAf77VnYbyAy6b8ab5LEddfQLM2VaYrUUFNNp6Pk22dsTAibj”

【Rust】reqwestによるPostのレスポンスタイムの差

幾つかのサイトにデータをPostして、レスポンスタイムに差があるかを調査する。

    let now = time::Instant::now();
    let mut post_url = format!("http://www.jsontest.com/");
    let client = reqwest::Client::new();
    let resp = client.post(post_url)
        .header(reqwest::header::CONTENT_TYPE, "application/json")
        .json(&str)
        .send()
        .await
        .unwrap();
    println!("{:?}", resp);
    println!("{:?}", now.elapsed());

↓レスポンスタイムの差

同じサーバ内
67.997892ms
76.798063ms
73.945108ms

http://httpbin.org/post
626.023117ms
560.575466ms
1.050126063s

https://dummyjson.com/posts
695.071869ms
825.34323ms
676.196368ms

http://www.jsontest.com/
229.820077ms
256.854971ms
203.667686ms

当たり前だが、同じサーバ内のレスポンスは早い。
それ以外でも、サイトごとによって、レスポンスタイムが早いものと遅いものの違いが生じている。
なるほど、なかなか面白いね。

【Rust】バイト列からリトルエンディアンへの変換

    let s = "sato".to_string().into_bytes();
    println!("{:?}", s);
    let l = LittleEndian::read_u32(&s);
    println!("{}", l);
    let b = BigEndian::read_u32(&s);
    println!("{}", b);

    let mut wtr = vec![];
    wtr.write_u32::<LittleEndian>(l).unwrap();
    println!("{:?}", wtr);

[115, 97, 116, 111]
1869898099
1935766639
[115, 97, 116, 111]

構造体をバイト列にする

#[derive(Serialize, Deserialize, Debug)]
struct Name {
    family: Vec<u8>,
    first: Vec<u8>,
}

    let name1 = Name {family: "sato".to_string().into_bytes(), first: "taro".to_string().into_bytes()};
    println!("{:?}", name1);
    let family = String::from_utf8(name1.family).unwrap();
    println!("{}", family);

Name { family: [115, 97, 116, 111], first: [116, 97, 114, 111] }
sato

なるほどー 前処理、後処理が多くなるが、安全性は高まりそう。

【Rust】reqwest Postのレスポンス(body payload)を表示する

“{:?}”, resp だとstatusだが、resp.text().await.unwrap()とすると、bodyを表示できる。

    let mut post_url = format!("http://192.168.33.10:3000/health");
    let client = reqwest::Client::new();
    let resp = client.post(post_url)
        .header(reqwest::header::CONTENT_TYPE, "application/json")
        .json(&str)
        .send()
        .await
        .unwrap();
    println!("{:?}", resp);
    println!("{}", resp.text().await.unwrap());

Finished `dev` profile [unoptimized + debuginfo] target(s) in 7.43s
Running `target/debug/axum`
Response { url: Url { scheme: “http”, cannot_be_a_base: false, username: “”, password: None, host: Some(Ipv4(192.168.33.10)), port: Some(3000), path: “/health”, query: None, fragment: None }, status: 200, headers: {“content-type”: “text/plain; charset=utf-8”, “content-length”: “8”, “date”: “Thu, 13 Mar 2025 13:44:24 GMT”} }
All good

【Rust】tokio::spawnのawait

下のように書くと、DNSエラーとなる。

#[tokio::main]
async fn main()  {
    let res = tokio::spawn(async move {
        let v = get_myip().await.unwrap();
        println!("{}", v);
    });    
}   

pub async fn get_myip() -> Result<String, Box<dyn std::error::Error>> {

    let ip: Ip = serde_json::from_str(&reqwest::get("https://httpbin.org/ip")
        .await?
        .text()
        .await?)?;
    Ok(ip.origin)
}

しかし、実行されるまで待って、res.await.unwrap();とすれば、エラーは解消される。

    let res = tokio::spawn(async move {
        let v = get_myip().await.unwrap();
        println!("{}", v);
    });
    res.await.unwrap();

レスポンスがないままtokio::spawnが終了してしまうからっぽい。
うーん、奥が深い… httpbin.org に問題があるかと思ったけど、全然違った…

【Rust】websocketのreceive

echoサーバにリクエストを送り、このレスポンスが、”foo”なので、received_msgがfooだと思っていたが、

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut ws = WebSocket::connect("wss://echo.websocket.org/").await?;
    ws.send_text("foo".to_string()).await?;
    ws.receive().await?;
    if let Frame::Text { payload: received_msg, .. } =  ws.receive().await? {
        println!("{}", received_msg);
    }
    ws.close(None).await?;
    Ok(())
}  

Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.60s
Running `target/debug/app`
foo

以下のように書くと、”Request served by *****”がレスポンスで帰ってくる。websocketのreceiveは以下のように書くのが正解

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut ws = WebSocket::connect("wss://echo.websocket.org/").await?;
    ws.send_text("foo".to_string()).await?;
    if let Frame::Text { payload: received_msg, .. } =  ws.receive().await? {
        println!("{}", received_msg);
    }
    ws.close(None).await?;
    Ok(())
}   

Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.07s
Running `target/debug/app`
Request served by 7811941c69e658

【Rust】ファイルの上書き処理

File::createとwrite_allで元のデータは上書かれる。
例えば、元データが4行で、上書きデータが2行だった場合でも、上書き後は2行になる。

{"family":"john","first":"fox"}
{"family":"adam","first":"can"}
{"family":"john","first":"fox"}
{"family":"adam","first":"can"}
use serde::{Serialize, Deserialize};
use std::path::Path;
use std::fs::{File};
use std::io::{Write};

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

#[tokio::main]
async fn main() {

    let name1 = Name {family:"yamada".to_string(), first: "taro".to_string()};
    let name2 = Name {family:"sato".to_string(), first: "kazuki".to_string()};

    let mut vect: Vec<Name> = Vec::new();
    vect.push(name1);
    vect.push(name2);

    let file_path = format!("./data/names.txt");
    let mut file = File::create(file_path.clone()).unwrap();
    for name in vect {
        let serialized: Vec<u8> = serde_json::to_vec(&name).unwrap();
        file.write_all(&serialized).expect("write failed");
        file.write_all(b"\n").expect("write failed");
    }
    println!("done");

}  
{"family":"yamada","first":"taro"}
{"family":"sato","first":"kazuki"}

なるほどね!

【ubuntu2204】error: linking with `cc` failed: exit status: 1

$ cargo run

error: linking with `cc` failed: exit status: 1
|
= note: LC_ALL=”C” PATH=”/home/ubuntu/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/bin:/home/ubuntu/.local/bin:/home/ubuntu/.cargo/bin:/usr/local/sbin:
//

“-nodefaultlibs”
= note: some arguments are omitted. use `–verbose` to show all linker arguments
= note: collect2: fatal error: ld terminated with signal 9 [Killed]
compilation terminated.

最後に、「collect2: fatal error: ld terminated with signal 9 [Killed]」と表示されていますが、これが、メモリ不足のエラーの模様
https://stackoverflow.com/questions/46259776/collect2-fatal-error-ld-terminated-with-signal-9-killed

swapファイルを作成すると、エラーが消えます。
$ sudo fallocate -l 1G /swapfile
$ ls -lh /swapfile
$ sudo chmod 600 /swapfile
$ sudo mkswap /swapfile
$ sudo swapon /swapfile
$ sudo swapon –show
$ free -h

【Rust】日にち、時間差の計算

Unix Timeに変換して計算する。UnixTimeは秒単位のため、1日の場合は、60 * 60 * 24 = 86400となる。

    let utc_datetime: DateTime<Utc> = DateTime::parse_from_rfc3339("2025-01-01T00:00:00Z").unwrap().into();
    println!("{}", utc_datetime.timestamp());
    let utc_datetime2: DateTime<Utc> = DateTime::parse_from_rfc3339("2025-01-02T00:00:00Z").unwrap().into();
    println!("{}", utc_datetime2.timestamp());

    // 86400
    println!("{}", utc_datetime2.timestamp() - utc_datetime.timestamp());

Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.24s
Running `target/debug/app`
1735689600
1735776000
86400

経過時間は、unixtimeの差を86400で割れば日数となるので、

    let base_date = "2025-01-01T00:00:00Z".to_string();
    let utc_datetime: DateTime<Utc> = DateTime::parse_from_rfc3339(&base_date).unwrap().into();
    let utc_datetime2: DateTime<Utc> = Utc::now();
    println!("{}", utc_datetime2.timestamp());
    println!("{}", (utc_datetime2.timestamp() - utc_datetime.timestamp()) / 86400);

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.67s
Running `target/debug/app`
1741587141
68

1月1日から、68日経過していることになる。

【Rust】Vectorのstructを1行ずつファイルに書き込む

async fn handle_index()-> Response {
    let mut name_list: Vec<Name> = Vec::new();
    let name1 = Name { family: "tanaka".to_string(), first: "taro".to_string(), age: 12};
    let name2 = Name { family: "yamada".to_string(), first: "koji".to_string(), age: 13};
    let name3 = Name { family: "okamoto".to_string(), first: "ai".to_string(), age: 14};
    name_list.push(name1);
    name_list.push(name2);
    name_list.push(name3);
    let name_list_serialized = serde_json::json!(&name_list);

    let mut post_url = format!("http://192.168.33.10:3000/fetch");
    let client = reqwest::Client::new();
    let resp = client.post(post_url)
        .header(reqwest::header::CONTENT_TYPE, "application/json")
        .json(&name_list_serialized)
        .send()
        .await
        .unwrap();
    // println!("{:?}", resp);

    "All good".into_response()
}

async fn handle_fetch(extract::Json(names): extract::Json<Vec<Name>>)-> Response {

    println!("get names: {:?}", names);

    let file_path = format!("./data/names.txt");
    if !Path::new(&file_path.clone()).exists() {
        let mut file = File::create(file_path.clone()).unwrap();
        for name in names {
            let serialized: Vec<u8> = serde_json::to_vec(&name).unwrap();
            file.write_all(&serialized).expect("write failed");
            file.write_all(b"\n").expect("write failed");
        }
        println!("done");
    } else {
        println!("file exist");
    }
    "All good".into_response()
}

names.txt
{“family”:”tanaka”,”first”:”taro”,”age”:12}
{“family”:”yamada”,”first”:”koji”,”age”:13}
{“family”:”okamoto”,”first”:”ai”,”age”:14}

うん、期待通りです。