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