【Rust】Durationをmillisecondでcountする

.subsec_millis() でミリセカンドで取得できる。
.subsec_nanos() だとナノセカンドで値が大きすぎてしまう。

use std::{time};

    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!("{}", now.elapsed().subsec_millis());

278

【Rust】anyhowを使ったエラーハンドリング

anyhow = “1.0.97”
https://docs.rs/anyhow/latest/anyhow/

anyhowを使うと、エラー処理が簡単にできるようになる。
anyhow::Errorはエラー型のラッパー、あらゆるエラー型からanyhow::Errorに変換できる。変換できる。
Resultにすると、?演算子で異なる複数の型のエラーを呼び出し元に移譲できる。
contextやwith_contextを使うと、Noneをanyhow::Errorとして移譲することも可能
anyhow::ResultがResultの型エイリアスとして定義されているので、use anyhow::Result;とすると、ResultをResultと書ける。

use anyhow::{ensure, Context, Result};
use std::fs;

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

    let path_123 = "./data/temp/123.txt";
    println!("{}", with_unwrap(&path_123));
    
    // let path_abc = "./data/temp/abc.txt";
    // println!("{}", with_unwrap(&path_abc));

    let path_non_existent = "./data/to/non_existent.txt";
    println!("{}", with_unwrap(&path_non_existent));
}

fn with_unwrap(path: &str) -> i32 {
    let s = fs::read_to_string(path).unwrap();
    let i = s.parse::<i32>().unwrap();

    i * 10
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.76s
Running `target/debug/app`
1230
thread ‘main’ panicked at src/main.rs:16:30:
called `Result::unwrap()` on an `Err` value: ParseIntError { kind: InvalidDigit }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

called `Result::unwrap()` on an `Err` value: Os { code: 2, kind: NotFound, message: “No such file or directory” }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
vagrant@vagrant:~/dev/rust/app$

エラーメッセージが、InvalidDigit、No such file or directoryでそれぞれ異なる。

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

    let path_123 = "./data/temp/123.txt";
    println!("{}", with_unwrap(&path_123).unwrap());
    
    let path_abc = "./data/temp/abc.txt";
    println!("{:?}", with_unwrap(&path_abc).unwrap_err());

    let path_non_existent = "./data/to/non_existent.txt";
    println!("{:?}", with_unwrap(&path_non_existent).unwrap_err());
}

fn with_unwrap(path: &str) -> Result<i32> {
    let s = fs::read_to_string(path)?;
    let i = s.parse::<i32>()?;

    Ok(i * 10)
}

1230
invalid digit found in string
No such file or directory (os error 2)

### anyhow::Contextでコンテキストを追加
with_contextは遅延評価でエラーの時のみ指定

fn with_unwrap(path: &str) -> Result<i32> {
    let s = fs::read_to_string(path).with_context(|| format!("Failed to read {path:?}"))?;
    let i = s.parse::<i32>()
            .with_context(|| format!("Failed to parse {s:?}"))?;

    Ok(i * 10)
}

1230
Failed to parse “abc”

Caused by:
invalid digit found in string
Failed to read “./data/to/non_existent.txt”

Caused by:
No such file or directory (os error 2)

### OptionをResultに変換
contextおよび with_contextはResultだけでなく、Optionからも呼べる。Noneも呼び出し元に移譲できる。

async fn main()  {

    let path_max = "data/temp/max.txt";
    fs::write(path_max, i32::MAX.to_string()).unwrap();
    println!("{:?}", with_anyhow_option(path_max).unwrap_err());

    let path_123 = "./data/temp/123.txt";
    println!("{}", with_unwrap(&path_123).unwrap());
    
    let path_abc = "./data/temp/abc.txt";
    println!("{:?}", with_unwrap(&path_abc).is_err());

    let path_non_existent = "./data/to/non_existent.txt";
    println!("{:?}", with_unwrap(&path_non_existent).is_err());
}

fn with_anyhow_option(path: &str) -> Result<i32> {
    let s = fs::read_to_string(path).with_context(|| format!("Failed to read {path:?}"))?;
    let i = s.parse::<i32>()
            .with_context(|| format!("Failed to parse {s:?}"))?;
    let i = i.checked_add(100).context("Overflow occurred")?;

    Ok(i)
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.86s
Running `target/debug/app`
Overflow occurred
1230
true
true

### マクロ(anyhow!, bail!, ensure!)で単発のエラーを返す
ensureで値に応じてエラーとすることもできる。

fn with_anyhow_macro(path: &str) -> Result<i32> {
    let s = fs::read_to_string(path).with_context(|| format!("Failed to read {path:?}"))?;
    let i = s.parse::<i32>()
            .with_context(|| format!("Failed to parse {s:?}"))?;
    let i = i.checked_add(100).context("Overflow occurred")?;

    ensure!(i >= 1000, "Value must be at least 1000, got {}", i);

    Ok(i)
}

match resultでの処理

    let path_123 = "./data/temp/123.txt";
    let result = with_anyhow_macro(&path_123);
    match result {
        Ok(n) => {
            println!("{}", n);
        }
        Err(error) => {
            println!("{}", error);
        }
    }

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.81s
Running `target/debug/app`
Value must be at least 1000, got 223

【JavaScript】ファイルの更新があれば、値を更新する

setTimeoutでループ処理ができる。
data.split(“\n”).lengthで、ファイルの行数を取得できる。

async function fetchData() {
  try {
    fetch('./data/names.txt')
    .then(response => response.text())
    .then(data => {
        console.log('テキストファイルの内容:');
        console.log(data.split("\n").length - 1);
        document.getElementById('target').textContent = data.split("\n").length - 1;
    })
    .catch(error => {
        console.error('エラー:', error);
    });
  } catch (error) {
    console.error('リクエストエラー:', error);
  }
  setTimeout(fetchData, 3000);
}
fetchData();

これだと、更新自体はできますが、データをpublicな場所には置きたくないですね…

【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”

【Python】シャミアの秘密計算

import random
from typing import List, Tuple

def generate_coefficients(secret: int, threshold: int) -> List[int]:
    coefficients = [secret]
    for _ in range(threshold - 1):
        coefficients.append(random.randint(1, 256))
    return coefficients

def create_shares(
    secret: int, total_shares: int, threshold: int
) -> List[Tuple[int, int]]: 
    coefficients = generate_coefficients(secret, threshold)
    shares = []
    for x in range(1, total_shares + 1):
        y = sum(coeff * (x**exp) for exp, coeff in enumerate(coefficients))
        shares.append((x, y))
    return shares

def reconstruct_secret(shares: List[Tuple[int, int]], threshold: int) -> int:
    def _lagrange_interpolation(x: int, x_s: List[int], y_s: List[int]) -> int:
        def _basis(j: int) -> int:
            num = 1
            den = 1
            for m in range(len(x_s)):
                if m != j:
                    num *= x - x_s[m]
                    den *= x_s[j] - x_s[m]
            return num // den

        result = 0
        for j in range(len(y_s)):
            result += y_s[j] * _basis(j)
        return result

    x_s, y_s = zip(*shares)
    return _lagrange_interpolation(0, x_s, y_s)

if __name__ == "__main__":
    secret = 2732
    total_shares = 5
    threshold = 2

    shares = create_shares(secret, total_shares, threshold)
    print("shares:", shares)

    selected_shares = shares[:threshold]
    print(zip(*selected_shares))
    x_s, y_s = zip(*selected_shares)
    print(x_s, y_s)
    recovered_secret = reconstruct_secret(selected_shares, threshold)
    print("Recovered Secret:", recovered_secret)

$ python3 test.py
shares: [(1, 2961), (2, 3190), (3, 3419), (4, 3648), (5, 3877)]

(1, 2) (2961, 3190)
Recovered Secret: 2732

【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

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

【PSQL16】Ident authentication failed for user “postgres”

以下のようなエラーが出る時
$ python3 migration.py
Traceback (most recent call last):
File “/home/ec2-user/cmc_test/assets/migration.py”, line 8, in
connection = psycopg2.connect(
File “/home/ec2-user/cmc_test/venv/lib64/python3.9/site-packages/psycopg2/__init__.py”, line 122, in connect
conn = _connect(dsn, connection_factory=connection_factory, **kwasync)
psycopg2.OperationalError: connection to server at “localhost” (127.0.0.1), port 5432 failed: FATAL: Ident authentication failed for user “postgres”

sudo vi /var/lib/pgsql/data/pg_hba.conf
identからtrustに変更します。

# IPv4 local connections:
# host    all             all             127.0.0.1/32            ident
host    all             all             127.0.0.1/32            trust

$ sudo service postgresql restart
$ python3 migration.py

ふぅ~

【Ubuntu】conoha VPSのport開放の設定

conohaコンソール側のセキュリティグループで指定したポートを開放してもアクセスできない時。
Ubuntu側の設定を確認する。

# sudo ufw status
Status: active

To Action From
— —— —-
OpenSSH ALLOW Anywhere
OpenSSH (v6) ALLOW Anywhere (v6)

OpenSSHしか許可されていないので、開放したいportを追加する。
# sudo ufw allow 3000/tcp
Rule added
Rule added (v6)

# sudo ufw status
Status: active

To Action From
— —— —-
OpenSSH ALLOW Anywhere
3000/tcp ALLOW Anywhere
OpenSSH (v6) ALLOW Anywhere (v6)
3000/tcp (v6) ALLOW Anywhere (v6)

焦った……

【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

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