Beispiel #1
0
    def __init__(self, block_template_class, coinbaser, bitcoin_rpc, aux_rpc,
                 instance_id, on_template_callback, on_block_callback):
        self.prevhashes = {}
        self.jobs = weakref.WeakValueDictionary()

        self.extranonce_counter = ExtranonceCounter(instance_id)
        self.extranonce2_size = block_template_class.coinbase_transaction_class.extranonce_size \
                - self.extranonce_counter.get_size()
        self.coinbaser = coinbaser
        self.block_template_class = block_template_class
        self.bitcoin_rpc = bitcoin_rpc
        self.on_block_callback = on_block_callback
        self.on_template_callback = on_template_callback

        self.last_block = None
        self.update_in_progress = False
        self.last_update = 0
        self.last_height = None

        self.aux_rpc = aux_rpc
        self.aux_update_in_progress = False
        self.aux_new_block = False
        self.aux_last_update = 0
        self.aux_update_counter = 0
        self.aux_data = []

        # Create first block template on startup
        self.update_auxs()
Beispiel #2
0
    def __init__(self,
                 block_template_class,
                 coinbaser,
                 bitcoin_rpc,
                 instance_id,
                 on_template_callback,
                 on_block_callback,
                 rootstock_rpc=None):
        self.prevhashes = {}
        self.jobs = weakref.WeakValueDictionary()

        self.extranonce_counter = ExtranonceCounter(instance_id)
        self.extranonce2_size = block_template_class.coinbase_transaction_class.extranonce_size \
                - self.extranonce_counter.get_size()

        self.coinbaser = coinbaser
        self.block_template_class = block_template_class
        self.bitcoin_rpc = bitcoin_rpc
        self.rootstock_rpc = rootstock_rpc
        self.on_block_callback = on_block_callback
        self.on_template_callback = on_template_callback

        self.last_block = None
        self.update_in_progress = False
        self.last_update = None
        self.rsk_last_update = 0
        self.rsk_update_in_progress = False
        self.last_data = dict()
        self.last_rsk_hash = ""

        # Create first block template on startup
        self.update_block()
        if self.rootstock_rpc is not None:
            self.rsk_timeout_counter = 0
Beispiel #3
0
    def __init__(self, block_template_class, coinbaser, bitcoin_rpc, mm_rpc,
                 instance_id, on_template_callback, on_block_callback):
        self.prevhashes = {}
        self.jobs = weakref.WeakValueDictionary()

        self.extranonce_counter = ExtranonceCounter(instance_id)
        self.extranonce2_size = block_template_class.coinbase_transaction_class.extranonce_size \
                - self.extranonce_counter.get_size()
        log.debug("Got to Template Registry")
        self.coinbaser = coinbaser
        self.block_template_class = block_template_class
        self.bitcoin_rpc = bitcoin_rpc
        self.mm_rpc = mm_rpc
        self.on_block_callback = on_block_callback
        self.on_template_callback = on_template_callback

        self.last_block = None
        self.update_in_progress = False
        self.update_mm_in_progress = False
        self.last_update = None
        self.last_update_mm = None

        self.mm_hash = ""
        self.mm_script = ""
        self.mm_target = None
        self.last_height = None

        if settings.COINDAEMON_ALGO == 'scrypt':
            self.algo = 1
        else:
            self.algo = 0

        # Create first block template on startup
        self.update_block()
        self.update_mm_block()
    def __init__(self, block_template_class, coinbaser, bitcoin_rpc, mm_rpc, instance_id,
                 on_template_callback, on_block_callback):
        self.prevhashes = {}
        self.jobs = weakref.WeakValueDictionary()
        
        self.extranonce_counter = ExtranonceCounter(instance_id)
        self.extranonce2_size = block_template_class.coinbase_transaction_class.extranonce_size \
                - self.extranonce_counter.get_size()
        log.debug("Got to Template Registry")
        self.coinbaser = coinbaser
        self.block_template_class = block_template_class
        self.bitcoin_rpc = bitcoin_rpc
        self.mm_rpc = mm_rpc
        self.on_block_callback = on_block_callback
        self.on_template_callback = on_template_callback
        
        self.last_block = None
        self.update_in_progress = False
        self.update_mm_in_progress = False
        self.last_update = None
        self.last_update_mm = None

        self.mm_hash = ""
        self.mm_script = ""
        self.mm_target = None
        self.last_height = None

        if settings.COINDAEMON_ALGO == 'scrypt':
            self.algo = 1
        else:
            self.algo = 0
        
        # Create first block template on startup
        self.update_block()
        self.update_mm_block()
    def __init__(self, block_template_class, coinbaser, bitcoin_rpc, aux_rpc, instance_id,
                 on_template_callback, on_block_callback):
        self.prevhashes = {}
        self.jobs = weakref.WeakValueDictionary()
        
        self.extranonce_counter = ExtranonceCounter(instance_id)
        self.extranonce2_size = block_template_class.coinbase_transaction_class.extranonce_size \
                - self.extranonce_counter.get_size()
        self.coinbaser = coinbaser
        self.block_template_class = block_template_class
        self.bitcoin_rpc = bitcoin_rpc
        self.on_block_callback = on_block_callback
        self.on_template_callback = on_template_callback
        
        self.last_block = None
        self.update_in_progress = False
        self.last_update = 0
        self.last_height = None

        self.aux_rpc = aux_rpc
        self.aux_update_in_progress = False
        self.aux_new_block = False
        self.aux_last_update = 0
        self.aux_update_counter = 0
        self.aux_data = []
        
        # Create first block template on startup
        self.update_auxs()
Beispiel #6
0
    def __init__(self, block_template_class, bitcoin_rpc, instance_id,
                 on_template_callback, on_block_callback):
        self.prevhashes = {}
        self.jobs = weakref.WeakValueDictionary()

        self.extranonce_counter = ExtranonceCounter(instance_id)
        self.extranonce2_size = block_template_class.coinbase_transaction_class.extranonce_size \
                - self.extranonce_counter.get_size()

        self.coinbasers_value = []

        for address, percent in settings.BITCOIN_ADDRESSES.iteritems():
            coinbaser = SimpleCoinbaser(address, bitcoin_rpc)
            self.coinbasers_value.append((coinbaser, percent))
        self.block_template_class = block_template_class
        self.bitcoin_rpc = bitcoin_rpc
        self.on_block_callback = on_block_callback
        self.on_template_callback = on_template_callback

        self.last_block = None
        self.last_update = None
 def __init__(self, block_template_class, coinbaser, bitcoin_rpc, instance_id,
              on_template_callback, on_block_callback):
     self.prevhashes = {}
     self.jobs = weakref.WeakValueDictionary()
     
     self.extranonce_counter = ExtranonceCounter(instance_id)
     self.extranonce2_size = block_template_class.coinbase_transaction_class.extranonce_size \
             - self.extranonce_counter.get_size()
     log.debug("Got to Template Registry")
     self.coinbaser = coinbaser
     self.block_template_class = block_template_class
     self.bitcoin_rpc = bitcoin_rpc
     self.on_block_callback = on_block_callback
     self.on_template_callback = on_template_callback
     
     self.last_block = None
     self.update_in_progress = False
     self.last_update = None
     
     # Create first block template on startup
     self.update_block()
    def __init__(self, block_template_class, coinbaser, bitcoin_rpc, instance_id,
                 on_template_callback, on_block_callback):
        self.current_prevhash = ''
        self.jobs = weakref.WeakValueDictionary()
        self.minimal_job_id = 0

        self.extranonce_counter = ExtranonceCounter(instance_id)
        self.extranonce2_size = block_template_class.coinbase_transaction_class.extranonce_size \
                - self.extranonce_counter.get_size()

        self.coinbaser = coinbaser
        self.block_template_class = block_template_class
        self.bitcoin_rpc = bitcoin_rpc
        self.on_block_callback = on_block_callback
        self.on_template_callback = on_template_callback

        self._last_template = None
        self.update_in_progress = False
        self.last_update = None

        # Create first block template on startup
        self.update_block()
Beispiel #9
0
    def __init__(self, template_generator, bitcoin_rpc, instance_id,
                 on_template_callback, on_block_callback):
        log.debug("Got to Template Registry")

        self.prevhashes = {}
        self.jobs = weakref.WeakValueDictionary()

        self.template_generator = template_generator

        self.extranonce_counter = ExtranonceCounter(instance_id)
        self.extranonce2_size = template_generator.get_extranonce_size(
        ) - self.extranonce_counter.get_size()

        self.bitcoin_rpc = bitcoin_rpc
        self.on_block_callback = on_block_callback
        self.on_template_callback = on_template_callback

        self.last_template = None
        self.update_in_progress = False
        self.GBT_RPC_ATTEMPT = None
        self.last_block_update_start_time = None

        # Create first block template on startup
        self.update_block()
Beispiel #10
0
    def __init__(self, block_template_class, bitcoin_rpc, instance_id,
                 on_template_callback, on_block_callback):
        self.prevhashes = {}
        self.jobs = weakref.WeakValueDictionary()
        
        self.extranonce_counter = ExtranonceCounter(instance_id)
        self.extranonce2_size = block_template_class.coinbase_transaction_class.extranonce_size \
                - self.extranonce_counter.get_size()
                 
        self.coinbasers_value = []

        for address, percent in settings.BITCOIN_ADDRESSES.iteritems():
            coinbaser = SimpleCoinbaser(address, bitcoin_rpc)
            self.coinbasers_value.append((coinbaser, percent))
        self.block_template_class = block_template_class
        self.bitcoin_rpc = bitcoin_rpc
        self.on_block_callback = on_block_callback
        self.on_template_callback = on_template_callback
        
        self.last_block = None
        self.last_update = None
Beispiel #11
0
 def __init__(self, block_template_class, coinbaser, bitcoin_rpc, instance_id,
              on_template_callback, on_block_callback):
     self.prevhashes = {}
     self.jobs = weakref.WeakValueDictionary()
     
     self.extranonce_counter = ExtranonceCounter(instance_id)
     self.extranonce2_size = block_template_class.coinbase_transaction_class.extranonce_size \
             - self.extranonce_counter.get_size()
     log.debug("Got to Template Registry")
     self.coinbaser = coinbaser
     self.block_template_class = block_template_class
     self.bitcoin_rpc = bitcoin_rpc
     self.on_block_callback = on_block_callback
     self.on_template_callback = on_template_callback
     
     self.last_block = None
     self.update_in_progress = False
     self.last_update = None
     
     # Create first block template on startup
     self.update_block()
Beispiel #12
0
	def __init__(self, template_generator, bitcoin_rpc, instance_id, on_template_callback, on_block_callback):
		log.debug("Got to Template Registry")

		self.prevhashes = {}
		self.jobs = weakref.WeakValueDictionary()

		self.template_generator = template_generator

		self.extranonce_counter = ExtranonceCounter(instance_id)
		self.extranonce2_size = template_generator.get_extranonce_size() - self.extranonce_counter.get_size()

		self.bitcoin_rpc = bitcoin_rpc
		self.on_block_callback = on_block_callback
		self.on_template_callback = on_template_callback

		self.last_template = None
		self.update_in_progress = False
		self.GBT_RPC_ATTEMPT = None
		self.last_block_update_start_time = None

		# Create first block template on startup
		self.update_block()
class TemplateRegistry(object):
    '''Implements the main logic of the pool. Keep track
    on valid block templates, provide internal interface for stratum
    service and implements block validation and submits.'''
    def __init__(self, block_template_class, coinbaser, bitcoin_rpc,
                 instance_id, on_template_callback, on_block_callback):
        self.prevhashes = {}
        self.jobs = weakref.WeakValueDictionary()

        self.extranonce_counter = ExtranonceCounter(instance_id)
        self.extranonce2_size = block_template_class.coinbase_transaction_class.extranonce_size \
                - self.extranonce_counter.get_size()
        log.debug("Got to Template Registry")
        self.coinbaser = coinbaser
        self.block_template_class = block_template_class
        self.bitcoin_rpc = bitcoin_rpc
        self.on_block_callback = on_block_callback
        self.on_template_callback = on_template_callback

        self.last_block = None
        self.update_in_progress = False
        self.last_update = None

        # Create first block template on startup
        self.update_block()

    def get_new_extranonce1(self):
        '''Generates unique extranonce1 (e.g. for newly
        subscribed connection.'''
        return self.extranonce_counter.get_new_bin()

    def get_last_broadcast_args(self):
        '''Returns arguments for mining.notify
        from last known template.'''
        return self.last_block.broadcast_args

    def add_template(self, block, block_height):
        '''Adds new template to the registry.
        It also clean up templates which should
        not be used anymore.'''

        prevhash = block.prevhash_hex

        if prevhash in self.prevhashes.keys():
            new_block = False
        else:
            new_block = True
            self.prevhashes[prevhash] = []

        # Blocks sorted by prevhash, so it's easy to drop
        # them on blockchain update
        self.prevhashes[prevhash].append(block)

        # Weak reference for fast lookup using job_id
        self.jobs[block.job_id] = block

        # Use this template for every new request
        self.last_block = block

        # Drop templates of obsolete blocks
        for ph in self.prevhashes.keys():
            if ph != prevhash:
                del self.prevhashes[ph]

        if new_block:
            # Tell the system about new block
            # It is mostly important for share manager
            self.on_block_callback(prevhash, block_height)

        # Everything is ready, let's broadcast jobs!
        self.on_template_callback(new_block)

    def update_block(self):
        '''Registry calls the getblocktemplate() RPC
        and build new block template.'''

        if self.update_in_progress:
            # Block has been already detected
            return

        self.update_in_progress = True
        self.last_update = Interfaces.timestamper.time()

        d = self.bitcoin_rpc.getblocktemplate()
        d.addCallback(self._update_block)
        d.addErrback(self._update_block_failed)

    def _update_block_failed(self, failure):
        log.error(str(failure))
        self.update_in_progress = False

    def _update_block(self, data):
        start = Interfaces.timestamper.time()

        template = self.block_template_class(Interfaces.timestamper,
                                             self.coinbaser,
                                             JobIdGenerator.get_new_id())
        log.info(template.fill_from_rpc(data))
        self.add_template(template, data['height'])

        log.info("[---- %s -----]Update finished, %.03f sec, %d txes" % \
                    (template.job_id, Interfaces.timestamper.time() - start, len(template.vtx)))

        self.update_in_progress = False
        return data

    def diff_to_target(self, difficulty):
        '''Converts difficulty to target'''
        diff1 = 0x00000000ffff0000000000000000000000000000000000000000000000000000
        if settings.CUSTOM_DIFF1 != None:
            diff1 = settings.CUSTOM_DIFF1
        return diff1 / difficulty

    def bits_to_diff(self, nbits):
        diff1 = 0x00000000ffff0000000000000000000000000000000000000000000000000000
        return diff1 / util.uint256_from_compact(nbits)

    def get_job(self, job_id, worker_name, ip=False):
        '''For given job_id returns BlockTemplate instance or None'''
        try:
            j = self.jobs[job_id]
        except:
            log.info("Job id '%s' not found, worker_name: '%s'" %
                     (job_id, worker_name))

            if ip:
                log.info("Worker submited invalid Job id: IP %s", str(ip))

            return None

        # Now we have to check if job is still valid.
        # Unfortunately weak references are not bulletproof and
        # old reference can be found until next run of garbage collector.
        if j.prevhash_hex not in self.prevhashes:
            log.info("Prevhash of job '%s' is unknown" % job_id)
            return None

        if j not in self.prevhashes[j.prevhash_hex]:
            log.info("Job %s is unknown" % job_id)
            return None

        return j

    def submit_share(self, job_id, worker_name, session, extranonce1_bin,
                     extranonce2, ntime, nonce, difficulty, ip,
                     version_rolling):
        '''Check parameters and finalize block template. If it leads
           to valid block candidate, asynchronously submits the block
           back to the bitcoin network.

            - extranonce1_bin is binary. No checks performed, it should be from session data
            - job_id, extranonce2, ntime, nonce - in hex form sent by the client
            - difficulty - decimal number from session
            - submitblock_callback - reference to method which receive result of submitblock()
            - difficulty is checked to see if its lower than the vardiff minimum target or pool target
              from conf/config.py and if it is the share is rejected due to it not meeting the requirements for a share

        '''
        if difficulty < settings.VDIFF_MIN_TARGET or difficulty < settings.POOL_TARGET:
            # Share Diff Should never be 0
            raise SubmitException(
                "Diff is %s Share Rejected Reporting to Admin" % (difficulty))

        # normalize the case to prevent duplication of valid shares by the client
        ntime = ntime.lower()
        nonce = nonce.lower()
        extranonce2 = extranonce2.lower()

        # Check for job
        job = self.get_job(job_id, worker_name, ip)
        if job == None:
            raise SubmitException("Job '%s' not found" % job_id)

        # Check if ntime looks correct
        if len(ntime) != 8:
            raise SubmitException("Incorrect size of ntime. Expected 8 chars")

        if not job.check_ntime(int(ntime, 16)):
            raise SubmitException("Ntime out of range")

        # Check nonce
        if len(nonce) != 8:
            raise SubmitException("Incorrect size of nonce. Expected 8 chars")

        # 0. Some sugar
        extranonce2_bin = binascii.unhexlify(extranonce2)
        ntime_bin = binascii.unhexlify(ntime)
        nonce_bin = binascii.unhexlify(nonce)

        # Check for duplicated submit
        if not job.register_submit(extranonce1_bin, extranonce2_bin, ntime_bin,
                                   nonce_bin):
            log.debug("Duplicate from %s, (%s %s %s %s)" % \
                    (worker_name, binascii.hexlify(extranonce1_bin), extranonce2, ntime, nonce))
            raise SubmitException("Duplicate share")

        # Now let's do the hard work!
        # ---------------------------

        # 1. Build coinbase
        coinbase_bin = job.serialize_coinbase(extranonce1_bin, extranonce2_bin)
        coinbase_hash = util.doublesha(coinbase_bin)

        # 2. Calculate merkle root
        merkle_root_bin = job.merkletree.withFirst(coinbase_hash)
        merkle_root_int = util.uint256_from_str(merkle_root_bin)

        # 3. Serialize header with given merkle, ntime and nonce
        header_bin = job.serialize_header(merkle_root_int, ntime_bin,
                                          nonce_bin, version_rolling)

        # 4. Reverse header and compare it with target of the user
        hash_bin = util.doublesha(''.join(
            [header_bin[i * 4:i * 4 + 4][::-1] for i in range(0, 20)]))
        hash_int = util.uint256_from_str(hash_bin)
        scrypt_hash_hex = "%064x" % hash_int
        header_hex = binascii.hexlify(header_bin)
        if settings.CUSTOM_HEADER != None:
            header_hex = header_hex + settings.CUSTOM_HEADER

        target_user = self.diff_to_target(difficulty)
        if hash_int > target_user:
            raise SubmitException("Share is above target %064x > %064x" %
                                  (hash_int, target_user))

        # Algebra tells us the diff_to_target is the same as hash_to_diff
        share_diff = int(self.diff_to_target(hash_int))

        # 5. Compare hash with target of the network
        if hash_int <= job.target:
            # Yay! It is block candidate!
            log.info("We found a block candidate! %s" % scrypt_hash_hex)

            block_hash_bin = util.doublesha(''.join(
                [header_bin[i * 4:i * 4 + 4][::-1] for i in range(0, 20)]))
            block_hash_hex = block_hash_bin[::-1].encode('hex_codec')

            # 6. Finalize and serialize block object
            job.finalize(merkle_root_int, extranonce1_bin, extranonce2_bin,
                         int(ntime, 16), int(nonce, 16))

            if not job.is_valid():
                # Should not happen
                log.exception(
                    "FINAL JOB VALIDATION FAILED!(Try enabling/disabling tx messages)"
                )

            # 7. Submit block to the network
            serialized = binascii.hexlify(job.serialize())
            on_submit = self.bitcoin_rpc.submitblock(serialized,
                                                     block_hash_hex,
                                                     scrypt_hash_hex)
            if on_submit:
                self.update_block()

            if settings.SOLUTION_BLOCK_HASH:
                return (header_hex, block_hash_hex, share_diff, on_submit)
            else:
                return (header_hex, scrypt_hash_hex, share_diff, on_submit)

        if settings.SOLUTION_BLOCK_HASH:
            block_hash_bin = util.doublesha(''.join(
                [header_bin[i * 4:i * 4 + 4][::-1] for i in range(0, 20)]))
            block_hash_hex = block_hash_bin[::-1].encode('hex_codec')
            return (header_hex, block_hash_hex, share_diff, None)
        else:
            return (header_hex, scrypt_hash_hex, share_diff, None)
class TemplateRegistry(object):
    '''Implements the main logic of the pool. Keep track
    on valid block templates, provide internal interface for stratum
    service and implements block validation and submits.'''
    
    def __init__(self, block_template_class, coinbaser, bitcoin_rpc, instance_id,
                 on_template_callback, on_block_callback):
        self.prevhashes = {}
        self.jobs = weakref.WeakValueDictionary()
        
        self.extranonce_counter = ExtranonceCounter(instance_id)
        self.extranonce2_size = block_template_class.coinbase_transaction_class.extranonce_size \
                - self.extranonce_counter.get_size()
        log.debug("Got to Template Registry")
        self.coinbaser = coinbaser
        self.block_template_class = block_template_class
        self.bitcoin_rpc = bitcoin_rpc
        self.on_block_callback = on_block_callback
        self.on_template_callback = on_template_callback
        
        self.last_block = None
        self.update_in_progress = False
        self.last_update = None
        
        # Create first block template on startup
        self.update_block()
        
    def get_new_extranonce1(self):
        '''Generates unique extranonce1 (e.g. for newly
        subscribed connection.'''
        log.debug("Getting Unique Extronance")
        return self.extranonce_counter.get_new_bin()
    
    def get_last_broadcast_args(self):
        '''Returns arguments for mining.notify
        from last known template.'''
        log.debug("Getting Laat Template")
        return self.last_block.broadcast_args
        
    def add_template(self, block,block_height):
        '''Adds new template to the registry.
        It also clean up templates which should
        not be used anymore.'''
        
        prevhash = block.prevhash_hex

        if prevhash in self.prevhashes.keys():
            new_block = False
        else:
            new_block = True
            self.prevhashes[prevhash] = []
               
        # Blocks sorted by prevhash, so it's easy to drop
        # them on blockchain update
        self.prevhashes[prevhash].append(block)
        
        # Weak reference for fast lookup using job_id
        self.jobs[block.job_id] = block
        
        # Use this template for every new request
        self.last_block = block
        
        # Drop templates of obsolete blocks
        for ph in self.prevhashes.keys():
            if ph != prevhash:
                del self.prevhashes[ph]
                
        log.info("New template for %s" % prevhash)

        if new_block:
            # Tell the system about new block
            # It is mostly important for share manager
            self.on_block_callback(prevhash, block_height)

        # Everything is ready, let's broadcast jobs!
        self.on_template_callback(new_block)
        

        #from twisted.internet import reactor
        #reactor.callLater(10, self.on_block_callback, new_block) 
              
    def update_block(self):
        '''Registry calls the getblocktemplate() RPC
        and build new block template.'''
        
        if self.update_in_progress:
            # Block has been already detected
            return
        
        self.update_in_progress = True
        self.last_update = Interfaces.timestamper.time()
        
        d = self.bitcoin_rpc.getblocktemplate()
        d.addCallback(self._update_block)
        d.addErrback(self._update_block_failed)
        
    def _update_block_failed(self, failure):
        log.error(str(failure))
        self.update_in_progress = False
        
    def _update_block(self, data):
        start = Interfaces.timestamper.time()
                
        template = self.block_template_class(Interfaces.timestamper, self.coinbaser, JobIdGenerator.get_new_id())
        log.info(template.fill_from_rpc(data))
        self.add_template(template,data['height'])

        log.info("Update finished, %.03f sec, %d txes" % \
                    (Interfaces.timestamper.time() - start, len(template.vtx)))
        
        self.update_in_progress = False        
        return data
    
    def diff_to_target(self, difficulty):
        '''Converts difficulty to target'''
        if settings.COINDAEMON_ALGO == 'scrypt' or 'scrypt-jane':
            diff1 = 0x0000ffff00000000000000000000000000000000000000000000000000000000
        elif settings.COINDAEMON_ALGO == 'quark':
            diff1 = 0x000000ffff000000000000000000000000000000000000000000000000000000
        else:
            diff1 = 0x00000000ffff0000000000000000000000000000000000000000000000000000

        return diff1 / difficulty
    
    def get_job(self, job_id):
        '''For given job_id returns BlockTemplate instance or None'''
        try:
            j = self.jobs[job_id]
        except:
            log.info("Job id '%s' not found" % job_id)
            return None
        
        # Now we have to check if job is still valid.
        # Unfortunately weak references are not bulletproof and
        # old reference can be found until next run of garbage collector.
        if j.prevhash_hex not in self.prevhashes:
            log.info("Prevhash of job '%s' is unknown" % job_id)
            return None
        
        if j not in self.prevhashes[j.prevhash_hex]:
            log.info("Job %s is unknown" % job_id)
            return None
        
        return j
        
    def submit_share(self, job_id, worker_name, session, extranonce1_bin, extranonce2, ntime, nonce,
                     difficulty):
        '''Check parameters and finalize block template. If it leads
           to valid block candidate, asynchronously submits the block
           back to the bitcoin network.
        
            - extranonce1_bin is binary. No checks performed, it should be from session data
            - job_id, extranonce2, ntime, nonce - in hex form sent by the client
            - difficulty - decimal number from session, again no checks performed
            - submitblock_callback - reference to method which receive result of submitblock()
        '''
        
        # Check if extranonce2 looks correctly. extranonce2 is in hex form...
        if len(extranonce2) != self.extranonce2_size * 2:
            raise SubmitException("Incorrect size of extranonce2. Expected %d chars" % (self.extranonce2_size*2))
        
        # Check for job
        job = self.get_job(job_id)
        if job == None:
            raise SubmitException("Job '%s' not found" % job_id)
                
        # Check if ntime looks correct
        if len(ntime) != 8:
            raise SubmitException("Incorrect size of ntime. Expected 8 chars")

        if not job.check_ntime(int(ntime, 16)):
            raise SubmitException("Ntime out of range")
        
        # Check nonce        
        if len(nonce) != 8:
            raise SubmitException("Incorrect size of nonce. Expected 8 chars")
        
        # Check for duplicated submit
        if not job.register_submit(extranonce1_bin, extranonce2, ntime, nonce):
            log.info("Duplicate from %s, (%s %s %s %s)" % \
                    (worker_name, binascii.hexlify(extranonce1_bin), extranonce2, ntime, nonce))
            raise SubmitException("Duplicate share")
        
        # Now let's do the hard work!
        # ---------------------------
        
        # 0. Some sugar
        extranonce2_bin = binascii.unhexlify(extranonce2)
        ntime_bin = binascii.unhexlify(ntime)
        nonce_bin = binascii.unhexlify(nonce)
                
        # 1. Build coinbase
        coinbase_bin = job.serialize_coinbase(extranonce1_bin, extranonce2_bin)
        coinbase_hash = util.doublesha(coinbase_bin)
        
        # 2. Calculate merkle root
        merkle_root_bin = job.merkletree.withFirst(coinbase_hash)
        merkle_root_int = util.uint256_from_str(merkle_root_bin)
                
        # 3. Serialize header with given merkle, ntime and nonce
        header_bin = job.serialize_header(merkle_root_int, ntime_bin, nonce_bin)
    
        # 4. Reverse header and compare it with target of the user
        if settings.COINDAEMON_ALGO == 'scrypt':
            hash_bin = ltc_scrypt.getPoWHash(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ]))
        elif settings.COINDAEMON_ALGO  == 'scrypt-jane':
            hash_bin = yac_scrypt.getPoWHash(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ]), int(ntime, 16))
        elif settings.COINDAEMON_ALGO == 'quark':
            hash_bin = quark_hash.getPoWHash(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ]))
        else:
            hash_bin = util.doublesha(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ]))

        hash_int = util.uint256_from_str(hash_bin)
        scrypt_hash_hex = "%064x" % hash_int
        header_hex = binascii.hexlify(header_bin)
        if settings.COINDAEMON_ALGO == 'scrypt' or settings.COINDAEMON_ALGO == 'scrypt-jane':
            header_hex = header_hex+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
        elif settings.COINDAEMON_ALGO == 'quark':
            header_hex = header_hex+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
        else: pass
                 
        target_user = self.diff_to_target(difficulty)
        if hash_int > target_user:
            raise SubmitException("Share is above target")

        # Mostly for debugging purposes
        target_info = self.diff_to_target(100000)
        if hash_int <= target_info:
            log.info("Yay, share with diff above 100000")

        # Algebra tells us the diff_to_target is the same as hash_to_diff
        share_diff = int(self.diff_to_target(hash_int))

        # 5. Compare hash with target of the network
        if hash_int <= job.target:
            # Yay! It is block candidate! 
            log.info("We found a block candidate! %s" % scrypt_hash_hex)

            # Reverse the header and get the potential block hash (for scrypt only) 
            #if settings.COINDAEMON_ALGO == 'scrypt' or settings.COINDAEMON_ALGO == 'sha256d':
            #   if settings.COINDAEMON_Reward == 'POW':
            block_hash_bin = util.doublesha(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ]))
            block_hash_hex = block_hash_bin[::-1].encode('hex_codec')
            #else:   block_hash_hex = hash_bin[::-1].encode('hex_codec')
            #else:  block_hash_hex = hash_bin[::-1].encode('hex_codec')
            # 6. Finalize and serialize block object 
            job.finalize(merkle_root_int, extranonce1_bin, extranonce2_bin, int(ntime, 16), int(nonce, 16))
            
            if not job.is_valid():
                # Should not happen
                log.exception("FINAL JOB VALIDATION FAILED!(Try enabling/disabling tx messages)")
                            
            # 7. Submit block to the network
            serialized = binascii.hexlify(job.serialize())
            if settings.BLOCK_CHECK_SCRYPT_HASH:
                on_submit = self.bitcoin_rpc.submitblock(serialized, scrypt_hash_hex)
            else:
                on_submit = self.bitcoin_rpc.submitblock(serialized, block_hash_hex)
            if on_submit:
                self.update_block()

            if settings.SOLUTION_BLOCK_HASH:
                return (header_hex, block_hash_hex, share_diff, on_submit)
            else:
                return (header_hex, scrypt_hash_hex, share_diff, on_submit)
        
        if settings.SOLUTION_BLOCK_HASH:
        # Reverse the header and get the potential block hash (for scrypt only) only do this if we want to send in the block hash to the shares table
            block_hash_bin = util.doublesha(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ]))
            block_hash_hex = block_hash_bin[::-1].encode('hex_codec')
            return (header_hex, block_hash_hex, share_diff, None)
        else:
            return (header_hex, scrypt_hash_hex, share_diff, None)
Beispiel #15
0
class TemplateRegistry(object):
    '''Implements the main logic of the pool. Keep track
    on valid block templates, provide internal interface for stratum
    service and implements block validation and submits.'''
    
    def __init__(self, block_template_class, bitcoin_rpc, instance_id,
                 on_template_callback, on_block_callback):
        self.prevhashes = {}
        self.jobs = weakref.WeakValueDictionary()
        
        self.extranonce_counter = ExtranonceCounter(instance_id)
        self.extranonce2_size = block_template_class.coinbase_transaction_class.extranonce_size \
                - self.extranonce_counter.get_size()
                 
        self.coinbasers_value = []

        for address, percent in settings.BITCOIN_ADDRESSES.iteritems():
            coinbaser = SimpleCoinbaser(address, bitcoin_rpc)
            self.coinbasers_value.append((coinbaser, percent))
        self.block_template_class = block_template_class
        self.bitcoin_rpc = bitcoin_rpc
        self.on_block_callback = on_block_callback
        self.on_template_callback = on_template_callback
        
        self.last_block = None
        self.last_update = None
        
        # Create first block template on startup
        # self.update_block()
        
    def get_new_extranonce1(self):
        '''Generates unique extranonce1 (e.g. for newly
        subscribed connection.'''
        return self.extranonce_counter.get_new_bin()
    
    def get_last_broadcast_args(self):
        '''Returns arguments for mining.notify
        from last known template.'''
        return self.last_block.broadcast_args
        
    def add_template(self, block):
        '''Adds new template to the registry.
        It also clean up templates which should
        not be used anymore.'''
        
        prevhash = block.prevhash_hex

        if prevhash in self.prevhashes.keys():
            new_block = False
        else:
            new_block = True
            self.prevhashes[prevhash] = []
               
        # Blocks sorted by prevhash, so it's easy to drop
        # them on blockchain update
        self.prevhashes[prevhash].append(block)
        
        # Weak reference for fast lookup using job_id
        self.jobs[block.job_id] = block
        
        # Use this template for every new request
        self.last_block = block
        
        # Drop templates of obsolete blocks
        for ph in self.prevhashes.keys():
            if ph != prevhash:
                del self.prevhashes[ph]
                
        logger.log('debug', "New template for %s" % prevhash)

        if new_block:
            # Tell the system about new block
            # It is mostly important for share manager
            self.on_block_callback(prevhash)

        else:
            self.on_template_callback(new_block)
        

    def update_block(self):
        '''Registry calls the getblocktemplate() RPC
        and build new block template.'''
        
        self.last_update = time.time()
        
        data = self.bitcoin_rpc.getblocktemplate()
        
        start = time.time()
                
        template = self.block_template_class(self.coinbasers_value, JobIdGenerator.get_new_id())
        template.fill_from_rpc(data)
        self.add_template(template)

        logger.log('debug', "Update finished, %.03f sec, %d txes" % \
                    (time.time() - start, len(template.vtx)))
        
        return data
    
    def diff_to_target(self, difficulty):
        '''Converts difficulty to target'''
        diff1 = 0x00000000ffff0000000000000000000000000000000000000000000000000000 
        return diff1 / difficulty
    
    def get_job(self, job_id):
        '''For given job_id returns BlockTemplate instance or None'''
        try:
            j = self.jobs[job_id]
        except:
            logger.log('bad_client', "Job id '%s' not found, %s" % (job_id, self.jobs.keys()))
            return None
        
        # Now we have to check if job is still valid.
        # Unfortunately weak references are not bulletproof and
        # old reference can be found until next run of garbage collector.
        if j.prevhash_hex not in self.prevhashes:
            logger.log('bad_client', "Prevhash of job '%s' is unknown" % job_id)
            return None
        
        if j not in self.prevhashes[j.prevhash_hex]:
            logger.log('bad_client', "Job %s is unknown" % job_id)
            return None
        
        return j
        
    def submit_share(self, job_id, worker_name, extranonce1_bin, extranonce2, ntime, nonce,
                     difficulty, payment_type):
        '''Check parameters and finalize block template. If it leads
           to valid block candidate, asynchronously submits the block
           back to the bitcoin network.
        
            - extranonce1_bin is binary. No checks performed, it should be from session data
            - job_id, extranonce2, ntime, nonce - in hex form sent by the client
            - difficulty - decimal number from session, again no checks performed
            - submitblock_callback - reference to method which receive result of submitblock()
        '''
        
        # Check if extranonce2 looks correctly. extranonce2 is in hex form...
        if len(extranonce2) != self.extranonce2_size * 2:
            raise SubmitException("Incorrect size of extranonce2. Expected %d chars" % (self.extranonce2_size*2))
        
        # Check for job
        job = self.get_job(job_id)
        if job == None:
            raise SubmitException("Job '%s' not found" % job_id)
                
        # Check if ntime looks correct
        if len(ntime) != 8:
            raise SubmitException("Incorrect size of ntime. Expected 8 chars")

        if not job.check_ntime(int(ntime, 16)):
            raise SubmitException("Ntime out of range")
        
        # Check nonce        
        if len(nonce) != 8:
            raise SubmitException("Incorrect size of nonce. Expected 8 chars")
        
        # Check for duplicated submit
        if not job.register_submit(extranonce1_bin, extranonce2, ntime, nonce):
            logger.log('bad_client', worker_name, "Duplicate from %s, (%s %s %s %s)" % \
                    (worker_name, binascii.hexlify(extranonce1_bin), extranonce2, ntime, nonce))
            raise SubmitException("Duplicate share")
        
        # Now let's do the hard work!
        # ---------------------------
        
        # 0. Some sugar
        extranonce2_bin = binascii.unhexlify(extranonce2)
        ntime_bin = binascii.unhexlify(ntime)
        nonce_bin = binascii.unhexlify(nonce)
                
        # 1. Build coinbase
        coinbase_bin = job.serialize_coinbase(extranonce1_bin, extranonce2_bin)
        coinbase_hash = util.doublesha(coinbase_bin)
        
        # 2. Calculate merkle root
        merkle_root_bin = job.merkletree.withFirst(coinbase_hash)
        merkle_root_int = util.uint256_from_str(merkle_root_bin)
                
        # 3. Serialize header with given merkle, ntime and nonce
        header_bin = job.serialize_header(merkle_root_int, ntime_bin, nonce_bin)
    
        # 4. Reverse header and compare it with target of the user
        hash_bin = util.doublesha(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ]))
        hash_int = util.uint256_from_str(hash_bin)
        block_hash_hex = "%064x" % hash_int
        header_hex = binascii.hexlify(header_bin)
                 
        target_user = self.diff_to_target(difficulty)        

        #logger.log('share', worker_name, 'HASH %064x' % hash_int, 'TARGET %064x' % job.target, 'DIFF %d' % difficulty)

        if hash_int > target_user:
            logger.log('bad_client', 'Share is above target')
            raise SubmitException("Share is above target")

        if payment_type == 'PPS':    
            logger.log('share', worker_name, 'HASH %064x' % hash_int, 'TARGET %064x' % job.target, 'DIFF %d' % difficulty)
            logger.log('pplns', worker_name, 'HASH %064x' % hash_int, 'DIFF %d' % difficulty, 'PAY 0')
        if payment_type == 'PPLNS':
            logger.log('pplns', worker_name, 'HASH %064x' % hash_int, 'DIFF %d' % difficulty, 'PAY 1')

        # 5. Compare hash with target of the network        
        if hash_int <= job.target:
            # Yay! It is block candidate! 
            logger.log('block', worker_name, block_hash_hex)
           
            # 6. Finalize and serialize block object 
            job.finalize(merkle_root_int, extranonce1_bin, extranonce2_bin, int(ntime, 16), int(nonce, 16))
            
            if not job.is_valid():
                # Should not happen
                logger.log('error', "Final job validation failed!")
                            
            # 7. Submit block to the network
            serialized = binascii.hexlify(job.serialize())
            on_submit = self.bitcoin_rpc.submitblock(serialized)
            logger.log('submitblock', serialized)
            return (True, worker_name, block_hash_hex)
        
        return (False, worker_name, block_hash_hex)
Beispiel #16
0
class TemplateRegistry(object):
    '''Implements the main logic of the pool. Keep track
    on valid block templates, provide internal interface for stratum
    service and implements block validation and submits.'''
    def __init__(self, block_template_class, bitcoin_rpc, instance_id,
                 on_template_callback, on_block_callback):
        self.prevhashes = {}
        self.jobs = weakref.WeakValueDictionary()

        self.extranonce_counter = ExtranonceCounter(instance_id)
        self.extranonce2_size = block_template_class.coinbase_transaction_class.extranonce_size \
                - self.extranonce_counter.get_size()

        self.coinbasers_value = []

        for address, percent in settings.BITCOIN_ADDRESSES.iteritems():
            coinbaser = SimpleCoinbaser(address, bitcoin_rpc)
            self.coinbasers_value.append((coinbaser, percent))
        self.block_template_class = block_template_class
        self.bitcoin_rpc = bitcoin_rpc
        self.on_block_callback = on_block_callback
        self.on_template_callback = on_template_callback

        self.last_block = None
        self.last_update = None

        # Create first block template on startup
        # self.update_block()

    def get_new_extranonce1(self):
        '''Generates unique extranonce1 (e.g. for newly
        subscribed connection.'''
        return self.extranonce_counter.get_new_bin()

    def get_last_broadcast_args(self):
        '''Returns arguments for mining.notify
        from last known template.'''
        return self.last_block.broadcast_args

    def add_template(self, block):
        '''Adds new template to the registry.
        It also clean up templates which should
        not be used anymore.'''

        prevhash = block.prevhash_hex

        if prevhash in self.prevhashes.keys():
            new_block = False
        else:
            new_block = True
            self.prevhashes[prevhash] = []

        # Blocks sorted by prevhash, so it's easy to drop
        # them on blockchain update
        self.prevhashes[prevhash].append(block)

        # Weak reference for fast lookup using job_id
        self.jobs[block.job_id] = block

        # Use this template for every new request
        self.last_block = block

        # Drop templates of obsolete blocks
        for ph in self.prevhashes.keys():
            if ph != prevhash:
                del self.prevhashes[ph]

        logger.log('debug', "New template for %s" % prevhash)

        if new_block:
            # Tell the system about new block
            # It is mostly important for share manager
            self.on_block_callback(prevhash)

        else:
            self.on_template_callback(new_block)

    def update_block(self):
        '''Registry calls the getblocktemplate() RPC
        and build new block template.'''

        self.last_update = time.time()

        data = self.bitcoin_rpc.getblocktemplate()

        start = time.time()

        template = self.block_template_class(self.coinbasers_value,
                                             JobIdGenerator.get_new_id())
        template.fill_from_rpc(data)
        self.add_template(template)

        logger.log('debug', "Update finished, %.03f sec, %d txes" % \
                    (time.time() - start, len(template.vtx)))

        return data

    def diff_to_target(self, difficulty):
        '''Converts difficulty to target'''
        diff1 = 0x00000000ffff0000000000000000000000000000000000000000000000000000
        return diff1 / difficulty

    def get_job(self, job_id):
        '''For given job_id returns BlockTemplate instance or None'''
        try:
            j = self.jobs[job_id]
        except:
            logger.log(
                'bad_client',
                "Job id '%s' not found, %s" % (job_id, self.jobs.keys()))
            return None

        # Now we have to check if job is still valid.
        # Unfortunately weak references are not bulletproof and
        # old reference can be found until next run of garbage collector.
        if j.prevhash_hex not in self.prevhashes:
            logger.log('bad_client',
                       "Prevhash of job '%s' is unknown" % job_id)
            return None

        if j not in self.prevhashes[j.prevhash_hex]:
            logger.log('bad_client', "Job %s is unknown" % job_id)
            return None

        return j

    def submit_share(self, job_id, worker_name, extranonce1_bin, extranonce2,
                     ntime, nonce, difficulty, payment_type):
        '''Check parameters and finalize block template. If it leads
           to valid block candidate, asynchronously submits the block
           back to the bitcoin network.
        
            - extranonce1_bin is binary. No checks performed, it should be from session data
            - job_id, extranonce2, ntime, nonce - in hex form sent by the client
            - difficulty - decimal number from session, again no checks performed
            - submitblock_callback - reference to method which receive result of submitblock()
        '''

        # Check if extranonce2 looks correctly. extranonce2 is in hex form...
        if len(extranonce2) != self.extranonce2_size * 2:
            raise SubmitException(
                "Incorrect size of extranonce2. Expected %d chars" %
                (self.extranonce2_size * 2))

        # Check for job
        job = self.get_job(job_id)
        if job == None:
            raise SubmitException("Job '%s' not found" % job_id)

        # Check if ntime looks correct
        if len(ntime) != 8:
            raise SubmitException("Incorrect size of ntime. Expected 8 chars")

        if not job.check_ntime(int(ntime, 16)):
            raise SubmitException("Ntime out of range")

        # Check nonce
        if len(nonce) != 8:
            raise SubmitException("Incorrect size of nonce. Expected 8 chars")

        # Check for duplicated submit
        if not job.register_submit(extranonce1_bin, extranonce2, ntime, nonce):
            logger.log('bad_client', worker_name, "Duplicate from %s, (%s %s %s %s)" % \
                    (worker_name, binascii.hexlify(extranonce1_bin), extranonce2, ntime, nonce))
            raise SubmitException("Duplicate share")

        # Now let's do the hard work!
        # ---------------------------

        # 0. Some sugar
        extranonce2_bin = binascii.unhexlify(extranonce2)
        ntime_bin = binascii.unhexlify(ntime)
        nonce_bin = binascii.unhexlify(nonce)

        # 1. Build coinbase
        coinbase_bin = job.serialize_coinbase(extranonce1_bin, extranonce2_bin)
        coinbase_hash = util.doublesha(coinbase_bin)

        # 2. Calculate merkle root
        merkle_root_bin = job.merkletree.withFirst(coinbase_hash)
        merkle_root_int = util.uint256_from_str(merkle_root_bin)

        # 3. Serialize header with given merkle, ntime and nonce
        header_bin = job.serialize_header(merkle_root_int, ntime_bin,
                                          nonce_bin)

        # 4. Reverse header and compare it with target of the user
        hash_bin = util.doublesha(''.join(
            [header_bin[i * 4:i * 4 + 4][::-1] for i in range(0, 20)]))
        hash_int = util.uint256_from_str(hash_bin)
        block_hash_hex = "%064x" % hash_int
        header_hex = binascii.hexlify(header_bin)

        target_user = self.diff_to_target(difficulty)

        #logger.log('share', worker_name, 'HASH %064x' % hash_int, 'TARGET %064x' % job.target, 'DIFF %d' % difficulty)

        if hash_int > target_user:
            logger.log('bad_client', 'Share is above target')
            raise SubmitException("Share is above target")

        if payment_type == 'PPS':
            logger.log('share', worker_name, 'HASH %064x' % hash_int,
                       'TARGET %064x' % job.target, 'DIFF %d' % difficulty)
            logger.log('pplns', worker_name, 'HASH %064x' % hash_int,
                       'DIFF %d' % difficulty, 'PAY 0')
        if payment_type == 'PPLNS':
            logger.log('pplns', worker_name, 'HASH %064x' % hash_int,
                       'DIFF %d' % difficulty, 'PAY 1')

        # 5. Compare hash with target of the network
        if hash_int <= job.target:
            # Yay! It is block candidate!
            logger.log('block', worker_name, block_hash_hex)

            # 6. Finalize and serialize block object
            job.finalize(merkle_root_int, extranonce1_bin, extranonce2_bin,
                         int(ntime, 16), int(nonce, 16))

            if not job.is_valid():
                # Should not happen
                logger.log('error', "Final job validation failed!")

            # 7. Submit block to the network
            serialized = binascii.hexlify(job.serialize())
            on_submit = self.bitcoin_rpc.submitblock(serialized)
            logger.log('submitblock', serialized)
            return (True, worker_name, block_hash_hex)

        return (False, worker_name, block_hash_hex)
Beispiel #17
0
class TemplateRegistry(object):
    '''Implements the main logic of the pool. Keep track
	on valid block templates, provide internal interface for stratum
	service and implements block validation and submits.'''
    def __init__(self, template_generator, bitcoin_rpc, instance_id,
                 on_template_callback, on_block_callback):
        log.debug("Got to Template Registry")

        self.prevhashes = {}
        self.jobs = weakref.WeakValueDictionary()

        self.template_generator = template_generator

        self.extranonce_counter = ExtranonceCounter(instance_id)
        self.extranonce2_size = template_generator.get_extranonce_size(
        ) - self.extranonce_counter.get_size()

        self.bitcoin_rpc = bitcoin_rpc
        self.on_block_callback = on_block_callback
        self.on_template_callback = on_template_callback

        self.last_template = None
        self.update_in_progress = False
        self.GBT_RPC_ATTEMPT = None
        self.last_block_update_start_time = None

        # Create first block template on startup
        self.update_block()

    def get_new_extranonce1(self):
        '''Generates unique extranonce1 (e.g. for newly
		subscribed connection.'''
        log.debug("Getting Unique Extronance")
        return self.extranonce_counter.get_new_bin()

    def get_last_broadcast_args(self):
        '''Returns arguments for mining.notify
		from last known template.'''
        log.debug("Getting arguments needed for mining.notify")
        return self.last_template.broadcast_args

    def add_template(self, template, block_height):
        '''Adds new template to the registry.
		It also clean up templates which should
		not be used anymore.'''

        prevhash = template.prevhash_hex

        if prevhash in self.prevhashes.keys():
            new_block = False
        else:
            new_block = True
            self.prevhashes[prevhash] = []

        # Blocks sorted by prevhash, so it's easy to drop
        # them on blockchain update
        self.prevhashes[prevhash].append(template)

        # Weak reference for fast lookup using job_id
        self.jobs[template.job_id] = template

        # Use this template for every new request
        self.last_template = template

        # Drop templates of obsolete blocks
        for ph in self.prevhashes.keys():
            if ph != prevhash:
                del self.prevhashes[ph]

        log.info("New template for %s" % prevhash)

        if new_block:
            # Tell the system about new block
            # It is mostly important for share manager
            self.on_block_callback(prevhash, block_height)

        # Everything is ready, let's broadcast jobs!
        self.on_template_callback(new_block)

        #from twisted.internet import reactor
        #reactor.callLater(10, self.on_block_callback, new_block)

    def update_block(self, force=False):
        '''Registry calls the getblocktemplate() RPC
		and build new block template.'''
        log.info("A block update has been requested.")

        if self.update_in_progress and force and not self.GBT_RPC_ATTEMPT is None:
            # Cancel current block update request (if any)
            log.warning("Forcing block update.")
            self.GBT_RPC_ATTEMPT.cancel()
            self.update_in_progress = False

        if self.update_in_progress:
            # Block has been already detected
            log.warning("Block update already in progress.  Started at: %s" %
                        str(self.last_block_update_start_time))
            # It's possible for this process to get 'hung', lets see how long
            running_time = max(
                Interfaces.timestamper.time() -
                self.last_block_update_start_time, 0)
            log.info("Block update running for %i seconds" % running_time)
            # If it's been more than 30 seconds, then cancel it
            # But we don't run in this instance.
            if running_time >= 30:
                log.error(
                    "Block update appears to be hung.  Running for more than %i seconds.  Canceling..."
                    % running_time)
                self.GBT_RPC_ATTEMPT.cancel()
                self.update_in_progress = False

            return

        log.debug("Block update started.")
        self.update_in_progress = True
        self.last_block_update_start_time = Interfaces.timestamper.time()

        # Polls the upstream network daemon for new block template
        # This is done asyncronisly
        self.GBT_RPC_ATTEMPT = self.bitcoin_rpc.getblocktemplate()
        log.debug("Block template request sent")
        self.GBT_RPC_ATTEMPT.addCallback(self._update_block)
        self.GBT_RPC_ATTEMPT.addErrback(self._update_block_failed)

    def _update_block_failed(self, failure):
        # Runs when upstream 'getblocktemplate()' RPC call has failed
        try:
            log.error("Could not load block template: %s" % str(failure))
        except:
            log.error("Could not load block template.")
        finally:
            self.update_in_progress = False

    def _update_block(self, data):
        # Runs when upstream 'getblocktemplate()' RPC call has completed with no error
        log.debug("Block template data recived: Creating new template...")
        # Generate a new template
        template = self.template_generator.new_template(
            Interfaces.timestamper, JobIdGenerator.get_new_id())
        # Apply newly obtained template 'data' as recived from upstream network
        log.debug("Filling RPC Data")
        template.fill_from_rpc(data)
        # Add it the template registry
        log.debug("Adding to Registry")
        self.add_template(template, data['height'])

        # All done
        log.info("Block update finished, %.03f sec, %d txes" %
                 (Interfaces.timestamper.time() -
                  self.last_block_update_start_time, len(template.block.vtx)))
        self.update_in_progress = False
        '''
		TODO: Investigate
		I don't think it is nessesary to return the raw block template data 
		or anything at all from the '_update_block' function since it's called asyncronosly. 

		Leaving it for now.
		'''
        return data

    def diff_to_target(self, difficulty):
        '''Converts difficulty to target'''
        return util.get_diff_target(difficulty)

    def get_job(self, job_id, worker_name, ip=False):
        '''For given job_id returns BlockTemplate instance or None'''
        try:
            j = self.jobs[job_id]
        except:
            log.info("Job id '%s' not found, worker_name: '%s'" %
                     (job_id, worker_name))
            if ip:
                log.debug("Worker submited invalid Job id: IP %s", str(ip))

            return None

        # Now we have to check if job is still valid.
        # Unfortunately weak references are not bulletproof and
        # old reference can be found until next run of garbage collector.
        if j.prevhash_hex not in self.prevhashes:
            log.debug("Prevhash of job '%s' is unknown" % job_id)
            return None

        if j not in self.prevhashes[j.prevhash_hex]:
            log.debug("Job %s is unknown" % job_id)
            return None

        return j

    def submit_share(self,
                     job_id,
                     worker_name,
                     session,
                     extranonce1_bin,
                     extranonce2,
                     ntime,
                     nonce,
                     difficulty,
                     ip=False):
        '''Check parameters and finalize block template. If it leads
			to valid block candidate, asynchronously submits the block
			back to the bitcoin network.

			- extranonce1_bin is binary. No checks performed, it should be from session data
			- job_id, extranonce2, ntime, nonce - in hex form sent by the client
			- difficulty - decimal number from session
			- submitblock_callback - reference to method which receive result of submitblock()
			- difficulty is checked to see if its lower than the vardiff minimum target or pool target
			  from conf/config.py and if it is the share is rejected due to it not meeting the requirements for a share
			  
		'''
        log.debug("Session: %s" % session)

        # Share Difficulty should never be 0 or below
        if difficulty <= 0:
            log.exception(
                "Worker %s @ IP: %s seems to be submitting Fake Shares" %
                (worker_name, ip))
            raise SubmitException(
                "Diff is %s Share Rejected Reporting to Admin" % (difficulty))

        # Check if extranonce2 looks correctly. extranonce2 is in hex form...
        if len(extranonce2) != self.extranonce2_size * 2:
            raise SubmitException(
                "Incorrect size of extranonce2. Expected %d chars" %
                (self.extranonce2_size * 2))

        # Check for job
        job = self.get_job(job_id, worker_name, ip)
        if job == None:
            if settings.REJECT_STALE_SHARES:
                # Reject stale share
                raise SubmitException("Job '%s' not found" % job_id)
            else:
                # Accept stale share but do not continue checking, return a bunch of nothingness
                log.info("Accepted Stale Share from %s, (%s %s %s %s)" % \
                 (worker_name, binascii.hexlify(extranonce1_bin), extranonce2, ntime, nonce))
                return (None, None, None, None, None, None)

        # Check if ntime looks correct
        check_result, error_message = util.check_ntime(ntime)
        if not check_result:
            raise SubmitException(error_message)

        if not job.check_ntime(int(ntime, 16), getattr(settings, 'NTIME_AGE')):
            raise SubmitException("Ntime out of range")

        # Check nonce
        check_result, error_message = util.check_nonce(nonce)
        if not check_result:
            raise SubmitException(error_message)

        # Check for duplicated submit
        if not job.register_submit(extranonce1_bin, extranonce2, ntime, nonce):
            log.info("Duplicate from %s, (%s %s %s %s)" % \
              (worker_name, binascii.hexlify(extranonce1_bin), extranonce2, ntime, nonce))
            raise SubmitException("Duplicate share")

        # Now let's do the hard work!
        # ---------------------------

        # 0. Some sugar
        extranonce2_bin = binascii.unhexlify(extranonce2)
        ntime_bin = util.get_ntime_bin(ntime)
        nonce_bin = util.get_nonce_bin(nonce)
        target_user = self.diff_to_target(difficulty)
        target_info = self.diff_to_target(100000)

        # 1. Build coinbase
        coinbase_bin = job.serialize_coinbase(extranonce1_bin, extranonce2_bin)
        coinbase_hash = util.get_coinbase_hash(coinbase_bin)

        # 2. Calculate merkle root
        merkle_root_bin = job.merkletree.withFirst(coinbase_hash)
        merkle_root_int = util.uint256_from_str(merkle_root_bin)

        # 3. Serialize header with given merkle, ntime and nonce
        header_bin = job.serialize_header(merkle_root_int, ntime_bin,
                                          nonce_bin)

        # 4. Convert header into hex according to hash algorythim
        block_hash = util.get_hash_hex(header_bin, ntime, nonce)

        # 5a Compare it with target of the user
        check_result, message = util.check_header_target(
            block_hash['int'], target_user)
        if not check_result:
            log.debug(
                "Oops, somthing is wrong: target_user=%s, difficulty=%s, share_diff=%s"
                % (target_user, difficulty,
                   int(self.diff_to_target(block_hash['int']))))
            raise SubmitException("%s. Hash: %s" %
                                  (message, block_hash['hex']))

        # Mostly for debugging purposes, just a celebratory message that's being carried over from older versions
        check_result, message = util.check_above_yay_target(
            block_hash['int'], target_info)
        if check_result:
            log.info(message)

        # Algebra tells us the diff_to_target is the same as hash_to_diff
        if settings.VDIFF_FLOAT:
            share_diff = float(self.diff_to_target(block_hash['int']))
        else:
            share_diff = int(self.diff_to_target(block_hash['int']))

        log.debug("share_diff: %s" % share_diff)
        log.debug("job.target: %s" % job.target)
        log.debug("block_hash_int: %s" % block_hash['int'])

        # 6. Compare hash with target of the network
        if util.is_block_candidate(block_hash['int'], job.target):
            # Yay! It is block candidate!
            log.info("We found a block candidate! for %i: %s | %s" %
                     (job.height, block_hash['hex'], block_hash['check_hex']))

            # 7. Finalize and serialize block object
            job.finalize(merkle_root_int, extranonce1_bin, extranonce2_bin,
                         int(ntime, 16), int(nonce, 16))

            if not job.is_valid(difficulty):
                # Should not happen
                log.exception(
                    "FINAL JOB VALIDATION FAILED!(Try enabling/disabling tx messages)"
                )

            # 8. Submit block to the network
            serialized = binascii.hexlify(job.serialize())
            on_submit = self.bitcoin_rpc.submitblock(serialized,
                                                     block_hash['check_hex'],
                                                     block_hash['hex'])

            if on_submit:
                self.update_block()

            return (block_hash['header_hex'], block_hash['solution_hex'],
                    share_diff, job.prevhash_hex, job.height, on_submit)

        # Not a potential Block
        return (block_hash['header_hex'], block_hash['solution_hex'],
                share_diff, job.prevhash_hex, job.height, None)
Beispiel #18
0
class TemplateRegistry(object):
    '''Implements the main logic of the pool. Keep track
    on valid block templates, provide internal interface for stratum
    service and implements block validation and submits.'''
    def __init__(self,
                 block_template_class,
                 coinbaser,
                 bitcoin_rpc,
                 instance_id,
                 on_template_callback,
                 on_block_callback,
                 rootstock_rpc=None):
        self.prevhashes = {}
        self.jobs = weakref.WeakValueDictionary()

        self.extranonce_counter = ExtranonceCounter(instance_id)
        self.extranonce2_size = block_template_class.coinbase_transaction_class.extranonce_size \
                - self.extranonce_counter.get_size()

        self.coinbaser = coinbaser
        self.block_template_class = block_template_class
        self.bitcoin_rpc = bitcoin_rpc
        self.rootstock_rpc = rootstock_rpc
        self.on_block_callback = on_block_callback
        self.on_template_callback = on_template_callback

        self.last_block = None
        self.update_in_progress = False
        self.last_update = None
        self.rsk_last_update = 0
        self.rsk_update_in_progress = False
        self.last_data = dict()
        self.last_rsk_hash = ""

        # Create first block template on startup
        self.update_block()
        if self.rootstock_rpc is not None:
            self.rsk_timeout_counter = 0

    def get_new_extranonce1(self):
        '''Generates unique extranonce1 (e.g. for newly
        subscribed connection.'''
        return self.extranonce_counter.get_new_bin()

    def get_last_broadcast_args(self):
        '''Returns arguments for mining.notify
        from last known template.'''
        return self.last_block.broadcast_args

    def add_template(self, block):
        '''Adds new template to the registry.
        It also clean up templates which should
        not be used anymore.'''

        prevhash = block.prevhash_hex
        if hasattr(block, 'rsk_flag'):
            rsk_is_old_block = self.rootstock_rpc.rsk_parent_hash == self.rootstock_rpc.rsk_last_parent_hash

        call = False
        if hasattr(settings, 'RSK_NOTIFY_POLICY') and hasattr(
                block, 'rsk_flag') and settings.RSK_NOTIFY_POLICY is not 0:
            if settings.RSK_NOTIFY_POLICY == 1:
                if self.rootstock_rpc.rsk_notify:
                    call = True
            if settings.RSK_NOTIFY_POLICY == 2:
                if not rsk_is_old_block:
                    call = True
        else:
            # Everything is ready, let's broadcast jobs!
            call = True
        if not call:
            return

        if prevhash in self.prevhashes.keys():
            new_block = False
        else:
            new_block = True
            self.prevhashes[prevhash] = []

        # Blocks sorted by prevhash, so it's easy to drop
        # them on blockchain update
        self.prevhashes[prevhash].append(block)

        # Weak reference for fast lookup using job_id
        self.jobs[block.job_id] = block

        # Use this template for every new request
        self.last_block = block

        # Drop templates of obsolete blocks
        for ph in self.prevhashes.keys():
            if ph != prevhash:
                del self.prevhashes[ph]

        log.info("New template for %s" % prevhash)

        if new_block:
            # Tell the system about new block
            # It is mostly important for share manager
            self.on_block_callback(prevhash)

        self.on_template_callback(new_block)

        #from twisted.internet import reactor
        #reactor.callLater(10, self.on_block_callback, new_block)

    def _rsk_genheader(self, bhfmm):
        '''
        Helper function for generating the rsk header in the expected format
        '''
        return "RSKBLOCK:" + binascii.unhexlify(bhfmm[2:])

    def _is_rsk_tag_in_coinbase(self, coinbase):

        rsk_tag_header = self._rsk_genheader(
            self.rootstock_rpc.rsk_blockhashformergedmining)

        return binascii.hexlify(rsk_tag_header) in binascii.hexlify(coinbase)

    def _rsk_fill_data(self, data):
        '''
        Helper function for filling out the Bitcoin RPCs RSK data
        '''
        start = Interfaces.timestamper.time()
        logid = util.id_generator()
        self.rootstock_rpc.rsk_notify = data['notify']
        self.rootstock_rpc.rsk_blockhashformergedmining = data[
            'blockHashForMergedMining']
        self.rootstock_rpc.rsk_last_header = self.rootstock_rpc.rsk_header
        self.rootstock_rpc.rsk_miner_fees = data['feesPaidToMiner']
        self.rootstock_rpc.rsk_last_parent_hash = self.rootstock_rpc.rsk_parent_hash
        self.rootstock_rpc.rsk_parent_hash = data['parentBlockHash']
        self.rootstock_rpc.rsk_header = self._rsk_genheader(
            self.rootstock_rpc.rsk_blockhashformergedmining)
        if settings.RSK_DEV_MODE:
            self.rootstock_rpc.rsk_target = int(settings.RSK_DEV_TARGET)
        else:
            self.rootstock_rpc.rsk_target = int(data['target'], 16)

    def update_block(self):
        '''Registry calls the getblocktemplate() RPC
        and build new block template.'''

        if self.update_in_progress:
            # Block has been already detected
            return

        self.update_in_progress = True
        self.last_update = Interfaces.timestamper.time()
        btc_block_received_start = Interfaces.timestamper.time()
        btc_block_received_id = util.id_generator()
        log.info(
            json.dumps({
                "rsk": "[RSKLOG]",
                "tag": "[BTC_BLOCK_RECEIVED_START]",
                "start": btc_block_received_start,
                "elapsed": 0,
                "uuid": btc_block_received_id
            }))
        d = self.bitcoin_rpc.getblocktemplate()
        d.addCallback(self._update_block, btc_block_received_id)
        d.addErrback(self._update_block_failed)

    def _update_block_failed(self, failure):
        log.error(str(failure))
        self.update_in_progress = False

    def _update_block(self, data, id):
        start = Interfaces.timestamper.time()
        self.last_data = data

        template = self.block_template_class(Interfaces.timestamper,
                                             self.coinbaser,
                                             JobIdGenerator.get_new_id())

        data[
            'rsk_header'] = None if self.rootstock_rpc is None else self.rootstock_rpc.rsk_header

        template.fill_from_rpc(data)
        self.add_template(template)

        log.info("Update finished, %.03f sec, %d txes" % \
                    (Interfaces.timestamper.time() - start, len(template.vtx)))
        log.info(
            json.dumps({
                "uuid": id,
                "rsk": "[RSKLOG]",
                "tag": "[BTC_BLOCK_RECEIVED_TEMPLATE]",
                "start": start,
                "elapsed": 0,
                "data": self.last_block.__dict__['broadcast_args']
            }))
        self.update_in_progress = False
        return data

    def rsk_update_block(self):
        try:
            currentTime = Interfaces.timestamper.time()
            if self.rsk_update_in_progress and not (
                    currentTime - self.rsk_last_update > 3):
                return
            self.rsk_last_update = currentTime
            self.rsk_update_in_progress = True
            rsk_block_received_id = util.id_generator()
            log.info(
                json.dumps({
                    "rsk": "[RSKLOG]",
                    "tag": "[RSK_BLOCK_RECEIVED_START]",
                    "start": Interfaces.timestamper.time(),
                    "elapsed": 0,
                    "uuid": rsk_block_received_id
                }))
            rsk = self.rootstock_rpc.getwork()
            rsk.addCallback(self._rsk_getwork, rsk_block_received_id)
            rsk.addErrback(self._rsk_getwork_err)
        except AttributeError as e:
            if "'NoneType' object has no attribute 'getwork'" in str(e):
                pass  #RSK dropped recently so we're letting this pass

    def _rsk_getwork(self, result, id):

        try:
            self._rsk_fill_data(result)
            template = self.block_template_class(Interfaces.timestamper,
                                                 self.coinbaser,
                                                 JobIdGenerator.get_new_id(),
                                                 True)
            self.last_data['rsk_flag'] = True
            if settings.RSK_DEV_MODE:
                self.rootstock_rpc.rsk_target = int(settings.RSK_DEV_TARGET)
            else:
                self.rootstock_rpc.rsk_target = int(result['target'], 16)

            self.last_data['rsk_target'] = self.rootstock_rpc.rsk_target
            self.last_data['rsk_header'] = self.rootstock_rpc.rsk_header
            self.last_data['rsk_notify'] = self.rootstock_rpc.rsk_notify
            template.fill_from_rpc(self.last_data)
            self.add_template(template)
            start = Interfaces.timestamper.time()
            log.info(
                json.dumps({
                    "uuid": id,
                    "rsk": "[RSKLOG]",
                    "tag": "[RSK_BLOCK_RECEIVED_TEMPLATE]",
                    "start": start,
                    "elapsed": 0,
                    "data": self.last_block.__dict__['broadcast_args']
                }))  #job_id

            return self.last_data
        finally:
            self.rsk_update_in_progress = False

    def _rsk_getwork_err(self, err):
        self.rsk_update_in_progress = False
        log.error("_RSK_GETWORK_ERR: " + str(err))
        if "111: Connection refused" in str(err):
            log.info("RSKD Connection refused...")
            if self.rsk_timeout_counter < 3:
                log.info("RSKD Connection refused... trying %d more times",
                         3 - self.rsk_timeout_counter)
                self.rsk_update_block()
                self.rsk_timeout_counter += 1
            else:
                log.info("RSKD Connection refused trying again in %s seconds",
                         settings.RSK_POLL_PERIOD)
                self.rsk_timeout_counter = 0

    def diff_to_target(self, difficulty):
        '''Converts difficulty to target'''
        diff1 = 0x00000000ffff0000000000000000000000000000000000000000000000000000
        return diff1 / difficulty

    def get_job(self, job_id):
        '''For given job_id returns BlockTemplate instance or None'''
        try:
            j = self.jobs[job_id]
        except:
            log.info("Job id '%s' not found" % job_id)
            return None

        # Now we have to check if job is still valid.
        # Unfortunately weak references are not bulletproof and
        # old reference can be found until next run of garbage collector.
        if j.prevhash_hex not in self.prevhashes:
            log.info("Prevhash of job '%s' is unknown" % job_id)
            return None

        if j not in self.prevhashes[j.prevhash_hex]:
            log.info("Job %s is unknown" % job_id)
            return None

        return j

    def submit_share(self, job_id, worker_name, extranonce1_bin, extranonce2,
                     ntime, nonce, difficulty):
        '''Check parameters and finalize block template. If it leads
           to valid block candidate, asynchronously submits the block
           back to the bitcoin network.

            - extranonce1_bin is binary. No checks performed, it should be from session data
            - job_id, extranonce2, ntime, nonce - in hex form sent by the client
            - difficulty - decimal number from session, again no checks performed
            - submitblock_callback - reference to method which receive result of submitblock()
        '''
        global rsk_last_received_share_time
        global rsk_submitted_shares
        start = Interfaces.timestamper.time()
        logid = util.id_generator()
        log.info(
            json.dumps({
                "rsk": "[RSKLOG]",
                "tag": "[SHARE_RECEIVED_START]",
                "uuid": logid,
                "start": start,
                "elapsed": 0
            }))
        # Check if extranonce2 looks correctly. extranonce2 is in hex form...
        if len(extranonce2) != self.extranonce2_size * 2:
            raise SubmitException(
                "Incorrect size of extranonce2. Expected %d chars" %
                (self.extranonce2_size * 2))

        # Check for job
        job = self.get_job(job_id)
        if job == None:
            raise SubmitException("Job '%s' not found" % job_id)

        # Check if ntime looks correct
        if len(ntime) != 8:
            raise SubmitException("Incorrect size of ntime. Expected 8 chars")

        if not job.check_ntime(int(ntime, 16)):
            raise SubmitException("Ntime out of range")

        # Check nonce
        if len(nonce) != 8:
            raise SubmitException("Incorrect size of nonce. Expected 8 chars")

        # Convert from hex to binary
        extranonce2_bin = binascii.unhexlify(extranonce2)
        ntime_bin = binascii.unhexlify(ntime)
        nonce_bin = binascii.unhexlify(nonce)

        # Check for duplicated submit
        if not job.register_submit(extranonce1_bin, extranonce2_bin, ntime_bin,
                                   nonce_bin):
            log.info("Duplicate from %s, (%s %s %s %s)" % \
                    (worker_name, binascii.hexlify(extranonce1_bin), extranonce2, ntime, nonce))
            raise SubmitException("Duplicate share")

        # Now let's do the hard work!
        # ---------------------------

        # 1. Build coinbase
        coinbase_bin = job.serialize_coinbase(extranonce1_bin, extranonce2_bin)
        coinbase_hash = util.doublesha(coinbase_bin)

        # 2. Calculate merkle root
        merkle_root_bin = job.merkletree.withFirst(coinbase_hash)
        merkle_root_int = util.uint256_from_str(merkle_root_bin)

        # 3. Serialize header with given merkle, ntime and nonce
        header_bin = job.serialize_header(merkle_root_int, ntime_bin,
                                          nonce_bin)

        # 4. Reverse header and compare it with target of the user

        # header 80-bytes (19*4 + 4)
        header_le = ''.join(
            [header_bin[i * 4:i * 4 + 4][::-1] for i in range(0, 20)])
        hash_bin = util.doublesha(header_le)

        hash_int = util.uint256_from_str(hash_bin)
        block_hash_hex = "%064x" % hash_int
        header_hex = binascii.hexlify(header_bin)

        log.info(
            json.dumps({
                "rsk": "[RSKLOG]",
                "tag": "[SHARE_RECEIVED_HEX]",
                "uuid": logid,
                "start": Interfaces.timestamper.time(),
                "elapsed": 0,
                "data": block_hash_hex
            }))

        if not settings.RSK_DEV_MODE:
            target_user = self.diff_to_target(difficulty)
            if hash_int > target_user:
                raise SubmitException("Share is above target")

        # Mostly for debugging purposes
        target_info = self.diff_to_target(100000)
        if hash_int <= target_info:
            log.info("Yay, share with diff above 100000")

        # 5. Compare hash with target of the network
        log.info("Hash_Int: %s, Job.Target %s" % (hash_int, job.target))
        btc_solution = hash_int <= job.target
        rsk_solution = False

        if self.rootstock_rpc is not None:
            rsk_solution = hash_int <= self.rootstock_rpc.rsk_target and self._is_rsk_tag_in_coinbase(
                coinbase_bin)

        on_submit_rsk = None
        on_submit = None

        if btc_solution or rsk_solution:
            log.info("We found a block candidate! %s" % block_hash_hex)
            job.finalize(merkle_root_int, extranonce1_bin, extranonce2_bin,
                         int(ntime, 16), int(nonce, 16))

            if btc_solution:
                serialized = binascii.hexlify(job.serialize())
                on_submit = self.bitcoin_rpc.submitblock(serialized)
                log.info(
                    json.dumps({
                        "rsk": "[RSKLOG]",
                        "tag": "[BTC_SUBMITBLOCK]",
                        "uuid": util.id_generator(),
                        "start": start,
                        "elapsed": Interfaces.timestamper.time(),
                        "data": block_hash_hex
                    }))

            if rsk_solution:
                if rsk_last_received_share_time is None:
                    rsk_last_received_share_time = int(round(time() * 1000))
                    rsk_submitted_shares = 0
                last_received_share_time_now = int(round(time() * 1000))
                if last_received_share_time_now - rsk_last_received_share_time >= 1000:
                    rsk_submitted_shares = 0
                    rsk_last_received_share_time = last_received_share_time_now

                if last_received_share_time_now - rsk_last_received_share_time < 1000 and rsk_submitted_shares < 3:
                    rsk_submitted_shares += 1
                else:
                    return (header_hex, block_hash_hex, on_submit,
                            on_submit_rsk)

                serialized = binascii.hexlify(job.serialize())

                block_header_hex = binascii.hexlify(header_le)
                coinbase_hex = binascii.hexlify(coinbase_bin)
                coinbase_hash_hex = binascii.hexlify(coinbase_hash)
                merkle_hashes_array = [
                    binascii.hexlify(x) for x in job.merkletree._steps
                ]
                merkle_hashes_array.insert(0, coinbase_hash_hex)
                merkle_hashes = ' '.join(merkle_hashes_array)
                txn_count = hex(len(merkle_hashes_array))[2:]

                on_submit_rsk = self.rootstock_rpc.submitBitcoinBlockPartialMerkle(
                    block_hash_hex, block_header_hex, coinbase_hex,
                    merkle_hashes, txn_count)

                log.info(
                    json.dumps({
                        "rsk": "[RSKLOG]",
                        "tag": "[RSK_SUBMITBLOCK]",
                        "uuid": util.id_generator(),
                        "start": start,
                        "elapsed": Interfaces.timestamper.time(),
                        "data": block_hash_hex
                    }))

        return (header_hex, block_hash_hex, on_submit, on_submit_rsk)
Beispiel #19
0
class TemplateRegistry(object):
    '''Implements the main logic of the pool. Keep track
    on valid block templates, provide internal interface for stratum
    service and implements block validation and submits.'''
    def __init__(self, block_template_class, coinbaser, bitcoin_rpc, aux_rpc,
                 instance_id, on_template_callback, on_block_callback):
        self.prevhashes = {}
        self.jobs = weakref.WeakValueDictionary()

        self.extranonce_counter = ExtranonceCounter(instance_id)
        self.extranonce2_size = block_template_class.coinbase_transaction_class.extranonce_size \
                - self.extranonce_counter.get_size()
        self.coinbaser = coinbaser
        self.block_template_class = block_template_class
        self.bitcoin_rpc = bitcoin_rpc
        self.on_block_callback = on_block_callback
        self.on_template_callback = on_template_callback

        self.last_block = None
        self.update_in_progress = False
        self.last_update = 0
        self.last_height = None

        self.aux_rpc = aux_rpc
        self.aux_update_in_progress = False
        self.aux_new_block = False
        self.aux_last_update = 0
        self.aux_update_counter = 0
        self.aux_data = []

        # Create first block template on startup
        self.update_auxs()
        #self.update_block()

    def get_new_extranonce1(self):
        '''Generates unique extranonce1 (e.g. for newly
        subscribed connection.'''
        return self.extranonce_counter.get_new_bin()

    def get_last_broadcast_args(self):
        '''Returns arguments for mining.notify
        from last known template.'''
        return self.last_block.broadcast_args

    def add_template(self, block, block_height):
        '''Adds new template to the registry.
        It also clean up templates which should
        not be used anymore.'''

        prevhash = block.prevhash_hex

        if prevhash in self.prevhashes.keys() and not self.aux_new_block:
            new_block = False
        else:
            new_block = True
            self.prevhashes[prevhash] = []
        self.aux_new_block = False
        # Blocks sorted by prevhash, so it's easy to drop
        # them on blockchain update
        self.prevhashes[prevhash].append(block)

        # Weak reference for fast lookup using job_id
        self.jobs[block.job_id] = block

        # Use this template for every new request
        self.last_block = block

        # Drop templates of obsolete blocks
        for ph in self.prevhashes.keys():
            if ph != prevhash:
                del self.prevhashes[ph]

        log.info("New template for %s" % prevhash)

        if new_block:
            # Tell the system about new block
            # It is mostly important for share manager
            self.on_block_callback(block_height)

        # Everything is ready, let's broadcast jobs!
        self.on_template_callback(new_block)

    def update_block(self):
        '''Registry calls the getblocktemplate() RPC
        and build new block template.'''

        if self.update_in_progress:
            # Block has been already detected
            return

        self.update_in_progress = True
        self.last_update = Interfaces.timestamper.time()

        d = self.bitcoin_rpc.getblocktemplate()
        d.addCallback(self._update_block)
        d.addErrback(self._update_block_failed)

    def _update_block_failed(self, failure):
        log.error(str(failure))
        self.update_in_progress = False

    def _update_block(self, data):
        if self.aux_data is None:
            return
        start = Interfaces.timestamper.time()

        template = self.block_template_class(Interfaces.timestamper,
                                             self.coinbaser,
                                             JobIdGenerator.get_new_id())
        template.fill_from_rpc(data, self.aux_data)
        self.last_height = data['height']
        self.add_template(template, data['height'])

        log.info("Update finished, %.03f sec, %d txes" % \
                    (Interfaces.timestamper.time() - start, len(template.vtx)))

        self.update_in_progress = False
        return data

    def update_auxs(self):
        if self.aux_update_in_progress:
            return
        self.aux_update_in_progress = True
        self.aux_last_update = Interfaces.timestamper.time()
        self.aux_data = []
        self.aux_update_counter = 0
        for chain in range(len(self.aux_rpc.conns)):
            aux_block = self.aux_rpc.conns[chain].getauxblock()
            aux_block.addCallback(self._update_auxs)
            aux_block.addErrback(self._update_auxs_failed)

    def _update_auxs(self, data):
        self.aux_data.append(data)
        self.aux_update_counter += 1
        if self.aux_update_counter == len(self.aux_rpc.conns):
            self.aux_update_counter = 0
            self.aux_update_in_progress = False
            self.update_block()
        return data

    def _update_auxs_failed(self, failure):
        log.error(str(failure))
        self.aux_update_in_progress = False
        self.aux_update_counter = 0

    def diff_to_target(self, difficulty):
        '''Converts difficulty to target'''
        if settings.DAEMON_ALGO == 'scrypt':
            diff1 = 0x0000ffff00000000000000000000000000000000000000000000000000000000
        elif settings.DAEMON_ALGO == 'yescrypt':
            diff1 = 0x0000ffff00000000000000000000000000000000000000000000000000000000
        elif settings.DAEMON_ALGO == 'qubit':
            diff1 = 0x000000ffff000000000000000000000000000000000000000000000000000000
        else:
            diff1 = 0x00000000ffff0000000000000000000000000000000000000000000000000000

        return float(diff1) / float(difficulty)

    def get_job(self, job_id):
        '''For given job_id returns BlockTemplate instance or None'''
        try:
            j = self.jobs[job_id]
        except:
            log.info("Job id '%s' not found" % job_id)
            return None

        # Now we have to check if job is still valid.
        # Unfortunately weak references are not bulletproof and
        # old reference can be found until next run of garbage collector.
        if j.prevhash_hex not in self.prevhashes:
            log.info("Prevhash of job '%s' is unknown" % job_id)
            return None

        if j not in self.prevhashes[j.prevhash_hex]:
            log.info("Job %s is unknown" % job_id)
            return None

        return j

    def submit_share(self, job_id, worker_name, session, extranonce1_bin,
                     extranonce2, ntime, nonce, difficulty, ip, submit_time):
        '''Check parameters and finalize block template. If it leads
           to valid block candidate, asynchronously submits the block
           back to the bitcoin network.
        
            - extranonce1_bin is binary. No checks performed, it should be from session data
            - job_id, extranonce2, ntime, nonce - in hex form sent by the client
            - difficulty - decimal number from session, again no checks performed
            - submitblock_callback - reference to method which receive result of submitblock()
        '''

        # Check if extranonce2 looks correctly. extranonce2 is in hex form...
        if len(extranonce2) != self.extranonce2_size * 2:
            raise SubmitException(
                "Incorrect size of extranonce2. Expected %d chars" %
                (self.extranonce2_size * 2))

        # Check for job
        job = self.get_job(job_id)
        if job == None:
            raise SubmitException("Job '%s' not found" % job_id)

        # Check if ntime looks correct
        if len(ntime) != 8:
            raise SubmitException("Incorrect size of ntime. Expected 8 chars")

        if not job.check_ntime(int(ntime, 16)):
            raise SubmitException("Ntime out of range")

        # Check nonce
        if len(nonce) != 8:
            raise SubmitException("Incorrect size of nonce. Expected 8 chars")

        # Check for duplicated submit
        if not job.register_submit(extranonce1_bin, extranonce2, ntime, nonce):
            log.info("Duplicate from %s, (%s %s %s %s)" % \
                    (worker_name, binascii.hexlify(extranonce1_bin), extranonce2, ntime, nonce))
            raise SubmitException("Duplicate share")

        # Now let's do the hard work!
        # ---------------------------

        # 0. Some sugar
        extranonce2_bin = binascii.unhexlify(extranonce2)
        ntime_bin = binascii.unhexlify(ntime)
        nonce_bin = binascii.unhexlify(nonce)

        # 1. Build coinbase
        coinbase_bin = job.serialize_coinbase(extranonce1_bin, extranonce2_bin)
        coinbase_hash = util.doublesha(coinbase_bin)

        # 2. Calculate merkle root
        merkle_root_bin = job.merkletree.withFirst(coinbase_hash)
        merkle_root_int = util.uint256_from_str(merkle_root_bin)

        # 3. Serialize header with given merkle, ntime and nonce
        header_bin = job.serialize_header(merkle_root_int, ntime_bin,
                                          nonce_bin)

        # 4. Reverse header and compare it with target of the user
        if settings.DAEMON_ALGO == 'scrypt':
            hash_bin = ltc_scrypt.getPoWHash(header_bin)
        elif settings.DAEMON_ALGO == 'yescrypt':
            hash_bin = yescrypt_hash.getPoWHash(header_bin)
        elif settings.DAEMON_ALGO == 'qubit':
            hash_bin = qubit_hash.getPoWHash(header_bin)
        else:
            hash_bin = util.doublesha(header_bin)

        hash_int = util.uint256_from_str(hash_bin)
        pow_hash_hex = "%064x" % hash_int
        header_hex = binascii.hexlify(header_bin)

        target_user = self.diff_to_target(difficulty)
        if hash_int > target_user:
            raise SubmitException("Share is above target")

        # Mostly for debugging purposes
        target_info = self.diff_to_target(1000)
        if hash_int <= target_info:
            log.info("Yay, share with diff above 1000")

        # Algebra tells us the diff_to_target is the same as hash_to_diff
        share_diff = float(self.diff_to_target(hash_int))

        on_submit = None
        aux_submit = None

        block_hash_bin = util.doublesha(header_bin)
        block_hash_hex = block_hash_bin[::-1].encode('hex_codec')

        if hash_int <= job.target:
            log.info(
                "MAINNET BLOCK CANDIDATE! %s diff(%f/%f)" %
                (block_hash_hex, share_diff, self.diff_to_target(job.target)))
            job.finalize(merkle_root_int, extranonce1_bin, extranonce2_bin,
                         int(ntime, 16), int(nonce, 16))

            if not job.is_valid():
                log.exception("FINAL JOB VALIDATION FAILED!")

            serialized = binascii.hexlify(job.serialize())
            if settings.SOLUTION_BLOCK_HASH:
                on_submit = self.bitcoin_rpc.submitblock(
                    serialized, block_hash_hex)
            else:
                on_submit = self.bitcoin_rpc.submitblock(
                    serialized, pow_hash_hex)
            '''if on_submit:
                self.update_block()'''

        # Check auxiliary merged chains
        for chain in range(len(job.auxs)):
            if hash_int <= job.aux_targets[chain]:
                log.info("FOUND MERGED BLOCK! %s diff(%f/%f)" %
                         (job.auxs[chain]['hash'], share_diff,
                          self.diff_to_target(job.aux_targets[chain])))
                coinbase_hex = binascii.hexlify(coinbase_bin)
                branch_count = job.merkletree.branchCount()
                branch_hex = job.merkletree.branchHex()
                merkle_link = util.calculate_merkle_link(
                    job.merkle_hashes, job.tree[job.auxs[chain]['chainid']])
                submission = coinbase_hex + block_hash_hex + branch_count + branch_hex + '00000000' + merkle_link + header_hex
                aux_submit = self.aux_rpc.conns[chain].getauxblock(
                    job.auxs[chain]['hash'], submission)
                aux_submit.addCallback(
                    Interfaces.share_manager.on_submit_aux_block, worker_name,
                    header_hex, job.auxs[chain]['hash'], submit_time, ip,
                    share_diff)
                '''if aux_submit:
                    self.update_auxs()'''

        if settings.SOLUTION_BLOCK_HASH:
            return (header_hex, block_hash_hex, share_diff, on_submit)
        else:
            return (header_hex, pow_hash_hex, share_diff, on_submit)
class TemplateRegistry(object):
    '''Implements the main logic of the pool. Keep track
    on valid block templates, provide internal interface for stratum
    service and implements block validation and submits.'''

    def __init__(self, block_template_class, coinbaser, bitcoin_rpc, instance_id,
                 on_template_callback, on_block_callback):
        self.prevhashes = {}
        self.jobs = weakref.WeakValueDictionary()

        self.extranonce_counter = ExtranonceCounter(instance_id)
        self.extranonce2_size = block_template_class.coinbase_transaction_class.extranonce_size \
                - self.extranonce_counter.get_size()

        self.coinbaser = coinbaser
        self.block_template_class = block_template_class
        self.bitcoin_rpc = bitcoin_rpc
        self.on_block_callback = on_block_callback
        self.on_template_callback = on_template_callback

        self.last_block = None
        self.update_in_progress = False
        self.last_update = None

        # Create first block template on startup
        self.update_block()

    def get_new_extranonce1(self):
        '''Generates unique extranonce1 (e.g. for newly
        subscribed connection.'''
        return self.extranonce_counter.get_new_bin()

    def get_last_broadcast_args(self):
        '''Returns arguments for mining.notify
        from last known template.'''
        return self.last_block.broadcast_args

    def add_template(self, block):
        '''Adds new template to the registry.
        It also clean up templates which should
        not be used anymore.'''

        prevhash = block.prevhash_hex

        if prevhash in self.prevhashes.keys():
            new_block = False
        else:
            new_block = True
            self.prevhashes[prevhash] = []

        # Blocks sorted by prevhash, so it's easy to drop
        # them on blockchain update
        self.prevhashes[prevhash].append(block)

        # Weak reference for fast lookup using job_id
        self.jobs[block.job_id] = block

        # Use this template for every new request
        self.last_block = block

        # Drop templates of obsolete blocks
        for ph in self.prevhashes.keys():
            if ph != prevhash:
                del self.prevhashes[ph]

        log.info("New template for %s" % prevhash)

        if new_block:
            # Tell the system about new block
            # It is mostly important for share manager
            self.on_block_callback(prevhash)

        # Everything is ready, let's broadcast jobs!
        self.on_template_callback(new_block)


        #from twisted.internet import reactor
        #reactor.callLater(10, self.on_block_callback, new_block)

    def update_block(self):
        '''Registry calls the getblocktemplate() RPC
        and build new block template.'''

        if self.update_in_progress:
            # Block has been already detected
            return

        self.update_in_progress = True
        self.last_update = Interfaces.timestamper.time()

        d = self.bitcoin_rpc.getblocktemplate()
        d.addCallback(self._update_block)
        d.addErrback(self._update_block_failed)

    def _update_block_failed(self, failure):
        log.error(str(failure))
        self.update_in_progress = False

    def _update_block(self, data):
        start = Interfaces.timestamper.time()

        template = self.block_template_class(Interfaces.timestamper, self.coinbaser, JobIdGenerator.get_new_id())
        template.fill_from_rpc(data)
        self.add_template(template)

        log.info("Update finished, %.03f sec, %d txes" % \
                    (Interfaces.timestamper.time() - start, len(template.vtx)))

        self.update_in_progress = False
        return data

    def diff_to_target(self, difficulty):
        '''Converts difficulty to target'''
        diff1 = 0x00000000ffff0000000000000000000000000000000000000000000000000000
        return diff1 / difficulty

    def get_job(self, job_id):
        '''For given job_id returns BlockTemplate instance or None'''
        try:
            j = self.jobs[job_id]
        except:
            log.info("Job id '%s' not found" % job_id)
            return None

        # Now we have to check if job is still valid.
        # Unfortunately weak references are not bulletproof and
        # old reference can be found until next run of garbage collector.
        if j.prevhash_hex not in self.prevhashes:
            log.info("Prevhash of job '%s' is unknown" % job_id)
            return None

        if j not in self.prevhashes[j.prevhash_hex]:
            log.info("Job %s is unknown" % job_id)
            return None

        return j

    def submit_share(self, job_id, worker_name, extranonce1_bin, extranonce2, ntime, nonce,
                     difficulty, address=None):
        '''Check parameters and finalize block template. If it leads
           to valid block candidate, asynchronously submits the block
           back to the bitcoin network.

            - extranonce1_bin is binary. No checks performed, it should be from session data
            - job_id, extranonce2, ntime, nonce - in hex form sent by the client
            - difficulty - decimal number from session, again no checks performed
            - submitblock_callback - reference to method which receive result of submitblock()
        '''

        # Check if extranonce2 looks correctly. extranonce2 is in hex form...
        if len(extranonce2) != self.extranonce2_size * 2:
            raise SubmitException("Incorrect size of extranonce2. Expected %d chars" % (self.extranonce2_size*2))

        # Check for job
        job = self.get_job(job_id)
        if job == None:
            raise SubmitException("Job '%s' not found" % job_id)

        # Check if ntime looks correct
        if len(ntime) != 8:
            raise SubmitException("Incorrect size of ntime. Expected 8 chars")

        if not job.check_ntime(int(ntime, 16)):
            raise SubmitException("Ntime out of range")

        # Check nonce
        if len(nonce) != 8:
            raise SubmitException("Incorrect size of nonce. Expected 8 chars")

        # Convert from hex to binary
        extranonce2_bin = binascii.unhexlify(extranonce2)
        ntime_bin = binascii.unhexlify(ntime)
        nonce_bin = binascii.unhexlify(nonce)

        # Check for duplicated submit
        if not job.register_submit(extranonce1_bin, extranonce2_bin, ntime_bin, nonce_bin):
            log.info("Duplicate from %s, (%s %s %s %s)" % \
                    (worker_name, binascii.hexlify(extranonce1_bin), extranonce2, ntime, nonce))
            raise SubmitException("Duplicate share")

        # Now let's do the hard work!
        # ---------------------------

        # 0. Make a copy of the job if this is a submitfull()
        #    and change the coinbase address. We make a copy first
        #    because we don't want to ruin this job for regular
        #    submit() users

        if address:
            oldjob = job
            job = copy.deepcopy(job)
            job.vtx[0].address = address
            # we know it's not our own address, and we don't care
            job.vtx[0].is_valid = True

        # 1. Build coinbase
        coinbase_bin = job.serialize_coinbase(extranonce1_bin, extranonce2_bin)
        coinbase_hash = util.doublesha(coinbase_bin)

        # 2. Calculate merkle root
        merkle_root_bin = job.merkletree.withFirst(coinbase_hash)
        merkle_root_int = util.uint256_from_str(merkle_root_bin)

        # 3. Serialize header with given merkle, ntime and nonce
        header_bin = job.serialize_header(merkle_root_int, ntime_bin, nonce_bin)

        # 4. Reverse header and compare it with target of the user
        hash_bin = util.doublesha(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ]))
        hash_int = util.uint256_from_str(hash_bin)
        block_hash_hex = "%064x" % hash_int
        header_hex = binascii.hexlify(header_bin)

        target_user = self.diff_to_target(difficulty)
        if hash_int > target_user:
            raise SubmitException("Share is above target")

        # Mostly for debugging purposes
        target_info = self.diff_to_target(100000)
        if hash_int <= target_info:
            log.info("Yay, share with diff above 100000")

        # 5. Compare hash with target of the network
        if hash_int <= job.target:
            # Yay! It is block candidate!
            log.info("We found a block candidate! %s" % block_hash_hex)

            # 6. Finalize and serialize block object
            job.finalize(merkle_root_int, extranonce1_bin, extranonce2_bin, int(ntime, 16), int(nonce, 16))

            if not job.is_valid():
                # Should not happen
                log.error("Final job validation failed!")

            # 7. Submit block to the network
            serialized = binascii.hexlify(job.serialize())
            on_submit = self.bitcoin_rpc.submitblock(serialized)

            return (header_hex, block_hash_hex, on_submit)

        return (header_hex, block_hash_hex, None)
Beispiel #21
0
class TemplateRegistry(object):
	'''Implements the main logic of the pool. Keep track
	on valid block templates, provide internal interface for stratum
	service and implements block validation and submits.'''

	def __init__(self, template_generator, bitcoin_rpc, instance_id, on_template_callback, on_block_callback):
		log.debug("Got to Template Registry")

		self.prevhashes = {}
		self.jobs = weakref.WeakValueDictionary()

		self.template_generator = template_generator

		self.extranonce_counter = ExtranonceCounter(instance_id)
		self.extranonce2_size = template_generator.get_extranonce_size() - self.extranonce_counter.get_size()

		self.bitcoin_rpc = bitcoin_rpc
		self.on_block_callback = on_block_callback
		self.on_template_callback = on_template_callback

		self.last_template = None
		self.update_in_progress = False
		self.GBT_RPC_ATTEMPT = None
		self.last_block_update_start_time = None

		# Create first block template on startup
		self.update_block()

	def get_new_extranonce1(self):
		'''Generates unique extranonce1 (e.g. for newly
		subscribed connection.'''
		log.debug("Getting Unique Extronance")
		return self.extranonce_counter.get_new_bin()

	def get_last_broadcast_args(self):
		'''Returns arguments for mining.notify
		from last known template.'''
		log.debug("Getting arguments needed for mining.notify")
		return self.last_template.broadcast_args

	def add_template(self, template, block_height):
		'''Adds new template to the registry.
		It also clean up templates which should
		not be used anymore.'''

		prevhash = template.prevhash_hex

		if prevhash in self.prevhashes.keys():
			new_block = False
		else:
			new_block = True
			self.prevhashes[prevhash] = []

		# Blocks sorted by prevhash, so it's easy to drop
		# them on blockchain update
		self.prevhashes[prevhash].append(template)

		# Weak reference for fast lookup using job_id
		self.jobs[template.job_id] = template

		# Use this template for every new request
		self.last_template = template

		# Drop templates of obsolete blocks
		for ph in self.prevhashes.keys():
			if ph != prevhash:
				del self.prevhashes[ph]

		log.info("New template for %s" % prevhash)

		if new_block:
			# Tell the system about new block
			# It is mostly important for share manager
			self.on_block_callback(prevhash, block_height)

		# Everything is ready, let's broadcast jobs!
		self.on_template_callback(new_block)

		#from twisted.internet import reactor
		#reactor.callLater(10, self.on_block_callback, new_block) 

	def update_block(self, force = False):
		'''Registry calls the getblocktemplate() RPC
		and build new block template.'''
		log.info("A block update has been requested.")

		if self.update_in_progress and force and not self.GBT_RPC_ATTEMPT is None:
			# Cancel current block update request (if any)
			log.warning("Forcing block update.")
			self.GBT_RPC_ATTEMPT.cancel()
			self.update_in_progress = False

		if self.update_in_progress:
			# Block has been already detected
			log.warning("Block update already in progress.  Started at: %s" % str(self.last_block_update_start_time))
			# It's possible for this process to get 'hung', lets see how long
			running_time = max(Interfaces.timestamper.time() - self.last_block_update_start_time , 0)
			log.info("Block update running for %i seconds" % running_time)
			# If it's been more than 30 seconds, then cancel it
			# But we don't run in this instance.
			if running_time >= 30:
				log.error("Block update appears to be hung.  Running for more than %i seconds.  Canceling..." % running_time)
				self.GBT_RPC_ATTEMPT.cancel()
				self.update_in_progress = False

			return

		log.debug("Block update started.")
		self.update_in_progress = True
		self.last_block_update_start_time = Interfaces.timestamper.time()

		# Polls the upstream network daemon for new block template
		# This is done asyncronisly
		self.GBT_RPC_ATTEMPT = self.bitcoin_rpc.getblocktemplate()
		log.debug("Block template request sent")
		self.GBT_RPC_ATTEMPT.addCallback(self._update_block)
		self.GBT_RPC_ATTEMPT.addErrback(self._update_block_failed)

	def _update_block_failed(self, failure):
		# Runs when upstream 'getblocktemplate()' RPC call has failed
		try:
			log.error("Could not load block template: %s" % str(failure))
		except:
			log.error("Could not load block template.")
		finally:
			self.update_in_progress = False

	def _update_block(self, data):
		# Runs when upstream 'getblocktemplate()' RPC call has completed with no error
		log.debug("Block template data recived: Creating new template...")
		# Generate a new template
		template = self.template_generator.new_template(Interfaces.timestamper, JobIdGenerator.get_new_id())
		# Apply newly obtained template 'data' as recived from upstream network
		log.debug("Filling RPC Data")
		template.fill_from_rpc(data)
		# Add it the template registry
		log.debug("Adding to Registry")
		self.add_template(template, data['height'])

		# All done
		log.info("Block update finished, %.03f sec, %d txes" % (Interfaces.timestamper.time() - self.last_block_update_start_time, len(template.block.vtx)))
		self.update_in_progress = False

		'''
		TODO: Investigate
		I don't think it is nessesary to return the raw block template data 
		or anything at all from the '_update_block' function since it's called asyncronosly. 

		Leaving it for now.
		'''
		return data

	def diff_to_target(self, difficulty):
		'''Converts difficulty to target'''
		return util.get_diff_target(difficulty);

	def get_job(self, job_id, worker_name, ip=False):
		'''For given job_id returns BlockTemplate instance or None'''
		try:
			j = self.jobs[job_id]
		except:
			log.info("Job id '%s' not found, worker_name: '%s'" % (job_id, worker_name))
			if ip:
				log.debug("Worker submited invalid Job id: IP %s", str(ip))

			return None

		# Now we have to check if job is still valid.
		# Unfortunately weak references are not bulletproof and
		# old reference can be found until next run of garbage collector.
		if j.prevhash_hex not in self.prevhashes:
			log.debug("Prevhash of job '%s' is unknown" % job_id)
			return None

		if j not in self.prevhashes[j.prevhash_hex]:
			log.debug("Job %s is unknown" % job_id)
			return None

		return j

	def submit_share(self, job_id, worker_name, session, extranonce1_bin, extranonce2, ntime, nonce, difficulty, ip=False):
		'''Check parameters and finalize block template. If it leads
			to valid block candidate, asynchronously submits the block
			back to the bitcoin network.

			- extranonce1_bin is binary. No checks performed, it should be from session data
			- job_id, extranonce2, ntime, nonce - in hex form sent by the client
			- difficulty - decimal number from session
			- submitblock_callback - reference to method which receive result of submitblock()
			- difficulty is checked to see if its lower than the vardiff minimum target or pool target
			  from conf/config.py and if it is the share is rejected due to it not meeting the requirements for a share
			  
		'''
		log.debug("Session: %s" % session)

		# Share Difficulty should never be 0 or below
		if difficulty <= 0 :
			log.exception("Worker %s @ IP: %s seems to be submitting Fake Shares"%(worker_name,ip))
			raise SubmitException("Diff is %s Share Rejected Reporting to Admin"%(difficulty))

		# Check if extranonce2 looks correctly. extranonce2 is in hex form...
		if len(extranonce2) != self.extranonce2_size * 2:
			raise SubmitException("Incorrect size of extranonce2. Expected %d chars" % (self.extranonce2_size*2))

		# Check for job
		job = self.get_job(job_id, worker_name, ip)
		if job == None:
			if settings.REJECT_STALE_SHARES:
				# Reject stale share
				raise SubmitException("Job '%s' not found" % job_id)
			else:
				# Accept stale share but do not continue checking, return a bunch of nothingness
				log.info("Accepted Stale Share from %s, (%s %s %s %s)" % \
					(worker_name, binascii.hexlify(extranonce1_bin), extranonce2, ntime, nonce))
				return (None, None, None, None, None, None)

		# Check if ntime looks correct
		check_result, error_message = util.check_ntime(ntime)
		if not check_result:
			raise SubmitException(error_message)

		if not job.check_ntime(int(ntime, 16), getattr(settings, 'NTIME_AGE')):
			raise SubmitException("Ntime out of range")

		# Check nonce
		check_result, error_message = util.check_nonce(nonce)
		if not check_result:
			raise SubmitException(error_message)

		# Check for duplicated submit
		if not job.register_submit(extranonce1_bin, extranonce2, ntime, nonce):
			log.info("Duplicate from %s, (%s %s %s %s)" % \
					(worker_name, binascii.hexlify(extranonce1_bin), extranonce2, ntime, nonce))
			raise SubmitException("Duplicate share")

		# Now let's do the hard work!
		# ---------------------------

		# 0. Some sugar
		extranonce2_bin = binascii.unhexlify(extranonce2)
		ntime_bin = util.get_ntime_bin(ntime)
		nonce_bin = util.get_nonce_bin(nonce)
		target_user = self.diff_to_target(difficulty)
		target_info = self.diff_to_target(100000)

		# 1. Build coinbase
		coinbase_bin = job.serialize_coinbase(extranonce1_bin, extranonce2_bin)
		coinbase_hash = util.get_coinbase_hash(coinbase_bin)

		# 2. Calculate merkle root
		merkle_root_bin = job.merkletree.withFirst(coinbase_hash)
		merkle_root_int = util.uint256_from_str(merkle_root_bin)

		# 3. Serialize header with given merkle, ntime and nonce
		header_bin = job.serialize_header(merkle_root_int, ntime_bin, nonce_bin)

		# 4. Convert header into hex according to hash algorythim
		block_hash = util.get_hash_hex(header_bin, ntime, nonce)

		# 5a Compare it with target of the user
		check_result, message = util.check_header_target(block_hash['int'], target_user)
		if not check_result:
			log.debug("Oops, somthing is wrong: target_user=%s, difficulty=%s, share_diff=%s" % (target_user, difficulty, int(self.diff_to_target(block_hash['int']))))
			raise SubmitException("%s. Hash: %s" % (message, block_hash['hex']))

		# Mostly for debugging purposes, just a celebratory message that's being carried over from older versions
		check_result, message = util.check_above_yay_target(block_hash['int'], target_info)
		if check_result:
			log.info(message)

		# Algebra tells us the diff_to_target is the same as hash_to_diff
		if settings.VDIFF_FLOAT:
			share_diff = float(self.diff_to_target(block_hash['int']))
		else:
			share_diff = int(self.diff_to_target(block_hash['int']))

		log.debug("share_diff: %s" % share_diff)
		log.debug("job.target: %s" % job.target)
		log.debug("block_hash_int: %s" % block_hash['int'])

		# 6. Compare hash with target of the network
		if util.is_block_candidate(block_hash['int'], job.target):
			# Yay! It is block candidate!
			log.info("We found a block candidate! for %i: %s | %s" % (job.height, block_hash['hex'], block_hash['check_hex']))

			# 7. Finalize and serialize block object 
			job.finalize(merkle_root_int, extranonce1_bin, extranonce2_bin, int(ntime, 16), int(nonce, 16))

			if not job.is_valid(difficulty):
				# Should not happen
				log.exception("FINAL JOB VALIDATION FAILED!(Try enabling/disabling tx messages)")

			# 8. Submit block to the network
			serialized = binascii.hexlify(job.serialize())
			on_submit = self.bitcoin_rpc.submitblock(serialized, block_hash['check_hex'], block_hash['hex'])

			if on_submit:
				self.update_block()

			return (block_hash['header_hex'], block_hash['solution_hex'], share_diff, job.prevhash_hex, job.height, on_submit)

		# Not a potential Block
		return (block_hash['header_hex'], block_hash['solution_hex'], share_diff, job.prevhash_hex, job.height, None)
class TemplateRegistry(object):
    '''Implements the main logic of the pool. Keep track
    on valid block templates, provide internal interface for stratum
    service and implements block validation and submits.'''

    def __init__(self, block_template_class, coinbaser, bitcoin_rpc, instance_id,
                 on_template_callback, on_block_callback):
        self.current_prevhash = ''
        self.jobs = weakref.WeakValueDictionary()
        self.minimal_job_id = 0

        self.extranonce_counter = ExtranonceCounter(instance_id)
        self.extranonce2_size = block_template_class.coinbase_transaction_class.extranonce_size \
                - self.extranonce_counter.get_size()

        self.coinbaser = coinbaser
        self.block_template_class = block_template_class
        self.bitcoin_rpc = bitcoin_rpc
        self.on_block_callback = on_block_callback
        self.on_template_callback = on_template_callback

        self._last_template = None
        self.update_in_progress = False
        self.last_update = None

        # Create first block template on startup
        self.update_block()


    def get_block_min_job_id(self):
        return self.minimal_job_id


    def get_new_extranonce1(self):
        '''Generates unique extranonce1 (e.g. for newly
        subscribed connection.'''
        return self.extranonce_counter.get_new_bin()


    def get_last_template_broadcast_args(self):
        '''Returns arguments for mining.notify
        from last known template.'''
        return self._last_template.broadcast_args


    def get_last_template(self):
        ''' Returns the last known template. '''
        return self._last_template


    def _add_template(self, template):
        '''Adds new template to the registry.
        It also clean up templates which should
        not be used anymore.'''

        prevhash = template.prevhash_hex

        # Did we just detect a new block?
        new_block = (prevhash != self.current_prevhash)
        if new_block:
            # Update the current prevhash and throw away all previous jobs (templates)
            self.current_prevhash = prevhash
            self.jobs = {template.job_id: template}

            # Remember the first job's id for the new block. All next templates must have
            # higher job_id in order to be valid.
            self.minimal_job_id = template.job_id

            # Tell the system about new template
            # It is mostly important for share manager
            self.on_block_callback(prevhash)
        else:
            # Check if the new job (template) has valid job_id (related to the first job for the block)
            if template.job_id < self.minimal_job_id:
                log.error("New template has invalid job_id (%s) - minimal %s" % \
                          (template.job_id, self.minimal_job_id))
                return
            # Remember the job for the current block (prevhash)
            self.jobs[template.job_id] = template

        # Use this template for every new request
        self._last_template = template

        log.info("New template %x (cnt %d) for %s" % \
                 (template.job_id, len(self.jobs), prevhash))

        # Everything is ready, let's broadcast jobs!
        self.on_template_callback(new_block)


    def update_blank_block(self, prevhash):
        '''Pick current block, replaces it's prevhash and broadcast
        it as a new template to client. This is work-around for slow
        processing of blocks in bitcoind.'''

        start = posix_time()

        template = self.block_template_class(Interfaces.timestamper, self.coinbaser, JobIdGenerator.get_new_id())
        template.fill_from_another(self._last_template, prevhash)
        self._add_template(template)

        log.info("Blank block update finished, %.03f sec, %d txes" % \
                    (posix_time() - start, len(template.vtx)))

        # Now let's do standard update (with transactions)
        self.update_block()


    def update_block(self):
        '''Registry calls the getblocktemplate() RPC
        and build new block template.'''

        if self.update_in_progress:
            # Block has been already detected
            return

        self.update_in_progress = True
        self.last_update = posix_time()

        d = self.bitcoin_rpc.getblocktemplate()
        d.addCallback(self._update_block)
        d.addErrback(self._update_block_failed)


    def _update_block_failed(self, failure):
        self.update_in_progress = False
        log.error(str(failure))


    def _update_block(self, bitcoind_rpc_template):
        try:
            if isinstance(bitcoind_rpc_template, dict):
                start = posix_time()

                template = self.block_template_class(Interfaces.timestamper, self.coinbaser, JobIdGenerator.get_new_id())
                template.fill_from_rpc(bitcoind_rpc_template)
                self._add_template(template)

                log.info("Update finished, %.03f sec, %d txes, %s BTC" % \
                         (posix_time() - start, len(template.vtx), Decimal(template.get_value())/10**8)) #@UndefinedVariable
            else:
                log.error("Invalid data for block update: %s" % (bitcoind_rpc_template, ))
        finally:
            self.update_in_progress = False
        return bitcoind_rpc_template



    def diff_to_target(self, difficulty):
        '''Converts difficulty to target'''
        diff1 = 0x00000000ffff0000000000000000000000000000000000000000000000000000
        return diff1 / difficulty


    def get_job(self, job_id):
        '''For given job_id returns BlockTemplate instance or None'''
        try:
            return self.jobs[job_id]
        except:
            log.error("Job id '%s' not found" % job_id)
            return None


    def submit_share(self, job_id, worker_name, extranonce1_bin, extranonce2, ntime, nonce,
                     difficulty):
        '''Check parameters and finalize block template. If it leads
           to valid block candidate, asynchronously submits the block
           back to the bitcoin network.

            - extranonce1_bin is binary. No checks performed, it should be from session data
            - job_id, extranonce2, ntime, nonce - in hex form sent by the client
            - difficulty - decimal number from session, again no checks performed
            - submitblock_callback - reference to method which receive result of submitblock()
        '''

        # Check if extranonce2 looks correctly. extranonce2 is in hex form...
        if len(extranonce2) != self.extranonce2_size * 2:
            raise SubmitException("Incorrect size of extranonce2. Expected %d chars" % (self.extranonce2_size*2))

        # Check for job
        job = self.get_job(job_id)
        if job == None:
            raise SubmitException("Job '%s' not found" % job_id)

        # Check if ntime looks correct
        if len(ntime) != 8:
            raise SubmitException("Incorrect size of ntime. Expected 8 chars")

        if not job.check_ntime(int(ntime, 16)):
            raise SubmitException("Ntime out of range")

        # Check nonce
        if len(nonce) != 8:
            raise SubmitException("Incorrect size of nonce. Expected 8 chars")

        # Check for duplicated submit
        if not job.register_submit(extranonce1_bin, extranonce2, ntime, nonce):
            log.info("Duplicate from %s, (%s %s %s %s)" % \
                    (worker_name, binascii.hexlify(extranonce1_bin), extranonce2, ntime, nonce))
            raise SubmitException("Duplicate share")

        # Now let's do the hard work!
        # ---------------------------
        # 0. Some sugar
        extranonce2_bin = binascii.unhexlify(extranonce2)
        ntime_bin = binascii.unhexlify(ntime)
        nonce_bin = binascii.unhexlify(nonce)

        # 1. Build coinbase
        coinbase_bin = job.serialize_coinbase(extranonce1_bin, extranonce2_bin)
        coinbase_hash = util.doublesha(coinbase_bin)

        # 2. Calculate merkle root
        merkle_root_bin = job.merkletree.withFirst(coinbase_hash)
        merkle_root_int = util.uint256_from_str(merkle_root_bin)

        # 3. Serialize header with given merkle, ntime and nonce
        header_bin = job.serialize_header(merkle_root_int, ntime_bin, nonce_bin)

        # 4. Reverse header and compare it with target of the user
        hash_bin = util.doublesha(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ]))
        hash_int = util.uint256_from_str(hash_bin)
        block_hash_hex = "%064x" % hash_int
        header_hex = binascii.hexlify(header_bin)

        target_user = self.diff_to_target(difficulty)
        if hash_int > target_user:
            # For testing purposes ONLY
            if not config.ACCEPT_SHARES_ABOVE_TARGET:
                raise SubmitException("Share is above target")

        # 5. Compare hash with target of the network
        if hash_int <= job.target or \
                (nonce == config.ACCEPT_INVALID_BLOCK__NONCE):
            # Yay! It is block candidate!
            log.info("We found a block candidate! %s" % block_hash_hex)

            # 6. Finalize and serialize block object
            job.finalize(merkle_root_int, extranonce1_bin, extranonce2_bin, int(ntime, 16), int(nonce, 16))

            if not job.is_valid():
                # Should not happen
                log.error("Final job validation failed!")

            # 7. Get block value for statistical purposes
            block_value = job.get_value()

            # 8. Submit block to the network
            serialized = binascii.hexlify(job.serialize())
            on_submit = self.bitcoin_rpc.submitblock(serialized)

            return (header_hex, block_hash_hex, block_value, on_submit)

        return (header_hex, block_hash_hex, None, None)
class TemplateRegistry(object):
    '''Implements the main logic of the pool. Keep track
    on valid block templates, provide internal interface for stratum
    service and implements block validation and submits.'''
    
    def __init__(self, block_template_class, coinbaser, bitcoin_rpc, aux_rpc, instance_id,
                 on_template_callback, on_block_callback):
        self.prevhashes = {}
        self.jobs = weakref.WeakValueDictionary()
        
        self.extranonce_counter = ExtranonceCounter(instance_id)
        self.extranonce2_size = block_template_class.coinbase_transaction_class.extranonce_size \
                - self.extranonce_counter.get_size()
        self.coinbaser = coinbaser
        self.block_template_class = block_template_class
        self.bitcoin_rpc = bitcoin_rpc
        self.on_block_callback = on_block_callback
        self.on_template_callback = on_template_callback
        
        self.last_block = None
        self.update_in_progress = False
        self.last_update = 0
        self.last_height = None

        self.aux_rpc = aux_rpc
        self.aux_update_in_progress = False
        self.aux_new_block = False
        self.aux_last_update = 0
        self.aux_update_counter = 0
        self.aux_data = []
        
        # Create first block template on startup
        self.update_auxs()
        #self.update_block()
        
    def get_new_extranonce1(self):
        '''Generates unique extranonce1 (e.g. for newly
        subscribed connection.'''
        return self.extranonce_counter.get_new_bin()
    
    def get_last_broadcast_args(self):
        '''Returns arguments for mining.notify
        from last known template.'''
        return self.last_block.broadcast_args
        
    def add_template(self, block,block_height):
        '''Adds new template to the registry.
        It also clean up templates which should
        not be used anymore.'''
        
        prevhash = block.prevhash_hex

        if prevhash in self.prevhashes.keys() and not self.aux_new_block:
            new_block = False
        else:
            new_block = True
            self.prevhashes[prevhash] = []
        self.aux_new_block = False       
        # Blocks sorted by prevhash, so it's easy to drop
        # them on blockchain update
        self.prevhashes[prevhash].append(block)
        
        # Weak reference for fast lookup using job_id
        self.jobs[block.job_id] = block
        
        # Use this template for every new request
        self.last_block = block
        
        # Drop templates of obsolete blocks
        for ph in self.prevhashes.keys():
            if ph != prevhash:
                del self.prevhashes[ph]
                
        log.info("New template for %s" % prevhash)

        if new_block:
            # Tell the system about new block
            # It is mostly important for share manager
            self.on_block_callback(block_height)

        # Everything is ready, let's broadcast jobs!
        self.on_template_callback(new_block)

    def update_block(self):
        '''Registry calls the getblocktemplate() RPC
        and build new block template.'''
        
        if self.update_in_progress:
            # Block has been already detected
            return
        
        self.update_in_progress = True
        self.last_update = Interfaces.timestamper.time()
        
        d = self.bitcoin_rpc.getblocktemplate()
        d.addCallback(self._update_block)
        d.addErrback(self._update_block_failed)
        
    def _update_block_failed(self, failure):
        log.error(str(failure))
        self.update_in_progress = False
        
    def _update_block(self, data):
        if self.aux_data is None:
            return
        start = Interfaces.timestamper.time()
                
        template = self.block_template_class(Interfaces.timestamper, self.coinbaser, JobIdGenerator.get_new_id())
        template.fill_from_rpc(data, self.aux_data)
        self.last_height = data['height']
        self.add_template(template, data['height'])

        log.info("Update finished, %.03f sec, %d txes" % \
                    (Interfaces.timestamper.time() - start, len(template.vtx)))
        
        self.update_in_progress = False        
        return data

    def update_auxs(self):
        if self.aux_update_in_progress:
            return
        self.aux_update_in_progress = True
        self.aux_last_update = Interfaces.timestamper.time()
        self.aux_data = []
        self.aux_update_counter = 0
        for chain in range(len(self.aux_rpc.conns)):
            aux_block = self.aux_rpc.conns[chain].getauxblock()
            aux_block.addCallback(self._update_auxs)
            aux_block.addErrback(self._update_auxs_failed)

    def _update_auxs(self, data):
        self.aux_data.append(data)
        self.aux_update_counter += 1
        if self.aux_update_counter == len(self.aux_rpc.conns):
            self.aux_update_counter = 0
            self.aux_update_in_progress = False
            self.update_block()
        return data

    def _update_auxs_failed(self,failure):
        log.error(str(failure))
        self.aux_update_in_progress = False
        self.aux_update_counter = 0
    
    def diff_to_target(self, difficulty):
        '''Converts difficulty to target'''
        if settings.DAEMON_ALGO == 'scrypt':
            diff1 = 0x0000ffff00000000000000000000000000000000000000000000000000000000
        elif settings.DAEMON_ALGO == 'yescrypt':
            diff1 = 0x0000ffff00000000000000000000000000000000000000000000000000000000
        elif settings.DAEMON_ALGO == 'qubit':
            diff1 = 0x000000ffff000000000000000000000000000000000000000000000000000000
        else:
            diff1 = 0x00000000ffff0000000000000000000000000000000000000000000000000000

        return float(diff1) / float(difficulty)
    
    def get_job(self, job_id):
        '''For given job_id returns BlockTemplate instance or None'''
        try:
            j = self.jobs[job_id]
        except:
            log.info("Job id '%s' not found" % job_id)
            return None
        
        # Now we have to check if job is still valid.
        # Unfortunately weak references are not bulletproof and
        # old reference can be found until next run of garbage collector.
        if j.prevhash_hex not in self.prevhashes:
            log.info("Prevhash of job '%s' is unknown" % job_id)
            return None
        
        if j not in self.prevhashes[j.prevhash_hex]:
            log.info("Job %s is unknown" % job_id)
            return None
        
        return j
        
    def submit_share(self, job_id, worker_name, session, extranonce1_bin, extranonce2, ntime, nonce,
                     difficulty, ip, submit_time):
        '''Check parameters and finalize block template. If it leads
           to valid block candidate, asynchronously submits the block
           back to the bitcoin network.
        
            - extranonce1_bin is binary. No checks performed, it should be from session data
            - job_id, extranonce2, ntime, nonce - in hex form sent by the client
            - difficulty - decimal number from session, again no checks performed
            - submitblock_callback - reference to method which receive result of submitblock()
        '''
        
        # Check if extranonce2 looks correctly. extranonce2 is in hex form...
        if len(extranonce2) != self.extranonce2_size * 2:
            raise SubmitException("Incorrect size of extranonce2. Expected %d chars" % (self.extranonce2_size*2))
        
        # Check for job
        job = self.get_job(job_id)
        if job == None:
            raise SubmitException("Job '%s' not found" % job_id)
                
        # Check if ntime looks correct
        if len(ntime) != 8:
            raise SubmitException("Incorrect size of ntime. Expected 8 chars")

        if not job.check_ntime(int(ntime, 16)):
            raise SubmitException("Ntime out of range")
        
        # Check nonce        
        if len(nonce) != 8:
            raise SubmitException("Incorrect size of nonce. Expected 8 chars")
        
        # Check for duplicated submit
        if not job.register_submit(extranonce1_bin, extranonce2, ntime, nonce):
            log.info("Duplicate from %s, (%s %s %s %s)" % \
                    (worker_name, binascii.hexlify(extranonce1_bin), extranonce2, ntime, nonce))
            raise SubmitException("Duplicate share")
        
        # Now let's do the hard work!
        # ---------------------------
        
        # 0. Some sugar
        extranonce2_bin = binascii.unhexlify(extranonce2)
        ntime_bin = binascii.unhexlify(ntime)
        nonce_bin = binascii.unhexlify(nonce)
                
        # 1. Build coinbase
        coinbase_bin = job.serialize_coinbase(extranonce1_bin, extranonce2_bin)
        coinbase_hash = util.doublesha(coinbase_bin)
        
        # 2. Calculate merkle root
        merkle_root_bin = job.merkletree.withFirst(coinbase_hash)
        merkle_root_int = util.uint256_from_str(merkle_root_bin)
                
        # 3. Serialize header with given merkle, ntime and nonce
        header_bin = job.serialize_header(merkle_root_int, ntime_bin, nonce_bin)
    
        # 4. Reverse header and compare it with target of the user
        if settings.DAEMON_ALGO == 'scrypt':
            hash_bin = ltc_scrypt.getPoWHash(header_bin)
        elif settings.DAEMON_ALGO == 'yescrypt':
            hash_bin = yescrypt_hash.getPoWHash(header_bin)
        elif settings.DAEMON_ALGO == 'qubit':
            hash_bin = qubit_hash.getPoWHash(header_bin)
        else:
            hash_bin = util.doublesha(header_bin)

        hash_int = util.uint256_from_str(hash_bin)
        pow_hash_hex = "%064x" % hash_int
        header_hex = binascii.hexlify(header_bin)
                 
        target_user = self.diff_to_target(difficulty)
        if hash_int > target_user:
            raise SubmitException("Share is above target")

        # Mostly for debugging purposes
        target_info = self.diff_to_target(1000)
        if hash_int <= target_info:
            log.info("Yay, share with diff above 1000")

        # Algebra tells us the diff_to_target is the same as hash_to_diff
        share_diff = float(self.diff_to_target(hash_int))

        on_submit = None
        aux_submit = None
        
        block_hash_bin = util.doublesha(header_bin)
        block_hash_hex = block_hash_bin[::-1].encode('hex_codec')        

        if hash_int <= job.target:
            log.info("MAINNET BLOCK CANDIDATE! %s diff(%f/%f)" % (block_hash_hex, share_diff, self.diff_to_target(job.target)))
            job.finalize(merkle_root_int, extranonce1_bin, extranonce2_bin, int(ntime, 16), int(nonce, 16))
            
            if not job.is_valid():
                log.exception("FINAL JOB VALIDATION FAILED!")
                            
            serialized = binascii.hexlify(job.serialize())
            if settings.SOLUTION_BLOCK_HASH:
                on_submit = self.bitcoin_rpc.submitblock(serialized, block_hash_hex)
            else:
                on_submit = self.bitcoin_rpc.submitblock(serialized, pow_hash_hex)
            
            '''if on_submit:
                self.update_block()'''

        # Check auxiliary merged chains
        for chain in range(len(job.auxs)):
            if hash_int <= job.aux_targets[chain]:
                log.info("FOUND MERGED BLOCK! %s diff(%f/%f)" % (job.auxs[chain]['hash'], share_diff, self.diff_to_target(job.aux_targets[chain])))
                coinbase_hex = binascii.hexlify(coinbase_bin)
                branch_count = job.merkletree.branchCount()
                branch_hex = job.merkletree.branchHex()
                merkle_link = util.calculate_merkle_link(job.merkle_hashes, job.tree[job.auxs[chain]['chainid']])
                submission = coinbase_hex + block_hash_hex + branch_count + branch_hex + '00000000' + merkle_link + header_hex;
                aux_submit = self.aux_rpc.conns[chain].getauxblock(job.auxs[chain]['hash'], submission)
                aux_submit.addCallback(Interfaces.share_manager.on_submit_aux_block, worker_name, header_hex, job.auxs[chain]['hash'], submit_time, ip, share_diff)
                '''if aux_submit:
                    self.update_auxs()'''
            
        if settings.SOLUTION_BLOCK_HASH:
            return (header_hex, block_hash_hex, share_diff, on_submit)
        else:
            return (header_hex, pow_hash_hex, share_diff, on_submit)
Beispiel #24
0
class TemplateRegistry(object):
    '''Implements the main logic of the pool. Keep track
    on valid block templates, provide internal interface for stratum
    service and implements block validation and submits.'''
    
    def __init__(self, block_template_class, coinbaser, bitcoin_rpc, instance_id,
                 on_template_callback, on_block_callback):
        self.prevhashes = {}
        self.jobs = weakref.WeakValueDictionary()
        
        self.extranonce_counter = ExtranonceCounter(instance_id)
        self.extranonce2_size = block_template_class.coinbase_transaction_class.extranonce_size \
                - self.extranonce_counter.get_size()
        log.debug("Got to Template Registry")
        self.coinbaser = coinbaser
        self.block_template_class = block_template_class
        self.bitcoin_rpc = bitcoin_rpc
        self.on_block_callback = on_block_callback
        self.on_template_callback = on_template_callback
        
        self.last_block = None
        self.update_in_progress = False
        self.last_update = None
        
        # Create first block template on startup
        self.update_block()
        
    def get_new_extranonce1(self):
        '''Generates unique extranonce1 (e.g. for newly
        subscribed connection.'''
        log.debug("Getting Unique Extronance")
        return self.extranonce_counter.get_new_bin()
    
    def get_last_broadcast_args(self):
        '''Returns arguments for mining.notify
        from last known template.'''
        log.debug("Getting Laat Template")
        return self.last_block.broadcast_args
        
    def add_template(self, block,block_height):
        '''Adds new template to the registry.
        It also clean up templates which should
        not be used anymore.'''
        
        prevhash = block.prevhash_hex

        if prevhash in self.prevhashes.keys():
            new_block = False
        else:
            new_block = True
            self.prevhashes[prevhash] = []
               
        # Blocks sorted by prevhash, so it's easy to drop
        # them on blockchain update
        self.prevhashes[prevhash].append(block)
        
        # Weak reference for fast lookup using job_id
        self.jobs[block.job_id] = block
        
        # Use this template for every new request
        self.last_block = block
        
        # Drop templates of obsolete blocks
        for ph in self.prevhashes.keys():
            if ph != prevhash:
                del self.prevhashes[ph]
                
        log.info("New template for %s" % prevhash)

        if new_block:
            # Tell the system about new block
            # It is mostly important for share manager
            self.on_block_callback(prevhash, block_height)

        # Everything is ready, let's broadcast jobs!
        self.on_template_callback(new_block)
        

        #from twisted.internet import reactor
        #reactor.callLater(10, self.on_block_callback, new_block) 
              
    def update_block(self):
        '''Registry calls the getblocktemplate() RPC
        and build new block template.'''
        
        if self.update_in_progress:
            # Block has been already detected
            return
        
        self.update_in_progress = True
        self.last_update = Interfaces.timestamper.time()
        
        d = self.bitcoin_rpc.getblocktemplate()
        d.addCallback(self._update_block)
        d.addErrback(self._update_block_failed)
        
    def _update_block_failed(self, failure):
        log.error(str(failure))
        self.update_in_progress = False
        
    def _update_block(self, data):
        start = Interfaces.timestamper.time()
                
        template = self.block_template_class(Interfaces.timestamper, self.coinbaser, JobIdGenerator.get_new_id())
        log.info(template.fill_from_rpc(data))
        self.add_template(template,data['height'])

        log.info("Update finished, %.03f sec, %d txes" % \
                    (Interfaces.timestamper.time() - start, len(template.vtx)))
        
        self.update_in_progress = False        
        return data
    
    def diff_to_target(self, difficulty):
        '''Converts difficulty to target'''
        if settings.COINDAEMON_ALGO == 'scrypt' or 'scrypt-jane':
            diff1 = 0x0000ffff00000000000000000000000000000000000000000000000000000000
        elif settings.COINDAEMON_ALGO == 'quark':
            diff1 = 0x000000ffff000000000000000000000000000000000000000000000000000000
        else:
            diff1 = 0x00000000ffff0000000000000000000000000000000000000000000000000000

        return diff1 / difficulty
    
    def get_job(self, job_id):
        '''For given job_id returns BlockTemplate instance or None'''
        try:
            j = self.jobs[job_id]
        except:
            log.info("Job id '%s' not found" % job_id)
            return None
        
        # Now we have to check if job is still valid.
        # Unfortunately weak references are not bulletproof and
        # old reference can be found until next run of garbage collector.
        if j.prevhash_hex not in self.prevhashes:
            log.info("Prevhash of job '%s' is unknown" % job_id)
            return None
        
        if j not in self.prevhashes[j.prevhash_hex]:
            log.info("Job %s is unknown" % job_id)
            return None
        
        return j
        
    def submit_share(self, job_id, worker_name, session, extranonce1_bin, extranonce2, ntime, nonce,
                     difficulty):
        '''Check parameters and finalize block template. If it leads
           to valid block candidate, asynchronously submits the block
           back to the bitcoin network.
        
            - extranonce1_bin is binary. No checks performed, it should be from session data
            - job_id, extranonce2, ntime, nonce - in hex form sent by the client
            - difficulty - decimal number from session, again no checks performed
            - submitblock_callback - reference to method which receive result of submitblock()
        '''
        
        # Check if extranonce2 looks correctly. extranonce2 is in hex form...
        if len(extranonce2) != self.extranonce2_size * 2:
            raise SubmitException("Incorrect size of extranonce2. Expected %d chars" % (self.extranonce2_size*2))
        
        # Check for job
        job = self.get_job(job_id)
        if job == None:
            raise SubmitException("Job '%s' not found" % job_id)
                
        # Check if ntime looks correct
        if len(ntime) != 8:
            raise SubmitException("Incorrect size of ntime. Expected 8 chars")

        if not job.check_ntime(int(ntime, 16)):
            raise SubmitException("Ntime out of range")
        
        # Check nonce        
        if len(nonce) != 8:
            raise SubmitException("Incorrect size of nonce. Expected 8 chars")
        
        # Check for duplicated submit
        if not job.register_submit(extranonce1_bin, extranonce2, ntime, nonce):
            log.info("Duplicate from %s, (%s %s %s %s)" % \
                    (worker_name, binascii.hexlify(extranonce1_bin), extranonce2, ntime, nonce))
            raise SubmitException("Duplicate share")
        
        # Now let's do the hard work!
        # ---------------------------
        
        # 0. Some sugar
        extranonce2_bin = binascii.unhexlify(extranonce2)
        ntime_bin = binascii.unhexlify(ntime)
        nonce_bin = binascii.unhexlify(nonce)
                
        # 1. Build coinbase
        coinbase_bin = job.serialize_coinbase(extranonce1_bin, extranonce2_bin)
        coinbase_hash = util.doublesha(coinbase_bin)
        
        # 2. Calculate merkle root
        merkle_root_bin = job.merkletree.withFirst(coinbase_hash)
        merkle_root_int = util.uint256_from_str(merkle_root_bin)
                
        # 3. Serialize header with given merkle, ntime and nonce
        header_bin = job.serialize_header(merkle_root_int, ntime_bin, nonce_bin)
    
        # 4. Reverse header and compare it with target of the user
        if settings.COINDAEMON_ALGO == 'scrypt':
            hash_bin = ltc_scrypt.getPoWHash(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ]))
        elif settings.COINDAEMON_ALGO  == 'scrypt-jane':
            hash_bin = yac_scrypt.getPoWHash(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ]), int(ntime, 16))
        elif settings.COINDAEMON_ALGO == 'quark':
            hash_bin = quark_hash.getPoWHash(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ]))
        else:
            hash_bin = util.doublesha(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ]))

        hash_int = util.uint256_from_str(hash_bin)
        scrypt_hash_hex = "%064x" % hash_int
        header_hex = binascii.hexlify(header_bin)
        if settings.COINDAEMON_ALGO == 'scrypt' or settings.COINDAEMON_ALGO == 'scrypt-jane':
            header_hex = header_hex+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
        elif settings.COINDAEMON_ALGO == 'quark':
            header_hex = header_hex+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
        else: pass
                 
        target_user = self.diff_to_target(difficulty)
        if hash_int > target_user:
            raise SubmitException("Share is above target")

        # Mostly for debugging purposes
        target_info = self.diff_to_target(100000)
        if hash_int <= target_info:
            log.info("Yay, share with diff above 100000")

        # Algebra tells us the diff_to_target is the same as hash_to_diff
        share_diff = int(self.diff_to_target(hash_int))

        # 5. Compare hash with target of the network
        if hash_int <= job.target:
            # Yay! It is block candidate! 
            log.info("We found a block candidate! %s" % scrypt_hash_hex)

            # Reverse the header and get the potential block hash (for scrypt only) 
            #if settings.COINDAEMON_ALGO == 'scrypt' or settings.COINDAEMON_ALGO == 'sha256d':
            #   if settings.COINDAEMON_Reward == 'POW':
            block_hash_bin = util.doublesha(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ]))
            block_hash_hex = block_hash_bin[::-1].encode('hex_codec')
            #else:   block_hash_hex = hash_bin[::-1].encode('hex_codec')
            #else:  block_hash_hex = hash_bin[::-1].encode('hex_codec')
            # 6. Finalize and serialize block object 
            job.finalize(merkle_root_int, extranonce1_bin, extranonce2_bin, int(ntime, 16), int(nonce, 16))
            
            if not job.is_valid():
                # Should not happen
                log.exception("FINAL JOB VALIDATION FAILED!(Try enabling/disabling tx messages)")
                            
            # 7. Submit block to the network
            serialized = binascii.hexlify(job.serialize())
            #just try both block hash and scrypt hash when checking for block creation
            on_submit = self.bitcoin_rpc.submitblock(serialized, block_hash_hex, scrypt_hash_hex)

            if on_submit:
                self.update_block()

            if settings.SOLUTION_BLOCK_HASH:
                return (header_hex, block_hash_hex, share_diff, on_submit)
            else:
                return (header_hex, scrypt_hash_hex, share_diff, on_submit)

        
        if settings.SOLUTION_BLOCK_HASH:
        # Reverse the header and get the potential block hash (for scrypt only) only do this if we want to send in the block hash to the shares table
            block_hash_bin = util.doublesha(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ]))
            block_hash_hex = block_hash_bin[::-1].encode('hex_codec')
            return (header_hex, block_hash_hex, share_diff, None)
        else:
            return (header_hex, scrypt_hash_hex, share_diff, None)
Beispiel #25
0
class TemplateRegistry(object):
    '''Implements the main logic of the pool. Keep track
    on valid block templates, provide internal interface for stratum
    service and implements block validation and submits.'''
    def __init__(self, block_template_class, coinbaser, bitcoin_rpc,
                 instance_id, on_template_callback, on_block_callback):
        self.prevhashes = {}
        self.jobs = weakref.WeakValueDictionary()

        self.extranonce_counter = ExtranonceCounter(instance_id)
        self.extranonce2_size = block_template_class.coinbase_transaction_class.extranonce_size \
                - self.extranonce_counter.get_size()

        self.coinbaser = coinbaser
        self.block_template_class = block_template_class
        self.bitcoin_rpc = bitcoin_rpc
        self.on_block_callback = on_block_callback
        self.on_template_callback = on_template_callback

        self.last_block = None
        self.update_in_progress = False
        self.last_update = None

        # Create first block template on startup
        self.update_block()

    def get_new_extranonce1(self):
        '''Generates unique extranonce1 (e.g. for newly
        subscribed connection.'''
        log.debug("Getting Unique Extranonce")
        return self.extranonce_counter.get_new_bin()

    def get_last_broadcast_args(self):
        '''Returns arguments for mining.notify
        from last known template.'''
        #log.debug("Getting Laat Template")
        return self.last_block.broadcast_args

    def add_template(self, block, block_height):
        '''Adds new template to the registry.
        It also clean up templates which should
        not be used anymore.'''

        prevhash = block.prevhash_hex

        if prevhash in self.prevhashes.keys():
            new_block = False
        else:
            new_block = True
            self.prevhashes[prevhash] = []

        # Blocks sorted by prevhash, so it's easy to drop
        # them on blockchain update
        self.prevhashes[prevhash].append(block)

        # Weak reference for fast lookup using job_id
        self.jobs[block.job_id] = block

        # Use this template for every new request
        self.last_block = block

        # Drop templates of obsolete blocks
        for ph in self.prevhashes.keys():
            if ph != prevhash:
                del self.prevhashes[ph]

        log.info("New template for %s" % prevhash)

        if new_block:
            # Tell the system about new block
            # It is mostly important for share manager
            self.on_block_callback(block_height)

        # Everything is ready, let's broadcast jobs!
        self.on_template_callback(new_block)

        #from twisted.internet import reactor
        #reactor.callLater(10, self.on_block_callback, new_block)

    def update_block(self):
        '''Registry calls the getblocktemplate() RPC
        and build new block template.'''

        if self.update_in_progress:
            # Block has been already detected
            return

        self.update_in_progress = True
        self.last_update = Interfaces.timestamper.time()

        d = self.bitcoin_rpc.getblocktemplate()
        d.addCallback(self._update_block)
        d.addErrback(self._update_block_failed)

    def _update_block_failed(self, failure):
        log.error(str(failure))
        self.update_in_progress = False

    def _update_block(self, data):
        start = Interfaces.timestamper.time()

        template = self.block_template_class(Interfaces.timestamper,
                                             self.coinbaser,
                                             JobIdGenerator.get_new_id())
        template.fill_from_rpc(data)
        self.add_template(template, data['height'])

        log.info("Update finished, %.03f sec, %d txes" % \
                    (Interfaces.timestamper.time() - start, len(template.vtx)))

        self.update_in_progress = False
        return data

    def get_job(self, job_id):
        '''For given job_id returns BlockTemplate instance or None'''
        try:
            j = self.jobs[job_id]
        except:
            log.info("Job id '%s' not found" % job_id)
            return None

        # Now we have to check if job is still valid.
        # Unfortunately weak references are not bulletproof and
        # old reference can be found until next run of garbage collector.
        if j.prevhash_hex not in self.prevhashes:
            log.info("Prevhash of job '%s' is unknown" % job_id)
            return None

        if j not in self.prevhashes[j.prevhash_hex]:
            log.info("Job %s is unknown" % job_id)
            return None

        return j

    def submit_share(self, job_id, worker_name, session, extranonce1_bin, data,
                     difficulty):

        job = self.get_job(job_id)
        if job == None:
            raise SubmitException("Job '%s' not found" % job_id)

        ntime = util.flip(data[136:144])
        if not job.check_ntime(int(ntime, 16)):
            raise SubmitException("Ntime out of range")

        if not job.register_submit(data):
            log.info("Duplicate from %s, (%s %s)" % \
                    (worker_name, binascii.hexlify(extranonce1_bin), data))
            raise SubmitException("Duplicate share")

        hash_int = gapcoin_hash.getpowdiff(str(data))
        block_hash_bin = util.doublesha(binascii.unhexlify(data[0:168]))
        block_hash_hex = util.rev(binascii.hexlify(block_hash_bin))
        '''log.info("block_hash_hex %s" % block_hash_hex)
        log.info("shrint %s" % hash_int)
        log.info("jobint %s" % job.target)%f
        log.info("target %s" % difficulty)'''

        if hash_int < difficulty:
            raise SubmitException("Share less than target")

        share_diff = float(float(hash_int) / float(pow(2, 48)))

        if hash_int >= job.target:
            log.info("BLOCK CANDIDATE! %s" % block_hash_hex)

            extranonce2_bin = struct.pack('>L', 0)
            #self.last_block.vtx[0].set_extranonce(extranonce1_bin + extranonce2_bin)
            #txs = binascii.hexlify(util.ser_vector(self.last_block.vtx))

            job.vtx[0].set_extranonce(extranonce1_bin + extranonce2_bin)
            txs = binascii.hexlify(util.ser_vector(job.vtx))
            serialized = str(data) + str(txs)

            on_submit = self.bitcoin_rpc.submitblock(str(data), str(txs),
                                                     block_hash_hex)
            if on_submit:
                self.update_block()

            return (block_hash_hex, share_diff, on_submit)

        return (block_hash_hex, share_diff, None)
Beispiel #26
0
class TemplateRegistry(object):
    '''Implements the main logic of the pool. Keep track
    on valid block templates, provide internal interface for stratum
    service and implements block validation and submits.'''
    def __init__(self, block_template_class, coinbaser, bitcoin_rpc,
                 instance_id, on_template_callback, on_block_callback):
        self.prevhashes = {}
        self.jobs = weakref.WeakValueDictionary()

        self.extranonce_counter = ExtranonceCounter(instance_id)
        self.extranonce2_size = block_template_class.coinbase_transaction_class.extranonce_size \
                - self.extranonce_counter.get_size()

        self.coinbaser = coinbaser
        self.block_template_class = block_template_class
        self.bitcoin_rpc = bitcoin_rpc
        self.on_block_callback = on_block_callback
        self.on_template_callback = on_template_callback

        self.last_block = None
        self.update_in_progress = False
        self.last_update = None
        self.last_update_force = None

        # Create first block template on startup
        self.update_block()

    def get_new_extranonce1(self):
        '''Generates unique extranonce1 (e.g. for newly
        subscribed connection.'''
        log.debug("Getting Unique Extranonce")
        return self.extranonce_counter.get_new_bin()

    def get_last_broadcast_args(self):
        '''Returns arguments for mining.notify
        from last known template.'''
        #log.debug("Getting Laat Template")
        return self.last_block.broadcast_args

    def add_template(self, block):
        '''Adds new template to the registry.
        It also clean up templates which should
        not be used anymore.'''
        if self.last_update_force == None:
            self.last_update_force = Interfaces.timestamper.time()

        prevhash = block.prevhash_hex

        if Interfaces.timestamper.time(
        ) - self.last_update_force >= settings.FORCE_REFRESH_INTERVAL:
            log.info("FORCED UPDATE!")
            new_block = True
            self.prevhashes[prevhash] = []
            self.last_update_force = Interfaces.timestamper.time()
        elif prevhash in self.prevhashes.keys():
            new_block = False
        else:
            new_block = True
            self.prevhashes[prevhash] = []
            self.last_update_force = Interfaces.timestamper.time()

        # Blocks sorted by prevhash, so it's easy to drop
        # them on blockchain update
        self.prevhashes[prevhash].append(block)

        # Weak reference for fast lookup using job_id
        self.jobs[block.job_id] = block

        # Use this template for every new request
        self.last_block = block

        # Drop templates of obsolete blocks
        for ph in self.prevhashes.keys():
            if ph != prevhash:
                del self.prevhashes[ph]

        log.info("New template for %s" % prevhash)

        if new_block:
            # Tell the system about new block
            # It is mostly important for share manager
            self.on_block_callback()

        # Everything is ready, let's broadcast jobs!
        self.on_template_callback(new_block)

    def update_block(self):
        '''Registry calls the getblocktemplate() RPC
        and build new block template.'''

        if self.update_in_progress:
            # Block has been already detected
            return

        self.update_in_progress = True
        self.last_update = Interfaces.timestamper.time()

        d = self.bitcoin_rpc.getblocktemplate()
        d.addCallback(self._update_block)
        d.addErrback(self._update_block_failed)

    def _update_block_failed(self, failure):
        log.error(str(failure))
        self.update_in_progress = False

    def _update_block(self, data):
        start = Interfaces.timestamper.time()

        template = self.block_template_class(Interfaces.timestamper,
                                             self.coinbaser,
                                             JobIdGenerator.get_new_id())
        template.fill_from_rpc(data)
        #log.info("%s\n", repr(template))
        self.add_template(template)

        log.info("Update finished, %.03f sec, %d txes" % \
                    (Interfaces.timestamper.time() - start, len(template.vtx)))

        self.update_in_progress = False
        return data

    def get_job(self, job_id):
        '''For given job_id returns BlockTemplate instance or None'''

        if job_id == '00':
            log.info("Job %s is invalid" % job_id)
            return None

        try:
            j = self.jobs[job_id]
        except:
            log.info("Job id '%s' not found" % job_id)
            return None

        # Now we have to check if job is still valid.
        # Unfortunately weak references are not bulletproof and
        # old reference can be found until next run of garbage collector.
        if j.prevhash_hex not in self.prevhashes:
            log.info("Prevhash of job '%s' is unknown" % job_id)
            return None

        if j not in self.prevhashes[j.prevhash_hex]:
            log.info("Job %s is unknown" % job_id)
            return None

        return j

    def diff_to_target(self, difficulty):
        '''Converts difficulty to target'''
        diff1 = 0x000000ffff000000000000000000000000000000000000000000000000000000000000000000000
        return float(diff1 * 16) / float(difficulty)

    def submit_share(self, job_id, worker_name, session, extranonce1_bin,
                     extranonce2, ntime, nonce, difficulty):

        # Check for job
        job = self.get_job(job_id)
        if job == None:
            raise SubmitException("Job '%s' not found" % job_id)

        nonce = util.rev(nonce)
        ntime = util.rev(ntime)
        extranonce2_bin = binascii.unhexlify(extranonce2)
        ntime_bin = binascii.unhexlify(ntime)
        nonce_bin = binascii.unhexlify(nonce)

        # Check if extranonce2 looks correctly. extranonce2 is in hex form...
        if len(extranonce2) != self.extranonce2_size * 2:
            raise SubmitException(
                "Incorrect size of extranonce2. Expected %d chars" %
                (self.extranonce2_size * 2))

        # Check if ntime looks correct
        if len(ntime) != 8:
            raise SubmitException("Incorrect size of ntime. Expected 8 chars")

        if not job.check_ntime(int(ntime, 16)):
            raise SubmitException("Ntime out of range")

        # Check nonce
        if len(nonce) != 8:
            raise SubmitException("Incorrect size of nonce. Expected 8 chars")

        # Check for duplicated submit
        if not job.register_submit(extranonce1_bin, extranonce2, ntime, nonce):
            log.info("Duplicate from %s, (%s %s %s %s)" % \
                    (worker_name, binascii.hexlify(extranonce1_bin), extranonce2, ntime, nonce))
            raise SubmitException("Duplicate share")

        # 1. Build coinbase
        coinbase_bin = job.serialize_coinbase(extranonce1_bin, extranonce2_bin)
        coinbase_hash = kshake320_hash.getHash320(coinbase_bin)

        # 2. Calculate merkle root
        merkle_root_bin = job.merkletree.withFirst(coinbase_hash)
        merkle_root_int = util.uint320_from_str(merkle_root_bin)

        # 3. Serialize header with given merkle, ntime and nonce
        header_bin = job.serialize_header(merkle_root_bin, int(ntime, 16),
                                          int(nonce, 16))

        header_hex = binascii.hexlify(header_bin)
        header_hex = header_hex + "0000000000000000"

        # 4. Reverse header and compare it with target of the user
        hash_bin = kshake320_hash.getPoWHash(header_bin)
        hash_int = util.uint320_from_str(hash_bin)
        hash_hex = "%080x" % hash_int
        block_hash_hex = hash_bin[::-1].encode('hex_codec')

        target_user = float(self.diff_to_target(difficulty))
        if hash_int > target_user:
            log.info("ABOVE TARGET!")
            raise SubmitException("Share above target")

        target_info = self.diff_to_target(50)
        if hash_int <= target_info:
            log.info("YAY, share with diff above 50")

        # Algebra tells us the diff_to_target is the same as hash_to_diff
        share_diff = float(self.diff_to_target(hash_int))

        if hash_int <= job.target:
            # Yay! It is block candidate!
            log.info("BLOCK CANDIDATE! %s" % block_hash_hex)

            # Finalize and serialize block object
            job.finalize(merkle_root_int, extranonce1_bin, extranonce2_bin,
                         int(ntime, 16), int(nonce, 16))

            if not job.is_valid():
                # Should not happen
                log.info("FINAL JOB VALIDATION FAILED!")

            # Submit block to the network
            '''serialized = binascii.hexlify(job.serialize())
            on_submit = self.bitcoin_rpc.submitblock(str(serialized), block_hash_hex)'''

            job.vtx[0].set_extranonce(extranonce1_bin + extranonce2_bin)
            txs = binascii.hexlify(util.ser_vector(job.vtx))
            on_submit = self.bitcoin_rpc.submitblock_wtxs(
                str(header_hex), str(txs), block_hash_hex)
            '''if on_submit:
                self.update_block()'''

            return (block_hash_hex, share_diff, on_submit)

        return (block_hash_hex, share_diff, None)