supported and tested getwork responses without midstate
[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
10 def _swap4(s):
11     if len(s) % 4:
12         raise ValueError()
13     return ''.join(s[x:x+4][::-1] for x in xrange(0, len(s), 4))
14
15 class BlockAttempt(object):
16     def __init__(self, version, previous_block, merkle_root, timestamp, block_target, share_target):
17         self.version, self.previous_block, self.merkle_root, self.timestamp, self.block_target, self.share_target = version, previous_block, merkle_root, timestamp, block_target, share_target
18     
19     def __hash__(self):
20         return hash((self.version, self.previous_block, self.merkle_root, self.timestamp, self.block_target, self.share_target))
21     
22     def __eq__(self, other):
23         if not isinstance(other, BlockAttempt):
24             raise ValueError('comparisons only valid with other BlockAttempts')
25         return self.__dict__ == other.__dict__
26     
27     def __ne__(self, other):
28         return not (self == other)
29     
30     def __repr__(self):
31         return 'BlockAttempt(%s)' % (', '.join('%s=%r' % (k, v) for k, v in self.__dict__.iteritems()),)
32     
33     def getwork(self, _check=False, **extra):
34         if 'data' in extra or 'hash1' in extra or 'target' in extra or 'midstate' in extra:
35             raise ValueError()
36         
37         block_data = bitcoin_data.block_header_type.pack(dict(
38             version=self.version,
39             previous_block=self.previous_block,
40             merkle_root=self.merkle_root,
41             timestamp=self.timestamp,
42             target=self.block_target,
43             nonce=0,
44         ))
45         
46         getwork = {
47             'data': _swap4(block_data).encode('hex') + '000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000',
48             'hash1': '00000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000010000',
49             'target': bitcoin_data.HashType().pack(self.share_target).encode('hex'),
50             'midstate': _swap4(sha256.process(block_data[:64])).encode('hex'),
51         }
52         
53         if _check:
54             self_check = self.__class__.from_getwork(getwork, _check=False)
55             if self_check != self:
56                 raise AssertionError('failed check - input invalid or implementation error')
57         
58         getwork = dict(getwork)
59         getwork.update(extra)
60         
61         return getwork
62     
63     @classmethod
64     def from_getwork(cls, getwork, _check=True):
65         attrs = decode_data(getwork['data'])
66         
67         ba = cls(
68             version=attrs['version'],
69             previous_block=attrs['previous_block'],
70             merkle_root=attrs['merkle_root'],
71             timestamp=attrs['timestamp'],
72             block_target=attrs['target'],
73             share_target=bitcoin_data.HashType().unpack(getwork['target'].decode('hex')),
74         )
75         
76         if _check:
77             extra = dict(getwork)
78             del extra['data'], extra['hash1'], extra['target']
79             extra.pop('midstate', None)
80             getwork_check = ba.getwork(_check=False, **extra)
81             if getwork_check != getwork and dict((k, v) for k, v in getwork_check.iteritems() if k != 'midstate') != getwork:
82                 raise AssertionError('failed check - input invalid or implementation error')
83         
84         return ba
85     
86     def update(self, **kwargs):
87         d = self.__dict__.copy()
88         d.update(kwargs)
89         return self.__class__(**d)
90
91 def decode_data(data):
92     return bitcoin_data.block_header_type.unpack(_swap4(data.decode('hex'))[:80])
93
94 if __name__ == '__main__':
95     BlockAttempt.from_getwork({
96         'target': '0000000000000000000000000000000000000000000000f2b944000000000000',
97         'midstate': '5982f893102dec03e374b472647c4f19b1b6d21ae4b2ac624f3d2f41b9719404',
98         'hash1': '00000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000010000',
99         'data': '0000000163930d52a5ffca79b29b95a659a302cd4e1654194780499000002274000000002e133d9e51f45bc0886d05252038e421e82bff18b67dc14b90d9c3c2f422cd5c4dd4598e1a44b9f200000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000'
100 }, _check=100)
101     BlockAttempt.from_getwork({
102         'midstate' : 'f4a9b048c0cb9791bc94b13ee0eec21e713963d524fd140b58bb754dd7b0955f',
103         'data' : '000000019a1d7342fb62090bda686b22d90f9f73d0f5c418b9c980cd0000011a00000000680b07c8a2f97ecd831f951806857e09f98a3b81cdef1fa71982934fef8dc3444e18585d1a0abbcf00000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000',
104         'hash1' : '00000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000010000',
105         'target' : '0000000000000000000000000000000000000000000000cfbb0a000000000000',
106         'extrathing': 'hi!',
107     })
108     BlockAttempt.from_getwork({
109         'data' : '000000019a1d7342fb62090bda686b22d90f9f73d0f5c418b9c980cd0000011a00000000680b07c8a2f97ecd831f951806857e09f98a3b81cdef1fa71982934fef8dc3444e18585d1a0abbcf00000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000',
110         'hash1' : '00000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000010000',
111         'target' : '0000000000000000000000000000000000000000000000cfbb0a000000000000',
112         'extrathing': 'hi!',
113     })
114     ba = BlockAttempt(
115         1,
116         0x148135e10208db85abb62754341a392eab1f186aab077a831cf7,
117         0x534ea08be1ab529f484369344b6d5423ef5a0767db9b3ebb4e182bbb67962520,
118         1305759879,
119         bitcoin_data.FloatingInteger.from_target_upper_bound(0x44b9f20000000000000000000000000000000000000000000000),
120         0x44b9f20000000000000000000000000000000000000000000000,
121     )
122     ba.getwork(100)
123     ba = BlockAttempt(
124         1,
125         0x148135e10208db85abb62754341a392eab1f186aab077a831cf7,
126         0x534ea08be1ab529f484369344b6d5423ef5a0767db9b3ebb4e182bbb67962520,
127         1305759879,
128         bitcoin_data.FloatingInteger.from_target_upper_bound(0x44b9f20000000000000000000000000000000000000000000000),
129         432*2**230,
130     )
131     ba.getwork(100)
132     ba = BlockAttempt(
133         1,
134         0x148135e10208db85abb62754341a392eab1f186aab077a831cf7,
135         0x534ea08be1ab529f484369344b6d5423ef5a0767db9b3ebb4e182bbb67962520,
136         1305759879,
137         bitcoin_data.FloatingInteger.from_target_upper_bound(0x44b9f20000000000000000000000000000000000000000000000),
138         7*2**240,
139     )
140     ba.getwork(100)
141     ba.getwork()
142     ba.getwork(_check=100)
143     ba.update(previous_block=ba.previous_block - 10).getwork(_check=100, ident="hi")