Bugfix: Stratum: Replies should not be sent if request id is null
[bitcoin:eloipool.git] / jsonrpc_getmemorypool.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 # NOTE: This implements an obsolete draft of BIP 22 and is provided only for backward compatibility
18 # NOTE: See jsonrpc_getblocktemplate.py for current BIP 22 support
19 # NOTE: If you want to enable this module, add TWO lines to your config file:
20 # NOTE:     import jsonrpc_getblocktemplate
21 # NOTE:     import jsonrpc_getmemorypool
22 # NOTE: It is important that getblocktemplate be included first, if full backward compatibility is desired (getmemorypool's more-compatible submitblock will override getblocktemplate's)
23
24 from binascii import b2a_hex
25 from copy import deepcopy
26 from jsonrpcserver import JSONRPCHandler
27 from time import time
28 from util import RejectedShare
29
30 _NoParams = {}
31
32 class _getmemorypool:
33         def final_init(server):
34                 ShareTargetHex = '%064x' % (server.ShareTarget,)
35                 JSONRPCHandler.getmemorypool_rv_template['target'] = ShareTargetHex
36         
37         getmemorypool_rv_template = {
38                 'longpoll': '/LP',
39                 'mutable': [
40                         'coinbase/append',
41                 ],
42                 'noncerange': '00000000ffffffff',
43                 'target': '00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
44                 'version': 2,
45         }
46         def doJSON_getmemorypool(self, params = _NoParams, sp = _NoParams):
47                 if isinstance(params, str):
48                         if sp.get('mode', 'submit') != 'submit':
49                                 raise AttributeError('getmemorypool mode "%s" not supported' % (sp['mode'],))
50                         rr = self.doJSON_submitblock(params, sp)
51                         if sp is _NoParams:
52                                 return rr is None
53                         return rr
54                 elif not sp is _NoParams:
55                         raise TypeError('getmemorypool() takes at most 2 positional arguments (%d given)' % (len(a),))
56                 elif params.get('mode', 'template') != 'template':
57                         raise AttributeError('getmemorypool mode "%s" not supported' % (sp['mode'],))
58                 
59                 if 'longpollid' in params:
60                         self.processLP(params['longpollid'])
61                 
62                 rv = dict(self.getmemorypool_rv_template)
63                 (MC, wld, target) = self.server.getBlockTemplate(self.Username)
64                 (dummy, merkleTree, cb, prevBlock, bits) = MC[:5]
65                 rv['previousblockhash'] = b2a_hex(prevBlock[::-1]).decode('ascii')
66                 rv['longpollid'] = str(self.server.LPId)
67                 tl = []
68                 for txn in merkleTree.data[1:]:
69                         tl.append(b2a_hex(txn.data).decode('ascii'))
70                 rv['transactions'] = tl
71                 now = int(time())
72                 rv['time'] = now
73                 # FIXME: ensure mintime is always >= real mintime, both here and in share acceptance
74                 rv['mintime'] = now - 180
75                 rv['maxtime'] = now + 120
76                 rv['bits'] = b2a_hex(bits[::-1]).decode('ascii')
77                 rv['target'] = '%064x' % (target,)
78                 t = deepcopy(merkleTree.data[0])
79                 t.setCoinbase(cb)
80                 t.assemble()
81                 rv['coinbasetxn'] = b2a_hex(t.data).decode('ascii')
82                 return rv
83         
84         def doJSON_submitblock(self, data, params = _NoParams):
85                 data = bytes.fromhex(data)
86                 share = {
87                         'data': data[:80],
88                         'blkdata': data[80:],
89                         'username': self.Username,
90                         'remoteHost': self.remoteHost,
91                 }
92                 try:
93                         self.server.receiveShare(share)
94                 except RejectedShare as rej:
95                         if 'SBB' in self.quirks:
96                                 return False
97                         return str(rej)
98                 if 'SBB' in self.quirks:
99                         return True
100                 return None
101
102 JSONRPCHandler._register(_getmemorypool)