From 679245b565edcb2a03d865df3c08e86c0d6f402d Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Wed, 8 Jan 2014 23:29:14 -0600 Subject: [PATCH] remove dangling whitespace --- lib/bitcoin_rpc.py | 14 +++--- lib/block_template.py | 56 +++++++++++----------- lib/block_updater.py | 26 +++++----- lib/coinbaser.py | 28 +++++----- lib/coinbasetx.py | 18 ++++---- lib/extranonce_counter.py | 10 ++-- lib/halfnode.py | 40 ++++++++-------- lib/merkletree.py | 18 ++++---- lib/template_registry.py | 116 ++++++++++++++++++++++---------------------- lib/util.py | 16 +++--- mining/__init__.py | 16 +++--- mining/interfaces.py | 36 +++++++------- mining/service.py | 66 +++++++++++++------------- mining/subscription.py | 26 +++++----- 14 files changed, 243 insertions(+), 243 deletions(-) diff --git a/lib/bitcoin_rpc.py b/lib/bitcoin_rpc.py index 731e13a..d65c811 100644 --- a/lib/bitcoin_rpc.py +++ b/lib/bitcoin_rpc.py @@ -11,7 +11,7 @@ import stratum.logger log = stratum.logger.get_logger('bitcoin_rpc') class BitcoinRPC(object): - + def __init__(self, host, port, username, password): self.bitcoin_url = 'http://%s:%d' % (host, port) self.credentials = base64.b64encode("%s:%s" % (username, password)) @@ -19,7 +19,7 @@ class BitcoinRPC(object): 'Content-Type': 'text/json', 'Authorization': 'Basic %s' % self.credentials, } - + def _call_raw(self, data): return client.getPage( url=self.bitcoin_url, @@ -27,7 +27,7 @@ class BitcoinRPC(object): headers=self.headers, postdata=data, ) - + def _call(self, method, params): return self._call_raw(json.dumps({ 'jsonrpc': '2.0', @@ -48,12 +48,12 @@ class BitcoinRPC(object): def getinfo(self): resp = (yield self._call('getinfo', [])) defer.returnValue(json.loads(resp)['result']) - + @defer.inlineCallbacks def getblocktemplate(self): resp = (yield self._call('getblocktemplate', [])) defer.returnValue(json.loads(resp)['result']) - + @defer.inlineCallbacks def prevhash(self): resp = (yield self._call('getwork', [])) @@ -62,8 +62,8 @@ class BitcoinRPC(object): except Exception as e: log.exception("Cannot decode prevhash %s" % str(e)) raise - + @defer.inlineCallbacks def validateaddress(self, address): resp = (yield self._call('validateaddress', [address,])) - defer.returnValue(json.loads(resp)['result']) \ No newline at end of file + defer.returnValue(json.loads(resp)['result']) diff --git a/lib/block_template.py b/lib/block_template.py index 32b63b2..5293365 100644 --- a/lib/block_template.py +++ b/lib/block_template.py @@ -15,41 +15,41 @@ class BlockTemplate(halfnode.CBlock): '''Template is used for generating new jobs for clients. Let's iterate extranonce1, extranonce2, ntime and nonce to find out valid bitcoin block!''' - + coinbase_transaction_class = CoinbaseTransaction - + def __init__(self, timestamper, coinbaser, job_id): super(BlockTemplate, self).__init__() - - self.job_id = job_id + + self.job_id = job_id self.timestamper = timestamper self.coinbaser = coinbaser - + self.prevhash_bin = '' # reversed binary form of prevhash self.prevhash_hex = '' self.timedelta = 0 self.curtime = 0 self.target = 0 - #self.coinbase_hex = None + #self.coinbase_hex = None self.merkletree = None - + self.broadcast_args = [] - + # List of 4-tuples (extranonce1, extranonce2, ntime, nonce) # registers already submitted and checked shares # There may be registered also invalid shares inside! - self.submits = [] - + self.submits = [] + def fill_from_rpc(self, data): '''Convert getblocktemplate result into BlockTemplate instance''' - + #txhashes = [None] + [ binascii.unhexlify(t['hash']) for t in data['transactions'] ] txhashes = [None] + [ util.ser_uint256(int(t['hash'], 16)) for t in data['transactions'] ] mt = merkletree.MerkleTree(txhashes) coinbase = self.coinbase_transaction_class(self.timestamper, self.coinbaser, data['coinbasevalue'], data['coinbaseaux']['flags'], data['height'], settings.COINBASE_EXTRAS) - + self.height = data['height'] self.nVersion = data['version'] self.hashPrevBlock = int(data['previousblockhash'], 16) @@ -58,33 +58,33 @@ class BlockTemplate(halfnode.CBlock): self.nTime = 0 self.nNonce = 0 self.vtx = [ coinbase, ] - + for tx in data['transactions']: t = halfnode.CTransaction() t.deserialize(StringIO.StringIO(binascii.unhexlify(tx['data']))) self.vtx.append(t) - + self.curtime = data['curtime'] - self.timedelta = self.curtime - int(self.timestamper.time()) + self.timedelta = self.curtime - int(self.timestamper.time()) self.merkletree = mt self.target = util.uint256_from_compact(self.nBits) - + # Reversed prevhash self.prevhash_bin = binascii.unhexlify(util.reverse_hash(data['previousblockhash'])) self.prevhash_hex = "%064x" % self.hashPrevBlock - + self.broadcast_args = self.build_broadcast_args() - + def register_submit(self, extranonce1, extranonce2, ntime, nonce): '''Client submitted some solution. Let's register it to prevent double submissions.''' - + t = (extranonce1, extranonce2, ntime, nonce) if t not in self.submits: self.submits.append(t) return True return False - + def build_broadcast_args(self): '''Build parameters of mining.notify call. All clients may receive the same params, because they include @@ -98,7 +98,7 @@ class BlockTemplate(halfnode.CBlock): nbits = binascii.hexlify(struct.pack(">I", self.nBits)) ntime = binascii.hexlify(struct.pack(">I", self.curtime)) clean_jobs = True - + return (job_id, prevhash, coinb1, coinb2, merkle_branch, version, nbits, ntime, clean_jobs) def serialize_coinbase(self, extranonce1, extranonce2): @@ -106,17 +106,17 @@ class BlockTemplate(halfnode.CBlock): in binary form''' (part1, part2) = self.vtx[0]._serialized return part1 + extranonce1 + extranonce2 + part2 - + def check_ntime(self, ntime): '''Check for ntime restrictions.''' if ntime < self.curtime: return False - + if ntime > (self.timestamper.time() + 1000): # Be strict on ntime into the near future # may be unnecessary return False - + return True def serialize_header(self, merkle_root_int, ntime_bin, nonce_bin): @@ -126,15 +126,15 @@ class BlockTemplate(halfnode.CBlock): r += util.ser_uint256_be(merkle_root_int) r += ntime_bin r += struct.pack(">I", self.nBits) - r += nonce_bin - return r + r += nonce_bin + return r def finalize(self, merkle_root_int, extranonce1_bin, extranonce2_bin, ntime, nonce): '''Take all parameters required to compile block candidate. self.is_valid() should return True then...''' - + self.hashMerkleRoot = merkle_root_int self.nTime = ntime self.nNonce = nonce - self.vtx[0].set_extranonce(extranonce1_bin + extranonce2_bin) + self.vtx[0].set_extranonce(extranonce1_bin + extranonce2_bin) self.sha256 = None # We changed block parameters, let's reset sha256 cache diff --git a/lib/block_updater.py b/lib/block_updater.py index b80a024..fd03711 100644 --- a/lib/block_updater.py +++ b/lib/block_updater.py @@ -11,48 +11,48 @@ class BlockUpdater(object): ''' Polls upstream's getinfo() and detecting new block on the network. This will call registry.update_block when new prevhash appear. - + This is just failback alternative when something - with ./bitcoind -blocknotify will go wrong. + with ./bitcoind -blocknotify will go wrong. ''' - + def __init__(self, registry, bitcoin_rpc): self.bitcoin_rpc = bitcoin_rpc self.registry = registry self.clock = None self.schedule() - + def schedule(self): when = self._get_next_time() #log.debug("Next prevhash update in %.03f sec" % when) #log.debug("Merkle update in next %.03f sec" % \ # ((self.registry.last_update + settings.MERKLE_REFRESH_INTERVAL)-Interfaces.timestamper.time())) self.clock = reactor.callLater(when, self.run) - + def _get_next_time(self): when = settings.PREVHASH_REFRESH_INTERVAL - (Interfaces.timestamper.time() - self.registry.last_update) % \ settings.PREVHASH_REFRESH_INTERVAL - return when - + return when + @defer.inlineCallbacks def run(self): update = False - - try: + + try: if self.registry.last_block: current_prevhash = "%064x" % self.registry.last_block.hashPrevBlock else: current_prevhash = None - + prevhash = util.reverse_hash((yield self.bitcoin_rpc.prevhash())) if prevhash and prevhash != current_prevhash: log.info("New block! Prevhash: %s" % prevhash) update = True - + elif Interfaces.timestamper.time() - self.registry.last_update >= settings.MERKLE_REFRESH_INTERVAL: log.info("Merkle update! Prevhash: %s" % prevhash) update = True - + if update: self.registry.update_block() @@ -61,4 +61,4 @@ class BlockUpdater(object): finally: self.schedule() - \ No newline at end of file + diff --git a/lib/coinbaser.py b/lib/coinbaser.py index f3fd14b..5111c36 100644 --- a/lib/coinbaser.py +++ b/lib/coinbaser.py @@ -5,18 +5,18 @@ import stratum.logger log = stratum.logger.get_logger('coinbaser') # TODO: Add on_* hooks in the app - + class SimpleCoinbaser(object): '''This very simple coinbaser uses constant bitcoin address for all generated blocks.''' - + def __init__(self, bitcoin_rpc, address): # Fire callback when coinbaser is ready self.on_load = defer.Deferred() - + self.address = address self.is_valid = False # We need to check if pool can use this address - + self.bitcoin_rpc = bitcoin_rpc self._validate() @@ -24,35 +24,35 @@ class SimpleCoinbaser(object): d = self.bitcoin_rpc.validateaddress(self.address) d.addCallback(self._address_check) d.addErrback(self._failure) - + def _address_check(self, result): if result['isvalid'] and result['ismine']: self.is_valid = True log.info("Coinbase address '%s' is valid" % self.address) - + if not self.on_load.called: self.on_load.callback(True) - + else: self.is_valid = False log.error("Coinbase address '%s' is NOT valid!" % self.address) - + def _failure(self, failure): log.error("Cannot validate Bitcoin address '%s'" % self.address) raise - + #def on_new_block(self): # pass - + #def on_new_template(self): # pass - + def get_script_pubkey(self): if not self.is_valid: # Try again, maybe bitcoind was down? self._validate() raise Exception("Coinbase address is not validated!") - return util.script_to_address(self.address) - + return util.script_to_address(self.address) + def get_coinbase_data(self): - return '' \ No newline at end of file + return '' diff --git a/lib/coinbasetx.py b/lib/coinbasetx.py index 11c7e36..8953106 100644 --- a/lib/coinbasetx.py +++ b/lib/coinbasetx.py @@ -7,16 +7,16 @@ class CoinbaseTransaction(halfnode.CTransaction): '''Construct special transaction used for coinbase tx. It also implements quick serialization using pre-cached scriptSig template.''' - + extranonce_type = '>Q' extranonce_placeholder = struct.pack(extranonce_type, int('f000000ff111111f', 16)) extranonce_size = struct.calcsize(extranonce_type) def __init__(self, timestamper, coinbaser, value, flags, height, data): super(CoinbaseTransaction, self).__init__() - + #self.extranonce = 0 - + if len(self.extranonce_placeholder) != self.extranonce_size: raise Exception("Extranonce placeholder don't match expected length!") @@ -28,22 +28,22 @@ class CoinbaseTransaction(halfnode.CTransaction): chr(self.extranonce_size), util.ser_string(coinbaser.get_coinbase_data() + data) ) - + tx_in.scriptSig = tx_in._scriptSig_template[0] + self.extranonce_placeholder + tx_in._scriptSig_template[1] - + tx_out = halfnode.CTxOut() tx_out.nValue = value tx_out.scriptPubKey = coinbaser.get_script_pubkey() - + self.vin.append(tx_in) self.vout.append(tx_out) - + # Two parts of serialized coinbase, just put part1 + extranonce + part2 to have final serialized tx self._serialized = super(CoinbaseTransaction, self).serialize().split(self.extranonce_placeholder) def set_extranonce(self, extranonce): if len(extranonce) != self.extranonce_size: raise Exception("Incorrect extranonce size") - + (part1, part2) = self.vin[0]._scriptSig_template - self.vin[0].scriptSig = part1 + extranonce + part2 \ No newline at end of file + self.vin[0].scriptSig = part1 + extranonce + part2 diff --git a/lib/extranonce_counter.py b/lib/extranonce_counter.py index 19254f4..266f913 100644 --- a/lib/extranonce_counter.py +++ b/lib/extranonce_counter.py @@ -4,22 +4,22 @@ class ExtranonceCounter(object): '''Implementation of a counter producing unique extranonce across all pool instances. This is just dumb "quick&dirty" solution, - but it can be changed at any time without breaking anything.''' + but it can be changed at any time without breaking anything.''' def __init__(self, instance_id): if instance_id < 0 or instance_id > 31: raise Exception("Current ExtranonceCounter implementation needs an instance_id in <0, 31>.") - + # Last 5 most-significant bits represents instance_id # The rest is just an iterator of jobs. self.counter = instance_id << 27 self.size = struct.calcsize('>L') - + def get_size(self): '''Return expected size of generated extranonce in bytes''' return self.size - + def get_new_bin(self): self.counter += 1 return struct.pack('>L', self.counter) - \ No newline at end of file + diff --git a/lib/halfnode.py b/lib/halfnode.py index 233a5c1..d46ff75 100644 --- a/lib/halfnode.py +++ b/lib/halfnode.py @@ -145,12 +145,12 @@ class CTransaction(object): r += ser_vector(self.vout) r += struct.pack(" 1: newhashes = [] for i in xrange(0, len(hashes), 2): i2 = min(i+1, len(hashes)-1) newhashes.append(SHA256.new(SHA256.new(hashes[i] + hashes[i2]).digest()).digest()) hashes = newhashes - + if uint256_from_str(hashes[0]) != self.hashMerkleRoot: return False return True @@ -237,7 +237,7 @@ class msg_version(object): self.nNonce = random.getrandbits(64) self.strSubVer = MY_SUBVERSION self.nStartingHeight = 0 - + def deserialize(self, f): self.nVersion = struct.unpack(" target_user: raise SubmitException("Share is above target") @@ -233,22 +233,22 @@ class TemplateRegistry(object): if hash_int <= target_info: log.info("Yay, share with diff above 100000") - # 5. Compare hash with target of the network + # 5. Compare hash with target of the network if hash_int <= job.target: - # Yay! It is block candidate! + # Yay! It is block candidate! log.info("We found a block candidate! %s" % block_hash_hex) - - # 6. Finalize and serialize block object + + # 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) \ No newline at end of file + + return (header_hex, block_hash_hex, None) diff --git a/lib/util.py b/lib/util.py index 45da424..36e259d 100644 --- a/lib/util.py +++ b/lib/util.py @@ -139,14 +139,14 @@ def b58decode(v, length): result = chr(0)*nPad + result if length is not None and len(result) != length: return None - + return result def reverse_hash(h): # This only revert byte order, nothing more if len(h) != 64: raise Exception('hash must have 64 hexa chars') - + return ''.join([ h[56-i:64-i] for i in range(0, 64, 8) ]) def doublesha(b): @@ -160,17 +160,17 @@ def address_to_pubkeyhash(addr): addr = b58decode(addr, 25) except: return None - + if addr is None: return None - + ver = addr[0] cksumA = addr[-4:] cksumB = doublesha(addr[:-4])[:4] - + if cksumA != cksumB: return None - + return (ver, addr[1:-4]) def ser_uint256_be(u): @@ -179,7 +179,7 @@ def ser_uint256_be(u): for i in xrange(8): rs += struct.pack(">I", u & 0xFFFFFFFFL) u >>= 32 - return rs + return rs def deser_uint256_be(f): r = 0L @@ -203,4 +203,4 @@ def script_to_address(addr): if not d: raise ValueError('invalid address') (ver, pubkeyhash) = d - return b'\x76\xa9\x14' + pubkeyhash + b'\x88\xac' \ No newline at end of file + return b'\x76\xa9\x14' + pubkeyhash + b'\x88\xac' diff --git a/mining/__init__.py b/mining/__init__.py index 0bb7f7e..a8f3af9 100644 --- a/mining/__init__.py +++ b/mining/__init__.py @@ -13,17 +13,17 @@ def setup(on_startup): from stratum import settings from interfaces import Interfaces - + # Let's wait until share manager and worker manager boot up (yield Interfaces.share_manager.on_load) (yield Interfaces.worker_manager.on_load) - + from lib.block_updater import BlockUpdater from lib.template_registry import TemplateRegistry from lib.bitcoin_rpc import BitcoinRPC from lib.block_template import BlockTemplate from lib.coinbaser import SimpleCoinbaser - + bitcoin_rpc = BitcoinRPC(settings.BITCOIN_TRUSTED_HOST, settings.BITCOIN_TRUSTED_PORT, settings.BITCOIN_TRUSTED_USER, @@ -42,10 +42,10 @@ def setup(on_startup): break except: time.sleep(1) - + coinbaser = SimpleCoinbaser(bitcoin_rpc, settings.CENTRAL_WALLET) (yield coinbaser.on_load) - + registry = TemplateRegistry(BlockTemplate, coinbaser, bitcoin_rpc, @@ -56,11 +56,11 @@ def setup(on_startup): # Template registry is the main interface between Stratum service # and pool core logic Interfaces.set_template_registry(registry) - + # Set up polling mechanism for detecting new block on the network # This is just failsafe solution when -blocknotify - # mechanism is not working properly + # mechanism is not working properly BlockUpdater(registry, bitcoin_rpc) log.info("MINING SERVICE IS READY") - on_startup.callback(True) + on_startup.callback(True) diff --git a/mining/interfaces.py b/mining/interfaces.py index 1fef21a..40f1d2f 100644 --- a/mining/interfaces.py +++ b/mining/interfaces.py @@ -2,7 +2,7 @@ Default implementation do almost nothing, you probably want to override these classes and customize references to interface instances in your launcher. (see launcher_demo.tac for an example). -''' +''' import time from twisted.internet import reactor, defer @@ -15,22 +15,22 @@ class WorkerManagerInterface(object): # Fire deferred when manager is ready self.on_load = defer.Deferred() self.on_load.callback(True) - + def authorize(self, worker_name, worker_password): return True class ShareLimiterInterface(object): '''Implement difficulty adjustments here''' - + def submit(self, connection_ref, current_difficulty, timestamp): '''connection - weak reference to Protocol instance current_difficulty - difficulty of the connection timestamp - submission time of current share - + - raise SubmitException for stop processing this request - call mining.set_difficulty on connection to adjust the difficulty''' pass - + class ShareManagerInterface(object): def __init__(self): # Fire deferred when manager is ready @@ -40,13 +40,13 @@ class ShareManagerInterface(object): def on_network_block(self, prevhash): '''Prints when there's new block coming from the network (possibly new round)''' pass - + def on_submit_share(self, worker_name, block_header, block_hash, shares, timestamp, is_valid): log.info("%s %s %s" % (block_hash, 'valid' if is_valid else 'INVALID', worker_name)) - + def on_submit_block(self, is_accepted, worker_name, block_header, block_hash, timestamp): log.info("Block %s %s" % (block_hash, 'ACCEPTED' if is_accepted else 'REJECTED')) - + class TimestamperInterface(object): '''This is the only source for current time in the application. Override this for generating unix timestamp in different way.''' @@ -57,34 +57,34 @@ class PredictableTimestamperInterface(TimestamperInterface): '''Predictable timestamper may be useful for unit testing.''' start_time = 1345678900 # Some day in year 2012 delta = 0 - + def time(self): self.delta += 1 return self.start_time + self.delta - + class Interfaces(object): worker_manager = None share_manager = None share_limiter = None timestamper = None template_registry = None - + @classmethod def set_worker_manager(cls, manager): - cls.worker_manager = manager - - @classmethod + cls.worker_manager = manager + + @classmethod def set_share_manager(cls, manager): cls.share_manager = manager - @classmethod + @classmethod def set_share_limiter(cls, limiter): cls.share_limiter = limiter - + @classmethod def set_timestamper(cls, manager): cls.timestamper = manager - + @classmethod def set_template_registry(cls, registry): - cls.template_registry = registry \ No newline at end of file + cls.template_registry = registry diff --git a/mining/service.py b/mining/service.py index 30451d9..7bf4dce 100644 --- a/mining/service.py +++ b/mining/service.py @@ -9,78 +9,78 @@ from lib.exceptions import SubmitException import stratum.logger log = stratum.logger.get_logger('mining') - + class MiningService(GenericService): '''This service provides public API for Stratum mining proxy or any Stratum-compatible miner software. - + Warning - any callable argument of this class will be propagated over Stratum protocol for public audience!''' - + service_type = 'mining' service_vendor = 'stratum' is_default = True - + @admin def update_block(self): - '''Connect this RPC call to 'bitcoind -blocknotify' for + '''Connect this RPC call to 'bitcoind -blocknotify' for instant notification about new block on the network. See blocknotify.sh in /scripts/ for more info.''' - + log.info("New block notification received") Interfaces.template_registry.update_block() - return True - + return True + def authorize(self, worker_name, worker_password): '''Let authorize worker on this connection.''' - + session = self.connection_ref().get_session() session.setdefault('authorized', {}) - + if Interfaces.worker_manager.authorize(worker_name, worker_password): session['authorized'][worker_name] = worker_password return True - + else: if worker_name in session['authorized']: del session['authorized'][worker_name] return False - + def subscribe(self, *args): '''Subscribe for receiving mining jobs. This will return subscription details, extranonce1_hex and extranonce2_size''' - + extranonce1 = Interfaces.template_registry.get_new_extranonce1() extranonce2_size = Interfaces.template_registry.extranonce2_size extranonce1_hex = binascii.hexlify(extranonce1) - + session = self.connection_ref().get_session() session['extranonce1'] = extranonce1 session['difficulty'] = 1 # Following protocol specs, default diff is 1 return Pubsub.subscribe(self.connection_ref(), MiningSubscription()) + (extranonce1_hex, extranonce2_size) - - ''' + + ''' def submit(self, worker_name, job_id, extranonce2, ntime, nonce): import time start = time.time() - + for x in range(100): try: ret = self.submit2(worker_name, job_id, extranonce2, ntime, nonce) except: pass - + log.info("LEN %.03f" % (time.time() - start)) return ret ''' - + 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)): @@ -90,12 +90,12 @@ class MiningService(GenericService): extranonce1_bin = session.get('extranonce1', None) if not extranonce1_bin: raise SubmitException("Connection is not subscribed for mining") - + 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: @@ -104,13 +104,13 @@ class MiningService(GenericService): except SubmitException: # 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) + 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 @@ -118,18 +118,18 @@ class MiningService(GenericService): worker_name, block_header, block_hash, submit_time) return True - + # Service documentation for remote discovery update_block.help_text = "Notify Stratum server about new block on the network." update_block.params = [('password', 'string', 'Administrator password'),] - + authorize.help_text = "Authorize worker for submitting shares on this connection." authorize.params = [('worker_name', 'string', 'Name of the worker, usually in the form of user_login.worker_id.'), ('worker_password', 'string', 'Worker password'),] - + subscribe.help_text = "Subscribes current connection for receiving new mining jobs." subscribe.params = [] - + submit.help_text = "Submit solved share back to the server. Excessive sending of invalid shares "\ "or shares above indicated target (see Stratum mining docs for set_target()) may lead "\ "to temporary or permanent ban of user,worker or IP address." @@ -138,4 +138,4 @@ class MiningService(GenericService): ('extranonce2', 'string', 'hex-encoded big-endian extranonce2, length depends on extranonce2_size from mining.notify.'), ('ntime', 'string', 'UNIX timestamp (32bit integer, big-endian, hex-encoded), must be >= ntime provided by mining,notify and <= current time'), ('nonce', 'string', '32bit integer, hex-encoded, big-endian'),] - + diff --git a/mining/subscription.py b/mining/subscription.py index d04c62e..3eea5cc 100644 --- a/mining/subscription.py +++ b/mining/subscription.py @@ -7,48 +7,48 @@ log = stratum.logger.get_logger('subscription') class MiningSubscription(Subscription): '''This subscription object implements logic for broadcasting new jobs to the clients.''' - + event = 'mining.notify' - + @classmethod def on_template(cls, is_new_block): '''This is called when TemplateRegistry registers new block which we have to broadcast clients.''' - + start = Interfaces.timestamper.time() - + clean_jobs = is_new_block (job_id, prevhash, coinb1, coinb2, merkle_branch, version, nbits, ntime, _) = \ Interfaces.template_registry.get_last_broadcast_args() - + # Push new job to subscribed clients cls.emit(job_id, prevhash, coinb1, coinb2, merkle_branch, version, nbits, ntime, clean_jobs) - + cnt = Pubsub.get_subscription_count(cls.event) log.info("BROADCASTED to %d connections in %.03f sec" % (cnt, (Interfaces.timestamper.time() - start))) - + def _finish_after_subscribe(self, result): '''Send new job to newly subscribed client''' - try: + try: (job_id, prevhash, coinb1, coinb2, merkle_branch, version, nbits, ntime, _) = \ Interfaces.template_registry.get_last_broadcast_args() except Exception: log.error("Template not ready yet") return result - + # Force set higher difficulty # TODO #self.connection_ref().rpc('mining.set_difficulty', [2,], is_notification=True) #self.connection_ref().rpc('client.get_version', []) - + # Force client to remove previous jobs if any (eg. from previous connection) clean_jobs = True self.emit_single(job_id, prevhash, coinb1, coinb2, merkle_branch, version, nbits, ntime, True) - + return result - + def after_subscribe(self, *args): '''This will send new job to the client *after* he receive subscription details. on_finish callback solve the issue that job is broadcasted *during* the subscription request and client receive messages in wrong order.''' - self.connection_ref().on_finish.addCallback(self._finish_after_subscribe) \ No newline at end of file + self.connection_ref().on_finish.addCallback(self._finish_after_subscribe) -- 1.7.1