暗号鍵の生成など安全な乱数を生成するためのクラス。推測されにくいランダムな数値や文字列を生成するツールとして、Java や Ruby、PowerShell などで使用されている。
Category: Blockchain
【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
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)の場合はランダムに選別するので、より公平性が保たれる仕組みですね。
【Rust】Proof of StakeをRustで書きたい
use std::collections::HashMap; use chrono::{Utc, Local, DateTime, Date}; use sha2::Sha256; use sha2::Digest; use rand::Rng; use std::io; use std::io::Error; #[derive(Debug)] struct Block { index: u32, previous_hash: String, timestamp: DateTime<Utc>, data: Vec<String>, validator: String, hash: String, } #[derive(Debug)] struct BlockChain { chain: Vec<Block>, unconfirmed_data: Vec<String>, validators: HashMap<String, i32>, staked_tokens: Vec<i32>, } impl BlockChain { const minimum_stake: i32 = 10; fn last_block(&self) -> &Block { return self.chain.last().unwrap(); } fn add_data(&mut self, new_data:String) { self.unconfirmed_data.push(new_data); } fn add_validator(&mut self, validator: String, stake: i32) { if stake >= Self::minimum_stake { self.validators.insert(validator, stake); self.staked_tokens.push(stake); } else { println!("{} does not meet the minimum stake requirement.", validator); } } fn select_validator(self) -> String { let total_stake: i32 = self.staked_tokens.iter().sum(); let mut selected_validator: String = "".to_string(); let mut rnd = rand::thread_rng(); while selected_validator == "".to_string() { let pick = rnd.gen_range(0..total_stake); let mut current: i32 = 0; for (validator, stake) in &self.validators { // println!("{} {}", validator, stake); current += stake; if current > pick{ selected_validator = validator.to_string(); break; } } } return selected_validator; } fn create_block(mut self, validator: String)-> Result<(), String> { if self.unconfirmed_data.is_empty() { return Err("No transaction".to_string()); } let last_block = self.last_block(); let new_block = Block { index: last_block.index + 1, previous_hash: last_block.hash.clone(), timestamp: Utc::now(), data : self.unconfirmed_data.clone(), validator: validator, hash: "".to_string() }; self.chain.push(new_block); self.unconfirmed_data.clear(); Ok(()) } fn display_chain(self) { println!("{:?}", self.chain); } } fn calculate_hash(data: String) -> String { let mut hasher = Sha256::new(); hasher.update(data); let hashed_sha256 = hasher.finalize(); return hex::encode(hashed_sha256); } fn main() { let genesis_hash = calculate_hash("Genesis Block".to_string()); let genesis_block = Block { index: 0, previous_hash: "".to_string(), timestamp: Utc::now(), data: vec!["Genesis Block".to_string()], validator: "".to_string(), hash: genesis_hash }; let mut blockchain = BlockChain { chain: vec![genesis_block], unconfirmed_data: vec![], validators: HashMap::from([]), staked_tokens: vec![]}; blockchain.add_validator("Alice".to_string(), 200); blockchain.add_validator("Bob".to_string(), 300); blockchain.add_validator("Chan".to_string(), 400); blockchain.add_data("James got 1000 token".to_string()); blockchain.add_data("James sent 500 token to Jhon".to_string()); let selected_validator = blockchain.select_validator(); println!("Selected validator is {}", selected_validator); }
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.02s
Running `target/debug/sample`
Selected validator is Bob
基本的にPythonで書くのとそこまで変わりませんね。
【Rust】APIでBTC, ETHのUSD価格を取得する
use reqwest::Client; use serde::{Serialize, Deserialize}; use serde_json::{Value}; #[tokio::main] async fn main() { let btc: String = "BTC".to_string(); let btc_price: Value = get_price(btc).await.unwrap(); println!("{}", btc_price); let eth: String = "ETH".to_string(); let eth_price: Value = get_price(eth).await.unwrap(); println!("{}", eth_price); } async fn get_price(code: String) -> Result<Value, Box<dyn std::error::Error>> { let mut url:String = "https://api.coinbase.com/v2/exchange-rates?currency=".to_string(); url.push_str(&code); let contents = reqwest::get(url).await?.text().await?; let res: Value = serde_json::from_str(&contents).unwrap(); println!("{:?}", res["data"]["rates"]["USD"]); Ok((res["data"]["rates"]["USD"]).clone()) }
Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.97s
Running `target/debug/wallet`
String(“94359.295”)
“94359.295”
String(“3246.82”)
“3246.82”
処理速度が遅いが、取得はできる。
よし、さて、PoSでもやるか!
【Rust】p256でhexにencodeしたpublic keyをdecodeしてverify
hex::decode(&public_key_hex) でdecodeして、さらにfrom_sec1_bytesでpublic keyにします。
let public_key = secret_key.public_key(); let public_key_serialized = public_key.to_string(); println!("Public Key: \n{}", public_key_serialized); let public_key_hex = hex::encode(&public_key.to_sec1_bytes()); println!("hex Public Key: \n{}", public_key_hex); let public_key_decode = hex::decode(&public_key_hex).unwrap(); println!("decode Public Key: \n{:?}", &public_key_decode); let public_key = p256::PublicKey::from_sec1_bytes(&public_key_decode).expect("import error"); println!("decode Public Key: \n{:?}", &public_key); let signing_key: SigningKey = secret_key.into(); let message = b"ECDSA proves knowledge of a secret number in the context of a single message"; let signature: Signature = signing_key.sign(message); let verifying_key: VerifyingKey = public_key.into(); assert!(verifying_key.verify(message, &signature).is_ok());
$ cargo run
// 省略
hex Public Key:
040b807b5e5fee8c91df858e09aa6dc085eae1b9b70783ce32ec68ad4994f9d16a991e277a1515a22e35d2d7935dec95946f31a2e3ea1e125a9067a177523063b0
decode Public Key:
[4, 11, 128, 123, 94, 95, 238, 140, 145, 223, 133, 142, 9, 170, 109, 192, 133, 234, 225, 185, 183, 7, 131, 206, 50, 236, 104, 173, 73, 148, 249, 209, 106, 153, 30, 39, 122, 21, 21, 162, 46, 53, 210, 215, 147, 93, 236, 149, 148, 111, 49, 162, 227, 234, 30, 18, 90, 144, 103, 161, 119, 82, 48, 99, 176]
decode Public Key:
PublicKey { point: AffinePoint { x: FieldElement(0x3B226D0B94EFABC8980328AB7FE8AE347D418871A770F1EFFE8A24FCAC9EE9DB), y: FieldElement(0x307BB7BAC4B807E9D59F65BEC20EC8AB54591BE07549140238927D01CA9152EF), infinity: 0 } }
おおお、このdecodeと署名の確認は本当に上手くいくのか再度トランザクションを実際に作ってテストする必要がありますね。
### signatureとpublic keyをPostされた側でのverify
let signed_ut1 = SignedTransaction {time: unsignedtransaction.time, sender: unsignedtransaction.sender, receiver: unsignedtransaction.receiver, amount: unsignedtransaction.amount, signature: sig1.to_string()}; println!("{:?}", signed_ut1); let public_key_decode = hex::decode(&signed_ut1.sender).unwrap(); let public_key = p256::PublicKey::from_sec1_bytes(&public_key_decode).expect("import error"); let verifying_key: VerifyingKey = public_key.into(); let signature: Signature = signed_ut1.signature.parse::<Signature>().unwrap(); let posted_transaction = UnsignedTransaction { time:signed_ut1.time, sender:signed_ut1.sender, receiver: signed_ut1.receiver, amount: signed_ut1.amount}; let posted_serialized: String = serde_json::to_string(&posted_transaction).unwrap(); assert!(verifying_key.verify(posted_serialized.as_bytes(), &signature).is_ok());
おおおおおおお、hexのpublic keyからちゃんと署名の検証ができてます。
【Rust】別のノードのブロックサイズが大きければ更新
自分が持っているblockのサイズと別のノードが持っているブロックサイズを比較して、相手の方が大きければ自分のブロックチェーンを更新する
let path = "own_blocks.txt"; let own_blocks = count_lines(path); println!("{}", own_blocks); let json_data = json(); let deserialized = json!(**&json_data); let objs: Vec<Block> = serde_json::from_value(deserialized).unwrap(); if objs.len() > own_blocks { let mut file = File::create("data/blocks.txt").expect("file not found."); for obj in &objs { let t = serde_json::to_string(&obj).unwrap(); writeln!(file, "{}", t).expect("can not write."); } } println!("{}", objs.len());
OK、これを関数化して繋げる。