2 from twisted.internet import defer
4 from stratum.services import GenericService, admin
5 from stratum.pubsub import Pubsub
6 from interfaces import Interfaces
7 from subscription import MiningSubscription
8 from lib.exceptions import SubmitException
11 log = stratum.logger.get_logger('mining')
13 class MiningService(GenericService):
14 '''This service provides public API for Stratum mining proxy
15 or any Stratum-compatible miner software.
17 Warning - any callable argument of this class will be propagated
18 over Stratum protocol for public audience!'''
20 service_type = 'mining'
21 service_vendor = 'stratum'
25 def update_block(self):
26 '''Connect this RPC call to 'bitcoind -blocknotify' for
27 instant notification about new block on the network.
28 See blocknotify.sh in /scripts/ for more info.'''
29 Interfaces.template_registry.update_block()
32 def authorize(self, worker_name, worker_password):
33 '''Let authorize worker on this connection.'''
35 session = self.connection_ref().get_session()
36 session.setdefault('authorized', {})
38 if Interfaces.worker_manager.authorize(worker_name, worker_password):
39 session['authorized'][worker_name] = worker_password
43 if worker_name in session['authorized']:
44 del session['authorized'][worker_name]
48 '''Subscribe for receiving mining jobs. This will
49 return subscription details, extranonce1_hex and extranonce2_size'''
51 extranonce1 = Interfaces.template_registry.get_new_extranonce1()
52 extranonce2_size = Interfaces.template_registry.extranonce2_size
54 session = self.connection_ref().get_session()
55 session['extranonce1'] = extranonce1
56 session['difficulty'] = 1 # Following protocol specs, default diff is 1
58 extranonce1_hex = binascii.hexlify(extranonce1)
60 return Pubsub.subscribe(self.connection_ref(), MiningSubscription()) + (extranonce1_hex, extranonce2_size)
63 def submit(self, worker_name, job_id, extranonce2, ntime, nonce):
69 ret = self.submit2(worker_name, job_id, extranonce2, ntime, nonce)
73 log.info("LEN %.03f" % (time.time() - start))
77 def submit(self, worker_name, job_id, extranonce2, ntime, nonce):
78 '''Try to solve block candidate using given parameters.'''
80 session = self.connection_ref().get_session()
81 session.setdefault('authorized', {})
83 # Check if worker is authorized to submit shares
84 if not Interfaces.worker_manager.authorize(worker_name,
85 session['authorized'].get(worker_name)):
86 raise SubmitException("Worker is not authorized")
88 # Check if extranonce1 is in connection session
89 extranonce1_bin = session.get('extranonce1', None)
90 if not extranonce1_bin:
91 raise SubmitException("Connection is not subscribed for mining")
93 difficulty = session['difficulty']
95 submit_time = Interfaces.timestamper.time()
97 # This checks if submitted share meet all requirements
98 # and it is valid proof of work.
100 (block_header, block_hash, on_submit) = Interfaces.template_registry.submit_share(job_id,
101 worker_name, extranonce1_bin, extranonce2, ntime, nonce, difficulty)
102 except SubmitException:
103 # block_header and block_hash are None when submitted data are corrupted
104 Interfaces.share_manager.on_submit_share(worker_name, None, None, difficulty,
109 Interfaces.share_manager.on_submit_share(worker_name, block_header, block_hash, difficulty,
112 if on_submit != None:
113 # Pool performs submitblock() to bitcoind. Let's hook
114 # to result and report it to share manager
115 on_submit.addCallback(Interfaces.share_manager.on_submit_block,
116 worker_name, block_header, block_hash, submit_time)
120 # Service documentation for remote discovery
121 update_block.help_text = "Notify Stratum server about new block on the network."
122 update_block.params = [('password', 'string', 'Administrator password'),]
124 authorize.help_text = "Authorize worker for submitting shares on this connection."
125 authorize.params = [('worker_name', 'string', 'Name of the worker, usually in the form of user_login.worker_id.'),
126 ('worker_password', 'string', 'Worker password'),]
128 subscribe.help_text = "Subscribes current connection for receiving new mining jobs."
129 subscribe.params = []
131 submit.help_text = "Submit solved share back to the server. Excessive sending of invalid shares "\
132 "or shares above indicated target (see Stratum mining docs for set_target()) may lead "\
133 "to temporary or permanent ban of user,worker or IP address."
134 submit.params = [('worker_name', 'string', 'Name of the worker, usually in the form of user_login.worker_id.'),
135 ('job_id', 'string', 'ID of job (received by mining.notify) which the current solution is based on.'),
136 ('extranonce2', 'string', 'hex-encoded big-endian extranonce2, length depends on extranonce2_size from mining.notify.'),
137 ('ntime', 'string', 'UNIX timestamp (32bit integer, big-endian, hex-encoded), must be >= ntime provided by mining,notify and <= current time'),
138 ('nonce', 'string', '32bit integer, hex-encoded, big-endian'),]