Fixes https://github.com/slush0/stratum/issues/4
[stratum-mining.git] / mining / service.py
index 8bdccd2..30451d9 100644 (file)
@@ -2,16 +2,13 @@ import binascii
 from twisted.internet import defer
 
 from stratum.services import GenericService, admin
-from stratum.custom_exceptions import ServiceException
 from stratum.pubsub import Pubsub
 from interfaces import Interfaces
 from subscription import MiningSubscription
+from lib.exceptions import SubmitException
 
 import stratum.logger
 log = stratum.logger.get_logger('mining')
-
-class SubmitException(ServiceException):
-    pass
                 
 class MiningService(GenericService):
     '''This service provides public API for Stratum mining proxy
@@ -29,26 +26,38 @@ class MiningService(GenericService):
         '''Connect this RPC call to 'bitcoind -blocknotify' for 
         instant notification about new block on the network.
         See blocknotify.sh in /scripts/ for more info.'''
+        
+        log.info("New block notification received")
         Interfaces.template_registry.update_block()
         return True 
     
     def authorize(self, worker_name, worker_password):
         '''Let authorize worker on this connection.'''
-        return Interfaces.worker_manager.authorize(worker_name, worker_password)
         
-    def subscribe(self):
+        session = self.connection_ref().get_session()
+        session.setdefault('authorized', {})
+        
+        if Interfaces.worker_manager.authorize(worker_name, worker_password):
+            session['authorized'][worker_name] = worker_password
+            return True
+        
+        else:
+            if worker_name in session['authorized']:
+                del session['authorized'][worker_name]
+            return False
+        
+    def subscribe(self, *args):
         '''Subscribe for receiving mining jobs. This will
         return subscription details, extranonce1_hex and extranonce2_size'''
         
         extranonce1 = Interfaces.template_registry.get_new_extranonce1()
         extranonce2_size = Interfaces.template_registry.extranonce2_size
-
+        extranonce1_hex = binascii.hexlify(extranonce1)
+        
         session = self.connection_ref().get_session()
         session['extranonce1'] = extranonce1
         session['difficulty'] = 1 # Following protocol specs, default diff is 1
 
-        extranonce1_hex = binascii.hexlify(extranonce1)
-            
         return Pubsub.subscribe(self.connection_ref(), MiningSubscription()) + (extranonce1_hex, extranonce2_size)
     
     '''    
@@ -65,11 +74,17 @@ class MiningService(GenericService):
         log.info("LEN %.03f" % (time.time() - start))
         return ret
     '''
-    
+        
     def submit(self, worker_name, job_id, extranonce2, ntime, nonce):
         '''Try to solve block candidate using given parameters.'''
         
         session = self.connection_ref().get_session()
+        session.setdefault('authorized', {})
+        
+        # Check if worker is authorized to submit shares
+        if not Interfaces.worker_manager.authorize(worker_name,
+                        session['authorized'].get(worker_name)):
+            raise SubmitException("Worker is not authorized")
 
         # Check if extranonce1 is in connection session
         extranonce1_bin = session.get('extranonce1', None)
@@ -77,19 +92,30 @@ class MiningService(GenericService):
             raise SubmitException("Connection is not subscribed for mining")
         
         difficulty = session['difficulty']
-
+        submit_time = Interfaces.timestamper.time()
+    
+        Interfaces.share_limiter.submit(self.connection_ref, difficulty, submit_time)
+            
         # This checks if submitted share meet all requirements
         # and it is valid proof of work.
-        (is_valid, reason, block_header, block_hash) = Interfaces.template_registry.submit_share(job_id,
-                                                worker_name, extranonce1_bin, extranonce2, ntime, nonce, difficulty,
-                                                Interfaces.share_manager.on_submit_block)
-   
-        # block_header and block_hash may be None when submitted data are corrupted     
+        try:
+            (block_header, block_hash, on_submit) = Interfaces.template_registry.submit_share(job_id,
+                                                worker_name, extranonce1_bin, extranonce2, ntime, nonce, difficulty)
+        except SubmitException:
+            # block_header and block_hash are None when submitted data are corrupted
+            Interfaces.share_manager.on_submit_share(worker_name, None, None, difficulty,
+                                                 submit_time, False)    
+            raise
+            
+             
         Interfaces.share_manager.on_submit_share(worker_name, block_header, block_hash, difficulty,
-                                                 Interfaces.timestamper.time(), is_valid)
+                                                 submit_time, True)
         
-        if not is_valid:
-            raise SubmitException(reason)
+        if on_submit != None:
+            # Pool performs submitblock() to bitcoind. Let's hook
+            # to result and report it to share manager
+            on_submit.addCallback(Interfaces.share_manager.on_submit_block,
+                        worker_name, block_header, block_hash, submit_time)
 
         return True
             
@@ -112,4 +138,4 @@ class MiningService(GenericService):
                      ('extranonce2', 'string', 'hex-encoded big-endian extranonce2, length depends on extranonce2_size from mining.notify.'),
                      ('ntime', 'string', 'UNIX timestamp (32bit integer, big-endian, hex-encoded), must be >= ntime provided by mining,notify and <= current time'),
                      ('nonce', 'string', '32bit integer, hex-encoded, big-endian'),]
-        
\ No newline at end of file
+