1 from __future__ import division
6 from twisted.internet import defer, reactor
7 from twisted.python import log
10 from p2pool.util import jsonrpc, deferred_resource, variable
12 # TODO: branch on User-Agent to remove overhead of workarounds
14 def get_memory(user_agent):
15 user_agent2 = '' if user_agent is None else user_agent.lower()
16 if 'java' in user_agent2: return 0 # hopefully diablominer...
17 if 'cpuminer' in user_agent2: return 0
18 if 'cgminer' in user_agent2: return 1
19 if 'poclbm' in user_agent2: return 1
20 if 'phoenix' in user_agent2: return 2
21 print 'Unknown miner User-Agent:', repr(user_agent)
25 return request.getClientIP(), request.getHeader('Authorization'), request.getHeader('User-Agent')
27 last_cache_invalidation = {} # XXX remove global
30 if gw1['hash1'] != gw2['hash1']:
32 if gw1['target'] != gw2['target']:
36 midstate=gw2['midstate'],
41 class LongPollingWorkerInterface(deferred_resource.DeferredResource):
42 def __init__(self, work, compute):
44 self.compute = compute
46 @defer.inlineCallbacks
47 def render_GET(self, request):
50 id = random.randrange(10000)
54 request_id = get_id(request)
55 memory = get_memory(request.getHeader('User-Agent'))
57 if request_id not in last_cache_invalidation:
58 last_cache_invalidation[request_id] = variable.Variable((None, None))
61 work = self.work.value
62 thought_work = last_cache_invalidation[request_id].value
63 if work != thought_work[-1]:
66 print 'POLL %i WAITING' % (id,)
67 yield defer.DeferredList([self.work.changed.get_deferred(), last_cache_invalidation[request_id].changed.get_deferred()], fireOnOneCallback=True)
69 if thought_work[-1] is not None and work != thought_work[-1] and any(x is None or work['previous_block'] == x['previous_block'] for x in thought_work[-memory or len(thought_work):]):
70 # clients won't believe the update
72 newwork['previous_block'] = random.randrange(2**256)
74 print 'longpoll faked', id
75 res = self.compute(work, request.getHeader('X-All-Targets') is not None)
76 newres = self.compute(newwork, request.getHeader('X-All-Targets') is not None)
79 newres = res = self.compute(work, request.getHeader('X-All-Targets') is not None)
81 reactor.callLater(.01, lambda: last_cache_invalidation[request_id].set((thought_work[-1], newwork)))
83 request.setHeader('X-Long-Polling', '/long-polling')
84 request.setHeader('Content-Type', 'application/json')
85 request.write(json.dumps({
88 'result': merge(newres.getwork(), res.getwork()),
94 log.err(None, 'Squelched long polling error:')
95 raise jsonrpc.Error(-32099, u'Unknown error')
97 except jsonrpc.Error, e:
98 request.write(json.dumps({
102 'error': e._to_obj(),
107 print 'END POLL %i %x' % (id, work['best_share_hash'] % 2**32 if work['best_share_hash'] is not None else 0)
108 render_POST = render_GET
110 class RateInterface(deferred_resource.DeferredResource):
111 def __init__(self, get_rate):
112 self.get_rate = get_rate
114 def render_GET(self, request):
115 request.setHeader('Content-Type', 'application/json')
116 request.write(json.dumps(self.get_rate()))
118 class WorkerInterface(jsonrpc.Server):
119 def __init__(self, work, compute, response_callback, get_rate):
120 jsonrpc.Server.__init__(self)
123 self.compute = compute
124 self.response_callback = response_callback
125 self.get_rate = get_rate
127 self.putChild('long-polling',
128 LongPollingWorkerInterface(self.work, self.compute))
129 self.putChild('rate',
130 RateInterface(get_rate))
131 self.putChild('', self)
133 def rpc_getwork(self, request, data=None):
134 request.setHeader('X-Long-Polling', '/long-polling')
137 return self.response_callback(data)
139 request_id = get_id(request)
140 memory = get_memory(request.getHeader('User-Agent'))
142 if request_id not in last_cache_invalidation:
143 last_cache_invalidation[request_id] = variable.Variable((None, None))
145 work = self.work.value
146 thought_work = last_cache_invalidation[request_id].value
148 if thought_work[-1] is not None and work != thought_work[-1] and any(x is None or work['previous_block'] == x['previous_block'] for x in thought_work[-memory or len(thought_work):]):
149 # clients won't believe the update
150 newwork = work.copy()
151 newwork['previous_block'] = random.randrange(2**256)
153 print 'getwork faked'
154 res = self.compute(work, request.getHeader('X-All-Targets') is not None)
155 newres = self.compute(newwork, request.getHeader('X-All-Targets') is not None)
158 newres = res = self.compute(work, request.getHeader('X-All-Targets') is not None)
160 reactor.callLater(.01, lambda: last_cache_invalidation[request_id].set((thought_work[-1], newwork)))
162 print 'END GETWORK %i' % (work['best_share_hash'] % 2**32 if work['best_share_hash'] is not None else 0,)
164 return merge(newres.getwork(), res.getwork())
165 rpc_getwork.takes_request = True