【Rust】sha256を使用する

[dependencies]
sha2 = "0.10.8"
use sha2::{Digest, Sha256};

fn main() {
    let data = b"hello world";
    let hash = Sha256::digest(data);
    println!("{:x}", hash);

    let data2 = b"hello world";
    let hash2 = Sha256::digest(data2);
    println!("{:x}", hash2);
}

Running `target/debug/crypt`
b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9

うーむ、そのまんまな感じがしますね…

【Rust】Rustでpsqlを操作する

$ cargo new psql

crateはpostgresを使用する
https://docs.rs/postgres/latest/postgres/#example

[dependencies]
postgres = "0.19.9"
use postgres::{Client, NoTls};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = Client::connect("host=localhost user=postgres password=password", NoTls)?;

    client.batch_execute("
        CREATE TABLE person (
            id SERIAL PRIMARY KEY,
            name TEXT NOT NULL,
            data BYTEA
        )
    ")?;

    let name = "Ferris";
    let data = None::<&[u8]>;
    client.execute(
        "INSERT INTO person (name, data) VALUES ($1, $2)",
        &[&name, &data],
    )?;

    for row in client.query("SELECT id, name, data From person", &[])? {
        let id: i32 = row.get(0);
        let name: &str = row.get(1);
        let data: Option<&[u8]> = row.get(2);

        println!("found person: {} {} {:?}", id, name, data);
    }
    Ok(())
}

$ cargo run
Compiling psql v0.1.0 (/home/vagrant/dev/rust/psql)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.54s
Running `target/debug/psql`
found person: 1 Ferris None

おおおおおおおおおお
create tableはrustではなく、setup.shでpythonで実行して、rustではinsertとselectのみを実行するようにしたいですね。

setup.sh

pip install psycopg2-binary
yes | sudo apt install libpq-dev
pip install psycopg2

python3 init.py

init.py

import psycopg2

connection = psycopg2.connect(
    host='localhost',
    user='postgres',
    password='password'
)

cur = connection.cursor()

cur.execute('CREATE TABLE person ( \
            id SERIAL PRIMARY KEY, \
            name TEXT NOT NULL, \
            data BYTEA \
        );')

connection.commit()
cur.close()
connection.close()

$ cargo run
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.17s
Running `target/debug/psql`
found person: 1 Ferris None

転送元ノードのIPを取得してDBに入れようと思ったけど、ちょっと難しいな…

【psql】ubuntu24.04にpostgresをインストール/アンインストール

$ cat /etc/os-release
PRETTY_NAME=”Ubuntu 22.04.3 LTS”
$ psql –version
bash: psql: command not found

### インストール
$ sudo apt install postgresql postgresql-contrib
$ psql –version
psql (PostgreSQL) 14.15 (Ubuntu 14.15-0ubuntu0.22.04.1)

### アンインストール
$ dpkg -l | grep postgres
ii postgresql 14+238 all object-relational SQL database (supported version)
ii postgresql-14

$ sudo apt remove –purge postgresql
$ sudo apt remove –purge postgresql-14
$ sudo apt remove –purge postgresql-client-14
$ sudo apt remove –purge postgresql-client-common
$ sudo apt remove –purge postgresql-common
$ sudo apt remove –purge postgresql-contrib
$ dpkg -l | grep postgres
$ psql –version
bash: /usr/bin/psql: No such file or directory

【shell】基礎から立ちかえる3

function test_func() {
    local var1
    var1=aaa

    local var2=bbb
}
function echo_self_args()
{
    echo $1
    echo "$2"
    echo ${3}
    echo "${4}"
    echo $#
    echo "$@"
}

echo_self_args arg1 arg2 'arg3 arg4' arg5
function echo_value1()
{
    local var1=local1
    echo ${FUNCNAME[0]} ':$var1:1' $var1

    echo ${FUNCNAME[0]} ':$var2:1' $var1
}

function echo_value2()
{
    local var1=local2
    echo ${FUNCNAME[0]} ':$var1:1' $var1

    local var2=local2
    echo ${FUNCNAME[0]} ':$var2:1' $var1

    echo_value1
    echo ${FUNCNAME[0]} ':$var1:1' $var1
    echo ${FUNCNAME[0]} ':$var2:1' $var1
}

var1=global
echo 'global :$var1:1:' $var1
echo 'global :$var2:1:' $var2

echo_value2
echo 'global :$var1:2:' $var1
echo 'global :$var2:2:' $var2
function return_test()
{
    if [[ -z $1 ]]; then
        echo 'arg1 is empty.'
        return 1
    fi

    echo $1
}

return_test test
echo '終了ステータス': $?
echo ---
return_test 
echo '終了ステータス': $?
echo 'Hello' > hello.txt
ls /bin /error > bin.txt 2> error.txt
ls /bin /error &> result.txt
tr hoge fuga < hoge.txt
var1=value
text=$(cat << EOF
    arg1: $1
    var1: $var1
EOF
)
echo "$text"
echo first message1 > output.txt
echo second message1 >> output.txt
echo third message1 >> output.txt

{
    echo first message2
    echo second message2
    echo third message2
} > output.txt

{ echo first message3;echo second message3;echo third message3; } > output.txt
cd /bin
pwd
var1=value1
echo $var1

(
    cd /home/vagrant
    pwd
    echo $var1
    var1=value2
    echo $var1
)

pwd
echo $var1
set -e

ls /error
mkdir /error/dir1
set -u

rm -rf $work_dir/
set -C

touch test1.txt
echo "aaa" >| test1.txt
echo "bbb" >> test1.txt

touch test2.txt
echo "ccc" > test2.txt

【shell】基礎から立ちかえる2

array=(item1 item2 'item3 item4' item5)

echo '${array[0]}': ${array[0]}
echo '${array[1]}': ${array[1]}
echo '${array[2]}': ${array[2]}
echo '${array[3]}': ${array[3]}
array=(item1 item2 'item3 item4' item5)

echo ${#array[@]}
array=(item0 [2]=item2 [4]=item4)

echo ${#array[@]}
echo ${array[0]}
echo ${array[1]}
echo ${array[2]}
echo ${array[3]}
echo ${array[4]}
echo ------------------

array[1]=item1
array[2]=
echo ${#array[@]}
echo ${array[0]}
echo ${array[1]}
echo ${array[2]}
echo ${array[3]}
echo ${array[4]}
echo ------------------
array=(item1 item2 'item3 item4' item5)

function echo_array_items() {
    echo $1
    echo $2
    echo $3
    echo $4
    echo $5
    echo ------------------------
}

echo Use '"${array[@]}"'
echo_array_items "${array[@]}"

echo Use '"${array[*]}"'
echo_array_items "${array[*]}"

echo Use '${array[@]}'
echo_array_items ${array[@]}

echo Use '${array[*]}'
echo_array_items ${array[*]}
array=(item1 item2 item3)
echo "${array[@]}"

array2=(item_a item_b "${array[@]}")
echo "${array2[@]}"

array3=("${array[@]}" item_c item_d)
echo "${array3[@]}"

array+=(item_e item_f)
echo "${array[@]}"
array=(item0 [2]=item2 [4]=item4)
echo "${!array[@]}"
if grep -n test test.txt; then
    echo $?
    echo success
else
    echo $?
    echo fail
fi
test "$1" = "test"
echo 'test コマンドの終了ステータス': $?

["$1" = "test"]
echo '[ コマンドの終了ステータス':$?

if ["$1" = "test"]; then
    echo success
else
    echo fail
fi
if [ \( "$1" = "a" -o "$2" = "b" \) -a -f test.txt ]; then
    echo '第一引数がaまたは第二引数がbで、かつtest.txtが存在しています'
fi
if [[ ("$1" = "a" || "$2" = "b" ) && -f test.txt ]]; then
    echo '第一引数がaまたは第二引数がbで、かつtest.txtが存在しています'
fi
var=$1
if [[ $var = 'hoge fuga' ]]; then
    echo success
else
    echo fail
fiv
case "$file" in
    *.csv)
        echo this is csv
        ;;
    special-* | important-*)
        echo this is special file
        ;;
    *)
        echo "Invalid file: $file"
esac
for arg in "$@"; do
    echo $arg
done
array=(aaa 'bbb ccc' ddd)
for element in "${array[@]}"; do
    echo $element
done
while [[ $# -gt 0 ]]; do
    echo $1
    shift
done
until [[ $# -eq 0 ]]; do
    echo $1
    shift
done

【shell】基礎から立ちかえる1

#!/bin/bash

echo 'hello world!'

$ chmod +x stest.sh
$ ./stest.sh
hello world!

#!/bin/bash

echo 'hello world!'
pwd
echo \
    'I' \
    'love' \
    'shell' \
    'script' \
echo
'End world!'
#!/bin/bash

var='value'
echo $var

var='change'
echo $var
#!/bin/bash

readonly var='value'
var='fuga'
var='set'
echo $var
unset var
echo $var
var=value
echo $var
var='hoge fuga'
echo $var
var=hoge fuga
echo $var
empty=''
echo \$empty: $empty
empty=
echo \$empty: $empty
echo \$undefined: $undefined
var=value
echo $var
echo \$var
echo \$var $var
echo \\

var=hoge\ fuga
echo $var
var1=value
var2='${var1}'
echo $var2
var1=value
var2="${var1}"
echo $var2
var1='hoge''fuga'
echo '$var1': $var1

var2=hoge
var3=$var1$var2
echo '$var3': $var3

echo "\$var1hoge": $var1hoge
echo "\$var1'hoge'": $var1'hoge'
echo "\${var1}hoge": ${var1}hoge
echo "\${var1}'hoge'": ${var1}'hoge'
echo '$1': $1
echo '${1}': ${2}
echo '"$3"': "$3"
echo '${10}': ${10}
echo '${11}': ${11}
echo '$10': $10
echo $0
echo $1
echo '入力値' $1
exit $1
echo ----------
echo 引数の数 $#
echo '$1'の値 $1
echo '$2'の値 $2
shift
echo $?
echo ----------
echo 引数の数 $#
echo '$1'の値 $1
echo '$2'の値 $2
shift
echo $?
echo ----------
echo 引数の数 $#
echo '$1'の値 $1
echo '$2'の値 $2
shift
echo $?
echo ----------
function echo_args() {
    echo '引数の数' $#
    echo '$1'の値 $1
    echo '$2'の値 $2
    echo '$3'の値 $3
    echo ----------
}

echo '"$@"'の場合
echo_args "$@"
echo '"$*"'の場合
echo_args "$*"
echo $$

(sleep 10; echo 'end') &
echo $!
wait $!

【Rust】グローバル(static)で変更可能なベクタ

クラスの外部から静的メンバ変数を定義するには「クラス名+”::”+静的メンバ変数名」という名前のグローバル変数を定義する。

static data: &str = "this is static variable";

fn func1(){
    println!("func1: {:?}", data);
}

fn func2(){
    println!("func2: {:?}", data);
}

fn main(){
    
    func1();
    func2();
    println!("main: {:?}", data);
}

$ ./main
func1: “this is static variable”
func2: “this is static variable”
main: “this is static variable”
staticな変数を宣言することはできるが、mutableにできない。

### once_cell
once_cellを使用すると、mutableなstaticを作成できる。

use once_cell::sync::Lazy;
use std::sync::Mutex;

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

fn do_a_call(){
    ARRAY.lock().unwrap().push(1);
}

fn main() {
    do_a_call();
    do_a_call();
    do_a_call();

    println!("called {}", ARRAY.lock().unwrap().len());
}

vectorの型をu8ではなく構造体にして、一定数以上になったら関数を実行するように編集する

use once_cell::sync::Lazy;
use std::sync::Mutex;

struct Transaction {
    x: String,
    y: String,
    z: u32,
}

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

fn make_block (){
    println!("blockを作成しました。");
    Pool.lock().unwrap().clear();

}

fn do_a_call(){
    let t = Transaction { x: "A".to_string(), y: "B".to_string(), z: 10};
    if Pool.lock().unwrap().len() > 4 {
        make_block();
    }
    Pool.lock().unwrap().push(t);
}

fn main() {
    do_a_call();
    do_a_call();
    do_a_call();
    do_a_call();
    do_a_call();
    do_a_call();
    do_a_call();

    println!("called {}", Pool.lock().unwrap().len());
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.25s
Running `target/debug/my_web_app`
blockを作成しました。
called 2

なるほど、やりたいことはできました。これをPostされたトランザクションに対して、トランザクションプールに入れるように実装します。

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

fn make_block (){
    println!("blockを作成しました。");
    Pool.lock().unwrap().clear();
}

#[post("/handshake")]
async fn handshake(req_body: String)-> impl Responder {
    let req : SignedTransaction = serde_json::from_str(&req_body).unwrap();
    Pool.lock().unwrap().push(req.clone());
    if Pool.lock().unwrap().len() > 1 {
        make_block();
    }
    println!("{}", req.signature);
    HttpResponse::Ok().body(req_body)

}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 4.94s
Running `target/debug/hoge`
8000E340A55A517D0F27F3A63FBE39ED576BA491DFAC89B44654AB147EC66B206B054BAAF53E318EB2721DC892B4736630F400547989AE9F7C069034ECB4DF98
blockを作成しました。
8000E340A55A517D

おおおおおおお、イイネ!

【Rust】Rustでトランザクションの受信

Post requestを受け取るにはactix-webを使う。
まず、受信のテストから。

use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};

#[post("/handshake")]
async fn handshake(req_body: String)-> impl Responder {
    println!("{}", req_body);
    HttpResponse::Ok().body(req_body)

}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(||{
        App::new()
            .service(handshake)
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

postする
$ curl -X POST -d “{\”time\”:\”2024-12-25 22:53:36.824066840 UTC\”,\”sender\”:\”4bac6cb0f4ad6397752c3d73b88c5c86e3d88ac695118494a1732e2abd16c76acad3d6586c37c8db7e69c2f812f99275198936957d72c38d71981991123\”,\”receiver\”:\”4bac6cb0f4ad6397752c3d73b88c5c86e3d88ac695118494a1732e2abd16c76acad3d6586c37c8db7e69c2f812f99275198936957d72c38d71981991124\”,\”amount\”:10,\”signature\”:\”8000E340A55A517D0F27F3A63FBE39ED576BA491DFAC89B44654AB147EC66B206B054BAAF53E318EB2721DC892B4736630F400547989AE9F7C069034ECB4DF98\”}”

サーバ側
Finished `dev` profile [unoptimized + debuginfo] target(s) in 3.70s
Running `target/debug/hoge`
{“time”:”2024-12-25 22:53:36.824066840 UTC”,”sender”:”4bac6cb0f4ad6397752c3d73b88c5c86e3d88ac695118494a1732e2abd16c76acad3d6586c37c8db7e69c2f812f99275198936957d72c38d71981991123″,”receiver”:”4bac6cb0f4ad6397752c3d73b88c5c86e3d88ac695118494a1732e2abd16c76acad3d6586c37c8db7e69c2f812f99275198936957d72c38d71981991124″,”amount”:10,”signature”:”8000E340A55A517D0F27F3A63FBE39ED576BA491DFAC89B44654AB147EC66B206B054BAAF53E318EB2721DC892B4736630F400547989AE9F7C069034ECB4DF98″}

受信できてます。
ここで受け取ったデータからverifyすればOKですね。

use serde::{Serialize, Deserialize};

#[derive(Debug, Serialize, Deserialize)]
struct SignedTransaction {
    time: String,
    sender: String,
    receiver: String,
    amount: i32,
    signature: String,
}

#[post("/handshake")]
async fn handshake(req_body: String)-> impl Responder {
    let req : SignedTransaction = serde_json::from_str(&req_body).unwrap();;
    println!("{}", req.signature);
    HttpResponse::Ok().body(req_body)

}

Postされたデータはjsonにして、SignedTransactionの構造体で受け取ると、req.signatureというように、値を取り出すことができる。

【Rust】Rustでトランザクションを送信する

use serde::{Serialize, Deserialize};
use std::io::prelude::*;
use hex_literal::hex;
use k256::{ecdsa::{SigningKey, Signature, signature::Signer, signature::Verifier, VerifyingKey}};
use chrono::{Utc, Local, DateTime, Date};

#[derive(Serialize, Deserialize, Debug)]
struct UnsignedTransaction {
    time: String,
    sender: String,
    receiver: String,
    amount: i32,
}

#[derive(Serialize, Deserialize, Debug)]
struct SignedTransaction {
    time: String,
    sender: String,
    receiver: String,
    amount: i32,
    signature: String,
}

fn hex(bytes: &[u8]) -> String {
    bytes.iter().fold("".to_owned(), |s, b| format!("{}{:x}", s, b))
}

#[async_std::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {

    let private_key: SigningKey = SigningKey::from_bytes(&hex!(
        "DCDFF4B7CA287CC7BD30ECAEF0622265DB4E14054E12954225457C3A6B84F135"
    ).into()).unwrap();
    let public_key: &VerifyingKey = private_key.verifying_key();
    let public_key_str = hex(&public_key.to_encoded_point(false).to_bytes());
    let public_key_b_str = "4bac6cb0f4ad6397752c3d73b88c5c86e3d88ac695118494a1732e2abd16c76acad3d6586c37c8db7e69c2f812f99275198936957d72c38d71981991124";

    let utc_datetime: DateTime<Utc> = Utc::now();
    let ut1 = UnsignedTransaction {time: utc_datetime.to_string(), sender: public_key_str.to_string(), receiver: public_key_b_str.to_string(), amount: 10};
    println!("{:?}", ut1);
    let serialized: String = serde_json::to_string(&ut1).unwrap();
    let sig1: Signature = private_key.sign(serialized.as_bytes());
    let signed_ut1 = SignedTransaction {time: utc_datetime.to_string(), sender: public_key_str.to_string(), receiver: public_key_b_str.to_string(), amount: 10, signature: sig1.to_string()};
 
    let uri = "https://httpbin.org/post";
    let mut res = surf::post(uri).body_json(&signed_ut1)?.await?;
    let body = res.body_string().await?;
    println!("{}", body);
    Ok(())
}

“files”: {},
“form”: {},
“headers”: {
“Accept”: “*/*”,
“Accept-Encoding”: “deflate, gzip”,
“Content-Length”: “471”,
“Content-Type”: “application/json”,
“Host”: “httpbin.org”,
“User-Agent”: “curl/8.11.0-DEV isahc/0.7.6”,
“X-Amzn-Trace-Id”: “Root=1-676c8cf1-39979cc169871725084b307e”
},
“json”: {
“amount”: 10,
“receiver”: “4bac6cb0f4ad6397752c3d73b88c5c86e3d88ac695118494a1732e2abd16c76acad3d6586c37c8db7e69c2f812f99275198936957d72c38d71981991124”,
“sender”: “4bac6cb0f4ad6397752c3d73b88c5c86e3d88ac695118494a1732e2abd16c76acad3d6586c37c8db7e69c2f812f99275198936957d72c38d71981991123”,
“signature”: “8000E340A55A517D0F27F3A63FBE39ED576BA491DFAC89B44654AB147EC66B206B054BAAF53E318EB2721DC892B4736630F400547989AE9F7C069034ECB4DF98”,
“time”: “2024-12-25 22:53:36.824066840 UTC”
},
“origin”: “hoge”,
“url”: “https://httpbin.org/post”
}

なるほど、送信側は相手のURL(IP)がわかっていれば、POSTするだけなので問題なく出来ますね。

【Rust】Actixの基礎

まずcargo newでプロジェクトを作成し、dependenciesにactixを追加します。

[dependencies]
actix-web = "4"
use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};

#[get("/")]
async fn hello() -> impl Responder {
    HttpResponse::Ok().body("Hello world!")
}

#[post("/echo")]
async fn echo(req_body: String) -> impl Responder {
    HttpResponse::Ok().body(req_body)
}

async fn manual_hello() -> impl Responder {
    HttpResponse::Ok().body("Hey there!")
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(||{
        App::new()
            .service(hello)
            .service(echo)
            .route("/hey", web::get().to(manual_hello))
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

$ curl -X POST -d “age=30” 127.0.0.1:8080/echo
age=30

おおおおおおお、get, postまではできたな…