位数が有限である体
四則演算が定義され閉じている有限集
整数の集合は無限の要素だが、有限体は要素が有限で、四則演算(足し算・引き算・掛け算・割り算)が閉じている
=> 閉じているとは、演算結果が、有理数でなくなるということがない
=> 演算を施した結果がふたたびもとの集合に属する
素数 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))