《区块链编程》第四章
序列化
- SEC: Standard for Effcient Crptography 高效加密标准,序列化公钥。
- DER:Distinguished Encoding Rules 可分别编码规则, 序列化签名
- BASE:base家族, 使SEC序列化公钥后人类可读。
- WIF:Wallet Import Format 钱包导入格式,序列化私钥
练习1
p73
代码实现
from ecc import PrivateKey
priv = PrivateKey(5000)
print(priv.point.sec(compressed=False).hex())
priv = PrivateKey(2018 ** 5)
print(priv.point.sec(compressed=False).hex())
priv = PrivateKey(0xdeadbeef12345)
print(priv.point.sec(compressed=False).hex())
测试
04ffe558e388852f0120e46af2d1b370f85854a8eb0841811ece0e3e03d282d57c315dc72890a4f10a1481c031b03b351b0dc79901ca18a00cf009dbdb157a1d10
04027f3da1918455e03c46f659266a1bb5204e959db7364d2f473bdf8f0a13cc9dff87647fd023c13b4a4994f17691895806e1b40b57f4fd22581a4f46851f3b06
04d90cd625ee87dd38656dd95cf79f65f60f7273b67d3096e68bd81e4f5342691f842efa762fd59961d0e99803c61edba8b3e3f7dc3a341836f97733aebf987121
[Finished in 365ms]
练习2
p77
代码实现
from ecc import PrivateKey
priv = PrivateKey(5000)
print(priv.point.sec(compressed=True).hex())
priv = PrivateKey(2018 ** 5)
print(priv.point.sec(compressed=True).hex())
priv = PrivateKey(0xdeadbeef12345)
print(priv.point.sec(compressed=True).hex())
测试
02ffe558e388852f0120e46af2d1b370f85854a8eb0841811ece0e3e03d282d57c
02027f3da1918455e03c46f659266a1bb5204e959db7364d2f473bdf8f0a13cc9d
03d90cd625ee87dd38656dd95cf79f65f60f7273b67d3096e68bd81e4f5342691f
[Finished in 400ms]
练习3
p79
代码实现
from ecc import Signature
r = 0x37206a0610995c58074999cb9767b87af4c4978db68c06e8e6e81d282047a7c6
s = 0x8ca63759c1157ebeaec0d03cecca119fc9a75bf8e6d0fa65c841c8e2738cdaec
sig = Signature(r, s)
print(sig.der().hex())
测试
3045022037206a0610995c58074999cb9767b87af4c4978db68c06e8e6e81d282047a7c60221008ca63759c1157ebeaec0d03cecca119fc9a75bf8e6d0fa65c841c8e2738cdaec
[Finished in 337ms]
练习4
p81
代码实现
from helper import encode_base58
h = '7c076ff316692a3d7eb3c3bb0f8b1488cf72e1afcd929e29307032997a838a3d'
print(encode_base58(bytes.fromhex(h)))
h = 'eff69ef2b1bd93a66ed5219add4fb51e11a840f404876325a1e8ffe0529a2c'
print(encode_base58(bytes.fromhex(h)))
h = 'c7207fee197d27c618aea621406f6bf5ef6fca38681d82b2f06fddbdce6feab6'
print(encode_base58(bytes.fromhex(h)))
运行结果
9MA8fRQrT4u8Zj8ZRd6MAiiyaxb2Y1CMpvVkHQu5hVM6
4fE3H2E6XMp4SsxtwinF7w9a34ooUrwWe4WsW1458Pd
EQJsjkd6JaGwxrjEhfeqPenqHwrBmPQZjJGNSCHBkcF7
[Finished in 325ms]
练习5
p82
代码实现
from ecc import PrivateKey
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))
运行结果
mmTPbXQFxboEtNRkwfh6K51jvdtHLxGeMA
mopVkxp8UhXqRYbCYJsbeE1h1fiF64jcoH
1F1Pn2y6pDb68E5nYJJeba4TLg2U7B6KF1
[Finished in 376ms]
练习6
p83
代码实现
from ecc import PrivateKey
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))
运行结果
cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN8rFTv2sfUK
91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjpWAxgzczjbCwxic
KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgiuQJv1h8Ytr2S53a
[Finished in 393ms]
练习7
p83
代码实现
def little_endian_to_int(b):
return int.from_bytes(b, 'little')
b = b'\x01\x02'
print(little_endian_to_int(b))
运行结果
513
[Finished in 273ms]
练习8
p83
代码实现
def int_to_little_endian(n, length):
return n.to_bytes(length, 'little')
n = 513
print(int_to_little_endian(n, 5))
运行结果
b'\x01\x02\x00\x00\x00'
[Finished in 252ms]
练习9
p84
代码实现
from ecc import PrivateKey
from helper import hash256, little_endian_to_int
//下面是你的明文私钥。
passphrase = b'*********************'
secret = little_endian_to_int(hash256(passphrase))
priv = PrivateKey(secret)
print(priv.point.address(testnet=True))
运行结果
m*************************************7
[Finished in 425ms]
领取测试币
领取测试币的网址 输入验证码及 练习9输出的地址。 成功
本章中涉及的ecc与helper
书中给的ecc
from io import BytesIO
from random import randint
from unittest import TestCase
import hashlib
import hmac
from helper import encode_base58_checksum, hash160
class FieldElement:
def __init__(self, num, prime):
if num >= prime or num < 0:
error = 'Num {} not in field range 0 to {}'.format(
num, prime - 1)
raise ValueError(error)
self.num = num
self.prime = prime
def __repr__(self):
return 'FieldElement_{}({})'.format(self.prime, self.num)
def __eq__(self, other):
if other is None:
return False
return self.num == other.num and self.prime == other.prime
def __ne__(self, other):
return not (self == other)
def __add__(self, other):
if self.prime != other.prime:
raise TypeError('Cannot add two numbers in different Fields')
num = (self.num + other.num) % self.prime
return self.__class__(num, self.prime)
def __sub__(self, other):
if self.prime != other.prime:
raise TypeError('Cannot subtract two numbers in different Fields')
num = (self.num - other.num) % self.prime
return self.__class__(num, self.prime)
def __mul__(self, other):
if self.prime != other.prime:
raise TypeError('Cannot multiply two numbers in different Fields')
num = (self.num * other.num) % self.prime
return self.__class__(num, self.prime)
def __pow__(self, exponent):
n = exponent % (self.prime - 1)
num = pow(self.num, n, self.prime)
return self.__class__(num, self.prime)
def __truediv__(self, other):
if self.prime != other.prime:
raise TypeError('Cannot divide two numbers in different Fields')
num = (self.num * pow(other.num, self.prime - 2, self.prime)) % self.prime
return self.__class__(num, self.prime)
def __rmul__(self, coefficient):
num = (self.num * coefficient) % self.prime
return self.__class__(num=num, prime=self.prime)
class FieldElementTest(TestCase):
def test_ne(self):
a = FieldElement(2, 31)
b = FieldElement(2, 31)
c = FieldElement(15, 31)
self.assertEqual(a, b)
self.assertTrue(a != c)
self.assertFalse(a != b)
def test_add(self):
a = FieldElement(2, 31)
b = FieldElement(15, 31)
self.assertEqual(a + b, FieldElement(17, 31))
a = FieldElement(17, 31)
b = FieldElement(21, 31)
self.assertEqual(a + b, FieldElement(7, 31))
def test_sub(self):
a = FieldElement(29, 31)
b = FieldElement(4, 31)
self.assertEqual(a - b, FieldElement(25, 31))
a = FieldElement(15, 31)
b = FieldElement(30, 31)
self.assertEqual(a - b, FieldElement(16, 31))
def test_mul(self):
a = FieldElement(24, 31)
b = FieldElement(19, 31)
self.assertEqual(a * b, FieldElement(22, 31))
def test_rmul(self):
a = FieldElement(24, 31)
b = 2
self.assertEqual(b * a, a + a)
def test_pow(self):
a = FieldElement(17, 31)
self.assertEqual(a**3, FieldElement(15, 31))
a = FieldElement(5, 31)
b = FieldElement(18, 31)
self.assertEqual(a**5 * b, FieldElement(16, 31))
def test_div(self):
a = FieldElement(3, 31)
b = FieldElement(24, 31)
self.assertEqual(a / b, FieldElement(4, 31))
a = FieldElement(17, 31)
self.assertEqual(a**-3, FieldElement(29, 31))
a = FieldElement(4, 31)
b = FieldElement(11, 31)
self.assertEqual(a**-4 * b, FieldElement(13, 31))
class Point:
def __init__(self, x, y, a, b):
self.a = a
self.b = b
self.x = x
self.y = y
if self.x is None and self.y is None:
return
if self.y**2 != self.x**3 + a * x + b:
raise ValueError('({}, {}) is not on the curve'.format(x, y))
def __eq__(self, other):
return self.x == other.x and self.y == other.y \
and self.a == other.a and self.b == other.b
def __ne__(self, other):
return not (self == other)
def __repr__(self):
if self.x is None:
return 'Point(infinity)'
elif isinstance(self.x, FieldElement):
return 'Point({},{})_{}_{} FieldElement({})'.format(
self.x.num, self.y.num, self.a.num, self.b.num, self.x.prime)
else:
return 'Point({},{})_{}_{}'.format(self.x, self.y, self.a, self.b)
def __add__(self, other):
if self.a != other.a or self.b != other.b:
raise TypeError('Points {}, {} are not on the same curve'.format(self, other))
if self.x is None:
return other
if other.x is None:
return self
if self.x == other.x and self.y != other.y:
return self.__class__(None, None, self.a, self.b)
if self.x != other.x:
s = (other.y - self.y) / (other.x - self.x)
x = s**2 - self.x - other.x
y = s * (self.x - x) - self.y
return self.__class__(x, y, self.a, self.b)
if self == other and self.y == 0 * self.x:
return self.__class__(None, None, self.a, self.b)
if self == other:
s = (3 * self.x**2 + self.a) / (2 * self.y)
x = s**2 - 2 * self.x
y = s * (self.x - x) - self.y
return self.__class__(x, y, self.a, self.b)
def __rmul__(self, coefficient):
coef = coefficient
current = self
result = self.__class__(None, None, self.a, self.b)
while coef:
if coef & 1:
result += current
current += current
coef >>= 1
return result
class PointTest(TestCase):
def test_ne(self):
a = Point(x=3, y=-7, a=5, b=7)
b = Point(x=18, y=77, a=5, b=7)
self.assertTrue(a != b)
self.assertFalse(a != a)
def test_on_curve(self):
with self.assertRaises(ValueError):
Point(x=-2, y=4, a=5, b=7)
Point(x=3, y=-7, a=5, b=7)
Point(x=18, y=77, a=5, b=7)
def test_add0(self):
a = Point(x=None, y=None, a=5, b=7)
b = Point(x=2, y=5, a=5, b=7)
c = Point(x=2, y=-5, a=5, b=7)
self.assertEqual(a + b, b)
self.assertEqual(b + a, b)
self.assertEqual(b + c, a)
def test_add1(self):
a = Point(x=3, y=7, a=5, b=7)
b = Point(x=-1, y=-1, a=5, b=7)
self.assertEqual(a + b, Point(x=2, y=-5, a=5, b=7))
def test_add2(self):
a = Point(x=-1, y=1, a=5, b=7)
self.assertEqual(a + a, Point(x=18, y=-77, a=5, b=7))
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)
def test_add(self):
prime = 223
a = FieldElement(0, prime)
b = FieldElement(7, prime)
additions = (
(192, 105, 17, 56, 170, 142),
(47, 71, 117, 141, 60, 139),
(143, 98, 76, 66, 47, 71),
)
for x1_raw, y1_raw, x2_raw, y2_raw, x3_raw, y3_raw in additions:
x1 = FieldElement(x1_raw, prime)
y1 = FieldElement(y1_raw, prime)
p1 = Point(x1, y1, a, b)
x2 = FieldElement(x2_raw, prime)
y2 = FieldElement(y2_raw, prime)
p2 = Point(x2, y2, a, b)
x3 = FieldElement(x3_raw, prime)
y3 = FieldElement(y3_raw, prime)
p3 = Point(x3, y3, a, b)
self.assertEqual(p1 + p2, p3)
def test_rmul(self):
prime = 223
a = FieldElement(0, prime)
b = FieldElement(7, prime)
multiplications = (
(2, 192, 105, 49, 71),
(2, 143, 98, 64, 168),
(2, 47, 71, 36, 111),
(4, 47, 71, 194, 51),
(8, 47, 71, 116, 55),
(21, 47, 71, None, None),
)
for s, x1_raw, y1_raw, x2_raw, y2_raw in multiplications:
x1 = FieldElement(x1_raw, prime)
y1 = FieldElement(y1_raw, prime)
p1 = Point(x1, y1, a, b)
if x2_raw is None:
p2 = Point(None, None, a, b)
else:
x2 = FieldElement(x2_raw, prime)
y2 = FieldElement(y2_raw, prime)
p2 = Point(x2, y2, a, b)
self.assertEqual(s * p1, p2)
A = 0
B = 7
P = 2**256 - 2**32 - 977
N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
class S256Field(FieldElement):
def __init__(self, num, prime=None):
super().__init__(num=num, prime=P)
def __repr__(self):
return '{:x}'.format(self.num).zfill(64)
def sqrt(self):
return self**((P + 1) // 4)
class S256Point(Point):
def __init__(self, x, y, a=None, b=None):
a, b = S256Field(A), S256Field(B)
if type(x) == int:
super().__init__(x=S256Field(x), y=S256Field(y), a=a, b=b)
else:
super().__init__(x=x, y=y, a=a, b=b)
def __repr__(self):
if self.x is None:
return 'S256Point(infinity)'
else:
return 'S256Point({}, {})'.format(self.x, self.y)
def __rmul__(self, coefficient):
coef = coefficient % N
return super().__rmul__(coef)
def verify(self, z, sig):
s_inv = pow(sig.s, N - 2, N)
u = z * s_inv % N
v = sig.r * s_inv % N
total = u * G + v * self
return total.x.num == sig.r
def sec(self, compressed=True):
'''returns the binary version of the SEC format'''
if compressed:
if self.y.num % 2 == 0:
return b'\x02' + self.x.num.to_bytes(32, 'big')
else:
return b'\x03' + self.x.num.to_bytes(32, 'big')
else:
return b'\x04' + self.x.num.to_bytes(32, 'big') + \
self.y.num.to_bytes(32, 'big')
def hash160(self, compressed=True):
return hash160(self.sec(compressed))
def address(self, compressed=True, testnet=False):
'''Returns the address string'''
h160 = self.hash160(compressed)
if testnet:
prefix = b'\x6f'
else:
prefix = b'\x00'
return encode_base58_checksum(prefix + h160)
@classmethod
def parse(self, sec_bin):
'''returns a Point object from a SEC binary (not hex)'''
if sec_bin[0] == 4:
x = int.from_bytes(sec_bin[1:33], 'big')
y = int.from_bytes(sec_bin[33:65], 'big')
return S256Point(x=x, y=y)
is_even = sec_bin[0] == 2
x = S256Field(int.from_bytes(sec_bin[1:], 'big'))
alpha = x**3 + S256Field(B)
beta = alpha.sqrt()
if beta.num % 2 == 0:
even_beta = beta
odd_beta = S256Field(P - beta.num)
else:
even_beta = S256Field(P - beta.num)
odd_beta = beta
if is_even:
return S256Point(x, even_beta)
else:
return S256Point(x, odd_beta)
G = S256Point(
0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,
0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8)
class S256Test(TestCase):
def test_order(self):
point = N * G
self.assertIsNone(point.x)
def test_pubpoint(self):
points = (
(7, 0x5cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc, 0x6aebca40ba255960a3178d6d861a54dba813d0b813fde7b5a5082628087264da),
(1485, 0xc982196a7466fbbbb0e27a940b6af926c1a74d5ad07128c82824a11b5398afda, 0x7a91f9eae64438afb9ce6448a1c133db2d8fb9254e4546b6f001637d50901f55),
(2**128, 0x8f68b9d2f63b5f339239c1ad981f162ee88c5678723ea3351b7b444c9ec4c0da, 0x662a9f2dba063986de1d90c2b6be215dbbea2cfe95510bfdf23cbf79501fff82),
(2**240 + 2**31, 0x9577ff57c8234558f293df502ca4f09cbc65a6572c842b39b366f21717945116, 0x10b49c67fa9365ad7b90dab070be339a1daf9052373ec30ffae4f72d5e66d053),
)
for secret, x, y in points:
point = S256Point(x, y)
self.assertEqual(secret * G, point)
def test_verify(self):
point = S256Point(
0x887387e452b8eacc4acfde10d9aaf7f6d9a0f975aabb10d006e4da568744d06c,
0x61de6d95231cd89026e286df3b6ae4a894a3378e393e93a0f45b666329a0ae34)
z = 0xec208baa0fc1c19f708a9ca96fdeff3ac3f230bb4a7ba4aede4942ad003c0f60
r = 0xac8d1c87e51d0d441be8b3dd5b05c8795b48875dffe00b7ffcfac23010d3a395
s = 0x68342ceff8935ededd102dd876ffd6ba72d6a427a3edb13d26eb0781cb423c4
self.assertTrue(point.verify(z, Signature(r, s)))
z = 0x7c076ff316692a3d7eb3c3bb0f8b1488cf72e1afcd929e29307032997a838a3d
r = 0xeff69ef2b1bd93a66ed5219add4fb51e11a840f404876325a1e8ffe0529a2c
s = 0xc7207fee197d27c618aea621406f6bf5ef6fca38681d82b2f06fddbdce6feab6
self.assertTrue(point.verify(z, Signature(r, s)))
def test_sec(self):
coefficient = 999**3
uncompressed = '049d5ca49670cbe4c3bfa84c96a8c87df086c6ea6a24ba6b809c9de234496808d56fa15cc7f3d38cda98dee2419f415b7513dde1301f8643cd9245aea7f3f911f9'
compressed = '039d5ca49670cbe4c3bfa84c96a8c87df086c6ea6a24ba6b809c9de234496808d5'
point = coefficient * G
self.assertEqual(point.sec(compressed=False), bytes.fromhex(uncompressed))
self.assertEqual(point.sec(compressed=True), bytes.fromhex(compressed))
coefficient = 123
uncompressed = '04a598a8030da6d86c6bc7f2f5144ea549d28211ea58faa70ebf4c1e665c1fe9b5204b5d6f84822c307e4b4a7140737aec23fc63b65b35f86a10026dbd2d864e6b'
compressed = '03a598a8030da6d86c6bc7f2f5144ea549d28211ea58faa70ebf4c1e665c1fe9b5'
point = coefficient * G
self.assertEqual(point.sec(compressed=False), bytes.fromhex(uncompressed))
self.assertEqual(point.sec(compressed=True), bytes.fromhex(compressed))
coefficient = 42424242
uncompressed = '04aee2e7d843f7430097859e2bc603abcc3274ff8169c1a469fee0f20614066f8e21ec53f40efac47ac1c5211b2123527e0e9b57ede790c4da1e72c91fb7da54a3'
compressed = '03aee2e7d843f7430097859e2bc603abcc3274ff8169c1a469fee0f20614066f8e'
point = coefficient * G
self.assertEqual(point.sec(compressed=False), bytes.fromhex(uncompressed))
self.assertEqual(point.sec(compressed=True), bytes.fromhex(compressed))
def test_address(self):
secret = 888**3
mainnet_address = '148dY81A9BmdpMhvYEVznrM45kWN32vSCN'
testnet_address = 'mieaqB68xDCtbUBYFoUNcmZNwk74xcBfTP'
point = secret * G
self.assertEqual(
point.address(compressed=True, testnet=False), mainnet_address)
self.assertEqual(
point.address(compressed=True, testnet=True), testnet_address)
secret = 321
mainnet_address = '1S6g2xBJSED7Qr9CYZib5f4PYVhHZiVfj'
testnet_address = 'mfx3y63A7TfTtXKkv7Y6QzsPFY6QCBCXiP'
point = secret * G
self.assertEqual(
point.address(compressed=False, testnet=False), mainnet_address)
self.assertEqual(
point.address(compressed=False, testnet=True), testnet_address)
secret = 4242424242
mainnet_address = '1226JSptcStqn4Yq9aAmNXdwdc2ixuH9nb'
testnet_address = 'mgY3bVusRUL6ZB2Ss999CSrGVbdRwVpM8s'
point = secret * G
self.assertEqual(
point.address(compressed=False, testnet=False), mainnet_address)
self.assertEqual(
point.address(compressed=False, testnet=True), testnet_address)
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 = bytes([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
@classmethod
def parse(cls, signature_bin):
s = BytesIO(signature_bin)
compound = s.read(1)[0]
if compound != 0x30:
raise SyntaxError("Bad Signature")
length = s.read(1)[0]
if length + 2 != len(signature_bin):
raise SyntaxError("Bad Signature Length")
marker = s.read(1)[0]
if marker != 0x02:
raise SyntaxError("Bad Signature")
rlength = s.read(1)[0]
r = int.from_bytes(s.read(rlength), 'big')
marker = s.read(1)[0]
if marker != 0x02:
raise SyntaxError("Bad Signature")
slength = s.read(1)[0]
s = int.from_bytes(s.read(slength), 'big')
if len(signature_bin) != 6 + rlength + slength:
raise SyntaxError("Signature too long")
return cls(r, s)
class SignatureTest(TestCase):
def test_der(self):
testcases = (
(1, 2),
(randint(0, 2**256), randint(0, 2**255)),
(randint(0, 2**256), randint(0, 2**255)),
)
for r, s in testcases:
sig = Signature(r, s)
der = sig.der()
sig2 = Signature.parse(der)
self.assertEqual(sig2.r, r)
self.assertEqual(sig2.s, s)
class PrivateKey:
def __init__(self, secret):
self.secret = secret
self.point = secret * G
def hex(self):
return '{:x}'.format(self.secret).zfill(64)
def sign(self, z):
k = self.deterministic_k(z)
r = (k * G).x.num
k_inv = pow(k, N - 2, N)
s = (z + r * self.secret) * k_inv % N
if s > N / 2:
s = N - s
return Signature(r, s)
def deterministic_k(self, z):
k = b'\x00' * 32
v = b'\x01' * 32
if z > N:
z -= N
z_bytes = z.to_bytes(32, 'big')
secret_bytes = self.secret.to_bytes(32, 'big')
s256 = hashlib.sha256
k = hmac.new(k, v + b'\x00' + secret_bytes + z_bytes, s256).digest()
v = hmac.new(k, v, s256).digest()
k = hmac.new(k, v + b'\x01' + secret_bytes + z_bytes, s256).digest()
v = hmac.new(k, v, s256).digest()
while True:
v = hmac.new(k, v, s256).digest()
candidate = int.from_bytes(v, 'big')
if candidate >= 1 and candidate < N:
return candidate
k = hmac.new(k, v + b'\x00', s256).digest()
v = hmac.new(k, v, s256).digest()
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)
class PrivateKeyTest(TestCase):
def test_sign(self):
pk = PrivateKey(randint(0, N))
z = randint(0, 2**256)
sig = pk.sign(z)
self.assertTrue(pk.point.verify(z, sig))
def test_wif(self):
pk = PrivateKey(2**256 - 2**199)
expected = 'L5oLkpV3aqBJ4BgssVAsax1iRa77G5CVYnv9adQ6Z87te7TyUdSC'
self.assertEqual(pk.wif(compressed=True, testnet=False), expected)
pk = PrivateKey(2**256 - 2**201)
expected = '93XfLeifX7Jx7n7ELGMAf1SUR6f9kgQs8Xke8WStMwUtrDucMzn'
self.assertEqual(pk.wif(compressed=False, testnet=True), expected)
pk = PrivateKey(0x0dba685b4511dbd3d368e5c4358a1277de9486447af7b3604a69b8d9d8b7889d)
expected = '5HvLFPDVgFZRK9cd4C5jcWki5Skz6fmKqi1GQJf5ZoMofid2Dty'
self.assertEqual(pk.wif(compressed=False, testnet=False), expected)
pk = PrivateKey(0x1cca23de92fd1862fb5b76e5f4f50eb082165e5191e116c18ed1a6b24be6a53f)
expected = 'cNYfWuhDpbNM1JWc3c6JTrtrFVxU4AGhUKgw5f93NP2QaBqmxKkg'
self.assertEqual(pk.wif(compressed=True, testnet=True), expected)
书中给的helper
from unittest import TestCase, TestSuite, TextTestRunner
import hashlib
BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
def run(test):
suite = TestSuite()
suite.addTest(test)
TextTestRunner().run(suite)
def hash160(s):
'''sha256 followed by ripemd160'''
return hashlib.new('ripemd160', hashlib.sha256(s).digest()).digest()
def hash256(s):
'''two rounds of sha256'''
return hashlib.sha256(hashlib.sha256(s).digest()).digest()
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
def encode_base58_checksum(b):
return encode_base58(b + hash256(b)[:4])
def decode_base58(s):
num = 0
for c in s:
num *= 58
num += BASE58_ALPHABET.index(c)
combined = num.to_bytes(25, byteorder='big')
checksum = combined[-4:]
if hash256(combined[:-4])[:4] != checksum:
raise ValueError('bad address: {} {}'.format(checksum, hash256(combined[:-4])[:4]))
return combined[1:-4]
def little_endian_to_int(b):
'''little_endian_to_int takes byte sequence as a little-endian number.
Returns an integer'''
return int.from_bytes(b, 'little')
def int_to_little_endian(n, length):
'''endian_to_little_endian takes an integer and returns the little-endian
byte sequence of length'''
return n.to_bytes(length, 'little')
class HelperTest(TestCase):
def test_little_endian_to_int(self):
h = bytes.fromhex('99c3980000000000')
want = 10011545
self.assertEqual(little_endian_to_int(h), want)
h = bytes.fromhex('a135ef0100000000')
want = 32454049
self.assertEqual(little_endian_to_int(h), want)
def test_int_to_little_endian(self):
n = 1
want = b'\x01\x00\x00\x00'
self.assertEqual(int_to_little_endian(n, 4), want)
n = 10011545
want = b'\x99\xc3\x98\x00\x00\x00\x00\x00'
self.assertEqual(int_to_little_endian(n, 8), want)
|