print(hash("abc")) print(hash("def")) print(hash(123)) print(hash(123.0)) print(hash(-123)) print(hash("abc"))
$ python3 test.py
-6216428559771217202
-5554558427243961036
123
123
-123
-6216428559771217202
ソフトウェアエンジニアの技術ブログ:Software engineer tech blog
随机应变 ABCD: Always Be Coding and … : хороший
print(hash("abc")) print(hash("def")) print(hash(123)) print(hash(123.0)) print(hash(-123)) print(hash("abc"))
$ python3 test.py
-6216428559771217202
-5554558427243961036
123
123
-123
-6216428559771217202
eval関数は文字列をPythonのコードとして実行する
num = eval("1 + 2 + 3") print(num)
ビットコインの秘密鍵は256ビットの16進数
ビットコインの秘密鍵は256ビット(32バイト、16進数で64文字)で、1から2^256の範囲であればほぼすべての数字が秘密鍵として利用
from ecc import S256Point, PrivateKey from helper import hash160, little_endian_to_int import os private_key = os.urandom(32) p = little_endian_to_int(hash160(private_key)) print(p) privateKey = PrivateKey(p) wallet = privateKey.point.address(testnet=True) print(wallet)
little_endian_to_int でint型にしてアドレスを作成する
バイトを受け取り、リトルエンディアンとして解釈して数値を返す関数
def little_endian_to_int(b): return int.from_bytes(b, 'little')
数字を受け取り、リトルエンディアンとしてバイトを返却
def int_to_little_endian(n, length): return n.to_bytes(length, 'little')
バイト数も256とか指定するのではなく、引数lengthとして受け付ける。また、数字はiよりnの方がベター
秘密鍵は256ビットの数値である。
WIFは人間が読めるように秘密鍵をシリアライズした形式。アドレスと同じようにBase58エンコーディングを用いる。
1. メインネット用の秘密鍵は0x80、テストネット用は0xefのプレフィックスで開始
2. 秘密鍵を32バイトのビッグエンディアンでエンコード
3. 公開鍵アドレス用のSECフォーマットが圧縮形式の場合、末尾に0x01を追加
4. 1.のプレフィックス、2.のシリアライズした秘密鍵、3.のサフィックスを順に結合
5. 4.の結果にhash256を実行し、先頭の4バイトを取得
6. 4.と5.を結合させて、Base58でエンコード
def wif(self, compressed=True, testnet=False): secret_bytes = self.secret.to_bytes(32, 'big') if testnet: prefix = b'\xef' else: prefix = b'\x80' if compressed: suffix = b'\x01' else: suffix = b'' return encode_base58_checksum(prefix + secret_bytes + suffix)
秘密鍵を”5003″とする場合のWIF
from ecc import S256Point, PrivateKey import helper privateKey = PrivateKey(5003) wif = privateKey.wif(compressed=True, testnet=True) print(wif)
$ python3 main.py
cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN8rFTv2sfUK
アドレスを短くして安全性を高めるには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)
digest()はバイト列、hexdigest()は16進数を返却する
import hashlib print(hashlib.sha256(b'test').digest()) print(hashlib.sha256(b'test').hexdigest())
$ python3 test.py
b’\x9f\x86\xd0\x81\x88L}e\x9a/\xea\xa0\xc5Z\xd0\x15\xa3\xbfO\x1b+\x0b\x82,\xd1]l\x15\xb0\xf0\n\x08′
9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
def encode_base58(s): count = 0 for s in s: if c == 0: count += 1 else: break num = int.from_bytes(s, 'big') prefix = '1' * count result = '' while num > 0: num, mod = divmod(num, 58) result = BASE58_ALPHABET[mod] + result return prefix + result
16進数をバイトに変換し、それをBase58に変換
from ecc import Signature import helper s = '7c076ff316692a3d7eb3c3bb0f8b1488cf72e1afcd929e29307032997a838a3d' print(helper.encode_base58(bytes.fromhex(s)))
$ python3 main.py
9MA8fRQrT4u8Zj8ZRd6MAiiyaxb2Y1CMpvVkHQu5hVM6
Pythonでは //で商、%で余りだが、divmodで両方を出力する。
q = 13 // 4 mod = 13 % 4 print(q, mod) q, mod = divmod(17, 7) print(q, mod)
$ python3 test.py
3 1
2 3
bin()は2進数に、hex()は16進数に変換する
num = 13 num_bin = bin(num) num_hex = hex(num) print(num_bin) print(num_hex)
$ python3 test.py
0b1101
0xd
format()を用いると、bで2進数、xで16進数に変換する