X-Git-Url: https://git.novaco.in/?p=stratum-mining.git;a=blobdiff_plain;f=lib%2Ftemplate_registry.py;h=25dad09b45fc843ad1a9f757906c92ded0fb7881;hp=4d3f061cb515759222550393aa521825f876e625;hb=HEAD;hpb=1e086b3786e3708cb71205962d9db1684f2271f2 diff --git a/lib/template_registry.py b/lib/template_registry.py index 4d3f061..25dad09 100644 --- a/lib/template_registry.py +++ b/lib/template_registry.py @@ -3,6 +3,9 @@ import binascii import util import StringIO +from twisted.internet import defer +from lib.exceptions import SubmitException + import stratum.logger log = stratum.logger.get_logger('template_registry') @@ -27,7 +30,7 @@ class TemplateRegistry(object): service and implements block validation and submits.''' def __init__(self, block_template_class, coinbaser, bitcoin_rpc, instance_id, - on_block_callback): + on_template_callback, on_block_callback): self.prevhashes = {} self.jobs = weakref.WeakValueDictionary() @@ -39,6 +42,7 @@ class TemplateRegistry(object): 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 @@ -57,7 +61,7 @@ class TemplateRegistry(object): from last known template.''' return self.last_block.broadcast_args - def add_template(self, block): + def add_template(self, block,block_height): '''Adds new template to the registry. It also clean up templates which should not be used anymore.''' @@ -86,7 +90,16 @@ class TemplateRegistry(object): del self.prevhashes[ph] log.info("New template for %s" % prevhash) - self.on_block_callback(new_block) + + 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) @@ -114,7 +127,7 @@ class TemplateRegistry(object): template = self.block_template_class(Interfaces.timestamper, self.coinbaser, JobIdGenerator.get_new_id()) template.fill_from_rpc(data) - self.add_template(template) + self.add_template(template,data['height']) log.info("Update finished, %.03f sec, %d txes" % \ (Interfaces.timestamper.time() - start, len(template.vtx))) @@ -124,7 +137,7 @@ class TemplateRegistry(object): def diff_to_target(self, difficulty): '''Converts difficulty to target''' - diff1 = 0x00000000ffff0000000000000000000000000000000000000000000000000000 + diff1 = 0x0000ffff00000000000000000000000000000000000000000000000000000000 return diff1 / difficulty def get_job(self, job_id): @@ -133,23 +146,23 @@ class TemplateRegistry(object): j = self.jobs[job_id] except: log.info("Job id '%s' not found" % job_id) - return False + 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 False + return None if j not in self.prevhashes[j.prevhash_hex]: log.info("Job %s is unknown" % job_id) - return False + return None - return True + return j - def submit_share(self, job_id, worker_name, extranonce1_bin, extranonce2, ntime, nonce, - difficulty, submitblock_callback): + 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. @@ -162,33 +175,34 @@ class TemplateRegistry(object): # Check if extranonce2 looks correctly. extranonce2 is in hex form... if len(extranonce2) != self.extranonce2_size * 2: - return (False, "Incorrect size of extranonce2. Expected %d chars" % (self.extranonce2_size*2), None, None) + raise SubmitException("Incorrect size of extranonce2. Expected %d chars" % (self.extranonce2_size*2)) # Check for job - if not self.get_job(job_id): - return (False, "Job '%s' not found" % job_id, None, None) - - try: - job = self.jobs[job_id] - except KeyError: - return (False, "Job '%s' not found" % job_id, None, None) + 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: - return (False, "Incorrect size of ntime. Expected 8 chars", None, None) + raise SubmitException("Incorrect size of ntime. Expected 8 chars") if not job.check_ntime(int(ntime, 16)): - return (False, "Ntime out of range", None, None) + raise SubmitException("Ntime out of range") # Check nonce if len(nonce) != 8: - return (False, "Incorrect size of nonce. Expected 8 chars", None, None) - + raise SubmitException("Incorrect size of nonce. Expected 8 chars") + + # normalize the case to prevent duplication of valid shares by the client + ntime = ntime.lower() + nonce = nonce.lower() + extranonce2 = extranonce2.lower() + # 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)) - return (False, "Duplicate share", None, None) + raise SubmitException("Duplicate share") # Now let's do the hard work! # --------------------------- @@ -210,15 +224,24 @@ class TemplateRegistry(object): 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_bin = util.scrypt(''.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 = self.diff_to_target(difficulty) - - if hash_int > target: - return (False, "Share is above target", None, None) + target_user = self.diff_to_target(difficulty) + if hash_int > target_user and \ + ( 'prev_jobid' not in session or session['prev_jobid'] < job_id \ + or 'prev_diff' not in session or hash_int > self.diff_to_target(session['prev_diff']) ): + 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: @@ -231,32 +254,11 @@ class TemplateRegistry(object): if not job.is_valid(): # Should not happen log.error("Final job validation failed!") - return (False, 'Job validation failed', header_hex, block_hash_hex) # 7. Submit block to the network - submit_time = Interfaces.timestamper.time() serialized = binascii.hexlify(job.serialize()) - d = self.bitcoin_rpc.submitblock(serialized) - - # Submit is lazy, we don't need to wait for the result - # Callback will just register success or failure to share manager - d.addCallback(self._on_submitblock, submitblock_callback, - worker_name, header_hex, block_hash_hex, submit_time) - d.addErrback(self._on_submitblock_failure, submitblock_callback, - worker_name, header_hex, block_hash_hex, submit_time) + on_submit = self.bitcoin_rpc.submitblock(serialized) - return (True, '', header_hex, block_hash_hex) + return (header_hex, block_hash_hex, share_diff, on_submit) - return (True, '', header_hex, block_hash_hex) - - def _on_submitblock(self, is_accepted, callback, worker_name, block_header, block_hash, timestamp): - '''Helper method, bridges call from deferred to method reference given in submit()''' - # Forward submitblock result to share manager - callback(worker_name, block_header, block_hash, timestamp, is_accepted) - return is_accepted - - def _on_submitblock_failure(self, failure, callback, worker_name, block_header, block_hash, timestamp): - '''Helper method, bridges call from deferred to method reference given in submit()''' - # Forward submitblock failure to share manager - callback(worker_name, block_header, block_hash, timestamp, False) - log.exception(failure) \ No newline at end of file + return (header_hex, block_hash_hex, share_diff, None)