Mysql share reporting fixed. Update config template.
[stratum-mining.git] / mining / service.py
1 import binascii
2 from twisted.internet import defer
3
4 from stratum import settings
5 from stratum.services import GenericService, admin
6 from stratum.pubsub import Pubsub
7 from interfaces import Interfaces
8 from subscription import MiningSubscription
9 from lib.exceptions import SubmitException
10
11 import stratum.logger
12 log = stratum.logger.get_logger('mining')
13                 
14 class MiningService(GenericService):
15     '''This service provides public API for Stratum mining proxy
16     or any Stratum-compatible miner software.
17     
18     Warning - any callable argument of this class will be propagated
19     over Stratum protocol for public audience!'''
20     
21     service_type = 'mining'
22     service_vendor = 'stratum'
23     is_default = True
24     
25     @admin
26     def update_block(self):
27         '''Connect this RPC call to 'bitcoind -blocknotify' for 
28         instant notification about new block on the network.
29         See blocknotify.sh in /scripts/ for more info.'''
30         
31         log.info("New block notification received")
32         Interfaces.template_registry.update_block()
33         return True 
34     
35     def authorize(self, worker_name, worker_password):
36         '''Let authorize worker on this connection.'''
37         
38         session = self.connection_ref().get_session()
39         session.setdefault('authorized', {})
40         
41         if Interfaces.worker_manager.authorize(worker_name, worker_password):
42             session['authorized'][worker_name] = worker_password
43             return True
44         
45         else:
46             if worker_name in session['authorized']:
47                 del session['authorized'][worker_name]
48             return False
49         
50     def subscribe(self,*arg):
51         '''Subscribe for receiving mining jobs. This will
52         return subscription details, extranonce1_hex and extranonce2_size'''
53         
54         extranonce1 = Interfaces.template_registry.get_new_extranonce1()
55         extranonce2_size = Interfaces.template_registry.extranonce2_size
56         extranonce1_hex = binascii.hexlify(extranonce1)
57         
58         session = self.connection_ref().get_session()
59         session['extranonce1'] = extranonce1
60         session['difficulty'] = settings.POOL_TARGET # Following protocol specs, default diff is 1
61
62         return Pubsub.subscribe(self.connection_ref(), MiningSubscription()) + (extranonce1_hex, extranonce2_size)
63     
64     '''    
65     def submit(self, worker_name, job_id, extranonce2, ntime, nonce):
66         import time
67         start = time.time()
68         
69         for x in range(100):
70             try:
71                 ret = self.submit2(worker_name, job_id, extranonce2, ntime, nonce)
72             except:
73                 pass
74             
75         log.info("LEN %.03f" % (time.time() - start))
76         return ret
77     '''
78         
79     def submit(self, worker_name, job_id, extranonce2, ntime, nonce):
80         '''Try to solve block candidate using given parameters.'''
81         
82         session = self.connection_ref().get_session()
83         session.setdefault('authorized', {})
84         
85         # Check if worker is authorized to submit shares
86         if not Interfaces.worker_manager.authorize(worker_name,
87                         session['authorized'].get(worker_name)):
88             raise SubmitException("Worker is not authorized")
89
90         # Check if extranonce1 is in connection session
91         extranonce1_bin = session.get('extranonce1', None)
92         if not extranonce1_bin:
93             raise SubmitException("Connection is not subscribed for mining")
94         
95         difficulty = session['difficulty']
96         submit_time = Interfaces.timestamper.time()
97         ip = self.connection_ref()._get_ip()
98     
99         Interfaces.share_limiter.submit(self.connection_ref, job_id, difficulty, submit_time, worker_name)
100             
101         # This checks if submitted share meet all requirements
102         # and it is valid proof of work.
103         try:
104             (block_header, block_hash, share_diff, on_submit) = Interfaces.template_registry.submit_share(job_id,
105                                                 worker_name, session, extranonce1_bin, extranonce2, ntime, nonce, difficulty)
106         except SubmitException as e:
107             # block_header and block_hash are None when submitted data are corrupted
108             Interfaces.share_manager.on_submit_share(worker_name, None, None, difficulty,
109                                                  submit_time, False, ip, e[0], 0)    
110             raise
111             
112              
113         Interfaces.share_manager.on_submit_share(worker_name, block_header, block_hash, difficulty,
114                                                  submit_time, True, ip, '', share_diff)
115         
116         if on_submit != None:
117             # Pool performs submitblock() to bitcoind. Let's hook
118             # to result and report it to share manager
119             on_submit.addCallback(Interfaces.share_manager.on_submit_block,
120                         worker_name, block_header, block_hash, submit_time,ip,share_diff)
121
122         return True
123             
124     # Service documentation for remote discovery
125     update_block.help_text = "Notify Stratum server about new block on the network."
126     update_block.params = [('password', 'string', 'Administrator password'),]
127     
128     authorize.help_text = "Authorize worker for submitting shares on this connection."
129     authorize.params = [('worker_name', 'string', 'Name of the worker, usually in the form of user_login.worker_id.'),
130                         ('worker_password', 'string', 'Worker password'),]
131     
132     subscribe.help_text = "Subscribes current connection for receiving new mining jobs."
133     subscribe.params = []
134     
135     submit.help_text = "Submit solved share back to the server. Excessive sending of invalid shares "\
136                        "or shares above indicated target (see Stratum mining docs for set_target()) may lead "\
137                        "to temporary or permanent ban of user,worker or IP address."
138     submit.params = [('worker_name', 'string', 'Name of the worker, usually in the form of user_login.worker_id.'),
139                      ('job_id', 'string', 'ID of job (received by mining.notify) which the current solution is based on.'),
140                      ('extranonce2', 'string', 'hex-encoded big-endian extranonce2, length depends on extranonce2_size from mining.notify.'),
141                      ('ntime', 'string', 'UNIX timestamp (32bit integer, big-endian, hex-encoded), must be >= ntime provided by mining,notify and <= current time'),
142                      ('nonce', 'string', '32bit integer, hex-encoded, big-endian'),]
143