– Signatureを圧縮する
– 署名をシリアライズする標準をDERフォーマットと呼ぶ
1. 0x30バイトで開始
2. 署名の残りの長さをエンコード
3. マーカーバイト0x02追加
4. rをビッグエンディアン整数としてエンコード
5. マーカーバイト0x02を追加
6. sをビッグエンディアン整数としてエンコード
class Signature: def __init__(self, r, s): self.r = r self.s = s def __repr__(self): return 'Signature({:x},{:x})'.format(self.r, self.s) def der(self): rbin = self.r.to_bytes(32, byteorder='big') rbin = rbin.lstrip(b'\x00') if rbin[0] & 0x80: rbin = b'\x00' + rbin result = byte([2, len(rbin)]) + rbin sbin = self.s.to_bytes(32, byteorder='big') sbin = sbin.lstrip(b'\x00') if sbin[0] & 0x80: sbin = b'\x00' + sbin result += bytes([2, len(sbin)]) + sbin return bytes([0x30, len(result)]) + result
r = 0x37206a0610995c58074999cb9767b87af4c4978db68c06e8e6e81d282047a7c6 s = 0x8ca63759c1157ebeaec0d03cecca119fc9a75bf8e6d0fa65c841c8e2738cdaec sig = Signature(r, s) print(sig.der().hex())
$ python3 app.py
3045022037206a0610995c58074999cb9767b87af4c4978db68c06e8e6e81d282047a7c60221008ca63759c1157ebeaec0d03cecca119fc9a75bf8e6d0fa65c841c8e2738cdaec
### Base58
def encode_base58(s): count = 0 for c 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 h = '7c076ff316692a3d7eb3c3bb0f8b1488cf72e1afcd929e29307032997a838a3d' print(encode_base58(bytes.fromhex(h))) h = 'eff69ef2b1bd93a66ed5219add4fb51e11a840f404876325a1e8ffe0529a2c' print(encode_base58(bytes.fromhex(h))) h = 'c7207fee197d27c618aea621406f6bf5ef6fca38681d82b2f06fddbdce6feab6' print(encode_base58(bytes.fromhex(h)))
### アドレス形式
1. メインネットアドレスは先頭を0x00, テストネットは0x6f
2. SECフォーマットを取り出し、sha256とripemd160操作を行う
3. 1のプレフィックスと2のハッシュ操作の結果を結合
4. 3の結果にhash256を行い先頭の4バイトを取得
5. 3と4を結合させてBase58でエンコード
def encode_base58_checksum(b): return encode_base58(b + hash256(b)[:4]) def hash160(s): return hashlib.new('ripemd160', hashlib.sha256(s).digest()).digest()
class S256Point:
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)
priv = PrivateKey(5002) print(priv.point.address(compressed=False, testnet=True)) priv = PrivateKey(2020**5) print(priv.point.address(compressed=True, testnet=True)) priv = PrivateKey(0x12345deadbeef) print(priv.point.address(compressed=True, testnet=False))
### WIF(ウォレットインポート形式)
1. メインネット用の秘密鍵は0x80、テストネット用は0xefプレフィックス
2. 秘密鍵を32バイトのビッグエンディアンでエンコード
3. SECフォーマットが圧縮形式の場合、末尾に0x01
4. 123を結合
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)
priv = PrivateKey(5003) print(priv.wif(compressed=True, testnet=True)) priv = PrivateKey(2021**5) print(priv.wif(compressed=False, testnet=True)) priv = PrivateKey(0x54321deadbeef) print(priv.wif(compressed=True, testnet=False))
### リトルエンディアン
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')
テストネットアドレスの作成
passphrase = b'info@hpscript.com my secret' secret = little_endian_to_int(hash256(passphrase)) priv = PrivateKey(secret) print(priv.point.address(testnet=True))
テストネットのfaucetサイト
https://testnet-faucet.mempool.co/
大分入ってきましたね