From b44277b43cde34b25506292c7e5bfc76bc352f5f Mon Sep 17 00:00:00 2001 From: ahmedbodi Date: Thu, 30 Jan 2014 09:37:14 +0000 Subject: [PATCH 1/7] Update bitcoin_rpc.py --- lib/bitcoin_rpc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/bitcoin_rpc.py b/lib/bitcoin_rpc.py index 5dab093..e26418a 100644 --- a/lib/bitcoin_rpc.py +++ b/lib/bitcoin_rpc.py @@ -61,7 +61,7 @@ class BitcoinRPC(object): @defer.inlineCallbacks - def submitblock(self, block_hex, hash_hex): + def submitblock(self, block_hex, hash_hex, scrypt_hex): #try 5 times? 500 Internal Server Error could mean random error or that TX messages setting is wrong attempts = 0 while True: From 58c12f18e2c34acdb2077d14a070d426c3ee5c51 Mon Sep 17 00:00:00 2001 From: ahmedbodi Date: Thu, 30 Jan 2014 09:40:45 +0000 Subject: [PATCH 2/7] Update bitcoin_rpc_manager.py --- lib/bitcoin_rpc_manager.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/bitcoin_rpc_manager.py b/lib/bitcoin_rpc_manager.py index 0244967..5c362dc 100644 --- a/lib/bitcoin_rpc_manager.py +++ b/lib/bitcoin_rpc_manager.py @@ -89,11 +89,11 @@ class BitcoinRPCManager(object): return self.conns[self.curr_conn]._call(method,params) except: self.next_connection() - def check_submitblock(self): - while True: - try: + def check_submitblock(self): + while True: + try: return self.conns[self.curr_conn].check_submitblock() - except: + except: self.next_connection() def submitblock(self, block_hex, hash_hex, scrypt_hex): From 52072d0a7b06925214e459ba789b743c711f654d Mon Sep 17 00:00:00 2001 From: ahmedbodi Date: Thu, 30 Jan 2014 10:53:10 +0000 Subject: [PATCH 3/7] Update README.md --- README.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index cb9410d..74499a3 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Stratum-mining is a pooled mining protocol. It is a replacement for *getwork* ba This is a implementation of stratum-mining for scrypt based coins. It is compatible with *MPOS* as it complies with the standards of *pushpool*. The end goal is to build on these standards to come up with a more stable solution. -The goal is to make a reliable stratum mining server for scrypt based coins. Over time I will develop this to be more feature rich and very stable. If you would like to see a feature please file a feature request. +The goal is to make a reliable stratum mining server for a wide range of coins unlike other forks where the code is limited to specific algorithm's. Over time I will develop this to be more feature rich and very stable. If you would like to see a feature please file a feature request. **NOTE:** This fork is still in development. Many features may be broken. Please report any broken features or issues. @@ -14,7 +14,6 @@ The goal is to make a reliable stratum mining server for scrypt based coins. Ove * Solved Block Confirmation * Job Based Vardiff support * Solution Block Hash Support -* *NEW* SHA256 and Scrypt Algo Support * Log Rotation * Initial low difficulty share confirmation * Multiple *coind* wallets @@ -34,7 +33,7 @@ The goal is to make a reliable stratum mining server for scrypt based coins. Ove * Doge: DLtBRYtNCzfiZfcpUeEr8KPvy5k1aR7jca * SRC: sMP2wHN5H2ik7FQDPjhSzFZUWux75BYZGe * ARG: AQvXPWVqGzcpH2j2XSRG7X5R9nA3y9D9aQ -* CryptsyTradeKey: ec13d183e304326ebd41258d6ae7188e303866fe +* Cryptsy Trade Key: ec13d183e304326ebd41258d6ae7188e303866fe #Requirements @@ -76,10 +75,9 @@ Please research and attempt to debug first. #Credits -* Original version by Slush0 (original stratum code) -* More Features added by GeneralFault, Wadee Womersley and Moopless +* Original version by Slush0 and ArtForz (original stratum code) +* More Features added by GeneralFault, Wadee Womersley, Viperaus, TheSeven and Moopless * Scrypt conversion from work done by viperaus -* PoS conversion done by TheSeven * Multi Algo, Vardiff, DB and MPOS support done by Ahmed_Bodi and Obigal #License From b9821911d10a979cf3f7a15bc89d4512169e7d59 Mon Sep 17 00:00:00 2001 From: ahmedbodi Date: Thu, 30 Jan 2014 11:28:20 +0000 Subject: [PATCH 4/7] Delete template_registry.py.save --- lib/template_registry.py.save | 288 ---------------------------------- 1 file changed, 288 deletions(-) delete mode 100644 lib/template_registry.py.save diff --git a/lib/template_registry.py.save b/lib/template_registry.py.save deleted file mode 100644 index 0a4cac7..0000000 --- a/lib/template_registry.py.save +++ /dev/null @@ -1,288 +0,0 @@ -import weakref -import binascii -import util -import StringIO -import settings -if settings.COINDAEMON_ALGO == 'scrypt': - import ltc_scrypt -else: pass -from twisted.internet import defer -from lib.exceptions import SubmitException - -import lib.logger -log = lib.logger.get_logger('template_registry') - -from mining.interfaces import Interfaces -from extranonce_counter import ExtranonceCounter -import lib.settings as settings - - -class JobIdGenerator(object): - '''Generate pseudo-unique job_id. It does not need to be absolutely unique, - because pool sends "clean_jobs" flag to clients and they should drop all previous jobs.''' - counter = 0 - - @classmethod - def get_new_id(cls): - cls.counter += 1 - if cls.counter % 0xffff == 0: - cls.counter = 1 - return "%x" % cls.counter - -class TemplateRegistry(object): - '''Implements the main logic of the pool. Keep track - on valid block templates, provide internal interface for stratum - service and implements block validation and submits.''' - - def __init__(self, block_template_class, coinbaser, bitcoin_rpc, instance_id, - on_template_callback, on_block_callback): - self.prevhashes = {} - self.jobs = weakref.WeakValueDictionary() - - self.extranonce_counter = ExtranonceCounter(instance_id) - self.extranonce2_size = block_template_class.coinbase_transaction_class.extranonce_size \ - - self.extranonce_counter.get_size() - - self.coinbaser = coinbaser - self.block_template_class = block_template_class - self.bitcoin_rpc = bitcoin_rpc - self.on_block_callback = on_block_callback - self.on_template_callback = on_template_callback - - self.last_block = None - self.update_in_progress = False - self.last_update = None - - # Create first block template on startup - self.update_block() - - def get_new_extranonce1(self): - '''Generates unique extranonce1 (e.g. for newly - subscribed connection.''' - return self.extranonce_counter.get_new_bin() - - def get_last_broadcast_args(self): - '''Returns arguments for mining.notify - from last known template.''' - return self.last_block.broadcast_args - - def add_template(self, block,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()) - print("hit template registry") - template.fill_from_rpc(data) template.fill_from_rpc(data) - self.add_template(template,data['height']) - - log.info("Update finished, %.03f sec, %d txes" % \ - (Interfaces.timestamper.time() - start, len(template.vtx))) - - self.update_in_progress = False - return data - - def diff_to_target(self, difficulty): - '''Converts difficulty to target''' - if settings.COINDAEMON_ALGO == 'scrypt': - diff1 = 0x0000ffff00000000000000000000000000000000000000000000000000000000 - else: diff1 = 0x00000000ffff0000000000000000000000000000000000000000000000000000 - return diff1 / difficulty - - def get_job(self, job_id): - '''For given job_id returns BlockTemplate instance or None''' - try: - j = self.jobs[job_id] - except: - log.info("Job id '%s' not found" % job_id) - return None - - # Now we have to check if job is still valid. - # Unfortunately weak references are not bulletproof and - # old reference can be found until next run of garbage collector. - if j.prevhash_hex not in self.prevhashes: - log.info("Prevhash of job '%s' is unknown" % job_id) - return None - - if j not in self.prevhashes[j.prevhash_hex]: - log.info("Job %s is unknown" % job_id) - return None - - return j - - def submit_share(self, job_id, worker_name, session, extranonce1_bin, extranonce2, ntime, nonce, - difficulty): - '''Check parameters and finalize block template. If it leads - to valid block candidate, asynchronously submits the block - back to the bitcoin network. - - - extranonce1_bin is binary. No checks performed, it should be from session data - - job_id, extranonce2, ntime, nonce - in hex form sent by the client - - difficulty - decimal number from session, again no checks performed - - submitblock_callback - reference to method which receive result of submitblock() - ''' - - # Check if extranonce2 looks correctly. extranonce2 is in hex form... - if len(extranonce2) != self.extranonce2_size * 2: - raise SubmitException("Incorrect size of extranonce2. Expected %d chars" % (self.extranonce2_size*2)) - - # Check for job - job = self.get_job(job_id) - if job == None: - raise SubmitException("Job '%s' not found" % job_id) - - # Check if ntime looks correct - if len(ntime) != 8: - raise SubmitException("Incorrect size of ntime. Expected 8 chars") - - if not job.check_ntime(int(ntime, 16)): - raise SubmitException("Ntime out of range") - - # Check nonce - if len(nonce) != 8: - raise SubmitException("Incorrect size of nonce. Expected 8 chars") - - # Check for duplicated submit - if not job.register_submit(extranonce1_bin, extranonce2, ntime, nonce): - log.info("Duplicate from %s, (%s %s %s %s)" % \ - (worker_name, binascii.hexlify(extranonce1_bin), extranonce2, ntime, nonce)) - raise SubmitException("Duplicate share") - - # Now let's do the hard work! - # --------------------------- - - # 0. Some sugar - extranonce2_bin = binascii.unhexlify(extranonce2) - ntime_bin = binascii.unhexlify(ntime) - nonce_bin = binascii.unhexlify(nonce) - - # 1. Build coinbase - coinbase_bin = job.serialize_coinbase(extranonce1_bin, extranonce2_bin) - coinbase_hash = util.doublesha(coinbase_bin) - - # 2. Calculate merkle root - merkle_root_bin = job.merkletree.withFirst(coinbase_hash) - merkle_root_int = util.uint256_from_str(merkle_root_bin) - - # 3. Serialize header with given merkle, ntime and nonce - header_bin = job.serialize_header(merkle_root_int, ntime_bin, nonce_bin) - - # 4. Reverse header and compare it with target of the user - if settings.COINDAEMON_ALGO == 'scrypt': - hash_bin = ltc_scrypt.getPoWHash(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ])) - 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': - header_hex = header_hex+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" - else: pass - - 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: - # 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.error("Final job validation failed!") - - # 7. Submit block to the network - serialized = binascii.hexlify(job.serialize()) - on_submit = self.bitcoin_rpc.submitblock(serialized, block_hash_hex) - if on_submit: - self.update_block() - - if settings.SOLUTION_BLOCK_HASH: - return (header_hex, block_hash_hex, share_diff, on_submit) - else: - return (header_hex, scrypt_hash_hex, share_diff, on_submit) - - if settings.SOLUTION_BLOCK_HASH: - # Reverse the header and get the potential block hash (for scrypt only) only do this if we want to send in the block hash to the shares table - block_hash_bin = util.doublesha(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ])) - block_hash_hex = block_hash_bin[::-1].encode('hex_codec') - return (header_hex, block_hash_hex, share_diff, None) - else: - return (header_hex, scrypt_hash_hex, share_diff, None) From df94a0967d0c0a010f677cb48a16c1894aef20e4 Mon Sep 17 00:00:00 2001 From: ahmedbodi Date: Thu, 30 Jan 2014 11:35:28 +0000 Subject: [PATCH 5/7] Attempt at force loading of shares Force load of shares when a block is submitted to the coind but before the db is updated --- mining/interfaces.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mining/interfaces.py b/mining/interfaces.py index eb4513f..4177aff 100644 --- a/mining/interfaces.py +++ b/mining/interfaces.py @@ -82,6 +82,7 @@ class ShareManagerInterface(object): def on_submit_block(self, is_accepted, worker_name, block_header, block_hash, timestamp, ip, share_diff): log.info("Block %s %s" % (block_hash, 'ACCEPTED' if is_accepted else 'REJECTED')) + dbi.do_import(True) dbi.found_block([worker_name, block_header, block_hash, -1, timestamp, is_accepted, ip, self.block_height, self.prev_hash, share_diff ]) class TimestamperInterface(object): From d1094c3715d44ebd2f84495d91f5bff7cd5d9398 Mon Sep 17 00:00:00 2001 From: Ahmed Bodiwala Date: Thu, 30 Jan 2014 22:12:25 +0000 Subject: [PATCH 6/7] Fixes --- lib/bitcoin_rpc_manager.py | 1 + twistd.pid | 1 + 2 files changed, 2 insertions(+) create mode 100644 twistd.pid diff --git a/lib/bitcoin_rpc_manager.py b/lib/bitcoin_rpc_manager.py index 5c362dc..7db401a 100644 --- a/lib/bitcoin_rpc_manager.py +++ b/lib/bitcoin_rpc_manager.py @@ -42,6 +42,7 @@ class BitcoinRPCManager(object): if len(self.conns) <= 1: log.error("Problem with Pool 0 -- NO ALTERNATE POOLS!!!") time.sleep(4) + self.curr_conn = 0 return log.error("Problem with Pool %i Switching to Next!" % (self.curr_conn) ) self.curr_conn = self.curr_conn + 1 diff --git a/twistd.pid b/twistd.pid new file mode 100644 index 0000000..8673aeb --- /dev/null +++ b/twistd.pid @@ -0,0 +1 @@ +13930 \ No newline at end of file From 41f11d9ac4606fb0a2ed3cbdc76a49b2027bf870 Mon Sep 17 00:00:00 2001 From: Ahmed Bodiwala Date: Thu, 30 Jan 2014 22:40:36 +0000 Subject: [PATCH 7/7] Multiple Fixed --- lib/bitcoin_rpc.py | 5 +++-- twistd.pid | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 twistd.pid diff --git a/lib/bitcoin_rpc.py b/lib/bitcoin_rpc.py index e26418a..7f23839 100644 --- a/lib/bitcoin_rpc.py +++ b/lib/bitcoin_rpc.py @@ -23,6 +23,7 @@ class BitcoinRPC(object): } client.HTTPClientFactory.noisy = False self.has_submitblock = False + def _call_raw(self, data): client.Headers return client.getPage( @@ -45,7 +46,7 @@ class BitcoinRPC(object): try: log.info("Checking for submitblock") resp = (yield self._call('submitblock', [])) - self.has_submitblock = Trie + self.has_submitblock = True except Exception as e: if (str(e) == "404 Not Found"): log.debug("No submitblock detected.") @@ -116,7 +117,7 @@ class BitcoinRPC(object): if json.loads(resp)['result'] == None: # make sure the block was created. log.info("CHECKING FOR BLOCK AFTER SUBMITBLOCK") - defer.returnValue((yield self.blockexists(hash_hex, block_hex))) + defer.returnValue((yield self.blockexists(hash_hex, scrypt_hex))) else: defer.returnValue(False) diff --git a/twistd.pid b/twistd.pid deleted file mode 100644 index 8673aeb..0000000 --- a/twistd.pid +++ /dev/null @@ -1 +0,0 @@ -13930 \ No newline at end of file