Add userAgent and submitProtocol keys to share objects for logging/statistical purposes
[bitcoin:eloipool.git] / jsonrpc_getwork.py
1 # Eloipool - Python Bitcoin pool server
2 # Copyright (C) 2011-2012  Luke Dashjr <luke-jr+eloipool@utopios.org>
3 #
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License as
6 # published by the Free Software Foundation, either version 3 of the
7 # License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU Affero General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 from binascii import b2a_hex
18 from jsonrpcserver import JSONRPCHandler
19 import logging
20 try:
21         import midstate
22         assert midstate.SHA256(b'This is just a test, ignore it. I am making it over 64-bytes long.')[:8] == (0x755f1a94, 0x999b270c, 0xf358c014, 0xfd39caeb, 0x0dcc9ebc, 0x4694cd1a, 0x8e95678e, 0x75fac450)
23 except:
24         logging.getLogger('jsonrpc_getwork').warning('Error importing \'midstate\' module; work will not provide midstates')
25         midstate = None
26 from struct import pack
27 from util import RejectedShare, swap32
28
29 _CheckForDupesHACK = {}
30 _RealDupes = {}
31 class _getwork:
32         def final_init(server):
33                 ShareTargetHex = '%064x' % (server.ShareTarget,)
34                 ShareTargetHexLE = b2a_hex(bytes.fromhex(ShareTargetHex)[::-1]).decode('ascii')
35                 JSONRPCHandler.getwork_rv_template['target'] = ShareTargetHexLE
36         
37         getwork_rv_template = {
38                 'data': '000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000',
39                 'target': 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000',
40                 'hash1': '00000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000010000',
41                 'submitold': True,
42         }
43         def doJSON_getwork(self, data=None):
44                 if not data is None:
45                         return self.doJSON_submitwork(data)
46                 rv = dict(self.getwork_rv_template)
47                 (hdr, x, target) = self.server.getBlockHeader(self.Username)
48                 
49                 # FIXME: this assumption breaks with internal rollntime
50                 # NOTE: noncerange needs to set nonce to start value at least
51                 global _CheckForDupesHACK
52                 uhdr = hdr[:68] + hdr[72:]
53                 if uhdr in _CheckForDupesHACK:
54                         _RealDupes[uhdr] = (_CheckForDupesHACK[uhdr], (hdr, x))
55                         raise self.server.RaiseRedFlags(RuntimeError('issuing duplicate work'))
56                 _CheckForDupesHACK[uhdr] = (hdr, x)
57                 
58                 data = b2a_hex(swap32(hdr)).decode('utf8') + rv['data']
59                 # TODO: endian shuffle etc
60                 rv['data'] = data
61                 if midstate and 'midstate' not in self.extensions and 'midstate' not in self.quirks:
62                         h = midstate.SHA256(hdr)[:8]
63                         rv['midstate'] = b2a_hex(pack('<LLLLLLLL', *h)).decode('ascii')
64                 
65                 ShareTargetHex = '%064x' % (target,)
66                 ShareTargetHexLE = b2a_hex(bytes.fromhex(ShareTargetHex)[::-1]).decode('ascii')
67                 rv['target'] = ShareTargetHexLE
68                 
69                 self._JSONHeaders['X-Roll-NTime'] = 'expire=120'
70                 
71                 return rv
72         
73         def doJSON_submitwork(self, datax):
74                 data = swap32(bytes.fromhex(datax))[:80]
75                 share = {
76                         'data': data,
77                         '_origdata' : datax,
78                         'username': self.Username,
79                         'remoteHost': self.remoteHost,
80                         'userAgent': self.reqinfo.get('UA').decode('latin-1'),  # technically ASCII, but latin-1 ignores errors
81                         'submitProtocol': 'getwork',
82                 }
83                 try:
84                         self.server.receiveShare(share)
85                 except RejectedShare as rej:
86                         self._JSONHeaders['X-Reject-Reason'] = str(rej)
87                         return False
88                 return True
89
90 JSONRPCHandler._register(_getwork)