【Solana】Proof of HistoryをPythonで書く

前のブロックのiter,hashoutをベースに iter%(2**delay) == 0 になるまで、ブロックごとに262144回sha256を計算して、hash値を求める。hashoutが決まっているので、先回りしてブロックを作れそうだが、SolanaはPoSのため、不正がバレるとstakingが没収されてしまう。なるほどねー

import datetime
import hashlib
import time
import os
import pathlib
import random
import string
from random import randint

iter=1
hashin = ''.join(random.choices(string.ascii_uppercase + string.digits, k = 10))
hashout = hashlib.sha256(hashin.encode()).hexdigest()

print(hashin)
print(hashout)

def datastream():
    v1 = int(randint(1200, 1500))
    v2 = int(randint(1300, 1700))
    v3 = int(randint(1100, 1500))
    v4 = int(randint(4000, 5600))
    v5 = int(randint(4000, 5600))
    v6 = int(randint(1900, 2400))
    v7 = int(randint(1920, 2300))
    v8 = int(randint(1850, 2200))
    v9 = int(randint(1900, 2300))
    v10 = int(randint(1800, 2200))
    return [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10];

class Block:
    blockNo = 0
    count = iter
    data = None
    next = None
    hash = "None"
    previous_hash = "None"
    timestamp = datetime.datetime.now()

    def __init__(self, data):
        self.data = datastream()

    def hash(self):
        file = pathlib.Path("TToken")
        if file.exists():
            tier=open("TToken").readline().rstrip()
        else:
            with open("benchmark.py") as infile:
                exec(infile.read())
            tier=open("TToken").readline().rstrip()

        if tier=="T1":
            h = hashlib.md5()
        elif tier=="T2":
            h = hashlib.sha1()
        elif tier=="T3":
            h = hashlib.blake2s()
        elif tier=="T4":
            h = hashlib.sha3_256()

        h.update(
		str(self.nonce).encode('utf-8') +
		str(self.data).encode('utf-8') +
		str(self.previous_hash).encode('utf-8') +
		str(self.timestamp).encode('utf-8') +
		str(self.blockNo).encode('utf-8')
		)
        return h.hexdigest()
    
        block.blockNo = self.block.blockNo + 1

    def __str__(self):
        return "Block Number: " + str(self.blockNo) + "\nHistory Count: " + str(self.count) + "\nBlock Data: " + str(self.data) + "\nBlock Hash: " + str(self.hash) + "\nPrevious Hash: " + str(self.previous_hash) + "\n--------------"

class Blockchain:
	block = Block("Genesis")
	dummy = head = block

	def add(self, block):
		if (self.block.blockNo ==0):
			block.previous_hash =  "Origin"
		else:
			block.previous_hash = self.block.hash
		block.blockNo = self.block.blockNo + 1

		self.block.next = block
		self.block = self.block.next

	def mine(self, block):
		global iter
		global hashin
		global hashout
		delay = 18
		cont = 1
		while(cont == 1):
			if int(iter%(2**delay) == 0):
				block.count = iter
				block.hash = hashout
				self.add(block)
				print(block)
				iter += 1
				hashin=hashout
				hashout = hashlib.sha256(hashin.encode()).hexdigest()
				cont = 0
				break
			else:
				iter += 1
				hashin=hashout
				hashout = hashlib.sha256(hashin.encode()).hexdigest()

t_initial = time.perf_counter()
blockchain = Blockchain()
b=int(input('Enter the number of Blocks for this simulation:'))

for n in range(b):
    blockchain.mine(Block(n+1))
t_final = time.perf_counter()
delta_t = t_final - t_initial
delta_unit = delta_t*1000/b
print("comutation Time per Block(ms):"+str(delta_unit))
input('Press ENTER to exit')

Enter the number of Blocks for this simulation:3
Block Number: 1
History Count: 262144
Block Data: [1297, 1642, 1372, 5138, 5301, 2188, 1998, 2150, 1914, 1862]
Block Hash: a53e39cef8be27ba38e73fe216fbfc2efc63bca056fa2a6a18380e9d93c98ea3
Previous Hash: Origin
————–
Block Number: 2
History Count: 524288
Block Data: [1211, 1633, 1307, 4757, 5133, 2206, 2032, 1891, 2257, 2139]
Block Hash: 79561ebd2627b432d1e619dee9db7ac85593a4357925827754b1faefd42c1b72
Previous Hash: a53e39cef8be27ba38e73fe216fbfc2efc63bca056fa2a6a18380e9d93c98ea3
————–
Block Number: 3
History Count: 786432
Block Data: [1459, 1682, 1131, 5339, 4983, 2057, 1948, 2192, 2017, 2076]
Block Hash: d33e10fa10273b5d64ccdad34ffcbaae7673cb785807c49f199b204a148e6cd9
Previous Hash: 79561ebd2627b432d1e619dee9db7ac85593a4357925827754b1faefd42c1b72
————–
comutation Time per Block(ms):795.6611973543962
Press ENTER to exit

【BNB】BEP-20 Token on BSCを作る

1. bitbankなどでbnbを取得して、metamaskに送信します。
※bitbankの表記では、bnbはビルドアンドビルドとなっており、binance cointと異なるかと思いってしまいますが、ビルドアンドビルドでOKです。
metamaskでbscネットワークに接続し、bnbが受け取れたことを確認してアドレスを取得

2. remixを表示し、bep-20.solファイルを作成する
https://remix.ethereum.org/

pragma solidity >=0.6.0;

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol";

contract HpsToken is ERC20 {
    constructor(uint256 initialSupply) public ERC20("HpscriptToken", "HPS") {
        _mint(msg.sender, initialSupply);
    }
}

3. コンパイル

4.DEPLOY & RUN TRANSACTIONS
– Environmentをmetamaskにする
– ACCOUNTがMetamaskのアドレスになっていることを確認
– CONTRACTが先ほどコンパイルしたTokenになっている
– トークン供給量は100,000,000に設定する
-> Deploy

5. txハッシュからトランザクション確認
https://bscscan.com/

6. トークンの受け取り
コントラクトのアドレスを入力

【Ethereum】ECR20を発行しよう

$ pip3 install eth-brownie
$ npm install -g ganache-cli
$ brownie init
$ tree
.
├── build
│ ├── contracts
│ ├── deployments
│ └── interfaces
├── contracts
├── interfaces
├── reports
├── scripts
└── tests

contract/TokenERC20.sol
https://github.com/PatrickAlphaC/erc20-brownie/tree/main/contracts

pragma solidity ^0.6.0;

interface tokenRecipient { 
    function receiveApproval(address _from, uint256 _value, address _token, bytes calldata _extraData) external; 
}

contract TokenERC20 {
    // Public variables of the token
    string public name;
    string public symbol;
    uint8 public decimals = 18;
    // 18 decimals is the strongly suggested default, avoid changing it
    uint256 public totalSupply;

    // This creates an array with all balances
    mapping (address => uint256) public balanceOf;
    mapping (address => mapping (address => uint256)) public allowance;

    // This generates a public event on the blockchain that will notify clients
    event Transfer(address indexed from, address indexed to, uint256 value);
    
    // This generates a public event on the blockchain that will notify clients
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);

    // This notifies clients about the amount burnt
    event Burn(address indexed from, uint256 value);

    /**
     * Constructor function
     *
     * Initializes contract with initial supply tokens to the creator of the contract
     */
    constructor(
        uint256 initialSupply,
        string memory tokenName,
        string memory tokenSymbol
    ) public {
        totalSupply = initialSupply * 10 ** uint256(decimals);  // Update total supply with the decimal amount
        balanceOf[msg.sender] = totalSupply;                // Give the creator all initial tokens
        name = tokenName;                                   // Set the name for display purposes
        symbol = tokenSymbol;                               // Set the symbol for display purposes
        emit Transfer(address(0), msg.sender, totalSupply);

    }

    /**
     * Internal transfer, only can be called by this contract
     */
    function _transfer(address _from, address _to, uint _value) internal {
        // Prevent transfer to 0x0 address. Use burn() instead
        require(_to != address(0x0));
        // Check if the sender has enough
        require(balanceOf[_from] >= _value);
        // Check for overflows
        require(balanceOf[_to] + _value >= balanceOf[_to]);
        // Save this for an assertion in the future
        uint previousBalances = balanceOf[_from] + balanceOf[_to];
        // Subtract from the sender
        balanceOf[_from] -= _value;
        // Add the same to the recipient
        balanceOf[_to] += _value;
        emit Transfer(_from, _to, _value);
        // Asserts are used to use static analysis to find bugs in your code. They should never fail
        assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
    }

    /**
     * Transfer tokens
     *
     * Send `_value` tokens to `_to` from your account
     *
     * @param _to The address of the recipient
     * @param _value the amount to send
     */
    function transfer(address _to, uint256 _value) public returns (bool success) {
        _transfer(msg.sender, _to, _value);
        return true;
    }

    /**
     * Transfer tokens from other address
     *
     * Send `_value` tokens to `_to` on behalf of `_from`
     *
     * @param _from The address of the sender
     * @param _to The address of the recipient
     * @param _value the amount to send
     */
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
        require(_value <= allowance[_from][msg.sender]);     // Check allowance
        allowance[_from][msg.sender] -= _value;
        _transfer(_from, _to, _value);
        return true;
    }

    /**
     * Set allowance for other address
     *
     * Allows `_spender` to spend no more than `_value` tokens on your behalf
     *
     * @param _spender The address authorized to spend
     * @param _value the max amount they can spend
     */
    function approve(address _spender, uint256 _value) public
        returns (bool success) {
        allowance[msg.sender][_spender] = _value;
        emit Approval(msg.sender, _spender, _value);
        return true;
    }

    /**
     * Set allowance for other address and notify
     *
     * Allows `_spender` to spend no more than `_value` tokens on your behalf, and then ping the contract about it
     *
     * @param _spender The address authorized to spend
     * @param _value the max amount they can spend
     * @param _extraData some extra information to send to the approved contract
     */
    function approveAndCall(address _spender, uint256 _value, bytes memory _extraData)
        public
        returns (bool success) {
        tokenRecipient spender = tokenRecipient(_spender);
        if (approve(_spender, _value)) {
            spender.receiveApproval(msg.sender, _value, address(this), _extraData);
            return true;
        }
    }

    /**
     * Destroy tokens
     *
     * Remove `_value` tokens from the system irreversibly
     *
     * @param _value the amount of money to burn
     */
    function burn(uint256 _value) public returns (bool success) {
        require(balanceOf[msg.sender] >= _value);   // Check if the sender has enough
        balanceOf[msg.sender] -= _value;            // Subtract from the sender
        totalSupply -= _value;                      // Updates totalSupply
        emit Burn(msg.sender, _value);
        return true;
    }

    /**
     * Destroy tokens from other account
     *
     * Remove `_value` tokens from the system irreversibly on behalf of `_from`.
     *
     * @param _from the address of the sender
     * @param _value the amount of money to burn
     */
    function burnFrom(address _from, uint256 _value) public returns (bool success) {
        require(balanceOf[_from] >= _value);                // Check if the targeted balance is enough
        require(_value <= allowance[_from][msg.sender]);    // Check allowance
        balanceOf[_from] -= _value;                         // Subtract from the targeted balance
        allowance[_from][msg.sender] -= _value;             // Subtract from the sender's allowance
        totalSupply -= _value;                              // Update totalSupply
        emit Burn(_from, _value);
        return true;
    }
}

$ brownie compile
$ ganache-cli
Ganache CLI v6.12.2 (ganache-core: 2.13.2)
(node:179457) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node –trace-deprecation …` to show where the warning was created)

Available Accounts
==================
(0) 0xe866BE96aA793C69dc4CB69D717D12e7b0d5267d (100 ETH)
(1) 0x0696f9fB2fFe037F6E13Deba40912562f97faDC2 (100 ETH)
(2) 0x3785c546Fe6Fc72a7468059d4f7eE6f3d4727F2C (100 ETH)
(3) 0x11165cCc75b3E917C9918C529778c9cE4E58d513 (100 ETH)
(4) 0x3B26Bfe04381231728b9ca3bbC42435aa6D2155e (100 ETH)
(5) 0x9aA0b1bd92d42Da1cA1e842A05Ec5c69F3Dcd123 (100 ETH)
(6) 0x0aC021eCA14d61880693fd6Cd0FAAD598f208778 (100 ETH)
(7) 0xbd07f6ad435698D878f94D9A29337E09c51fd900 (100 ETH)
(8) 0xb8d7f305778207E48A1b70D1D54226ABd76156De (100 ETH)
(9) 0x7e41f77B97d567178A1443E2fe44d3168111BaF6 (100 ETH)

Private Keys
==================
// 省略

HD Wallet
==================
Mnemonic: course acid cereal genuine old blind someone ticket thrive palace napkin time
Base HD Path: m/44’/60’/0’/0/{account_index}

Gas Price
==================
20000000000

Gas Limit
==================
6721975

Call Gas Limit
==================
9007199254740991

Listening on 127.0.0.1:8545

script/deploy_token.py

from brownie import accounts, config, TokenERC20

initial_supply = 1000000000000000000000  # 1000
token_name = "Hpscript"
token_symbol = "HPS"

def main():
    account = accounts.add(config["wallets"]["from_key"])
    erc20 = TokenERC20.deploy(
        initial_supply, token_name, token_symbol, {"from": account} , publish_source=True 
    )

brownie-config.yaml

dependencies:
  - OpenZeppelin/openzeppelin-contracts@3.4.0
compiler:
  solc:
    remappings:
      - '@openzeppelin=OpenZeppelin/openzeppelin-contracts@3.4.0'
dotenv: .env
wallets:
  from_key: ${PRIVATE_KEY}

.env

export WEB3_INFURA_PROJECT_ID=''
export PRIVATE_KEY=''
export ETHERSCAN_TOKEN=''

$ source .env
$ brownie run scripts/deploy_token.py –network sepolia

スマートコントラクトで作るのか… なるほどね〜

【Blockchain】トレーサビリティの仕組みを考える

### フロントエンド
プロセスの選択肢があり、写真を撮って、Postする。緯度、経度情報が必要な場合は付与する。

<head>
    <meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Document</title>
	<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>
    <style>body {margin: 50px;}</style>
</head>
<body>
<div id="app">
    <form action="/complete" method="post" enctype="multipart/form-data">
    <h2 class="subtitle is-5">1.製品番号</h2>
    <span>1111001</span>
    <input type="hidden" name="serial_num" value="1111001"/>
    <br><br>
    <h2 class="subtitle is-5">2.製造工程を選択してください<h2>
    <select name="process" id="pet-select" class="select">
        <option value="">--Please choose an option--</option>
        <option value="1">企画デザイン</option>
        <option value="2">皮の検品</option>
        <option value="3">型抜き</option>
        <option value="4">パーツ作り</option>
        <option value="5">糊付け</option>
        <option value="6">表断面加工</option>
        <option value="7">縫製</option>
        <option value="8">仕上げ</option>
    </select>
    <br><br><br>
    <h2 class="subtitle is-5">3.背面カメラ</h2>
    <input type="file" name="file" onchange="previewFile(this);" capture="environment" accept="image/*"></label><br><br>
    <img id="preview"><br><br>
    <h2 class="subtitle is-5">4.現在地</h2>
    <span v-if="err">
        <!-- 緯度経度の情報を取得できませんでした。 -->
    </span>
    <lat></lat><br>
    <long></long><br>
    <input type="hidden" name="lat" v-model="lat"/>
    <input type="hidden" name="long" v-model="long"/>
    <br>
    <button class="button" type="submit">登録する</button>
    </form>
</div>

<script>
let lat = "35.6895014";
let long = "139.6917337";
function success(pos) {
  const crd = pos.coords;

  lat = crd.latitude;
  long = crd.longitude;
 
  let url = "/index.html?lat=" +lat + "&long=" + long;
  var app = new Vue({
    el: '#app',
    data: {
        lat: lat,
        long: long,
        err: null
    }
  })
}

function error(err) {
    Vue.component('lat',{
        template : '<span>緯度:' + lat + '</span>'
    })
    Vue.component('long',{
        template : '<span>経度:' + long + '</span>'
    })
    console.warn(`ERROR(${err.code}): ${err.message}`);
    let url = "/index.html?lat=" +lat + "&long=" + long;
    var app = new Vue({
    el: '#app',
    data: {
        lat: lat,
        long: long,
        err: err
    }
  })
  
}
navigator.geolocation.getCurrentPosition(success, error);
</script>
<script>
function previewFile(file) {
    if (file.files[0].size < 3000000) {
    var fileData = new FileReader();
    fileData.onload = (function() {
        document.getElementById('preview').setAttribute("style","width:150px;height:150px");
        document.getElementById('preview').src = fileData.result;
        
    });
    fileData.readAsDataURL(file.files[0]);
    }
}
</script>
</body>

### サーバサイド
追跡情報をチェーン上に入れるは、トランザクションとして署名して、walletから誰かのwallet宛に送らなければならない。
=> transaction detaの中に、商品識別子(serial number等)は必ず含める必要がある。
=> 商品識別子、製造プロセス、緯度、経度などの情報を全てトランザクションデータの中に入れる場合は、データ容量がある程度大きくなくてはいけないが、 商品識別子だけ入れて、後の項目はアプリケーション側(smart contract)のDBに保存すれば、データ容量は少なくて済む。その場合、アプリケーション側の開発が必要。
=> トレーサビリティに特化したブロックチェーンを作ることもできなくはないが、開発効率を考えるとナンセンス。
=> transaction dataにmessageの項目を作るのが一番良さそうには見える。

DEXとは何か? Uniswap/Raydiumを触ってみる

DEXはDecentralized Exchangesの略称で、仲介者となる企業が存在しない、ユーザ同士が直接仮想通貨を取引できるブロックチェーン上に構築される取引所

### 代表的なDEX
– Uniswap (ECR-20規格であれば全て取引できる)
– PancakeSwap
– dYdX (仮想通貨の先物取引)

ECR20は、イーサリアムのブロックチェーン上で動作するトークンの統一ルール、規格の一つ

### 日本の仮想通貨法
仮想通貨法には第一号仮想通貨、第二号仮想通貨の分類があり、前者は物品の購入などに際して不特定の者に使用できかつ不特定の者を相手として交換でき、後者は不特定の者を相手として第一号仮想通貨と交換できる
ECR20トークンはイーサリアムと交換可能なことから、第二号仮想通貨に該当する

### uniswap
metamaskなどのwalletがあれば簡単にswapができる
Uniswapでswapを実行すると、metamask上のwalletでも、swapした通貨を保有していることが確認できる。
https://app.uniswap.org/

イーサリアム: UniSwap
Binance Smart Chain: PancakeSwap
Solana : Raydium, Jupiter

Raydimでswapする場合は、PhantomなどSolのwalletを作成して、あとは接続するだけです。

RaydimもUIはUniSwapとかなり似ています。

なるほど、DEXはかなり凄いですね…

【Internet Computer】Rust ではじめてのキャニスター開発をやってみる

こちらの記事を参考にRustでDapps開発を行います。
https://smacon.dev/posts/hello-icp-rust/

### dfxのインストール
sh -ci “$(curl -fsSL https://sdk.dfinity.org/install.sh)”

dfxのインストールだが、mac m1チップのARMアーキテクチャではできない。
https://forum.dfinity.org/t/ubuntu-vm-on-macbook-m1-dfx-install-unknown-cpu-type-aarch64/21486

$ uname -m
aarch64

おいおいおい、いきなりつまづいてしまった。これのトラブルシューティングに丸一日かかりました。
どうしてもやりたかったので、結局、別の環境で作ることに。
# uname -m
x86_64

# dfxのプロジェクトファイル実行
# dfx new –type=rust rust_hello
# cd rust_hello
# dfx start –background
# rustup target add wasm32-unknown-unknown
# dfx deploy
Installed code for canister rust_hello_backend, with canister ID bkyz2-fmaaa-aaaaa-qaaaq-cai

# dfx canister call bkyz2-fmaaa-aaaaa-qaaaq-cai greet ‘(“everyone”: text)’
(“Hello, everyone!”)

# dfx canister status bkyz2-fmaaa-aaaaa-qaaaq-cai
Canister status call result for bkyz2-fmaaa-aaaaa-qaaaq-cai.
Status: Running
Controllers: bnz7o-iuaaa-aaaaa-qaaaa-cai wbqzn-2jy52-icft2-erprw-b52eq-e4ixe-4yxqb-5neaf-miz3k-arx3m-gae
Memory allocation: 0 Bytes
Compute allocation: 0 %
Freezing threshold: 2_592_000 Seconds
Idle cycles burned per day: 1_257_808 Cycles
Memory Size: 1_600_075 Bytes
Balance: 3_061_352_105_201 Cycles
Reserved: 0 Cycles
Reserved cycles limit: 5_000_000_000_000 Cycles
Wasm memory limit: 3_221_225_472 Bytes
Wasm memory threshold: 0 Bytes
Module hash: 0xb60872271f3ca7c4ee2b18a54434c344e3095982685ce10f2c7217c08dfb1cf7
Number of queries: 0
Instructions spent in queries: 0
Total query request payload size: 0 Bytes
Total query response payload size: 0 Bytes
Log visibility: controllers

$ dfx stop

どうやら、「デプロイして、キャニスターを実行」という流れになるようだ。
私の理解では、このキャニスターがスマートコントラクト。
そして、FrontはデフォルトでReactが用意されている。

なるほど、

【Rust】ECDSA署名のverifyのエラーハンドリング

署名のverifyはtrue or falseで返したいが、、、

pub async fn verify_signature(signedtransaction: &kernel::SignedTransaction) -> bool {
    // 省略
    return verifying_key.verify(posted_serialized.as_bytes(), &signature).is_ok()
}

偽のpublic keyを送ってきた可能性もあるため、Result型で返却しないといけない。

pub async fn verify_signature(signedtransaction: &SignedTransaction) -> Result<bool, Box<dyn std::error::Error>>{
    // 省略
    Ok(verifying_key.verify(posted_serialized.as_bytes(), &signature).is_ok())
}

なるほど、こういうのは、UnitTestを書いてテストしないと気づかない…

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

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