close stratum connection if get_work fails to let miner know something is wrong inste...
[p2pool.git] / p2pool / bitcoin / stratum.py
1 import random
2 import sys
3
4 from twisted.internet import protocol, reactor
5 from twisted.python import log
6
7 from p2pool.bitcoin import data as bitcoin_data, getwork
8 from p2pool.util import expiring_dict, jsonrpc, pack
9
10
11 class StratumRPCMiningProvider(object):
12     def __init__(self, wb, other, transport):
13         self.wb = wb
14         self.other = other
15         self.transport = transport
16         
17         self.username = None
18         self.handler_map = expiring_dict.ExpiringDict(300)
19         
20         self.watch_id = self.wb.new_work_event.watch(self._send_work)
21     
22     def rpc_subscribe(self, miner_version=None, session_id=None):
23         reactor.callLater(0, self._send_work)
24         
25         return [
26             ["mining.notify", "ae6812eb4cd7735a302a8a9dd95cf71f"], # subscription details
27             "", # extranonce1
28             self.wb.COINBASE_NONCE_LENGTH, # extranonce2_size
29         ]
30     
31     def rpc_authorize(self, username, password):
32         self.username = username
33         
34         reactor.callLater(0, self._send_work)
35     
36     def _send_work(self):
37         try:
38             x, got_response = self.wb.get_work(*self.wb.preprocess_request('' if self.username is None else self.username))
39         except:
40             log.err()
41             self.transport.loseConnection()
42             return
43         jobid = str(random.randrange(2**128))
44         self.other.svc_mining.rpc_set_difficulty(bitcoin_data.target_to_difficulty(x['share_target'])*self.wb.net.DUMB_SCRYPT_DIFF).addErrback(lambda err: None)
45         self.other.svc_mining.rpc_notify(
46             jobid, # jobid
47             getwork._swap4(pack.IntType(256).pack(x['previous_block'])).encode('hex'), # prevhash
48             x['coinb1'].encode('hex'), # coinb1
49             x['coinb2'].encode('hex'), # coinb2
50             [pack.IntType(256).pack(s).encode('hex') for s in x['merkle_link']['branch']], # merkle_branch
51             getwork._swap4(pack.IntType(32).pack(x['version'])).encode('hex'), # version
52             getwork._swap4(pack.IntType(32).pack(x['bits'].bits)).encode('hex'), # nbits
53             getwork._swap4(pack.IntType(32).pack(x['timestamp'])).encode('hex'), # ntime
54             True, # clean_jobs
55         ).addErrback(lambda err: None)
56         self.handler_map[jobid] = x, got_response
57     
58     def rpc_submit(self, worker_name, job_id, extranonce2, ntime, nonce):
59         if job_id not in self.handler_map:
60             print >>sys.stderr, '''Couldn't link returned work's job id with its handler. This should only happen if this process was recently restarted!'''
61             return False
62         x, got_response = self.handler_map[job_id]
63         coinb_nonce = extranonce2.decode('hex')
64         assert len(coinb_nonce) == self.wb.COINBASE_NONCE_LENGTH
65         new_packed_gentx = x['coinb1'] + coinb_nonce + x['coinb2']
66         header = dict(
67             version=x['version'],
68             previous_block=x['previous_block'],
69             merkle_root=bitcoin_data.check_merkle_link(bitcoin_data.hash256(new_packed_gentx), x['merkle_link']),
70             timestamp=pack.IntType(32).unpack(getwork._swap4(ntime.decode('hex'))),
71             bits=x['bits'],
72             nonce=pack.IntType(32).unpack(getwork._swap4(nonce.decode('hex'))),
73         )
74         return got_response(header, worker_name, coinb_nonce)
75     
76     def close(self):
77         self.wb.new_work_event.unwatch(self.watch_id)
78
79 class StratumProtocol(jsonrpc.LineBasedPeer):
80     def connectionMade(self):
81         self.svc_mining = StratumRPCMiningProvider(self.factory.wb, self.other, self.transport)
82     
83     def connectionLost(self, reason):
84         self.svc_mining.close()
85
86 class StratumServerFactory(protocol.ServerFactory):
87     protocol = StratumProtocol
88     
89     def __init__(self, wb):
90         self.wb = wb