diff --git a/.gitignore b/.gitignore index 91b1cde..e118e77 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ conf/config.py *.log LOG +*.bak diff --git a/README.md b/README.md index 48857b8..e5b4c6e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [ ![Codeship Status for ahmedbodi/php-mpos](https://www.codeship.io/projects/b3003a70-61a3-0131-231e-26f75a0c690d/status?branch=master)](https://www.codeship.io/projects/12274) #Status -This project is now in what i'd consider v1 stage as such i dont believe there is much to add to stratum. so as of now this code is in bug fix stage only and should be considered unmaintained. +Riecoin support just implemented, needs testing. #Description Stratum-mining is a pooled mining protocol. It is a replacement for *getwork* based pooling servers by allowing clients to generate work. The stratum protocol is described [here](http://mining.bitcoin.cz/stratum-mining) in full detail. @@ -27,17 +27,6 @@ The goal is to make a reliable stratum mining server for a wide range of coins u * Proof Of Work and Proof of Stake Coin Support * Transaction Messaging Support -#Donations -* BTC: 18Xg4qP6RUvpeajanKPt5PDvvcqvU2pP6d -* BTE: 8UJLskr8eDYATvYzmaCBw3vbRmeNweT3rW -* DGC: DSBb5KmGWYKMJjxk3rETtvpk9sPqgCCYAw -* LTC: Lg4kXMqPsmMHrGr81LLe8oHpbsMiWiuMSB -* WDC: WeVFgZQsKSKXGak7NJPp9SrcUexghzTPGJ -* Doge: DLtBRYtNCzfiZfcpUeEr8KPvy5k1aR7jca -* SRC: sMP2wHN5H2ik7FQDPjhSzFZUWux75BYZGe -* ARG: AQvXPWVqGzcpH2j2XSRG7X5R9nA3y9D9aQ -* Cryptsy Trade Key: ec13d183e304326ebd41258d6ae7188e303866fe - #Requirements *stratum-mining* is built in python. I have been testing it with 2.7.3, but it should work with other versions. The requirements for running the software are below. @@ -45,7 +34,7 @@ The goal is to make a reliable stratum mining server for a wide range of coins u * python-twisted * stratum * MySQL Server -* SHA256 or Scrypt CoinDaemon +* CoinDaemon Other coins have been known to work with this implementation. I have tested with the following coins, but there may be many others that work. @@ -70,8 +59,8 @@ Other coins have been known to work with this implementation. I have tested with The installation of this *stratum-mining* can be found in the Repo Wiki. #Contact -I am available in the #MPOS, #crypto-expert, #digitalcoin, and #worldcoin channels on freenode. -Although i am willing to provide support through IRC please file issues on the repo. +See riecoin.org +Please file issues on the repo. Issues as a direct result of stratum will be helped with as much as possible However issues related to a coin daemon's setup and other non stratum issues, Please research and attempt to debug first. @@ -81,7 +70,8 @@ Please research and attempt to debug first. * Original version by Slush0 and ArtForz (original stratum code) * More Features added by GeneralFault, Wadee Womersley, Viperaus, TheSeven and Moopless * Multi Algo, Vardiff, DB and MPOS support done by Ahmed_Bodi, penner42 and Obigal +* Riecoin support implemented by gatra - RIC: RByJXMhtRa2Jc2ix6sWoVRZq3kyK3zb8nY - BTC: 1Ud6xgvXwKGksbguVgke8UTbJ9sYr9AuH #License -This software is provides AS-IS without any warranties of any kind. Please use at your own risk. +This software is provided AS-IS without any warranties of any kind. Please use at your own risk. diff --git a/conf/config_sample.py b/conf/config_sample.py index e34cd60..9f938fd 100644 --- a/conf/config_sample.py +++ b/conf/config_sample.py @@ -12,17 +12,17 @@ You NEED to set the parameters in BASIC SETTINGS CENTRAL_WALLET = 'set_valid_addresss_in_config!' # Local coin address where money goes COINDAEMON_TRUSTED_HOST = 'localhost' -COINDAEMON_TRUSTED_PORT = 8332 +COINDAEMON_TRUSTED_PORT = 28332 COINDAEMON_TRUSTED_USER = 'user' COINDAEMON_TRUSTED_PASSWORD = 'somepassword' # Coin algorithm is the option used to determine the algorithm used by stratum # This currently works with POW and POS coins # The available options are: -# scrypt, sha256d, scrypt-jane, skeinhash, and quark +# scrypt, sha256d, scrypt-jane, skeinhash, quark and riecoin # If the option does not meet either of these criteria stratum defaults to scrypt # For Coins which support TX Messages please enter yes in the TX selection -COINDAEMON_ALGO = 'scrypt' +COINDAEMON_ALGO = 'riecoin' COINDAEMON_TX = 'no' # ******************** BASIC SETTINGS *************** @@ -30,12 +30,12 @@ COINDAEMON_TX = 'no' # You can have up to 99 #COINDAEMON_TRUSTED_HOST_1 = 'localhost' -#COINDAEMON_TRUSTED_PORT_1 = 8332 +#COINDAEMON_TRUSTED_PORT_1 = 28332 #COINDAEMON_TRUSTED_USER_1 = 'user' #COINDAEMON_TRUSTED_PASSWORD_1 = 'somepassword' #COINDAEMON_TRUSTED_HOST_2 = 'localhost' -#COINDAEMON_TRUSTED_PORT_2 = 8332 +#COINDAEMON_TRUSTED_PORT_2 = 28332 #COINDAEMON_TRUSTED_USER_2 = 'user' #COINDAEMON_TRUSTED_PASSWORD_2 = 'somepassword' @@ -142,7 +142,7 @@ VDIFF_X2_TYPE = True # Powers of 2 e.g. 2,4,8,16,32,64,128,256,512,10 VDIFF_FLOAT = False # Use float difficulty # Pool Target (Base Difficulty) -POOL_TARGET = 32 # Pool-wide difficulty target int >= 1 +POOL_TARGET = 4 # Pool-wide difficulty target int >= 1 # Variable Difficulty Enable VARIABLE_DIFF = True # Master variable difficulty enable diff --git a/lib/block_template.py b/lib/block_template.py index bb67bca..d27b93f 100644 --- a/lib/block_template.py +++ b/lib/block_template.py @@ -69,6 +69,7 @@ class BlockTemplate(halfnode.CBlock): self.nVersion = data['version'] self.hashPrevBlock = int(data['previousblockhash'], 16) self.nBits = int(data['bits'], 16) + self.hashMerkleRoot = 0 self.nTime = 0 self.nNonce = 0 @@ -139,8 +140,12 @@ class BlockTemplate(halfnode.CBlock): r = struct.pack(">i", self.nVersion) r += self.prevhash_bin r += util.ser_uint256_be(merkle_root_int) - r += ntime_bin - r += struct.pack(">I", self.nBits) + if settings.COINDAEMON_ALGO == 'riecoin': + r += struct.pack(">I", self.nBits) + r += ntime_bin + else: + r += ntime_bin + r += struct.pack(">I", self.nBits) r += nonce_bin return r diff --git a/lib/config_default.py b/lib/config_default.py index 935748f..815cb75 100755 --- a/lib/config_default.py +++ b/lib/config_default.py @@ -109,7 +109,7 @@ COINDAEMON_TRUSTED_PASSWORD = '***somepassword***' # Until AutoReward Selecting Code has been implemented the below options are us$ # For Reward type there is POW and POS. please ensure you choose the currect ty$ # For SHA256 PoS Coins which support TX Messages please enter yes in the TX sel$ -COINDAEMON_ALGO = 'scrypt' +COINDAEMON_ALGO = 'riecoin' COINDAEMON_Reward = 'POW' COINDAEMON_SHA256_TX = 'yes' diff --git a/lib/halfnode.py b/lib/halfnode.py index eb29c4e..4ffc017 100644 --- a/lib/halfnode.py +++ b/lib/halfnode.py @@ -239,6 +239,8 @@ class CBlock(object): self.scrypt = None elif settings.COINDAEMON_ALGO == 'quark': self.quark = None + elif settings.COINDAEMON_ALGO == 'riecoin': + self.riecoin = None else: pass if settings.COINDAEMON_Reward == 'POS': self.signature = b"" @@ -248,9 +250,14 @@ class CBlock(object): self.nVersion = struct.unpack(" target: return False diff --git a/lib/template_registry.py b/lib/template_registry.py index 9bfdf0c..4202190 100644 --- a/lib/template_registry.py +++ b/lib/template_registry.py @@ -150,10 +150,14 @@ class TemplateRegistry(object): def diff_to_target(self, difficulty): '''Converts difficulty to target''' - if settings.COINDAEMON_ALGO == 'scrypt' or 'scrypt-jane': + if settings.COINDAEMON_ALGO == 'scrypt': + diff1 = 0x0000ffff00000000000000000000000000000000000000000000000000000000 + elif settings.COINDAEMON_ALGO == 'scrypt-jane': diff1 = 0x0000ffff00000000000000000000000000000000000000000000000000000000 elif settings.COINDAEMON_ALGO == 'quark': diff1 = 0x000000ffff000000000000000000000000000000000000000000000000000000 + elif settings.COINDAEMON_ALGO == 'riecoin': + return difficulty else: diff1 = 0x00000000ffff0000000000000000000000000000000000000000000000000000 @@ -202,15 +206,23 @@ class TemplateRegistry(object): 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 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 len(nonce) != 8: - raise SubmitException("Incorrect size of nonce. Expected 8 chars") + 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): @@ -225,6 +237,9 @@ class TemplateRegistry(object): 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) @@ -251,40 +266,58 @@ class TemplateRegistry(object): 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 == 'riecoin': + header_hex = header_hex+"00000080000000000000000080030000" 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") + 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 - if hash_int <= job.target: + 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 == '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) ])) + 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') - #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)") @@ -302,7 +335,10 @@ class TemplateRegistry(object): 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) ])) + 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: diff --git a/lib/util.py b/lib/util.py index f902b0a..f1d08ef 100644 --- a/lib/util.py +++ b/lib/util.py @@ -56,7 +56,10 @@ def uint256_from_str_be(s): def uint256_from_compact(c): nbytes = (c >> 24) & 0xFF - v = (c & 0xFFFFFFL) << (8 * (nbytes - 3)) + if nbytes <= 3: + v = (c & 0xFFFFFFL) >> (8 * (3 - nbytes)) + else: + v = (c & 0xFFFFFFL) << (8 * (nbytes - 3)) return v def deser_vector(f, c): @@ -211,6 +214,54 @@ def ser_number(n): s.append(n) return bytes(s) + +def isPrime( n ): + if pow( 2, n-1, n ) == 1: + return True + return False + +def riecoinPoW( hash_int, diff, nNonce ): + base = 1 << 8 + for i in range(256): + base = base << 1 + base = base | (hash_int & 1) + hash_int = hash_int >> 1 + trailingZeros = diff - 1 - 8 - 256 + if trailingZeros < 16 or trailingZeros > 20000: + return 0 + base = base << trailingZeros + + base += nNonce + + if (base % 210) != 97: + return 0 + + if not isPrime( base ): + return 0 + primes = 1 + + base += 4 + if isPrime( base ): + primes+=1 + + base += 2 + if isPrime( base ): + primes+=1 + + base += 4 + if isPrime( base ): + primes+=1 + + base += 2 + if isPrime( base ): + primes+=1 + + base += 4 + if isPrime( base ): + primes+=1 + + return primes + #if settings.COINDAEMON_Reward == 'POW': def script_to_address(addr): d = address_to_pubkeyhash(addr) diff --git a/mining/service.py b/mining/service.py index 6cb2efb..fd8a2d7 100644 --- a/mining/service.py +++ b/mining/service.py @@ -87,7 +87,7 @@ class MiningService(GenericService): else: session['difficulty'] = settings.POOL_TARGET # worker_log = (valid, invalid, is_banned, diff, is_ext_diff, timestamp) - Interfaces.worker_manager.worker_log['authorized'][worker_name] = (0, 0, False, session['difficulty'], is_ext_diff, Interfaces.timestamper.time()) + Interfaces.worker_manager.worker_log['authorized'][worker_name] = (0, 0, False, session['difficulty'], is_ext_diff, Interfaces.timestamper.time()) return True else: ip = self.connection_ref()._get_ip()