【Rust】sepc256k1の秘密鍵・公開鍵の作成

### 秘密鍵の作成

use k256::{ecdsa::{SigningKey, Signature, signature::Signer, signature::Verifier, VerifyingKey}};
use rand_core::OsRng;

fn new_privatekey() {
    let signing_key = SigningKey::random(&mut OsRng);
    let private_key = signing_key.to_bytes();
    println!("{:x?}", hex::encode(private_key));
}


fn main() {
    new_privatekey();
}

$ cargo run

“69c88f4f82746fbc5ebd0bad102ecf4c83c29eb91eb329961c071cab028a26f1”

rustで点P(2**256-2**32-2**9-2**8-2**7-2**6-2**4-1)を計算しようとすると、bigintのライブラリを使用してもoverflowするので、素直にk256を利用するのが良さそう。

### 公開鍵の追加

fn new_keys() {
    let signing_key = SigningKey::random(&mut OsRng);
    let private_key = hex::encode(signing_key.to_bytes());
    println!("{:x?}", private_key);
    let verifying_key = signing_key.verifying_key();
    let public_key = hex::encode(verifying_key.to_sec1_bytes());
    println!("{:x?}", public_key);
}


fn main() {
    new_keys();
}

$ cargo run

“0d531f6878384de2126386d1c31564c78c4fd5bdc93fc5a15ea8b2a99878b0a8”
“0240a4c5cbca113228016cc39a7bd938dc4b0917eccc97b100450133c48228eb3c”

【Blockchain】ウォレット機能の考察

### ウォレットとして必要な機能
– 秘密鍵、公開鍵の作成、保存
– ブロックチェーン残高の表示
– トランザクションの送信、受信
– ブロックチェーン価格のマーケットデータ表示
※ステーキング、スワッピング、dAppsなど応用機能もある

### 前準備
$ pip install flask
$ pip install web3
$ pip install requests

from aiohttp import request
from flask import Flask, jsonify, render_template, session
from web3 import Web3
import requests
import jsonify

app = Flask(__name__)

infura_url = 'https://mainnet.infura.io/v3/fuga'
app.config['SECRET_KEY'] = 'hoge'

@app.route('/new_account', methods=['GET'])
def new_account():
    account = web3.eth.account.create('hogehoge')
    session['account'] = {
        'privateKey': account.key.hex(),
        'address' : account.address
    }
    return jsonify(session['account'])
with open('erc20_abi.json') as f:
    erc20_abi = json.load(f)

@app.route('/balance/<contract_address>', methods=['GET'])
def get_balance(contract_address):
    address = session.get('account').get('address')
    checksum_address = Web3.to_checksum_address(address)
    print(checksum_address)
    contract = web3.eth.contract(address=contract_address, abi=erc20_abi)
    balance = contract.functions.balanceOf(checksum_address).call()
    return jsonify({'balance': balance})

@app.route('/send_transaction', methods=['POST'])
def send_transaction():
    data = request.get_json()
    nonce = web3.eth.getTransactionCount(session['account']['address'])
    txn_dict = {
        'to': data['to'],
        'value': web3.toWei(data['amount'], 'either'),
        'gas': 2000000,
        'gasPrice': web3.toWei('40', 'gwei'),
        'nonce': nonce,
        'chainId': 3
    }
    signed_txn = web3.eth.account.signTransaction(txn_dict, session['account']['privateKey'])
    txn_hash = web3.eth.sendRawTransaction(signed_txn.rawTransaction)
    return jsonify({'transaction_hash': txn_hash.hex()})

@app.route('/market_chart/<contract_address>/<days>', methods=['GET'])
def get_market_chart(contract_address, days):
    api_key = 'coingecho_api_key'
    response = requests.get(f'https://api.coingecko.com/api/v3/coins/ethereum/contract/{contract_address}/market_chart?vs_currency=usd&days={days}&api_key={api_key}')
    market_chart = response.json(f'https://api.coingecko.com/api/v3/coins/ethereum/contract/{contract_address}/market_chart?vs_currency=usd&days={days}&api_key={api_key}')
    market_chart = response.json()
    return jsonify(market_chart)

@app.route('/')
def home():
    return render_template('index.html')

if __name__ == '__main__':
    app.run(debug=True)

なるほど、walletのエッセンスは理解できました。Pythonでは”web3″のライブラリでかなり抽象化されているのがわかります。
balanceのところはUTXOにするか、Account型にするかで変わってきますね。
マーケットデータは一旦スタック。
テストネット、メインネットの概念はもう少し深掘りする

【Rust】RustでbitcoinのProof of work(PoW)を実装したい

まず、previous hashとnonceを足して、sha256で暗号化します。

use sha2::{Digest, Sha256};
use rand::Rng;

fn main() {
    let previous_hash = "b9b9ee9ffc95fa4956b63b6043a99d0a8f04e0e52e687fc1958d3c6dff885f01";
    let num = rand::thread_rng().gen_range(0..1000000);
    let hash_num = format!("{}{}", previous_hash, num.to_string());
    let header = Sha256::digest(hash_num);
    println!("{:x}", header);
}

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

うむ、やりたいことはできている。ここから、hash値先頭が0000… のheaderになるようなnonceの値を探す計算処理を行う

use sha2::{Digest, Sha256};
use rand::Rng;

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

$ cargo run

count: 37455 4b1d6582d1fed66a34041346d0f43cc7b6c2a803588da8c5d515a813c2dcff7a
count: 37456 34b478ac26947b93e63a747be64a06b904fe98953a8db9d4d3f773fdadf5abba
count: 37457 0000bdebe741af3994f4a2160b4480a23ca137aaf0ac51b10fe574f04afc7be4

凄い簡単なコードなんだけど、これ作るのに結構時間かかった…

あとは計算時間を計測してDifficultyの調整機能を作りたい

use sha2::{Digest, Sha256};
use rand::Rng;
use std::{thread, time};

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

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

$ cargo run

count: 70736 000074213e839089c9bd8e446dd5835d537cd7037cdf193bf9881df44d2a55b4
1.818378391s

これをmainに取り込む。powはminingとして別ファイルにして、ブロック作成時に読み込むようにする。

mod mining;

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

$ cargo run

count: 16990 0000ea27e22db290e4f2163f968bfaf3ff7d58ccf1cd4ab43b3fbc4326c0eb4a
428.967757ms
blockを作成しました。
8000E340A55A517D0F27F3A63FBE39ED576BA491DFAC89B44654AB147EC66B206B054BAAF53E318EB2721DC892B4736630F400547989AE9F7C069034ECB4DF98

### 課題
– トランザクションプールが出来た後のblock作成のロジックを詰める必要がある。(merkletree, serialize等)
– genesis block, minerへのコイン分配なども考える必要あり。
– トランザクションスピードを上げるために並列処理を導入する場合、どこを並列処理にするのか考える必要あり。

【Rust】rustでrandom intを実装したい

RustでいわゆるPythonのrandintを実装したい

[dependencies]
rand = "0.8"
use rand::Rng;

fn main() {
    let num = rand::thread_rng().gen_range(0..1000000);
    println!("{}", num);
}

$ cargo run
Compiling crypt v0.1.0 (/home/vagrant/dev/rust/crypt)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.28s
Running `target/debug/crypt`
759081

うん、ここまでは簡単

【Python】pythonでProof of work

from random import randint
from hashlib import sha256

previous_hash = "b9b9ee9ffc95fa4956b63b6043a99d0a8f04e0e52e687fc1958d3c6dff885f01"

cnt = 1

nonce = str(randint(0, 1000000))

header = sha256(f'{previous_hash}{nonce}'.encode()).hexdigest()

print(header)

while header[:4] != "0000":
    text = 'loop:{}, header:{}, header[:4]:{}, nonce:{}\n'
    print(text.format(cnt, header, header[:4], nonce))

    nonce = str(randint(0, 1000000))
    header = sha256(f'{previous_hash}{nonce}'.encode()).hexdigest()
    cnt += 1

text = 'loop:{}, header:{}, header[:4]:{}, nonce:{}\n'
print(text.format(cnt, header, header[:4], nonce))

$ python3 test.py

loop:17576, header:0000ea27e22db290e4f2163f968bfaf3ff7d58ccf1cd4ab43b3fbc4326c0eb4a, header[:4]:0000, nonce:8978

ほう、これをRustで書きたい & 処理時間に応じたdifficulty(0の個数)の調整機能も追加したい。

【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