Multi-Algo Support
This commit is contained in:
parent
e658a91d9a
commit
6aebe098b2
@ -43,12 +43,14 @@ class BitcoinRPC(object):
|
|||||||
def submitblock(self, block_hex, block_hash_hex):
|
def submitblock(self, block_hex, block_hash_hex):
|
||||||
# Try submitblock if that fails, go to getblocktemplate
|
# Try submitblock if that fails, go to getblocktemplate
|
||||||
try:
|
try:
|
||||||
|
print("Submitting Block with Submit Block ")
|
||||||
resp = (yield self._call('submitblock', [block_hex,]))
|
resp = (yield self._call('submitblock', [block_hex,]))
|
||||||
except Exception:
|
except Exception:
|
||||||
try:
|
try:
|
||||||
|
print("Submit Block call failed, trying GetBlockTemplate")
|
||||||
resp = (yield self._call('getblocktemplate', [{'mode': 'submit', 'data': block_hex}]))
|
resp = (yield self._call('getblocktemplate', [{'mode': 'submit', 'data': block_hex}]))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.exception("Problem Submitting block %s" % str(e))
|
log.exception("Both SubmitBlock and GetBlockTemplate failed. Problem Submitting block %s" % str(e))
|
||||||
raise
|
raise
|
||||||
|
|
||||||
if json.loads(resp)['result'] == None:
|
if json.loads(resp)['result'] == None:
|
||||||
|
|||||||
@ -111,6 +111,12 @@ COINDAEMON_TRUSTED_PORT = 8332 # RPC port
|
|||||||
COINDAEMON_TRUSTED_USER = 'stratum'
|
COINDAEMON_TRUSTED_USER = 'stratum'
|
||||||
COINDAEMON_TRUSTED_PASSWORD = '***somepassword***'
|
COINDAEMON_TRUSTED_PASSWORD = '***somepassword***'
|
||||||
|
|
||||||
|
# Coin Algorithm is the option used to determine the algortithm used by stratum
|
||||||
|
# This currently only works with POW SHA256 and Scrypt Coins
|
||||||
|
# The available options are scrypt and sha256d.
|
||||||
|
# If the option does not meet either of these criteria stratum defaults to scrypt
|
||||||
|
COINDAEMON_ALGO = 'scrypt'
|
||||||
|
|
||||||
# ******************** OTHER CORE SETTINGS *********************
|
# ******************** OTHER CORE SETTINGS *********************
|
||||||
# Use "echo -n '<yourpassword>' | sha256sum | cut -f1 -d' ' "
|
# Use "echo -n '<yourpassword>' | sha256sum | cut -f1 -d' ' "
|
||||||
# for calculating SHA256 of your preferred password
|
# for calculating SHA256 of your preferred password
|
||||||
|
|||||||
@ -15,7 +15,14 @@ from Crypto.Hash import SHA256
|
|||||||
from twisted.internet.protocol import Protocol
|
from twisted.internet.protocol import Protocol
|
||||||
from util import *
|
from util import *
|
||||||
|
|
||||||
import ltc_scrypt
|
import settings
|
||||||
|
if settings.COINDAEMON_ALGO == 'scrypt':
|
||||||
|
print("########################################### Loading LTC Scrypt Module #########################################################")
|
||||||
|
import ltc_scrypt
|
||||||
|
else:
|
||||||
|
print("########################################### NOT Loading LTC Scrypt Module ######################################################")
|
||||||
|
pass
|
||||||
|
|
||||||
import lib.logger
|
import lib.logger
|
||||||
log = lib.logger.get_logger('halfnode')
|
log = lib.logger.get_logger('halfnode')
|
||||||
|
|
||||||
@ -174,7 +181,9 @@ class CBlock(object):
|
|||||||
self.nNonce = 0
|
self.nNonce = 0
|
||||||
self.vtx = []
|
self.vtx = []
|
||||||
self.sha256 = None
|
self.sha256 = None
|
||||||
self.scrypt = None
|
if settings.COINDAEMON_ALGO == 'scrypt':
|
||||||
|
self.scrypt = None
|
||||||
|
else: pass
|
||||||
def deserialize(self, f):
|
def deserialize(self, f):
|
||||||
self.nVersion = struct.unpack("<i", f.read(4))[0]
|
self.nVersion = struct.unpack("<i", f.read(4))[0]
|
||||||
self.hashPrevBlock = deser_uint256(f)
|
self.hashPrevBlock = deser_uint256(f)
|
||||||
@ -193,37 +202,45 @@ class CBlock(object):
|
|||||||
r.append(struct.pack("<I", self.nNonce))
|
r.append(struct.pack("<I", self.nNonce))
|
||||||
r.append(ser_vector(self.vtx))
|
r.append(ser_vector(self.vtx))
|
||||||
return ''.join(r)
|
return ''.join(r)
|
||||||
def calc_sha256(self):
|
|
||||||
if self.sha256 is None:
|
|
||||||
r = []
|
|
||||||
r.append(struct.pack("<i", self.nVersion))
|
|
||||||
r.append(ser_uint256(self.hashPrevBlock))
|
|
||||||
r.append(ser_uint256(self.hashMerkleRoot))
|
|
||||||
r.append(struct.pack("<I", self.nTime))
|
|
||||||
r.append(struct.pack("<I", self.nBits))
|
|
||||||
r.append(struct.pack("<I", self.nNonce))
|
|
||||||
self.sha256 = uint256_from_str(SHA256.new(SHA256.new(''.join(r)).digest()).digest())
|
|
||||||
return self.sha256
|
|
||||||
|
|
||||||
def calc_scrypt(self):
|
if settings.COINDAEMON_ALGO == 'scrypt':
|
||||||
if self.scrypt is None:
|
def calc_scrypt(self):
|
||||||
r = []
|
if self.scrypt is None:
|
||||||
r.append(struct.pack("<i", self.nVersion))
|
r = []
|
||||||
r.append(ser_uint256(self.hashPrevBlock))
|
r.append(struct.pack("<i", self.nVersion))
|
||||||
r.append(ser_uint256(self.hashMerkleRoot))
|
r.append(ser_uint256(self.hashPrevBlock))
|
||||||
r.append(struct.pack("<I", self.nTime))
|
r.append(ser_uint256(self.hashMerkleRoot))
|
||||||
r.append(struct.pack("<I", self.nBits))
|
r.append(struct.pack("<I", self.nTime))
|
||||||
r.append(struct.pack("<I", self.nNonce))
|
r.append(struct.pack("<I", self.nBits))
|
||||||
self.scrypt = uint256_from_str(ltc_scrypt.getPoWHash(''.join(r)))
|
r.append(struct.pack("<I", self.nNonce))
|
||||||
return self.scrypt
|
self.scrypt = uint256_from_str(ltc_scrypt.getPoWHash(''.join(r)))
|
||||||
|
return self.scrypt
|
||||||
|
else:
|
||||||
|
def calc_sha256(self):
|
||||||
|
if self.sha256 is None:
|
||||||
|
r = []
|
||||||
|
r.append(struct.pack("<i", self.nVersion))
|
||||||
|
r.append(ser_uint256(self.hashPrevBlock))
|
||||||
|
r.append(ser_uint256(self.hashMerkleRoot))
|
||||||
|
r.append(struct.pack("<I", self.nTime))
|
||||||
|
r.append(struct.pack("<I", self.nBits))
|
||||||
|
r.append(struct.pack("<I", self.nNonce))
|
||||||
|
self.sha256 = uint256_from_str(SHA256.new(SHA256.new(''.join(r)).digest()).digest())
|
||||||
|
return self.sha256
|
||||||
|
|
||||||
|
|
||||||
def is_valid(self):
|
def is_valid(self):
|
||||||
#self.calc_sha256()
|
if settings.COINDAEMON_ALGO == 'scrypt':
|
||||||
self.calc_scrypt()
|
self.calc_scrypt()
|
||||||
|
else:
|
||||||
|
self.calc_sha256()
|
||||||
target = uint256_from_compact(self.nBits)
|
target = uint256_from_compact(self.nBits)
|
||||||
#if self.sha256 > target:
|
if settings.COINDAEMON_ALGO == 'scrypt':
|
||||||
if self.scrypt > target:
|
if self.scrypt > target:
|
||||||
return False
|
return false
|
||||||
|
else:
|
||||||
|
if self.sha256 > target:
|
||||||
|
return False
|
||||||
hashes = []
|
hashes = []
|
||||||
for tx in self.vtx:
|
for tx in self.vtx:
|
||||||
tx.sha256 = None
|
tx.sha256 = None
|
||||||
|
|||||||
@ -2,8 +2,10 @@ import weakref
|
|||||||
import binascii
|
import binascii
|
||||||
import util
|
import util
|
||||||
import StringIO
|
import StringIO
|
||||||
import ltc_scrypt
|
import settings
|
||||||
|
if settings.COINDAEMON_ALGO == 'scrypt':
|
||||||
|
import ltc_scrypt
|
||||||
|
else: pass
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
from lib.exceptions import SubmitException
|
from lib.exceptions import SubmitException
|
||||||
|
|
||||||
@ -140,9 +142,10 @@ class TemplateRegistry(object):
|
|||||||
|
|
||||||
def diff_to_target(self, difficulty):
|
def diff_to_target(self, difficulty):
|
||||||
'''Converts difficulty to target'''
|
'''Converts difficulty to target'''
|
||||||
#diff1 = 0x00000000ffff0000000000000000000000000000000000000000000000000000
|
if settings.COINDAEMON_ALGO == 'scrypt':
|
||||||
diff1 = 0x0000ffff00000000000000000000000000000000000000000000000000000000
|
diff1 = 0x0000ffff00000000000000000000000000000000000000000000000000000000
|
||||||
return diff1 / difficulty
|
else: diff1 = 0x00000000ffff0000000000000000000000000000000000000000000000000000
|
||||||
|
return diff1 / difficulty
|
||||||
|
|
||||||
def get_job(self, job_id):
|
def get_job(self, job_id):
|
||||||
'''For given job_id returns BlockTemplate instance or None'''
|
'''For given job_id returns BlockTemplate instance or None'''
|
||||||
@ -223,12 +226,15 @@ class TemplateRegistry(object):
|
|||||||
header_bin = job.serialize_header(merkle_root_int, ntime_bin, nonce_bin)
|
header_bin = job.serialize_header(merkle_root_int, ntime_bin, nonce_bin)
|
||||||
|
|
||||||
# 4. Reverse header and compare it with target of the user
|
# 4. Reverse header and compare it with target of the user
|
||||||
hash_bin = ltc_scrypt.getPoWHash(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ]))
|
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)
|
hash_int = util.uint256_from_str(hash_bin)
|
||||||
scrypt_hash_hex = "%064x" % hash_int
|
scrypt_hash_hex = "%064x" % hash_int
|
||||||
header_hex = binascii.hexlify(header_bin)
|
header_hex = binascii.hexlify(header_bin)
|
||||||
header_hex = header_hex+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
|
if settings.COINDAEMON_ALGO == 'scrypt':
|
||||||
|
header_hex = header_hex+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
|
||||||
|
else: pass
|
||||||
|
|
||||||
target_user = self.diff_to_target(difficulty)
|
target_user = self.diff_to_target(difficulty)
|
||||||
if hash_int > target_user and \
|
if hash_int > target_user and \
|
||||||
@ -245,16 +251,16 @@ class TemplateRegistry(object):
|
|||||||
share_diff = int(self.diff_to_target(hash_int))
|
share_diff = int(self.diff_to_target(hash_int))
|
||||||
|
|
||||||
|
|
||||||
# 5. Compare hash with target of the network
|
# 5. Compare hash with target of the network
|
||||||
if hash_int <= job.target:
|
if hash_int <= job.target:
|
||||||
# Yay! It is block candidate!
|
# Yay! It is block candidate!
|
||||||
log.info("We found a block candidate! %s" % scrypt_hash_hex)
|
log.info("We found a block candidate! %s" % scrypt_hash_hex)
|
||||||
|
|
||||||
# Reverse the header and get the potential block hash (for scrypt only)
|
# 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_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')
|
block_hash_hex = block_hash_bin[::-1].encode('hex_codec')
|
||||||
|
|
||||||
# 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))
|
job.finalize(merkle_root_int, extranonce1_bin, extranonce2_bin, int(ntime, 16), int(nonce, 16))
|
||||||
|
|
||||||
if not job.is_valid():
|
if not job.is_valid():
|
||||||
|
|||||||
287
lib/template_registry.py.save
Normal file
287
lib/template_registry.py.save
Normal file
@ -0,0 +1,287 @@
|
|||||||
|
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())
|
||||||
|
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:
|
||||||
|
print("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!
|
||||||
|
print("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)
|
||||||
Loading…
Reference in New Issue
Block a user