ビットコインのアドレス形式

アドレスを短くして安全性を高めるにはripemd160ハッシュを使用する。33バイトから20バイトへ短縮できる。

1. メインネットアドレスは先頭を0x00、テストネットは0x6fで開始する。
2. SECフォーマット(圧縮・非圧縮)を取り出し、sha256とripemd160ハッシュ操作を行う。この組み合わせをhash160操作と呼ぶ。
3. 1のプレフィックスと2のハッシュ操作を結合する。
4. 3の結果にhash256を行い、先頭の4バイトを取得する
5. 3と4を結合させてBase58でエンコードする

4の結果をチェックサムと呼ぶ

def encode_base58_checksum(b):
    return encode_base58(b + hash256(b)[:4])
import helper from encode_base58_checksum, hash160

def encode_base58_checksum(b):
    return encode_base58(b + hash256(b)[:4])

def hash160(s):
    return hashlib.new('ripemd160', hashlib.sha256(s).digest()).digest()
    def hash160(self, compressed=True):
        return hash160(self.sec(compressed))

    def address(self, compressed=True, testnet=False):
        h160 = self.hash160(compressed)
        if testnet:
            prefix = b'\x6f'
        else:
            prefix = b'\x00'
        return encode_base58_checksum(prefix + h160)

秘密鍵を”5002″とする場合のアドレスの求め方

from ecc import S256Point, PrivateKey

privateKey = PrivateKey(5002)
address = privateKey.point.address(compressed=False, testnet=True)
print(address)

$ python3 main.py
mmTPbXQFxboEtNRkwfh6K51jvdtHLxGeMA

圧縮・非圧縮やメインネット、テストネットは引数で切り替える
address = privateKey.point.address(compressed=True, testnet=False)

スカラー倍算のコーディング

    def __rmul__(self, coefficient):
        product = self.__class__(Nonee, None, self.a, self.b)
        for _ in range(coefficient):
            product += self
        return product

coefficient の和訳は係数です。係数の数だけ点を加算します。

ビットの倍算だと以下のようになる。2進展開。

    def __rmul__(self, coefficient):
        coef = coefficient 
        current = self 
        result = self.__class__(Nonee, None, self.a, self.b)
        while coef:
            if coef & 1:
                result += current
            current += current
        return result

点と曲線の確認をunittestで実行する

ecc.py

from unittest import TestCase

class ECCTest(TestCase):

    def test_on_curve(self):
        prime = 223
        a = FieldElement(0, prime)
        b = FieldElement(7, prime)  
        valid_points = ((192, 105),(17,56),(1,193))
        invalid_points = ((200, 119),(42, 99))
        for x_raw, y_raw in valid_points:
            x = FieldElement(x_raw, prime)
            y = FieldElement(y_raw, prime)
            Point(x, y, a, b)
        for x_raw, y_raw in invalid_points:
            x = FieldElement(x_raw, prime)
            y = FieldElement(y_raw, prime)
            with self.assertRaises(ValueError):
                Point(x, y, a, b)

helper.py

from unittest import TestSuite, TextTestRunner

def run(test):
    suite = TestSuite()
    suite.addTest(test)
    TextTestRunner().run(suite)

main.py

import ecc
from helper import run

run(ecc.ECCTest('test_on_curve'))

$ python3 main.py
.
———————————————————————-
Ran 1 test in 0.000s

OK

有限体とは

位数が有限である体
四則演算が定義され閉じている有限集

整数の集合は無限の要素だが、有限体は要素が有限で、四則演算(足し算・引き算・掛け算・割り算)が閉じている
=> 閉じているとは、演算結果が、有理数でなくなるということがない
=> 演算を施した結果がふたたびもとの集合に属する
素数 P で割った余り

これ、初手でいきなりつまづくな…

pythonとhmac-sha512

HMACは暗号ハッシュ関数を使用してメッセージ認証を行う仕組み

import hmac
import hashlib

key=b"secret2"
text=b"foo bar"
signature=hmac.new(key,text,hashlib.md5).hexdigest()
print(signature)

b””としないと、エラーになる。
self._hmac = _hashopenssl.hmac_new(key, msg, digestmod=digestmod)
TypeError: Strings must be encoded before hashing

HD Wallet

import os
import binascii
import ecdsa
import hmac
import hashlib

seed = os.urandom(32)
root_key = b'Bitcoin Seed'

def hmac_sha512(data, keymessage):
    hash = hmac.new(data, keymessage, 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("マスター秘密鍵")
print(binascii.hexlify(master_secretkey))
print("\n")
print("マスターチェーンコード")
print(binascii.hexlify(master_chaincode))
print("\n")
print("マスター公開鍵")
print(binascii.hexlify(master_publickey_x))

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

print("\n")
print("子秘密鍵")
print(binascii.hexlify(child_secretkey))

bitcoin アドレスの生成

import os
import ecdsa
import hashlib
import base58
from Crypto.Hash import RIPEMD160

private_key = os.urandom(32)
public_key = ecdsa.SigningKey.from_string(private_key, curve=ecdsa.SECP256k1).verifying_key.to_string()

prefix_and_pubkey = b"\x04" + public_key

intermediate = hashlib.sha256(prefix_and_pubkey).digest()
ripemd160 = RIPEMD160.new()
ripemd160.update(intermediate)
hash160 = ripemd160.digest()

prefix_and_hash160 = b"\x00" + hash160

double_hash = hashlib.sha256(hashlib.sha256(prefix_and_hash160).digest()).digest()
checksum = double_hash[:4]
pre_address = prefix_and_hash160 + checksum

address = base58.b58encode(pre_address)
print(address.decode())

hashlibで以下のように書くとエラーになる。
ripemd160 = hashlib.new(‘ripemd160’)

$ python3 address.py
Traceback (most recent call last):
File “/usr/lib/python3.10/hashlib.py”, line 160, in __hash_new
return _hashlib.new(name, data, **kwargs)
ValueError: [digital envelope routines] unsupported

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File “/home/vagrant/dev/work/address.py”, line 12, in
ripemd160 = hashlib.new(‘ripemd160’)
File “/usr/lib/python3.10/hashlib.py”, line 166, in __hash_new
return __get_builtin_constructor(name)(data)
File “/usr/lib/python3.10/hashlib.py”, line 123, in __get_builtin_constructor
raise ValueError(‘unsupported hash type ‘ + name)
ValueError: unsupported hash type ripemd160

従って、pycryptodomeをインストールして、ripemd160を使う
$ pip install pycryptodome

【Blockchain】トランザクションへの電子署名の実装

import pandas as pd
import datetime
from ecdsa import SigningKey, SECP256k1
import binascii
import json

secret_key_A_str = "47c5c280197c691be3f80462b72d60b7b2915753f1a81a6eedbfbb2f01f55cae"
public_key_B_str = "02cfbf50fe5873ac6e5352e7524c4934e32f01281fc7d6112fed86cf58e72b09bb302f1f7d10d7c37d13eb7e62fd8bb1950b263c51ac76581f8b0a38d0d8853e"

secret_key_A = SigningKey.from_string(binascii.unhexlify(secret_key_A_str), curve=SECP256k1)
public_key_A = secret_key_A.verifying_key
public_key_A_str = public_key_A.to_string().hex()
time_now = datetime.datetime.now(datetime.timezone.utc).isoformat()

unsigned_transaction = {"time": time_now, "sender": public_key_A_str, "receiver": public_key_B_str, "amount": 3}
signature = secret_key_A.sign(json.dumps(unsigned_transaction).encode('utf-8'))
transaction = {"time": time_now, "sender": public_key_A_str, "receiver": public_key_B_str, "amount": 3, "signature": signature.hex()}

pd.to_pickle(transaction, "signed_transaction.pkl")

取引記録の検証

import pandas as pd
from ecdsa import VerifyingKey, BadSignatureError, SECP256k1
import binascii
import json

transaction = pd.read_pickle("signed_transaction.pkl")
public_key_A = VerifyingKey.from_string(binascii.unhexlify(transaction["sender"]), curve=SECP256k1)
signature = binascii.unhexlify(transaction["signature"])

unsigned_transaction = {
	"time": transaction["time"],
	"sender": transaction["sender"],
	"receiver": transaction["receiver"],
	"amount": transaction["amount"]
}

try:
	public_key_A.verify(signature, json.dumps(unsigned_transaction).encode('utf-8'))
	print("トランザクションは改竄されていません。")
except BadSignatureError:
	print("トランザクションは改竄されています。")

$ python3 verify_transaction.py
トランザクションは改竄されていません。

cheat.py

import pandas as pd

transaction = pd.read_pickle("signed_transaction.pkl")
print("改竄前のトランザクション:")
print(transaction)

transaction = {"time": transaction["time"], "sender": transaction["sender"], "receiver": transaction["receiver"], "amount": 30, "signature": transaction["signature"]}
print("改竄後のトランザクション:")
print(transaction)
pd.to_pickle(transaction, "signed_transaction.pkl")

改竄するとsignatureが変更されていないので、改竄されていることがわかる

[Bitcoin] Walletの概要

秘密鍵 => 公開鍵 => ビットコインウォレットの順番で作られる。
OpenSSLで秘密鍵、公開鍵を作る手順と一緒
ウォレットは、「秘密鍵」「公開鍵」を保持し、管理する

### ウォレットの鍵の管理手法
ランダムウォレット と HDウォレットがある

– ランダムウォレット
秘密鍵公開鍵を1:1の関係で生成し管理する
ただし、複数の秘密鍵を管理保持しなければならない

– HDウォレット(Hierarchy Deterministic)
シードと呼ばれる値から秘密鍵を生成し、階層構造を用いて管理する
シードがあれば、秘密鍵は複数管理する必要はない(管理コストが下がる)

乱数生成器 -> シード -> HMAC-SHA512 -> 親秘密鍵(256bit) + 親ChainCode(256bit) *子秘密鍵生成に使用

親秘密鍵 + 親ChainCode + index -> HMAC-SHA512 -> 子秘密鍵 + 子ChainCode

### パスフレーズ, BIP32, BIP44
パスフレーズとは、どの階層にどの鍵が存在するのかを一意に表したものでスラッシュ区切りで表現
BIP32の標準規格: m/a’/c/i
BIP44の標準規格: m / purpose’ / coin_type’ / account’ / change /

### HMAC-sha512
HMACは、暗号ハッシュ関数を使用して、メッセージ認証を行う仕組み

import hmac
import hashlib
key="asdf"
text="public text"
signature = hmac.new(bytearray(key, "ASCII"),bytearray(text, "ASCII"), hashlib.sha512).hexdigest()
print(signature)

$ python3 wallet.py
b83ed32cab16c05e43f0922cf5123f04d2278d4fa50e4b1edab5f0ce04e74d5900b91dec501efd96de4721313f56533b55c1fec837b92bebe6126df7838dcc6d

### bitcoinのlibraryを使う場合

from bitcoin import *
my_private_key = random_key()
print(my_private_key)
public_key = privtopub(my_private_key)
print(public_key)
wallet_address = pubtoaddr(public_key)
print(wallet_address)

秘密鍵 => 公開鍵 => ビットコインウォレットの順番で作る

$ python3 wallet.py
336a4393ccef96b9eedb78deff766a6289d6e47983b35ee58860dd65c1a8fb86
04ef2a745e5853cea83031dd5a49de1716642ae55b1d537d2407f52757580071d7475b61b4efa2fa355d50251a286850a8548f808440c8e3739aa8bf31660dfab3
1FegPWcNYxpVMRg8rT8NhWXkzWAuk6WX2A

ああああああああああ
もうやだあ

[Blockchain] ラズパイでBitcoinのマイニングを実行する

ビットコイン用のウォレットを「Blockchain.com」で作成します。

### raspberry pi
$ git clone –depth 1 https://github.com/tpruvot/cpuminer-multi.git
$ cd cpuminer-multi/
$ sudo apt-get install automake autoconf pkg-config libcurl4-openssl-dev libjansson-dev libssl-dev libgmp-dev make g++
$ ./build.sh

### mining
$ ./cpuminer -a sha256d –user=${bitcoin address} –url=stratum+tcp://sha256.jp.nicehash.com:3334

[2021-12-11 19:37:49] sha256d block 14160074, diff 1110110280.114
[2021-12-11 19:37:49] CPU #3: 982.61 kH/s
[2021-12-11 19:37:49] CPU #1: 969.81 kH/s
[2021-12-11 19:37:49] CPU #2: 955.35 kH/s
[2021-12-11 19:37:49] CPU #0: 924.83 kH/s
[2021-12-11 19:37:56] sha256d block 717892, diff 206739871308.642
[2021-12-11 19:37:56] CPU #3: 1013 kH/s
[2021-12-11 19:37:56] CPU #1: 1020 kH/s
[2021-12-11 19:37:56] CPU #2: 1002 kH/s
[2021-12-11 19:37:56] CPU #0: 965.80 kH/s

おおおお、これなんか凄いわ
bitcoinはレベルが違う

### NiceHashの場合
$ ./cpuminer -a sha256d –user=${NiceHash wallet}.raspberrypi4 –url=stratum+tcp://sha256.jp.nicehash.com:3334 -p x –no-color

基本的にはBitcoin.comと同じ
ただ、NiceHashの場合はNiceHash側の管理画面で状況が表示されれる

これは凄いわ、全然レベちだわ