Beispiel #1
0
    def submit(self, worker_name, password, data):

        session = self.connection_ref().get_session()
        session.setdefault('authorized', {})

        # Check if worker is authorized to submit shares
        ip = self.connection_ref()._get_ip()
        if not Interfaces.worker_manager.authorize(
                worker_name, session['authorized'].get(worker_name)):
            log.info("Worker is not authorized: IP %s" % str(ip))
            raise SubmitException("Worker is not authorized")

        # Check if extranonce1 is in connection session
        extranonce1_bin = session.get('extranonce1', None)

        if not extranonce1_bin:
            log.info("Connection is not subscribed for mining: IP %s" %
                     str(ip))
            raise SubmitException("Connection is not subscribed for mining")

        # Get current block job_id
        difficulty = session['difficulty']
        basediff = session['basediff']
        merkle_root = data[72:136]

        if extranonce1_bin in Interfaces.worker_manager.job_log and merkle_root in Interfaces.worker_manager.job_log[
                extranonce1_bin]:
            (
                job_id, difficulty, basediff, job_ts
            ) = Interfaces.worker_manager.job_log[extranonce1_bin][merkle_root]
        else:
            raise SubmitException("Job not registered!")
        #log.debug("worker_job_log: %s" % repr(Interfaces.worker_manager.job_log))

        submit_time = Interfaces.timestamper.time()
        pool_share = settings.POOL_SHARE
        if ((basediff - settings.POOL_TARGET)) != 0:
            pool_share = round(
                pow(2, ((basediff - settings.POOL_TARGET) / 0.693147181)) *
                settings.POOL_SHARE)

        is_ext_diff = False
        if settings.ENABLE_WORKER_STATS:
            (valid, invalid, is_banned, is_ext_diff,
             last_ts) = Interfaces.worker_manager.worker_log['authorized'][
                 extranonce1_bin]
            percent = float(
                float(invalid) / (float(valid) if valid else 1) * 100)

            if is_banned and submit_time - last_ts > settings.WORKER_BAN_TIME:
                if percent > settings.INVALID_SHARES_PERCENT:
                    log.info("Worker invalid percent: %0.2f %s STILL BANNED!" %
                             (percent, worker_name))
                else:
                    is_banned = False
                    log.info("Clearing ban for worker: %s UNBANNED" %
                             worker_name)
                (valid, invalid, is_banned,
                 last_ts) = (0, 0, is_banned, Interfaces.timestamper.time())

            if submit_time - last_ts > settings.WORKER_CACHE_TIME and not is_banned:
                if percent > settings.INVALID_SHARES_PERCENT and settings.ENABLE_WORKER_BANNING:
                    is_banned = True
                    log.info("Worker invalid percent: %0.2f %s BANNED!" %
                             (percent, worker_name))
                else:
                    log.debug("Clearing worker stats for: %s" % worker_name)
                (valid, invalid, is_banned,
                 last_ts) = (0, 0, is_banned, Interfaces.timestamper.time())

        if settings.ENABLE_WORKER_STATS:
            log.debug(
                "%s (%d, %d, %s, %d) %0.2f%% job_id(%s) diff(%0.9f) share(%i)"
                % (worker_name, valid, invalid, is_banned, last_ts, percent,
                   job_id, basediff, pool_share))

        if not is_ext_diff:
            Interfaces.share_limiter.submit(self.connection_ref, job_id,
                                            basediff, submit_time, worker_name,
                                            extranonce1_bin)

        try:
            (block_hash, share_diff,
             on_submit) = Interfaces.template_registry.submit_share(
                 job_id, worker_name, session, extranonce1_bin, data,
                 difficulty)
        except SubmitException as e:
            # block_header and block_hash are None when submitted data are corrupted
            if settings.ENABLE_WORKER_STATS:
                invalid += 1
                if invalid > settings.INVALID_SHARES_SPAM:
                    is_banned = True
                    log.info("Worker SPAM %s BANNED! IP: %s" %
                             (worker_name, ip))
                Interfaces.worker_manager.worker_log['authorized'][
                    extranonce1_bin] = (valid, invalid, is_banned, is_ext_diff,
                                        last_ts)

                if is_banned:
                    raise SubmitException("Worker is temporarily banned")

            Interfaces.share_manager.on_submit_share(worker_name, basediff,
                                                     False, pool_share,
                                                     submit_time, False, ip,
                                                     e[0], 0, job_id)
            raise

        if settings.ENABLE_WORKER_STATS:
            valid += 1
            Interfaces.worker_manager.worker_log['authorized'][
                extranonce1_bin] = (valid, invalid, is_banned, is_ext_diff,
                                    last_ts)

            if is_banned:
                raise SubmitException("Worker is temporarily banned")

        Interfaces.share_manager.on_submit_share(worker_name, basediff,
                                                 block_hash, pool_share,
                                                 submit_time, True, ip, '',
                                                 share_diff, job_id)

        if on_submit != None:
            on_submit.addCallback(Interfaces.share_manager.on_submit_block,
                                  worker_name, basediff, block_hash,
                                  submit_time, ip, share_diff)

        return True
Beispiel #2
0
    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 settings.COINDAEMON_ALGO == 'riecoin':
            if len(ntime) != 16:
                raise SubmitException("Incorrect size of ntime. Expected 16 chars")
        else:
            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 settings.COINDAEMON_ALGO == 'riecoin':
            if len(nonce) != 64:
                 raise SubmitException("Incorrect size of nonce. Expected 64 chars")
        else:
            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)
        if settings.COINDAEMON_ALGO == 'riecoin':
            ntime_bin = (''.join([ ntime_bin[(1-i)*4:(1-i)*4+4] for i in range(0, 2) ]))
            nonce_bin = (''.join([ nonce_bin[(7-i)*4:(7-i)*4+4] for i in range(0, 8) ]))
                
        # 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) ]))
	elif settings.COINDAEMON_ALGO == 'skeinhash':
            hash_bin = skeinhash.skeinhash(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ]))
        elif settings.COINDAEMON_ALGO == 'magi':
            hash_bin = magi_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

        if settings.COINDAEMON_ALGO == 'riecoin':
            # this is kind of an ugly hack: we use hash_int to store the number of primes
            hash_int = util.riecoinPoW( hash_int, job.target, int(nonce, 16) )

        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"
        elif settings.COINDAEMON_ALGO == 'magi':
            header_hex = header_hex+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
        elif settings.COINDAEMON_ALGO == 'riecoin':
            header_hex = header_hex+"00000080000000000000000080030000"
        else: pass
                 
        target_user = self.diff_to_target(difficulty)
        if settings.COINDAEMON_ALGO == 'riecoin':
	    if hash_int < target_user:
                raise SubmitException("Share does not meet target")
        else:
	    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
        isBlockCandidate = False
        if settings.COINDAEMON_ALGO == 'riecoin':
            if hash_int == 6:
                isBlockCandidate = True
        else:
            if hash_int <= job.target:
                isBlockCandidate = True

        if isBlockCandidate == True:
            # 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 == 'riecoin':
            	block_hash_bin = util.doublesha(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 28) ]))
            else:
            	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:
        # 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
            if settings.COINDAEMON_ALGO == 'riecoin':
                block_hash_bin = util.doublesha(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 28) ]))
            else:
	        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 #3
0
    def submit(self, worker_name, work_id, extranonce2, ntime, nonce):
        '''Try to solve block candidate using given parameters.'''

        session = self.connection_ref().get_session()
        session.setdefault('authorized', {})

        # Check if worker is authorized to submit shares
        ip = self.connection_ref()._get_ip()
        if not Interfaces.worker_manager.authorize(
                worker_name, session['authorized'].get(worker_name)):
            log.info("Worker is not authorized: IP %s", str(ip))
            raise SubmitException("Worker is not authorized")

        # Check if extranonce1 is in connection session
        extranonce1_bin = session.get('extranonce1', None)

        if not extranonce1_bin:
            log.info("Connection is not subscribed for mining: IP %s", str(ip))
            raise SubmitException("Connection is not subscribed for mining")

        # Get current block job_id
        difficulty = session['difficulty']
        if worker_name in Interfaces.worker_manager.job_log and work_id in Interfaces.worker_manager.job_log[
                worker_name]:
            (job_id, difficulty,
             job_ts) = Interfaces.worker_manager.job_log[worker_name][work_id]
        else:
            job_ts = Interfaces.timestamper.time()
            Interfaces.worker_manager.job_log.setdefault(
                worker_name, {})[work_id] = (work_id, difficulty, job_ts)
            job_id = work_id
        #log.debug("worker_job_log: %s" % repr(Interfaces.worker_manager.job_log))

        submit_time = Interfaces.timestamper.time()

        (valid, invalid, is_banned, diff, is_ext_diff, last_ts
         ) = Interfaces.worker_manager.worker_log['authorized'][worker_name]
        percent = float(float(invalid) / (float(valid) if valid else 1) * 100)

        if is_banned and submit_time - last_ts > settings.WORKER_BAN_TIME:
            if percent > settings.INVALID_SHARES_PERCENT:
                log.debug("Worker invalid percent: %0.2f %s STILL BANNED!" %
                          (percent, worker_name))
            else:
                is_banned = False
                log.debug("Clearing ban for worker: %s UNBANNED" % worker_name)
            (valid, invalid, is_banned,
             last_ts) = (0, 0, is_banned, Interfaces.timestamper.time())

        if submit_time - last_ts > settings.WORKER_CACHE_TIME and not is_banned:
            if percent > settings.INVALID_SHARES_PERCENT and settings.ENABLE_WORKER_BANNING:
                is_banned = True
                log.debug("Worker invalid percent: %0.2f %s BANNED!" %
                          (percent, worker_name))
            else:
                log.debug("Clearing worker stats for: %s" % worker_name)
            (valid, invalid, is_banned,
             last_ts) = (0, 0, is_banned, Interfaces.timestamper.time())

        log.debug(
            "%s (%d, %d, %s, %s, %d) %0.2f%% work_id(%s) job_id(%s) diff(%f)" %
            (worker_name, valid, invalid, is_banned, is_ext_diff, last_ts,
             percent, work_id, job_id, difficulty))
        if not is_ext_diff:
            Interfaces.share_limiter.submit(self.connection_ref, job_id,
                                            difficulty, submit_time,
                                            worker_name)

        # This checks if submitted share meet all requirements
        # and it is valid proof of work.
        try:
            (block_header, block_hash, share_diff,
             on_submit) = Interfaces.template_registry.submit_share(
                 job_id, worker_name, session, extranonce1_bin, extranonce2,
                 ntime, nonce, difficulty)
        except SubmitException as e:
            # block_header and block_hash are None when submitted data are corrupted
            invalid += 1
            Interfaces.worker_manager.worker_log['authorized'][worker_name] = (
                valid, invalid, is_banned, difficulty, is_ext_diff, last_ts)

            if is_banned:
                raise SubmitException("Worker is temporarily banned")

            Interfaces.share_manager.on_submit_share(worker_name, False, False,
                                                     difficulty, submit_time,
                                                     False, ip, e[0], 0)
            raise

        valid += 1
        Interfaces.worker_manager.worker_log['authorized'][worker_name] = (
            valid, invalid, is_banned, difficulty, is_ext_diff, last_ts)

        if is_banned:
            raise SubmitException("Worker is temporarily banned")

        Interfaces.share_manager.on_submit_share(worker_name, block_header,
                                                 block_hash, difficulty,
                                                 submit_time, True, ip, '',
                                                 share_diff)

        if on_submit != None:
            # Pool performs submitblock() to vdinard. Let's hook
            # to result and report it to share manager
            on_submit.addCallback(Interfaces.share_manager.on_submit_block,
                                  worker_name, block_header, block_hash,
                                  submit_time, ip, share_diff)

        return True
    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
              
        '''
        if settings.VARIABLE_DIFF == True:
            # Share Diff Should never be 0 
            if difficulty < settings.VDIFF_MIN_TARGET :
        	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))
        else:
             if difficulty < settings.POOL_TARGET:
             	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:
            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':
        	if settings.SCRYPTJANE_NAME == 'vtc_scrypt':
            	     hash_bin = scryptjane.getPoWHash(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ]))
      		else: 
      		     hash_bin = scryptjane.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) ]))
	elif settings.COINDAEMON_ALGO == 'skeinhash':
            hash_bin = skeinhash.skeinhash(''.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())
	    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 #5
0
    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")

        # 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
        hash_bin = x13bcd_hash.getPoWHash(''.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 #6
0
    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(''.join(
                [header_bin[i * 4:i * 4 + 4][::-1] for i in range(0, 20)]))
        elif settings.DAEMON_ALGO == 'x11':
            hash_bin = x11_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)
        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(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))

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

        if hash_int <= job.target:
            log.info(
                "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()

        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)
    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)
Beispiel #8
0
    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 #9
0
    def submit(self, worker_name, work_id, extranonce2, ntime, nonce):
        '''Try to solve block candidate using given parameters.'''

        session = self.connection_ref().get_session()
        session.setdefault('authorized', {})

        # Check if worker is authorized to submit shares
        ip = self.connection_ref()._get_ip()
        if not Interfaces.worker_manager.authorize(
                worker_name, session['authorized'].get(worker_name)):
            log.info("Worker is not authorized: %s IP %s" %
                     (worker_name, str(ip)))
            raise SubmitException("Worker is not authorized")

        # Check if extranonce1 is in connection session
        extranonce1_bin = session.get('extranonce1', None)

        if not extranonce1_bin:
            log.info("Connection is not subscribed for mining: IP %s" %
                     str(ip))
            raise SubmitException("Connection is not subscribed for mining")

        difficulty = session['difficulty']
        submit_time = Interfaces.timestamper.time()

        if extranonce1_bin in Interfaces.worker_manager.job_log and work_id in Interfaces.worker_manager.job_log[
                extranonce1_bin]:
            (job_id, difficulty, job_ts
             ) = Interfaces.worker_manager.job_log[extranonce1_bin][work_id]
        else:
            job_ts = Interfaces.timestamper.time()
            Interfaces.worker_manager.job_log.setdefault(
                extranonce1_bin, {})[work_id] = (work_id, difficulty, job_ts)
            job_id = work_id
        #log.debug("worker_job_log: %s" % repr(Interfaces.worker_manager.job_log))

        pool_share = float(
            float(difficulty) * float(settings.SHARE_MULTIPLIER))

        if settings.ENABLE_WORKER_STATS:
            (valid, invalid, is_banned,
             last_ts) = Interfaces.worker_manager.worker_log['authorized'][
                 extranonce1_bin]
            percent = float(
                float(invalid) / (float(valid) if valid else 1) * 100)

            if is_banned and submit_time - last_ts > settings.WORKER_BAN_TIME:
                if percent > settings.INVALID_SHARES_PERCENT:
                    log.info("Worker invalid percent: %0.2f %s STILL BANNED!" %
                             (percent, worker_name))
                else:
                    is_banned = False
                    log.info("Clearing ban for worker: %s UNBANNED" %
                             worker_name)
                (valid, invalid, is_banned,
                 last_ts) = (0, 0, is_banned, Interfaces.timestamper.time())

            if submit_time - last_ts > settings.WORKER_CACHE_TIME and not is_banned:
                if percent > settings.INVALID_SHARES_PERCENT and settings.ENABLE_WORKER_BANNING:
                    is_banned = True
                    log.info("Worker invalid percent: %0.2f %s BANNED!" %
                             (percent, worker_name))
                else:
                    log.debug("Clearing worker stats for: %s" % worker_name)
                (valid, invalid, is_banned,
                 last_ts) = (0, 0, is_banned, Interfaces.timestamper.time())

        if settings.ENABLE_WORKER_STATS:
            log.debug(
                "%s (%d, %d, %s, %d) %0.2f%% job_id(%s) diff(%i) share(%i)" %
                (worker_name, valid, invalid, is_banned, last_ts, percent,
                 job_id, difficulty, pool_share))

        Interfaces.share_limiter.submit(self.connection_ref, job_id,
                                        difficulty, submit_time, worker_name,
                                        extranonce1_bin)

        try:
            (block_header, block_hash, share_diff,
             on_submit) = Interfaces.template_registry.submit_share(
                 job_id, worker_name, session, extranonce1_bin, extranonce2,
                 ntime, nonce, difficulty, ip, submit_time)
        except SubmitException as e:
            # block_header and block_hash are None when submitted data are corrupted
            if settings.ENABLE_WORKER_STATS:
                invalid += 1
                if invalid > settings.INVALID_SHARES_SPAM:
                    is_banned = True
                    log.info("Worker SPAM %s BANNED! IP: %s" %
                             (worker_name, ip))
                Interfaces.worker_manager.worker_log['authorized'][
                    extranonce1_bin] = (valid, invalid, is_banned, last_ts)

                if is_banned:
                    raise SubmitException("Worker is temporarily banned")

            Interfaces.share_manager.on_submit_share(worker_name, False, False,
                                                     difficulty, pool_share,
                                                     submit_time, False, ip,
                                                     e[0], 0, job_id)
            raise

        if settings.ENABLE_WORKER_STATS:
            valid += 1
            Interfaces.worker_manager.worker_log['authorized'][
                extranonce1_bin] = (valid, invalid, is_banned, last_ts)

            if is_banned:
                raise SubmitException("Worker is temporarily banned")

        Interfaces.share_manager.on_submit_share(worker_name, block_header,
                                                 block_hash, difficulty,
                                                 pool_share, submit_time, True,
                                                 ip, '', share_diff, job_id)

        if on_submit != None:
            on_submit.addCallback(Interfaces.share_manager.on_submit_block,
                                  worker_name, block_header, block_hash,
                                  submit_time, ip, share_diff)

        return True
Beispiel #10
0
    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 #11
0
    def submit(self, worker_name, job_id, extranonce2, ntime, nonce):
        '''Try to solve block candidate using given parameters.'''

        session = self.connection_ref().get_session()
        session.setdefault('authorized', {})

        # Check if worker is authorized to submit shares
        if not Interfaces.worker_manager.authorize(
                worker_name, session['authorized'].get(worker_name)):
            raise SubmitException("Worker is not authorized")

        start = Interfaces.timestamper.time()

        # Check if extranonce1 is in connection session
        extranonce1_bin = session.get('extranonce1', None)
        if not extranonce1_bin:
            raise SubmitException("Connection is not subscribed for mining")

        if settings.RSK_DEV_MODE and hasattr(settings,
                                             'RSK_STRATUM_DIFFICULTY'):
            difficulty = settings.RSK_STRATUM_DIFFICULTY
        else:
            difficulty = session['difficulty']
        submit_time = Interfaces.timestamper.time()

        Interfaces.share_limiter.submit(self.connection_ref, difficulty,
                                        submit_time)

        # This checks if submitted share meet all requirements
        # and it is valid proof of work.
        try:
            (block_header, block_hash, on_submit,
             on_submit_rsk) = Interfaces.template_registry.submit_share(
                 job_id, worker_name, extranonce1_bin, extranonce2, ntime,
                 nonce, difficulty)
        except SubmitException as e:
            log.error("SUBMIT EXCEPTION: %s", e)
            # block_header and block_hash are None when submitted data are corrupted
            Interfaces.share_manager.on_submit_share(worker_name, None, None,
                                                     difficulty, submit_time,
                                                     False)
            raise

        Interfaces.share_manager.on_submit_share(worker_name, block_header,
                                                 block_hash, difficulty,
                                                 submit_time, True)

        if on_submit != None:
            # Pool performs submitblock() to bitcoind. Let's hook
            # to result and report it to share manager
            on_submit.addCallback(Interfaces.share_manager.on_submit_block,
                                  worker_name, block_header, block_hash,
                                  submit_time)

        if on_submit_rsk != None:
            # Pool performs submitBitcoinBlockPartialMerkle() to rskd. Let's hook
            # to result and report it to share manager
            on_submit_rsk.addCallback(
                Interfaces.share_manager.on_submit_block_rsk, worker_name,
                block_header, block_hash, submit_time)

        if on_submit or on_submit_rsk:

            log.info(
                json.dumps({
                    "uuid": util.id_generator(),
                    "rsk": "[RSKLOG]",
                    "tag": "[SUBMITBLOCK_END]",
                    "start": submit_time,
                    "elapsed": Interfaces.timestamper.time() - submit_time,
                    "data": (block_hash, job_id)
                }))

        return True
Beispiel #12
0
                                is_ext_diff, last_ts)

            if is_banned:
                raise SubmitException(u"Worker is temporarily banned")

            Interfaces.share_manager.on_submit_share(worker_name, False, False,
                                                     difficulty, submit_time,
                                                     False, ip, e[0], 0)
            raise

        valid += 1
        Interfaces.worker_manager.worker_log[u'authorized'][worker_name] = (
            valid, invalid, is_banned, difficulty, is_ext_diff, last_ts)

        if is_banned:
            raise SubmitException(u"Worker is temporarily banned")

        Interfaces.share_manager.on_submit_share(worker_name, block_header,
                                                 block_hash, difficulty,
                                                 submit_time, True, ip, u'',
                                                 share_diff)

        if on_submit is not None:
            # Pool performs submitblock() to litecoind. Let's hook
            # to result and report it to share manager
            on_submit.addCallback(Interfaces.share_manager.on_submit_block,
                                  worker_name, block_header, block_hash,
                                  submit_time, ip, share_diff)

        return True
Beispiel #13
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'''
        diff1 = coindef.diff1()
        return diff1 / 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.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=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, 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, 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")
        
        # 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 = algo.getPoWHash(''.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 coindef.header() = True:
           header_hex = header_hex+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
                 
        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) 
            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:
        # 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 #14
0
    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, 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, 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")
        
        # 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 = algo.getPoWHash(''.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 coindef.header() = True:
           header_hex = header_hex+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
Beispiel #15
0
    def submit_share(self, job_id, worker_name, extranonce, ntime, nonce,
                     difficulty):
        '''Check parameters and finalize block template. If it leads
           to valid block candidate, asynchronously submits the block
           back to the network.

            - job_id, extranonce, 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 extranonce looks correctly. extranonce is in hex form...
        if len(extranonce) != self.extranonce_size * 2:
            raise SubmitException(
                "Incorrect size of extranonce. Expected 16 chars")

        # 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")

        ntime_int = util.hex_to_int32(ntime)
        if not job.check_ntime(ntime_int):
            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
        extranonce_int = util.hex_to_int64(extranonce)
        nonce_int = util.hex_to_int32(nonce)

        # Check for duplicated submit
        if not job.register_submit(extranonce_int, ntime_int, nonce_int):
            log.info("Duplicate from %s, (%s %s %s %s)" % \
                    (worker_name, extranonce_int, ntime_int, nonce_int))
            raise SubmitException("Duplicate share")

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

        # 1. Calculate merkle root
        mt = MerkleTree(job.intermediate_merkles)
        merkle_root_bin = mt.calculate_merkle_root(extranonce_int)

        # 2. Serialize header with given merkle, ntime and nonce
        header_bin = job.serialize_header(merkle_root_bin, ntime_int,
                                          nonce_int)

        # 3. Compare header with target of the user
        hash_bin = vblake.getPoWHash(header_bin)
        hash_int = util.uint192_from_str_be(hash_bin)
        block_hash_hex = "%048x" % 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")

        # 4. 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)

            # 5. Finalize and serialize block object
            job.finalize(merkle_root_bin, extranonce_int, ntime_int, nonce_int)

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

            # 6. Submit block to the network
            on_submit = self.protocol.submit_share(job.nc_job_id,
                                                   extranonce_int, ntime_int,
                                                   nonce_int)

            return (header_hex, block_hash_hex, on_submit)

        return (header_hex, block_hash_hex, None)
Beispiel #16
0
    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)