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