cleaned up sha256 implementation
[p2pool.git] / p2pool / bitcoin / getwork.py
1 '''
2 Representation of a getwork request/reply
3 '''
4
5 from __future__ import division
6
7 from . import data as bitcoin_data
8 from . import sha256
9 from p2pool.util import pack
10
11 def _swap4(s):
12     if len(s) % 4:
13         raise ValueError()
14     return ''.join(s[x:x+4][::-1] for x in xrange(0, len(s), 4))
15
16 class BlockAttempt(object):
17     def __init__(self, version, previous_block, merkle_root, timestamp, bits, share_target):
18         self.version, self.previous_block, self.merkle_root, self.timestamp, self.bits, self.share_target = version, previous_block, merkle_root, timestamp, bits, share_target
19     
20     def __hash__(self):
21         return hash((self.version, self.previous_block, self.merkle_root, self.timestamp, self.bits, self.share_target))
22     
23     def __eq__(self, other):
24         if not isinstance(other, BlockAttempt):
25             raise ValueError('comparisons only valid with other BlockAttempts')
26         return self.__dict__ == other.__dict__
27     
28     def __ne__(self, other):
29         return not (self == other)
30     
31     def __repr__(self):
32         return 'BlockAttempt(%s)' % (', '.join('%s=%r' % (k, v) for k, v in self.__dict__.iteritems()),)
33     
34     def getwork(self, _check=False, **extra):
35         if 'data' in extra or 'hash1' in extra or 'target' in extra or 'midstate' in extra:
36             raise ValueError()
37         
38         block_data = bitcoin_data.block_header_type.pack(dict(
39             version=self.version,
40             previous_block=self.previous_block,
41             merkle_root=self.merkle_root,
42             timestamp=self.timestamp,
43             bits=self.bits,
44             nonce=0,
45         ))
46         
47         getwork = {
48             'data': _swap4(block_data).encode('hex') + '000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000',
49             'hash1': '00000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000010000',
50             'target': pack.IntType(256).pack(self.share_target).encode('hex'),
51             'midstate': _swap4(sha256.process(sha256.initial_state, block_data[:64])).encode('hex'),
52         }
53         
54         if _check:
55             self_check = self.__class__.from_getwork(getwork, _check=False)
56             if self_check != self:
57                 raise AssertionError('failed check - input invalid or implementation error')
58         
59         getwork = dict(getwork)
60         getwork.update(extra)
61         
62         return getwork
63     
64     @classmethod
65     def from_getwork(cls, getwork, _check=True):
66         attrs = decode_data(getwork['data'])
67         
68         ba = cls(
69             version=attrs['version'],
70             previous_block=attrs['previous_block'],
71             merkle_root=attrs['merkle_root'],
72             timestamp=attrs['timestamp'],
73             bits=attrs['bits'],
74             share_target=pack.IntType(256).unpack(getwork['target'].decode('hex')),
75         )
76         
77         if _check:
78             extra = dict(getwork)
79             del extra['data'], extra['hash1'], extra['target']
80             extra.pop('midstate', None)
81             getwork_check = ba.getwork(_check=False, **extra)
82             if getwork_check != getwork and dict((k, v) for k, v in getwork_check.iteritems() if k != 'midstate') != getwork:
83                 raise AssertionError('failed check - input invalid or implementation error')
84         
85         return ba
86     
87     def update(self, **kwargs):
88         d = self.__dict__.copy()
89         d.update(kwargs)
90         return self.__class__(**d)
91
92 def decode_data(data):
93     return bitcoin_data.block_header_type.unpack(_swap4(data.decode('hex'))[:80])