speed optimizations for parsing
[p2pool.git] / p2pool / bitcoin / data.py
1 from __future__ import division
2
3 import struct
4 import cStringIO as StringIO
5 import hashlib
6 import warnings
7
8 from . import base58
9 from p2pool.util import bases, expiring_dict, math
10
11 class EarlyEnd(Exception):
12     pass
13
14 class LateEnd(Exception):
15     pass
16
17 def read((data, pos), length):
18     data2 = data[pos:pos + length]
19     if len(data2) != length:
20         raise EarlyEnd()
21     return data2, (data, pos + length)
22
23 class Type(object):
24     # the same data can have only one unpacked representation, but multiple packed binary representations
25     
26     #def __hash__(self):
27     #    return hash(tuple(self.__dict__.items()))
28     
29     #def __eq__(self, other):
30     #    if not isinstance(other, Type):
31     #        raise NotImplementedError()
32     #    return self.__dict__ == other.__dict__
33     
34     def _unpack(self, data):
35         f = StringIO.StringIO(data)
36         
37         obj, (data2, pos) = self.read((data, 0))
38         
39         assert data2 is data
40         if pos != len(data):
41             raise LateEnd('underread ' + repr((self, data)))
42         
43         return obj
44     
45     def unpack(self, data):
46         obj = self._unpack(data)
47         
48         if __debug__:
49             data2 = self._pack(obj)
50             if data2 != data:
51                 assert self._unpack(data2) == obj
52         
53         return obj
54     
55     def _pack(self, obj):
56         f = StringIO.StringIO()
57         
58         self.write(f, obj)
59         
60         data = f.getvalue()
61         
62         return data
63     
64     def pack(self, obj):
65         data = self._pack(obj)
66         assert self._unpack(data) == obj
67                 
68         return data
69     
70     
71     def pack_base58(self, obj):
72         return base58.base58_encode(self.pack(obj))
73     
74     def unpack_base58(self, base58_data):
75         return self.unpack(base58.base58_decode(base58_data))
76         
77     
78     def hash160(self, obj):
79         return ShortHashType().unpack(hashlib.new('ripemd160', hashlib.sha256(self.pack(obj)).digest()).digest())
80     
81     def hash256(self, obj):
82         return HashType().unpack(hashlib.sha256(hashlib.sha256(self.pack(obj)).digest()).digest())
83
84 class VarIntType(Type):
85     # redundancy doesn't matter here because bitcoin and p2pool both reencode before hashing
86     def read(self, file):
87         data, file = read(file, 1)
88         first = ord(data)
89         if first < 0xfd:
90             return first, file
91         elif first == 0xfd:
92             desc, length = '<H', 2
93         elif first == 0xfe:
94             desc, length = '<I', 4
95         elif first == 0xff:
96             desc, length = '<Q', 8
97         else:
98             raise AssertionError()
99         data, file = read(file, length)
100         return struct.unpack(desc, data)[0], file
101     
102     def write(self, file, item):
103         if item < 0xfd:
104             file.write(struct.pack('<B', item))
105         elif item <= 0xffff:
106             file.write(struct.pack('<BH', 0xfd, item))
107         elif item <= 0xffffffff:
108             file.write(struct.pack('<BI', 0xfe, item))
109         elif item <= 0xffffffffffffffff:
110             file.write(struct.pack('<BQ', 0xff, item))
111         else:
112             raise ValueError('int too large for varint')
113
114 class VarStrType(Type):
115     _inner_size = VarIntType()
116     
117     def read(self, file):
118         length, file = self._inner_size.read(file)
119         return read(file, length)
120     
121     def write(self, file, item):
122         self._inner_size.write(file, len(item))
123         file.write(item)
124
125 class FixedStrType(Type):
126     def __init__(self, length):
127         self.length = length
128     
129     def read(self, file):
130         return read(file, self.length)
131     
132     def write(self, file, item):
133         if len(item) != self.length:
134             raise ValueError('incorrect length!')
135         file.write(item)
136
137 class EnumType(Type):
138     def __init__(self, inner, values):
139         self.inner = inner
140         self.values = values
141         
142         self.keys = {}
143         for k, v in values.iteritems():
144             if v in self.keys:
145                 raise ValueError('duplicate value in values')
146             self.keys[v] = k
147     
148     def read(self, file):
149         data, file = self.inner.read(file)
150         return self.keys[data], file
151     
152     def write(self, file, item):
153         self.inner.write(file, self.values[item])
154
155 class HashType(Type):
156     def read(self, file):
157         data, file = read(file, 256//8)
158         return int(data[::-1].encode('hex'), 16), file
159     
160     def write(self, file, item):
161         if not 0 <= item < 2**256:
162             raise ValueError("invalid hash value")
163         if item != 0 and item < 2**160:
164             warnings.warn("very low hash value - maybe you meant to use ShortHashType? %x" % (item,))
165         file.write(('%064x' % (item,)).decode('hex')[::-1])
166
167 class ShortHashType(Type):
168     def read(self, file):
169         data, file = read(file, 160//8)
170         return int(data[::-1].encode('hex'), 16), file
171     
172     def write(self, file, item):
173         if item >= 2**160:
174             raise ValueError("invalid hash value")
175         file.write(('%040x' % (item,)).decode('hex')[::-1])
176
177 class ListType(Type):
178     _inner_size = VarIntType()
179     
180     def __init__(self, type):
181         self.type = type
182     
183     def read(self, file):
184         length, file = self._inner_size.read(file)
185         res = []
186         for i in xrange(length):
187             item, file = self.type.read(file)
188             res.append(item)
189         return res, file
190     
191     def write(self, file, item):
192         self._inner_size.write(file, len(item))
193         for subitem in item:
194             self.type.write(file, subitem)
195
196 class FastLittleEndianUnsignedInteger(Type):
197     def read(self, file):
198         data, file = read(file, 4)
199         data = map(ord, data)
200         return data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24), file
201     
202     def write(self, file, item):
203         StructType("<I").write(file, item)
204
205 class StructType(Type):
206     def __init__(self, desc):
207         self.desc = desc
208         self.length = struct.calcsize(self.desc)
209     
210     def read(self, file):
211         data, file = read(file, self.length)
212         res, = struct.unpack(self.desc, data)
213         return res, file
214     
215     def write(self, file, item):
216         data = struct.pack(self.desc, item)
217         if struct.unpack(self.desc, data)[0] != item:
218             # special test because struct doesn't error on some overflows
219             raise ValueError("item didn't survive pack cycle (%r)" % (item,))
220         file.write(data)
221
222 class IPV6AddressType(Type):
223     def read(self, file):
224         data, file = read(file, 16)
225         if data[:12] != '00000000000000000000ffff'.decode('hex'):
226             raise ValueError("ipv6 addresses not supported yet")
227         return '.'.join(str(ord(x)) for x in data[12:]), file
228     
229     def write(self, file, item):
230         bits = map(int, item.split('.'))
231         if len(bits) != 4:
232             raise ValueError("invalid address: %r" % (bits,))
233         data = '00000000000000000000ffff'.decode('hex') + ''.join(chr(x) for x in bits)
234         assert len(data) == 16, len(data)
235         file.write(data)
236
237 class ComposedType(Type):
238     def __init__(self, fields):
239         self.fields = fields
240     
241     def read(self, file):
242         item = {}
243         for key, type_ in self.fields:
244             item[key], file = type_.read(file)
245         return item, file
246     
247     def write(self, file, item):
248         for key, type_ in self.fields:
249             type_.write(file, item[key])
250
251 class ChecksummedType(Type):
252     def __init__(self, inner):
253         self.inner = inner
254     
255     def read(self, file):
256         obj, file = self.inner.read(file)
257         data = self.inner.pack(obj)
258         
259         if file.read(4) != hashlib.sha256(hashlib.sha256(data).digest()).digest()[:4]:
260             raise ValueError("invalid checksum")
261         
262         return obj, file
263     
264     def write(self, file, item):
265         data = self.inner.pack(item)
266         file.write(data)
267         file.write(hashlib.sha256(hashlib.sha256(data).digest()).digest()[:4])
268
269 class FloatingIntegerType(Type):
270     # redundancy doesn't matter here because bitcoin checks binary bits against its own computed bits
271     # so it will always be encoded 'normally' in blocks (they way bitcoin does it)
272     _inner = StructType("<I")
273     _inner = FastLittleEndianUnsignedInteger()
274     
275     def read(self, file):
276         bits, file = self._inner.read(file)
277         target = self._bits_to_target(bits)
278         if __debug__:
279             if self._target_to_bits(target) != bits:
280                 raise ValueError("bits in non-canonical form")
281         return target, file
282     
283     def write(self, file, item):
284         self._inner.write(file, self._target_to_bits(item))
285     
286     def truncate_to(self, x):
287         return self._bits_to_target(self._target_to_bits(x, _check=False))
288         
289     def _bits_to_target(self, bits2):
290         target = math.shift_left(bits2 & 0x00ffffff, 8 * ((bits2 >> 24) - 3))
291         assert target == self._bits_to_target1(struct.pack("<I", bits2))
292         assert self._target_to_bits(target, _check=False) == bits2
293         return target
294     
295     def _bits_to_target1(self, bits):
296         bits = bits[::-1]
297         length = ord(bits[0])
298         return bases.string_to_natural((bits[1:] + "\0"*length)[:length])
299
300     def _target_to_bits(self, target, _check=True):
301         n = bases.natural_to_string(target)
302         if n and ord(n[0]) >= 128:
303             n = "\x00" + n
304         bits2 = (chr(len(n)) + (n + 3*chr(0))[:3])[::-1]
305         bits = struct.unpack("<I", bits2)[0]
306         if _check:
307             if self._bits_to_target(bits) != target:
308                 raise ValueError(repr((target, self._bits_to_target(bits, _check=False))))
309         return bits
310
311 class PossiblyNone(Type):
312     def __init__(self, none_value, inner):
313         self.none_value = none_value
314         self.inner = inner
315     
316     def read(self, file):
317         value, file = self.inner.read(file)
318         return None if value == self.none_value else value, file
319     
320     def write(self, file, item):
321         if item == self.none_value:
322             raise ValueError("none_value used")
323         self.inner.write(file, self.none_value if item is None else item)
324
325 address_type = ComposedType([
326     ('services', StructType('<Q')),
327     ('address', IPV6AddressType()),
328     ('port', StructType('>H')),
329 ])
330
331 tx_type = ComposedType([
332     ('version', StructType('<I')),
333     ('tx_ins', ListType(ComposedType([
334         ('previous_output', PossiblyNone(dict(hash=0, index=2**32 - 1), ComposedType([
335             ('hash', HashType()),
336             ('index', StructType('<I')),
337         ]))),
338         ('script', VarStrType()),
339         ('sequence', PossiblyNone(2**32 - 1, StructType('<I'))),
340     ]))),
341     ('tx_outs', ListType(ComposedType([
342         ('value', StructType('<Q')),
343         ('script', VarStrType()),
344     ]))),
345     ('lock_time', StructType('<I')),
346 ])
347
348 block_header_type = ComposedType([
349     ('version', StructType('<I')),
350     ('previous_block', PossiblyNone(0, HashType())),
351     ('merkle_root', HashType()),
352     ('timestamp', StructType('<I')),
353     ('target', FloatingIntegerType()),
354     ('nonce', StructType('<I')),
355 ])
356
357 block_type = ComposedType([
358     ('header', block_header_type),
359     ('txs', ListType(tx_type)),
360 ])
361
362
363 merkle_record_type = ComposedType([
364     ('left', HashType()),
365     ('right', HashType()),
366 ])
367
368 def merkle_hash(tx_list):
369     if not tx_list:
370         return 0
371     hash_list = map(tx_type.hash256, tx_list)
372     while len(hash_list) > 1:
373         hash_list = [merkle_record_type.hash256(dict(left=left, right=left if right is None else right))
374             for left, right in zip(hash_list[::2], hash_list[1::2] + [None])]
375     return hash_list[0]
376
377 def target_to_average_attempts(target):
378     return 2**256//(target + 1)
379
380 # human addresses
381
382 human_address_type = ChecksummedType(ComposedType([
383     ('version', StructType("<B")),
384     ('pubkey_hash', ShortHashType()),
385 ]))
386
387 pubkey_type = FixedStrType(65)
388
389 def pubkey_hash_to_address(pubkey_hash, net):
390     return human_address_type.pack_base58(dict(version=net.BITCOIN_ADDRESS_VERSION, pubkey_hash=pubkey_hash))
391
392 def pubkey_to_address(pubkey, net):
393     return pubkey_hash_to_address(pubkey_type.hash160(pubkey), net)
394
395 def address_to_pubkey_hash(address, net):
396     x = human_address_type.unpack_base58(address)
397     if x['version'] != net.BITCOIN_ADDRESS_VERSION:
398         raise ValueError('address not for this net!')
399     return x['pubkey_hash']
400
401 # network definitions
402
403 class Mainnet(object):
404     BITCOIN_P2P_PREFIX = 'f9beb4d9'.decode('hex')
405     BITCOIN_P2P_PORT = 8333
406     BITCOIN_ADDRESS_VERSION = 0
407
408 class Testnet(object):
409     BITCOIN_P2P_PREFIX = 'fabfb5da'.decode('hex')
410     BITCOIN_P2P_PORT = 18333
411     BITCOIN_ADDRESS_VERSION = 111