-
Notifications
You must be signed in to change notification settings - Fork 45
/
utils.py
317 lines (271 loc) · 8.71 KB
/
utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
"""This submodule provides functions for accomplishing common tasks encountered
in creating and parsing Bitcoin objects, like turning difficulties into targets
or deserializing and serializing various kinds of packed byte formats."""
import base58
import codecs
import hashlib
import random
import struct
import os
MAX_TARGET = 0x00000000FFFF0000000000000000000000000000000000000000000000000000
def rand_bytes(n, secure=True):
""" Returns n random bytes.
Args:
n (int): number of bytes to return.
secure (bool): If True, uses os.urandom to generate
cryptographically secure random bytes. Otherwise, uses
random.randint() which generates pseudo-random numbers.
Returns:
b (bytes): n random bytes.
"""
if secure:
return os.urandom(n)
else:
return bytes([random.randint(0, 255) for i in range(n)])
def bytes_to_str(b):
""" Converts bytes into a hex-encoded string.
Args:
b (bytes): bytes to encode
Returns:
h (str): hex-encoded string corresponding to b.
"""
return codecs.encode(b, 'hex_codec').decode('ascii')
def hex_str_to_bytes(h):
""" Converts a hex-encoded string to bytes.
Args:
h (str): hex-encoded string to convert.
Returns:
b (bytes): bytes corresponding to h.
"""
return bytes.fromhex(h)
# Is there a better way of doing this?
def render_int(n):
""" Renders an int in the shortest possible form.
When packing the height into the coinbase script, the integer
representing the height must be encoded in the shortest possible
manner. See: https://bitcoin.org/en/developer-reference#coinbase.
Args:
n (int): number to be encoded.
Returns:
b (bytes): bytes representing n in the shortest possible form.
"""
# little-endian byte stream
if n < 0:
neg = True
n = -n
else:
neg = False
r = []
while n:
r.append(n & 0xff)
n >>= 8
if neg:
if r[-1] & 0x80:
r.append(0x80)
else:
r[-1] |= 0x80
elif r and (r[-1] & 0x80):
r.append(0)
return bytes(r)
def pack_compact_int(i):
""" See
https://bitcoin.org/en/developer-reference#compactsize-unsigned-integers
Args:
i (int): Integer to be serialized.
Returns:
b (bytes): Serialized bytes corresponding to i.
"""
if i < 0xfd:
return struct.pack('<B', i)
elif i <= 0xffff:
return struct.pack('<BH', 0xfd, i)
elif i <= 0xffffffff:
return struct.pack('<BI', 0xfe, i)
else:
return struct.pack('<BQ', 0xff, i)
def unpack_compact_int(bytestr):
""" See
https://bitcoin.org/en/developer-reference#compactsize-unsigned-integers
Args:
bytestr (bytes): bytes containing an unsigned integer to be
deserialized.
Returns:
n (int): deserialized integer.
"""
b0 = bytestr[0]
if b0 < 0xfd:
return (b0, bytestr[1:])
elif b0 == 0xfd:
return (struct.unpack('<H', bytestr[1:3])[0], bytestr[3:])
elif b0 == 0xfe:
return (struct.unpack('<I', bytestr[1:5])[0], bytestr[5:])
elif b0 == 0xff:
return (struct.unpack('<Q', bytestr[1:9])[0], bytestr[9:])
else:
return None
def pack_u32(i):
""" Serializes a 32-bit integer into little-endian form.
Args:
i (int): integer to be serialized.
Returns:
b (bytes): 4 bytes containing the little-endian serialization of i.
"""
return struct.pack('<I', i)
def unpack_u32(b):
""" Deserializes a 32-bit integer from bytes.
Args:
b (bytes): At least 4 bytes containing the serialized integer.
Returns:
(i, b) (tuple): A tuple containing the deserialized integer and the
remainder of the byte stream.
"""
u32 = struct.unpack('<I', b[0:4])
return (u32[0], b[4:])
def pack_u64(i):
""" Serializes a 64-bit integer into little-endian form.
Args:
i (int): integer to be serialized.
Returns:
b (bytes): 8 bytes containing the little-endian serialization of i.
"""
return struct.pack('<Q', i)
def unpack_u64(b):
""" Deserializes a 64-bit integer from bytes.
Args:
b (bytes): At least 8 bytes containing the serialized integer.
Returns:
(i, b) (tuple): A tuple containing the deserialized integer and the
remainder of the byte stream.
"""
u64 = struct.unpack('<Q', b[0:8])
return (u64[0], b[8:])
def pack_var_str(s):
""" Serializes a variable length byte stream.
Args:
s (bytes): byte stream to serialize
Return:
b (bytes): Serialized bytes, prepended with the length of the
byte stream.
"""
return pack_compact_int(len(s)) + s
def unpack_var_str(b):
""" Deserializes a variable length byte stream.
Args:
b (bytes): variable length byte stream to deserialize
Returns:
(s, b) (tuple): A tuple containing the variable length byte stream
and the remainder of the input byte stream.
"""
strlen, b0 = unpack_compact_int(b)
return (b0[:strlen], b0[strlen:])
def bits_to_target(bits):
""" Decodes the full target from a compact representation.
See: https://bitcoin.org/en/developer-reference#target-nbits
Args:
bits (int): Compact target (32 bits)
Returns:
target (Bignum): Full 256-bit target
"""
shift = bits >> 24
target = (bits & 0xffffff) * (1 << (8 * (shift - 3)))
return target
def bits_to_difficulty(bits):
""" Determines the difficulty corresponding to bits.
See: https://en.bitcoin.it/wiki/Difficulty
Args:
bits (int): Compact target (32 bits)
Returns:
diff (float): Measure of how hard it is to find a solution
below the target represented by bits.
"""
target = bits_to_target(bits)
return MAX_TARGET / target
def difficulty_to_target(difficulty):
""" Converts a difficulty to a long-form target.
Args:
difficulty (float): The difficulty to return the appropriate target for
Returns:
target (int): The corresponding target
"""
return int(MAX_TARGET / difficulty)
def target_to_bits(target):
""" Creates a compact target representation for a given target.
Args:
target (Bignum): The long-form target to make compact.
Returns:
ct (int): Compact target
"""
# Get bit length
nbits = target.bit_length()
# Round up to next 8-bits
nbits = ((nbits + 7) & ~0x7)
exponent = (int(nbits/8) & 0xff)
coefficient = (target >> (nbits - 24)) & 0xffffff
if coefficient & 0x800000:
coefficient >>= 8
exponent += 1
return (exponent << 24) | coefficient
def difficulty_to_bits(difficulty):
""" Converts a difficulty to a compact target.
Args:
difficulty (float): The difficulty to create a target for
Returns:
ct (int): Compact target
"""
return target_to_bits(difficulty_to_target(difficulty))
def address_to_key_hash(s):
""" Given a Bitcoin address decodes the version and
RIPEMD-160 hash of the public key.
Args:
s (bytes): The Bitcoin address to decode
Returns:
(version, h160) (tuple): A tuple containing the version and
RIPEMD-160 hash of the public key.
"""
n = base58.b58decode_check(s)
version = n[0]
h160 = n[1:]
return version, h160
def key_hash_to_address(hash160, version=0x0):
"""Convert RIPEMD-160 hash to bitcoin address.
Args:
hash160 (bytes/str): bitcoin hash160 to decode
version (int): The version prefix
Returns:
(bitcoin address): base58 encoded bitcoin address
"""
if isinstance(hash160, str):
# if 0x in string, strip it
if "0x" in hash160:
h160 = hex_str_to_bytes(hash160[2:])
else:
h160 = hex_str_to_bytes(hash160)
elif isinstance(hash160, bytes):
h160 = hash160
address = base58.b58encode_check(bytes([version]) + h160)
return address
def hash160(b):
""" Computes the HASH160 of b.
Args:
b (bytes): A byte string to compute the HASH160 of.
Returns:
The RIPEMD-160 digest of the SHA256 hash of b.
"""
r = hashlib.new('ripemd160')
r.update(hashlib.sha256(b).digest())
return r.digest()
def compute_reward(height):
""" Computes the block reward for a block at the supplied height.
See: https://en.bitcoin.it/wiki/Controlled_supply for the reward
schedule.
Args:
height (int): Block height
Returns:
reward (int): Number of satoshis rewarded for solving a block at the
given height.
"""
base_subsidy = 50 * 100000000
era = height // 210000
if era == 0:
return base_subsidy
return int(base_subsidy / 2 ** era)