位数が有限である体
四則演算が定義され閉じている有限集
整数の集合は無限の要素だが、有限体は要素が有限で、四則演算(足し算・引き算・掛け算・割り算)が閉じている
=> 閉じているとは、演算結果が、有理数でなくなるということがない
=> 演算を施した結果がふたたびもとの集合に属する
素数 P で割った余り
これ、初手でいきなりつまづくな…
ソフトウェアエンジニアの技術ブログ:Software engineer tech blog
随机应变 ABCD: Always Be Coding and … : хороший
位数が有限である体
四則演算が定義され閉じている有限集
整数の集合は無限の要素だが、有限体は要素が有限で、四則演算(足し算・引き算・掛け算・割り算)が閉じている
=> 閉じているとは、演算結果が、有理数でなくなるということがない
=> 演算を施した結果がふたたびもとの集合に属する
素数 P で割った余り
これ、初手でいきなりつまづくな…
a = 128 print(a.to_bytes(2, 'big')) print(a.to_bytes(4, 'big')) print(int.from_bytes(b'\x00\x80', 'big')) print(int.from_bytes(b'\x00\x00\x00\x80', 'big'))
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
0xはその文字列が16進数であることを示す
print(format(0xabcd, 'x')) print(hex(0xabcd)) print('%02x' % 0xabcd)
binasciiはバイト列に変換する
import binascii print(binascii.unhexlify('abcd')) print(str(binascii.hexlify(b'\xab\xcd'), 'utf-8'))
import hashlib class Block(): def __init__(self, data, prev_hash): self.index = 0 self.nonce = 0 self.prev_hash = prev_hash self.data = data def blockhash(self): blockheader = str(self.index) + str(self.prev_hash) + str(self.data) + str(self.nonce) block_hash = hashlib.sha256(blockheader.encode()).hexdigest() return block_hash def __str__(self): return "Block Hash: " + self.blockhash() + "\nPrevious Hash: " + self.prev_hash + "\nindex: " + str(self.index) + "\nData: " + str(self.data) + "\nNonce: " + str(self.nonce) + "\n--------------" class Hashchain(): def __init__(self): self.chain = ["0000000000000000000000000000000000000000000000000000000000000000"] def add(self, hash): self.chain.append(hash) hashchain = Hashchain() target = 0x777777 * 2**(8*(0x1e - 0x03)) for i in range(30): block = Block("Block " + str(i+1), hashchain.chain[-1]) block.index = block.index + i + 1 for n in range(4294967296): block.nonce = block.nonce + n if int(block.blockhash(), 16) < target: print(block) hashchain.add(block.blockhash()) break
簡易的なhash計算
import hashlib input_text = "satoshi" for nonce in range(20): input_data = input_text + str(nonce) hash = hashlib.sha256(input_data.encode("UTF-8")).hexdigest() print(input_data + " → " + hash)
ブロックヘッダのdifficulty bitsにハッシュ値の条件が書かれている
0x1e777777 のような形式で記述されている。
# difficulty_bits = 0x1e777777 # exponent = 0x1e # coefficient = 0x777777 target = 0x777777 * 2**(8*(0x1e - 0x03)) print(target) target_hex = hex(target)[2:].zfill(64) print(target_hex)
import hashlib class Block(): def __init__(self, data, prev_hash): self.index = 0 self.nonce = 0 self.prev_hash = prev_hash self.data = data def blockhash(self): blockheader = str(self.index) + str(self.prev_hash) + str(self.data) + str(self.nonce) block_hash = hashlib.sha256(blockheader.encode()).hexdigest() return block_hash def __str__(self): return "Block Hash: " + self.blockhash() + "\nPrevious Hash: " + self.prev_hash + "\nindex: " + str(self.index) + "\nData: " + str(self.data) + "\nNonce: " + str(self.nonce) + "\n--------------" class Hashchain(): def __init__(self): self.chain = ["0000000000000000000000000000000000000000000000000000000000000000"] def add(self, hash): self.chain.append(hash) hashchain = Hashchain() target = 0x777777 * 2**(8*(0x1e - 0x03)) for i in range(30): block = Block("Block " + str(i+1), hashchain.chain[-1]) block.index = block.index + i + 1 for n in range(4294967296): block.nonce = block.nonce + n if int(block.blockhash(), 16) < target: print(block) hashchain.add(block.blockhash()) break
import json import requests address = "3FkenCiXpSLqD8L79intRNXUgjRoH9sjXa" res = requests.get("https://blockchain.info/unspent?active=" + address) utxo_list = json.loads(res.text)["unspent_outputs"] print(str(len(utxo_list)) + "個のutxoが見つかりました") for utxo in utxo_list: print(utxo["tx_hash"] + ":" + str(utxo["value"]) + " satoshis")
blockcain exploreのtransactionからも確認できる。
https://www.blockchain.com/explorer
$ python3 utxo.py
2個のutxoが見つかりました
0aed6c01112af0a2dd51981e983ce41ef271c3bbec834121a47dbe31c30519d7:548546 satoshis
768b2e91eaa99208d32d3dde2a0f70214940584754b66378a33c9e3bd60136c3:311694 satoshis
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))
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
import os import binascii private_key = os.urandom(32) print(private_key) print(binascii.hexlify(private_key))
32バイトの乱数を生成して、バイナリデータを16進数に変換
$ python3 wallet.py
b’W~\x9cj\xcc}\x0f1J\xab\xa6Hh\x87\xe7\xa6x2\xb1c,\xe9\x1dZp{R\xc3\x8e9-\xe2′
b’577e9c6acc7d0f314aaba6486887e7a67832b1632ce91d5a707b52c38e392de2′
公開鍵に楕円曲線暗号を使用します。
$ pip install ecdsa
import os import ecdsa import binascii private_key = os.urandom(32) public_key = ecdsa.SigningKey.from_string(private_key, curve=ecdsa.SECP256k1).verifying_key.to_string() print(binascii.hexlify(private_key)) print(binascii.hexlify(public_key))
y^2 = x^3 + 7 mod p
import os import ecdsa import binascii private_key = os.urandom(32) public_key = ecdsa.SigningKey.from_string(private_key, curve=ecdsa.SECP256k1).verifying_key.to_string() print(binascii.hexlify(private_key)) print(binascii.hexlify(public_key)) # public_key y-axis public_key_y = int.from_bytes(public_key[32:], "big") # create compressed public_key if public_key_y % 2 == 0: public_key_compressed = b"\x02" + public_key[:32] else: public_key_compressed = b"\x03" + public_key[:32] print(binascii.hexlify(public_key_compressed))