【Rust】Peer情報をnode ip listに書き込む

bitcoinのIPアドレスリストは、ipアドレスとUnix timeが対になっている
(参考↓)
https://qiita.com/onokatio/items/04f23b7300dec7e287cb
その他にも、ipv4/ipv6や、ping, lastsend, lastrec, id, versionなどの情報も含まれる。

handshake後にpeers.datのファイルに書き込むようにして、ping, pongの箇所は、enumでactive/inactiveのstatusで表現することにした。

use serde::{Serialize, Deserialize};
use std::{io::Write};
use chrono::{Utc,DateTime};
use std::fs::OpenOptions;

#[derive(Serialize, Deserialize, Clone, Debug)]
enum Status {
    ACTIVE,
    INACTIVE,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
struct Peer {
    ip: String,
    unixtime: i64,
    nodetype: String,
    version: String,
    status: Status,
}

fn main(){
    let utc_datetime: DateTime<Utc> = Utc::now();
    println!("{}", utc_datetime.timestamp());

    let peer = Peer {ip: "192.168.33.10".to_string(), unixtime: utc_datetime.timestamp(), nodetype: "c".to_string(), version: "1.0.1".to_string(), status: Status::ACTIVE};
    println!("{:?}", peer);
    let serialized: Vec<u8> = serde_json::to_vec(&peer).unwrap();
    let mut file_ref = OpenOptions::new()
                        .append(true)
                        .open("./data/peers.dat")
                        .expect("Unable to open file");
    file_ref.write_all(&serialized).expect("write failed");
    file_ref.write_all(b"\n").expect("write failed");
}

Running `target/debug/sample`
1740366182
Peer { ip: “192.168.33.10”, unixtime: 1740366182, nodetype: “c”, version: “1.0.1”, status: ACTIVE }

nodeipリストをどうゆう風にするか、ファイルを分割するか、データ型などずっと悩んでいたけど、他のチェーンの設計は参考になりますね。

ちなみに取り出す際↓

fn get_peers() -> Result<Vec<Peer>, Box<dyn std::error::Error>>  {

    let mut peers: Vec<Peer> = Vec::new();
    for result in BufReader::new(File::open("./data/peers.dat")?).lines() {
        let line: Peer = serde_json::from_str(&result?).unwrap();
        peers.push(line);
    }
    Ok(peers)
}

【Rust】簡易的なBloomfilter

ブルームフィルタを更新していくロジックがいまいちわからんが、なんとなく

use std::hash::{DefaultHasher, Hash, Hasher};

#[derive(Debug, PartialEq, Clone)]
struct BloomFilter {
    filter: [i32; 10],
}

impl BloomFilter {
    fn set_v(&mut self, val: String) {
        let list: Vec<u8> = self.n_hash(val);
        for l in list {
            let i = usize::from(l);
            if self.filter[i] == 0 {
                self.filter[i] = 1
            } else {
                self.filter[i] = 2 }
        }    
    }

    fn n_hash(&self, val: String) -> Vec<u8>{
        let hashed = siphash(val);
        let list: Vec<u8> = s_digit(hashed);
        return list
    }

    fn check_v(self, val: String) -> bool {
        let list: Vec<u8> = self.n_hash(val);
        let mut c_bf = BloomFilter { filter: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]};
        for l in list {
            let i = usize::from(l);
            if c_bf.filter[i] == 0 {
                c_bf.filter[i] = 1
            } else {
                c_bf.filter[i] = 2 }
        }
        return self == c_bf
    }
}

fn siphash(s: String) -> u64 {
    let mut siphash = DefaultHasher::new();
    s.hash(&mut siphash);
    return siphash.finish()
}

fn s_digit(n: u64) -> Vec<u8> {
    n.to_string()
        .chars()
        .into_iter()
        .map(|char| char.to_digit(10).unwrap() as u8)
        .collect()
}

fn main(){
    let mut bf = BloomFilter { filter: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]};
    bf.set_v("hello world".to_string());
    println!("{:?}", bf);
    println!("{}", bf.clone().check_v("hello world!".to_string()));
    println!("{}", bf.clone().check_v("hello world".to_string()));
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.33s
Running `target/debug/wallet`
BloomFilter { filter: [2, 2, 0, 1, 2, 1, 1, 2, 2, 2] }
false
true

【Python】HD walletの親鍵/子鍵とchaincodeの概要

子の秘密鍵は、親のchaincdeと[親の公開鍵+index]をhmac_sha512でハッシュ化して作成している。

### マスター秘密鍵、公開鍵

import os
import binascii
import hmac
import hashlib
import ecdsa

seed = os.urandom(32)
root_key = b"Bitcoin seed"

def hmac_sha512(data, key_message):
    hash = hmac.new(data, key_message, hashlib.sha512).digest()
    return hash

def create_pubkey(private_key):
    publickey = ecdsa.SigningKey.from_string(private_key, curve=ecdsa.SECP256k1).verifying_key.to_string()
    return publickey

master = hmac_sha512(seed, root_key)

master_secretkey = master[:32]
master_chaincode = master[32:]

master_publickey = create_pubkey(master_secretkey)
master_publickey_integer = int.from_bytes(master_publickey[32:], byteorder="big")

if master_publickey_integer %2 == 0:
    master_publickey_x = b"\x02" + master_publickey[:32]
else:
    master_publickey_x = b"\x03" + master_publickey[:32]

print(binascii.hexlify(master_secretkey))
print(binascii.hexlify(master_chaincode))
print(binascii.hexlify(master_publickey_x))

$ python3 master_key.py
b’8a6dbaaff700682778dcbae2bc8718452fe5ed80fc9026a9b564420f8d5b0d80′
b’4ce8b10cc0c0874467d8f438c412fdbf21fba51517e668dbc4bd105af6861dec’
b’03cb15210804ca8f0d45b620832be935e2f90c3830f13f04c4bd6e8b4648f27817′
(secretkey, chaincode, pubkey)

### 子秘密鍵、子公開鍵

index = 0
index_bytes = index.to_bytes(8, "big")

data = master_publickey_x + index_bytes

result_hmac512 = hmac_sha512(data, master_chaincode)
sum_integer = int.from_bytes(master_secretkey,"big") + int.from_bytes(result_hmac512[:32],"big")

p = 2 ** 256 - 2**32 - 2**9 - 2**8 - 2**7 - 2**6 - 2**4 - 1
child_secretkey = (sum_integer % p).to_bytes(32,"big")

child_chaincode = result_hmac512[32:]

child_publickey = create_pubkey(child_secretkey)
child_publickey_integer = int.from_bytes(child_publickey[32:], byteorder="big")

if child_publickey_integer %2 == 0:
    child_publickey_x = b"\x02" + child_publickey[:32]
else:
    child_publickey_x = b"\x03" + child_publickey[:32]

print(binascii.hexlify(child_secretkey))
print(binascii.hexlify(child_chaincode))
print(binascii.hexlify(child_publickey_x))

b’5ff011a3e9cd672aaf0dc9fd52cb3172ac2815cb270f919135e6b0f0e6e03d54′
b’15f4d148b2d7730076d5e670249649ea8f0fd8572dad3818680e347196149dda’
b’03480a1dbb4a87d867bee3d364b608e21d685af271876707b9f9d5b75c6df6fde7′

b’23faf6fad81cd93e12c003c944ba3ef215dae714c638756386b6b9404da5aac9′
b’e3563dc6891e238cd0d8ebf99e65ebfc67cecf42364de9756b89859bbd049b62′
b’02039253af3e828bfbf1e560fe0e923a144fc4496ded3b6bbfa0d568cf7177d1c3′

なるほど、一見複雑そうに見えるが、なかなか面白いね

【Python】DH-Walletのライブラリを利用してみる

$ pip3 install hdwallet

#!/usr/bin/env python3

from hdwallet import HDWallet
from hdwallet.entropies import (
    BIP39Entropy, BIP39_ENTROPY_STRENGTHS
)
from hdwallet.mnemonics import BIP39_MNEMONIC_LANGUAGES
from hdwallet.cryptocurrencies import Bitcoin as Cryptocurrency
from hdwallet.hds import BIP32HD
from hdwallet.derivations import CustomDerivation
from hdwallet.const import PUBLIC_KEY_TYPES

import json

# Initialize Bitcoin HDWallet
hdwallet: HDWallet = HDWallet(
    cryptocurrency=Cryptocurrency,
    hd=BIP32HD,
    network=Cryptocurrency.NETWORKS.MAINNET,
    language=BIP39_MNEMONIC_LANGUAGES.KOREAN,
    public_key_type=PUBLIC_KEY_TYPES.COMPRESSED,
    passphrase="talonlab"
).from_entropy(  # Get Bitcoin HDWallet from entropy
    entropy=BIP39Entropy(
        entropy=BIP39Entropy.generate(
            strength=BIP39_ENTROPY_STRENGTHS.ONE_HUNDRED_SIXTY
        )
    )
).from_derivation(  # Drive from Custom derivation
    derivation=CustomDerivation(
        path="m/0'/0/0"
    )
)

# Print all Bitcoin HDWallet information's
print(json.dumps(hdwallet.dump(exclude={"indexes"}), indent=4, ensure_ascii=False)) 

$ python3 hd_wallet.py
{
“cryptocurrency”: “Bitcoin”,
“symbol”: “BTC”,
“network”: “mainnet”,
“coin_type”: 0,
“entropy”: “f90afa34cc3653845d06c179ba917569c60e1179”,
“strength”: 160,
“mnemonic”: “호흡 분리 여론 온종일 생신 조용히 숙소 추석 시월 청춘 사계절 철도 살림 건축 팩스”,
“passphrase”: “talonlab”,
“language”: “Korean”,
“seed”: “d79ac6b15a3782eefd52ae4b09c961ddd7263f6de4a852534497139516bc04549cb62dd2063f21090b13b58c442a35610f630034ca9ad85877ee8a544f8643b4”,
“ecc”: “SLIP10-Secp256k1”,
“hd”: “BIP32”,
“semantic”: “p2pkh”,
“root_xprivate_key”: “xprv9s21ZrQH143K35Wb8sjvrSbVYqX2xGJvX64QxwC1spNkdnakC1Mtwv2CQE1trAMGA83TGEZSjWtdh1s9pFT2P6z2PKepwjvhbkeU69UsGaE”,
“root_xpublic_key”: “xpub661MyMwAqRbcFZb4EuGwDaYE6sMXMj2mtJz1mKbdS9ujWautjYg9ViLgFWsQNQ3PjEnrdPhNhS9JvuH6WLG5pgJaTBezMoy7ecEKLK3BAvH”,
“root_private_key”: “4ba3752c7a474a3be62ef060fd79c6517b7f2af01d7e573f11685e20f55a0303”,
“root_wif”: “Kykk1CNJWm7sAsve2eDCezUykFyQfX9BavJNTWakMutPYSSHZ5kP”,
“root_chain_code”: “66708f4fdae1aec89612e24205d9244f0b8a1a4ada2fc6076e0eea8a91131831”,
“root_public_key”: “033bbd876465246c571f02df1aa65527c82dfa9779833a2840ecb63167f1f507a0”,
“strict”: true,
“public_key_type”: “compressed”,
“wif_type”: “wif-compressed”,
“derivation”: {
“at”: {
“path”: “m/0’/0/0”,
“depth”: 3,
“index”: 0
},
“xprivate_key”: “xprv9yoVptUsgY29Jz8tMnbLeoWQygM3m6zYSWdYDiVdBazjqyc11ioZHdowCxYi17fN5CwDnm3ith9tMeVjv5GToJY4S7NLrbuged6Q3DJiGWy”,
“xpublic_key”: “xpub6CnrEQ1mWuaSXUDMTp8M1wT9XiBYAZiPojZ926uEjvXiimw9ZG7oqS8R4Dev3hzwcXeqNmMiN9knr88cPtduVZ1Gqn1kchT23eB5wK7N8t1”,
“private_key”: “96ceeef7b35bfd428842d2dc08719d3c5b5cff9967a7110ece12d57af017a66b”,
“wif”: “L2GryxVSFxauZCLCQJqmV85tVNoXCXyRdC5zomodDWwd33BPjJNn”,
“chain_code”: “7a95361a7ffc067c5165411d55d1400bc5b3cb5ea2ca90741e918fd2a8fb3349”,
“public_key”: “02a0e4a452f270f2647949a57cc55909182eb2f232f4b5146ccdd232a4ffed8803”,
“uncompressed”: “04a0e4a452f270f2647949a57cc55909182eb2f232f4b5146ccdd232a4ffed88030259ef4fa9649169a66b61c26881426d68b6b51ad6ea3cb7dc0b41cc85b6ba00”,
“compressed”: “02a0e4a452f270f2647949a57cc55909182eb2f232f4b5146ccdd232a4ffed8803”,
“hash”: “1bff595df8d0d9695e612eca95afcf4c882e5f32”,
“fingerprint”: “1bff595d”,
“parent_fingerprint”: “9b0194d3”,
“addresses”: {
“p2pkh”: “13Z399RbXeELGgKRKq5sM8nNim9waZGoPW”,
“p2sh”: “35nKB4UKgHSzRYX4J36YxLFMqm7YpCkMKU”,
“p2tr”: “bc1pmsdcxya38lnkmrs62e4yuad0cgr322tp9cc7sgjrmfe3e3jasaeqcvrvwl”,
“p2wpkh”: “bc1qr0l4jh0c6rvkjhnp9m9ftt70fjyzuhejdwqt46”,
“p2wpkh_in_p2sh”: “3KNNVBigqcdPAsUU1apzbRJj5dx993YoGQ”,
“p2wsh”: “bc1qfznz7ncnv4xu29zx3cdv7dzdtgc9e600ukd0kxyca7dg6k9gswqqs8xs7w”,
“p2wsh_in_p2sh”: “36MvcLHZq8jAogs7rm8beCo6rZEybZRkzk”
}
}
}

#!/usr/bin/env python3

from hdwallet.utils import generate_passphrase
print(generate_passphrase(length=32))

$ python3 hd_wallet.py
MQIjC1twsYSqRYAYzUbXZGouqirSa65t

#!/usr/bin/env python3

from hdwallet.mnemonics.algorand import AlgorandMnemonic, ALGORAND_MNEMONIC_WORDS, ALGORAND_MNEMONIC_LANGUAGES
mnemonic: str = AlgorandMnemonic.from_words(words=ALGORAND_MNEMONIC_WORDS.TWENTY_FIVE, language=ALGORAND_MNEMONIC_LANGUAGES.ENGLISH)
print(AlgorandMnemonic.from_entropy(entropy="65234f4ec655b087dd74d186126e301d73d563961890b2f718476e1a32522329", language=ALGORAND_MNEMONIC_LANGUAGES.ENGLISH))

hole develop cheese fragile gaze giggle plunge sphere express reunion oblige crack priority ocean seven mosquito wagon glow castle plunge goddess stand empower ability empower

Secure randomとは?

暗号鍵の生成など安全な乱数を生成するためのクラス。推測されにくいランダムな数値や文字列を生成するツールとして、Java や Ruby、PowerShell などで使用されている。

【Blockchain】Walletの比較

人気のWallet比較

### coincheck(pc/mobile)
取引: 総資産、入金出金、購入、売却、送金、受取、積立、大口、NFT, チャート、トレードビュー
アカウント:アカウント変更、本人確認、電話番号、取引履歴、ログイン履歴
ログアウト

### bitcoin wallet(mobile)
btc amount, transaction, send, request, exchange rate, network monitor, block, peer情報

### Trust Crypto Wallet(mobile)
send, receive, buy, earn, crypt, NFT

### bitbank(pc/mobile)
総資産、現物、信用、保有銘柄、お気に入り、ピックアップ、入金、販売所、資産、お知らせ、メニュー、データ(履歴)、登録情報、設定、API

### SafePal wallet(app/hardware)
coin, Delfi, swap, bridge, exchange, market, favorite, deposit, Network

### coinbase wallet
Crypt, NFTs, DeFi,
Buy, swap, bridge, send, receive
Asset, Transaction, Browser, Explore, Setting

### blockchain.com
buy swap sell receive
Asset, price

### 楽天ウォレット
ホーム、運用状況、ニュース、お知らせ、メニュー

大体、どのような機能が必要かはわかりました。QRコードは必要ですね。

【Rust】トランザクションにnftデータを追加する

UnsignedTransaction, SignedTransactionの構造体に、nft_dataと、nft_originを追加する。
nft_dataは、nftとして保存するデータ。
nft_originはnftを発行したトランザクションのhashデータ。
NFTを譲渡する場合は、nft_dataを空にして、nft_originに値を入れる。

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

nft_holderというhashmapを作成して、そこにnftを発行したトランザクションのhashデータと、最後にNTFを譲渡されたreceiverの連想配列を入れていく。
誰が何を持っているかは、トランザクションのhashデータをデコードして、nft_dataの値を取り出す。

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

    let mut transaction_pool: Vec<SignedTransaction> = Vec::new();
    let transaction1 = UnsignedTransaction {time:Utc::now().to_string(), sender:"047683c00f6z".to_string(), receiver:"DyH5SHvezz".to_string(), amount: 0, nft_data:"hello world".to_string(),nft_origin:"".to_string()};
    let transaction2 = UnsignedTransaction {time:Utc::now().to_string(), sender:"DyH5SHvezz".to_string(), receiver:"655EFC80ss".to_string(), amount: 0, nft_data:"".to_string(),nft_origin:"eyJ0aW1lIjoiMjAyNS0wMS0xNyAwODoyOTo0Ni42NzgzNDU3OTQgVVRDIiwic2VuZGVyIjoiMDQ3NjgzYzAwZjZ6IiwicmVjZWl2ZXIiOiJEeUg1U0h2ZXp6IiwiYW1vdW50IjowLCJuZnRfZGF0YSI6ImhlbGxvIHdvcmxkIiwibmZ0X29yaWdpbiI6IiJ9".to_string()};
    let transaction3 = UnsignedTransaction {time:Utc::now().to_string(), sender:"047683c00f6z".to_string(), receiver:"DyH5SHvezz".to_string(), amount: 0, nft_data:"Milk Cafe".to_string(),nft_origin:"".to_string()};
    let transaction4 = UnsignedTransaction {time:Utc::now().to_string(), sender:"047683c00f6z".to_string(), receiver:"DyH5SHvezz".to_string(), amount: 1000, nft_data:"".to_string(),nft_origin:"".to_string()};

    // println!("{}", BASE64_STANDARD.encode(serde_json::to_vec(&transaction1.clone()).unwrap()));
    // println!("{}", base64_decode(&str));

    transaction_pool.push(sign_transaction(&transaction1));
    transaction_pool.push(sign_transaction(&transaction2));
    transaction_pool.push(sign_transaction(&transaction3));
    transaction_pool.push(sign_transaction(&transaction4));

    let nft_holder:HashMap<String, String> = nft_calc(transaction_pool);
    for (k, v) in nft_holder {
        let transaction_str = base64_decode(&k);
        let transaction:UnsignedTransaction = serde_json::from_str(&transaction_str).unwrap();
        println!("保有者:{}, NFT:{}", v, transaction.nft_data);
    }
}

fn nft_calc(transaction_pool: Vec<SignedTransaction>) -> HashMap<String, String> {
    let mut nft_holder: HashMap<String, String> = HashMap::new();
    for transaction in transaction_pool {
        if transaction.amount == 0 {
            let transaction_hash: String = BASE64_STANDARD.encode(serde_json::to_vec(&transaction.clone()).unwrap());
            if transaction.nft_origin == "" && transaction.nft_data != "" && (nft_holder.get(&transaction_hash) == None) {
                nft_holder.insert(transaction_hash, transaction.receiver);
            } else if (nft_holder.get(&transaction_hash) == Some(&transaction.sender)) && transaction.nft_data == "" {
                nft_holder.insert(transaction.nft_origin, transaction.receiver);
            }
        }
    }
    return nft_holder
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.13s
Running `target/debug/sample`
保有者:DyH5SHvezz, NFT:hello world
保有者:DyH5SHvezz, NFT:Milk Cafe

これは中々凄いな…

【Rust】トランザクションをbase64でエンコード、デコード

hello worldで挙動を確認します。

let str:String = "hello world".to_string();
println!("{}", BASE64_STANDARD.encode(str.clone()));

let b:String = "aGVsbG8gd29ybGQ=".to_string();
println!("{}", base64_decode(&b));

pub fn base64_decode_bytes(b64str: &str) -> Vec {
let t = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
let mut table: [u8; 256] = [0; 256];
for (i, v) in t.as_bytes().iter().enumerate() {
table[*v as usize] = i as u8;
}
let b64 = String::from(b64str).replace("\r", "").replace("\n", "");
let b64bytes = b64.as_bytes();
let mut result: Vec = vec![];
let cnt = b64bytes.len() / 4;
for i in 0..cnt {
let i0 = b64bytes[i*4+0];
let i1 = b64bytes[i*4+1];
let i2 = b64bytes[i*4+2];
let i3 = b64bytes[i*4+3];
let c0 = table[i0 as usize] as usize;
let c1 = table[i1 as usize] as usize;
let c2 = table[i2 as usize] as usize;
let c3 = table[i3 as usize] as usize;
let b24 = (c0 << 18) | (c1 << 12) | (c2 << 6) | (c3 << 0); let b0 = ((b24 >> 16) & 0xFF) as u8;
let b1 = ((b24 >> 8) & 0xFF) as u8;
let b2 = ((b24 >> 0) & 0xFF) as u8;
result.push(b0);
if i2 as char != '=' { result.push(b1); }
if i3 as char != '=' { result.push(b2); }
}
result
}
pub fn base64_decode(b64str: &str) -> String {
String::from_utf8(base64_decode_bytes(b64str)).unwrap()
}
[/cpde]
aGVsbG8gd29ybGQ=
hello world

上手くいっているようです。
これをトランザクションでやります。

let transaction1 = UnsignedTransaction {time:Utc::now().to_string(), sender:"047683c00f6z".to_string(), receiver:"DyH5SHvezz".to_string(), amount: 0, nft_data:"hello world".to_string(),nft_origin:"".to_string()};

println!("{}", BASE64_STANDARD.encode(serde_json::to_vec(&transaction1.clone()).unwrap()));

let str:String = "eyJ0aW1lIjoiMjAyNS0wMS0xNyAwODoyNzo1MS4wNTA0MjYxMDMgVVRDIiwic2VuZGVyIjoiMDQ3NjgzYzAwZjZ6IiwicmVjZWl2ZXIiOiJEeUg1U0h2ZXp6IiwiYW1vdW50IjowLCJuZnRfZGF0YSI6ImhlbGxvIHdvcmxkIiwibmZ0X29yaWdpbiI6IiJ9".to_string();
println!("{}", base64_decode(&str));
[/cpde]

eyJ0aW1lIjoiMjAyNS0wMS0xNyAwODoyOTo0Ni42NzgzNDU3OTQgVVRDIiwic2VuZGVyIjoiMDQ3NjgzYzAwZjZ6IiwicmVjZWl2ZXIiOiJEeUg1U0h2ZXp6IiwiYW1vdW50IjowLCJuZnRfZGF0YSI6ImhlbGxvIHdvcmxkIiwibmZ0X29yaWdpbiI6IiJ9
{"time":"2025-01-17 08:27:51.050426103 UTC","sender":"047683c00f6z","receiver":"DyH5SHvezz","amount":0,"nft_data":"hello world","nft_origin":""}
SignedTransaction { time: "2025-01-17 08:29:46.678345794 UTC", sender: "047683c00f6z", receiver: "DyH5SHvezz", amount: 0, nft_data: "hello world", nft_origin: "", signature: "568082531D765278ACF8999B94BAA7C621C538B00857DAC165208DB8C73FE4D4CB8FDBC742A4A47BC1D0B421A543134D869B2EC1AC8CEA0DDE902D39B034EA84" }

よし、O~K

【Rust】各ブロックチェーンの対BTCスワッピングレートを計算して表示

各コインのUSDレートをbitcoinのUSDレートで割れば、スワッピングレートが計算される。
f32のvectorはpartial_cmpで比較してソートする。

let mut coin_rate = get_price().await.unwrap();

    let objs: Vec<Rate> = serde_json::from_value(coin_rate).unwrap();
    let mut btcRate: f32 = 0.0;

    let mut crypt_objs: Vec<Rate> = Vec::new();
    for obj in objs {
        if obj.r#type == "crypto" {
            if obj.symbol == "BTC" {
                crypt_objs.insert(0, obj.clone());
                btcRate = obj.rateUsd.parse::<f32>().unwrap();
            } else {
                crypt_objs.push(obj);
            }   
        }
    }
    println!("{}", &btcRate);

    let mut data: Vec<RateSwap> = vec![];
    for crypt_obj in crypt_objs {
        let obj = RateSwap { id: crypt_obj.id, symbol: crypt_obj.symbol, rateUsd: crypt_obj.rateUsd.clone(), rateBtc: crypt_obj.rateUsd.parse::<f32>().unwrap() / &btcRate };
        data.push(obj);
    }
    data.sort_by(|a, b| a.rateBtc.partial_cmp(&b.rateBtc).unwrap().reverse());

    let mut context = tera::Context::new();
    context.insert("title", "Index page");
    context.insert("data", &data);

おおお、割とやりたいことはできている気がする。
対btcではなく、コイン対コインでスワッピングする場合も同様のロジックで、交換する両方のコインのUSDレートで割ればスワッピングレートが計算できる。

Nice、次はaxumのログイン機能およびDB連携

PoS(Proof of Stake)と DPoS(Delegated Proof of Stake)、NPosの違いは

PoS
トークンの保有量に応じて承認権が与えられる仕組みです。暗号資産を多く保有する承認者に権利が集中しやすいという課題があります。

DPoS
PoSの発展系で、トークンの保有量に応じて投票権が割り当てられ、投票によって取引の承認を委任する仕組みです。保有量と委任された票の合計で承認者が選ばれるため、PoSと比べて民主主義的な仕組みといえます。また、取引承認に必要な承認数を減らすことができるため、高速なトランザクション処理を実現できます。

NPos
NPoSではValidatorとNominatorのStakeの合計が多い上位50〜1,000人がValidatorプールとして選ばれます。ValidatorとNominatorをValidator選別のスキームに取り入れることによって、ネットワーク全体のセキュリティを担保することが可能になります。この仕組みにより単体の大量DOT保持者へのシステムの依存を回避することができ、DOT保持者全員が参加できることにより悪意を持つユーザーがValidatorになることを困難にします(Nominatorに選ばれるには信頼を築く必要があるため)。

PoSだと、保有量が多い方が有利なアルゴリズムだが、Nominated(NPoS)の場合はランダムに選別するので、より公平性が保たれる仕組みですね。