Skip to content

Commit

Permalink
Init
Browse files Browse the repository at this point in the history
  • Loading branch information
Coinotron committed Oct 15, 2015
1 parent 2739e32 commit 065bf64
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 215 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ debian/files
debian/*/
debian/*.debhelper*
debian/*.substvars
log
log
*.bat
73 changes: 10 additions & 63 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,77 +7,24 @@ as described here: http://mining.bitcoin.cz/stratum-mining.
Installation on Windows
-----------------------

1. Download official Windows binaries (EXE) from https://mining.bitcoin.cz/media/download/mining_proxy.exe
2. Open downloaded file. It will open console window. Using default settings, proxy connects to Slush's pool interface
3. If you want to connect to another pool or change other proxy settings, type "mining_proxy.exe --help" in console window.
1. Download official Windows binaries (EXE) from https://github.com/coinotron/eth-stratum-mining-proxy/
2. Open downloaded file. It will open console window. Using default settings, proxy connects to Coinotron
3. Another way to start proxy: mining_proxy.exe -o coinotron.com -p 3344
4. If you want to connect to another pool or change other proxy settings ( for example define custom worker and password ), type "mining_proxy.exe --help" in console window.

Installation on Linux - local hierarchy
Installation on Linux
---------------------------------------

1. Download TGZ file from https://github.com/slush0/stratum-mining-proxy/tarball/master
2. Unpack it by typing "tar xf slush0-stratum-mining_proxy*.tar.gz"
3. Most likely you already have Python respectively OpenSSL installed on your system. Otherwise install it by "sudo apt-get install python-dev libssl-dev"
(on Ubuntu and Debian).
3. Type "sudo python setup.py install" in the unpacked directory.
4. You can start the proxy by typing "./mining_proxy.py" in the terminal window. Using default settings,
proxy connects to Slush's pool interface.
5. If you want to connect to another pool or change other proxy settings, type "mining_proxy.py --help".
1. install twisted apt-get: install python-twisted
2. run proxy: python ./mining-proxy.py -o coinotron.com -p 3344
3. If you want to connect to another pool or change other proxy settings ( for example define custom worker and password ), type "mining_proxy.py --help".

Packaging for Debian
--------------------

1. Install devscripts, debhelper, pbuilder.
2. Download and unpack a tarball or clone this repository. Enter the unpacked/cloned direcotry.
3. Type "debuild-pbuilder -b -uc -us". You will be asked your password for sudo command. If you're a sudoer, skip to the last step.
4. If you're not a sudoer, an error will occur. Do `apt-get -f install' as root to correct the situation and call "debuild -b -uc -us".
5. A .deb package will be generated in parent directory. Use it to install stratum-mining-proxy on a Debian compatible system.

Installation on Mac
-------------------
1. Download TGZ file from https://github.com/slush0/stratum-mining-proxy/tarball/master
2. Unpack it by typing "tar xf slush0-stratum-mining-proxy*.tar.gz"
3. On Mac OS X you already have Python installed on your system, but you lack the llvm-gcc-4.2 binary required to run the setup.py file, so:
3. a) If you don't want to install Xcode, get gcc here: https://github.com/kennethreitz/osx-gcc-installer
3. b) OR download Xcode (free) from the App Store, Open it up (it's in your applications folder) and go to preferences, to the downloads section and download/install the 'command line tools'. This will install llvm-gc-4.2.
4. Type "sudo python setup.py install" in the unpacked directory from step 2.
5. You can start the proxy by typing "./mining_proxy.py" in the terminal window. Using default settings, proxy connects to Slush's pool interface.
6. If you want to connect to another pool or change other proxy settings, type "mining_proxy.py --help".

N.B. Once Apple releases Xcode 4.7 they will remove the optional install of gcc (they want you to use clang). When that happens you can either choose not to upgrade, or return to the aforementioned https://github.com/kennethreitz/osx-gcc-installer and download the specific gcc binary for your version of Mac OS.

Installation on Linux using Git
-------------------------------
This is advanced option for experienced users, but give you the easiest way for updating the proxy.

1. git clone git://github.com/slush0/stratum-mining-proxy.git
2. cd stratum-mining-proxy
3. sudo apt-get install python-dev # Development package of Python are necessary
4. sudo python distribute_setup.py # This will upgrade setuptools package
5. sudo python setup.py develop # This will install required dependencies (namely Twisted and Stratum libraries),
but don't install the package into the system.
6. You can start the proxy by typing "./mining_proxy.py" in the terminal window. Using default settings,
proxy connects to Slush's pool interface.
7. If you want to connect to another pool or change other proxy settings, type "./mining_proxy.py --help".
8. If you want to update the proxy, type "git pull" in the package directory.

Compiling midstate C extension
------------------------------
For some really big operations using getwork interface of this proxy, you'll find
useful "midstatec" C extension, which significantly speeds up midstate calculations
(yes, plain python implementation is *so* slow). For enabling this extension,
just type "make" in midstatec directory. Proxy will auto-detect compiled extension
on next startup.

Contact
-------

This proxy is provided by Slush's mining pool at http://mining.bitcoin.cz. You can contact the author
by email slush(at)satoshilabs.com.
This fork of proxy provided by Slush's mining pool at http://mining.bitcoin.cz. You can contact the author
by email coinotron(at)gmail.com.

Donation
--------
This project helps thousands of miners to improve their mining experience and optimize bandwidth of large
mining operations. Now it is listed on tip4commit service, so if you find this tool handy, feel free
to throw few satoshis to the basket :-).

[![tip for next commit](http://tip4commit.com/projects/322.svg)](http://tip4commit.com/projects/322)
22 changes: 9 additions & 13 deletions mining_libs/client_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,29 +44,25 @@ def handle_event(self, method, params, connection_ref):
if method == 'mining.notify':
'''Proxy just received information about new mining job'''

(job_id, prevhash, coinb1, coinb2, merkle_branch, version, nbits, ntime, clean_jobs) = params[:9]
(job_id, hexHeaderHash, hexSeedHash, hexShareTarget, clean_jobs) = params[:5]
#print len(str(params)), len(merkle_branch)

'''
log.debug("Received new job #%s" % job_id)
log.debug("prevhash = %s" % prevhash)
log.debug("version = %s" % version)
log.debug("nbits = %s" % nbits)
log.debug("ntime = %s" % ntime)
log.debug("hexHeaderHash = %s" % hexHeaderHash)
log.debug("hexSeedHash = %s" % hexSeedHash)
log.debug("hexShareTarget = %s" % hexShareTarget)
log.debug("clean_jobs = %s" % clean_jobs)
log.debug("coinb1 = %s" % coinb1)
log.debug("coinb2 = %s" % coinb2)
log.debug("merkle_branch = %s" % merkle_branch)
'''

# Broadcast to Stratum clients
stratum_listener.MiningSubscription.on_template(
job_id, prevhash, coinb1, coinb2, merkle_branch, version, nbits, ntime, clean_jobs)
#stratum_listener.MiningSubscription.on_template(
# job_id, hexHeaderHash, hexSeedHash, hexShareTarget, clean_jobs)

# Broadcast to getwork clients
job = Job.build_from_broadcast(job_id, prevhash, coinb1, coinb2, merkle_branch, version, nbits, ntime)
log.info("New job %s for prevhash %s, clean_jobs=%s" % \
(job.job_id, utils.format_hash(job.prevhash), clean_jobs))
job = Job.build_from_broadcast(job_id, hexHeaderHash, hexSeedHash, hexShareTarget)
log.info("New job %s for hexHeaderHash %s, clean_jobs=%s" % \
(job.job_id, hexHeaderHash, clean_jobs))

self.job_registry.add_template(job, clean_jobs)

Expand Down
31 changes: 26 additions & 5 deletions mining_libs/getwork_listener.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ def __init__(self, job_registry, workers, stratum_host, stratum_port,
self.custom_password = custom_password

def json_response(self, msg_id, result):
resp = json.dumps({'id': msg_id, 'result': result, 'error': None})
resp = json.dumps({'id': msg_id, 'jsonrpc': '2.0', 'result': result})
#print "RESPONSE", resp
return resp

def json_error(self, msg_id, code, message):
resp = json.dumps({'id': msg_id, 'result': None, 'error': {'code': code, 'message': message}})
resp = json.dumps({'id': msg_id, 'jsonrpc': '2.0', 'result': False, 'error': message})
#print "ERROR", resp
return resp

Expand Down Expand Up @@ -97,8 +97,25 @@ def _on_authorized(self, is_authorized, request, worker_name):
d.addCallback(self._on_submit, request, data.get('id', 0), data['params'][0][:160], worker_name, start_time)
d.addErrback(self._on_submit_failure, request, data.get('id', 0), data['params'][0][:160], worker_name, start_time)
return

request.write(self.json_error(data.get('id'), -1, "Unsupported method '%s'" % data['method']))
elif data['method'] == 'eth_getWork':
#log.info("Worker '%s' eth_getWork" % worker_name)
request.write(self.json_response(data.get('id', 0), self.job_registry.getwork()))
request.finish()
return
elif data['method'] == 'eth_submitWork':
d = defer.maybeDeferred(self.job_registry.submit, data['params'][0], data['params'][1], data['params'][2], worker_name)
start_time = time.time()
d.addCallback(self._on_submit, request, data.get('id', 0), data['params'], worker_name, start_time)
d.addErrback(self._on_submit_failure, request, data.get('id', 0), data['params'], worker_name, start_time)
return
elif data['method'] == 'eth_submitHashrate':
#log.info("Worker '%s' eth_submitHashrate" % worker_name)
request.write(self.json_response(data.get('id', 0), True))
request.finish()
return


request.write(self.json_error(1, -1, "Unsupported method '%s'" % data['method']))
request.finish()

def _on_failure(self, failure, request):
Expand Down Expand Up @@ -143,7 +160,11 @@ def _on_lp_broadcast(self, _, request):
def render_POST(self, request):
self._prepare_headers(request)

(worker_name, password) = (request.getUser(), request.getPassword())
worker_name = ''
uriSplit = request.uri.split(":")
if len(uriSplit) >= 2:
worker_name = uriSplit[0].replace("/", "")
password = uriSplit[1]

if self.custom_user:
worker_name = self.custom_user
Expand Down
142 changes: 31 additions & 111 deletions mining_libs/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import struct
import subprocess
import weakref
import datetime

from twisted.internet import defer

Expand Down Expand Up @@ -31,53 +32,27 @@
class Job(object):
def __init__(self):
self.job_id = None
self.prevhash = ''
self.coinb1_bin = ''
self.coinb2_bin = ''
self.merkle_branch = []
self.version = 1
self.nbits = 0
self.ntime_delta = 0

self.hexHeaderHash = ''
self.hexSeedHash = ''
self.hexShareTarget = ''
self.dtCreated = datetime.datetime.now()
self.extranonce2 = 0
self.merkle_to_extranonce2 = {} # Relation between merkle_hash and extranonce2

@classmethod
def build_from_broadcast(cls, job_id, prevhash, coinb1, coinb2, merkle_branch, version, nbits, ntime):
def build_from_broadcast(cls, job_id, hexHeaderHash, hexSeedHash, hexShareTarget):
'''Build job object from Stratum server broadcast'''
job = Job()
job.job_id = job_id
job.prevhash = prevhash
job.coinb1_bin = binascii.unhexlify(coinb1)
job.coinb2_bin = binascii.unhexlify(coinb2)
job.merkle_branch = [ binascii.unhexlify(tx) for tx in merkle_branch ]
job.version = version
job.nbits = nbits
job.ntime_delta = int(ntime, 16) - int(time.time())
job.hexHeaderHash = hexHeaderHash
job.hexSeedHash = hexSeedHash
job.hexShareTarget = hexShareTarget
return job

def increase_extranonce2(self):
self.extranonce2 += 1
return self.extranonce2

def build_coinbase(self, extranonce):
return self.coinb1_bin + extranonce + self.coinb2_bin

def build_merkle_root(self, coinbase_hash):
merkle_root = coinbase_hash
for h in self.merkle_branch:
merkle_root = utils.doublesha(merkle_root + h)
return merkle_root

def serialize_header(self, merkle_root, ntime, nonce):
r = self.version
r += self.prevhash
r += merkle_root
r += binascii.hexlify(struct.pack(">I", ntime))
r += self.nbits
r += binascii.hexlify(struct.pack(">I", nonce))
r += '000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000' # padding
return r

class JobRegistry(object):
def __init__(self, f, cmd, no_midstate, real_target, use_old_target=False, scrypt_target=False):
Expand Down Expand Up @@ -105,6 +80,8 @@ def __init__(self, f, cmd, no_midstate, real_target, use_old_target=False, scryp
# Hook for LP broadcasts
self.on_block = defer.Deferred()

self.headerHash2Job= weakref.WeakValueDictionary()

def execute_cmd(self, prevhash):
if self.cmd:
return subprocess.Popen(self.cmd.replace('%s', prevhash), shell=True)
Expand Down Expand Up @@ -149,19 +126,29 @@ def extranonce2_padding(self, extranonce2):
def add_template(self, template, clean_jobs):
if clean_jobs:
# Pool asked us to stop submitting shares from previous jobs
self.jobs = []
#log.info("Start deleting old jobs")
newJobs = []
for job in self.jobs:
dtDiff = datetime.datetime.now() - job.dtCreated
if dtDiff.total_seconds() < 300:
newJobs.append(job)
self.jobs = newJobs
for job in self.jobs:
dtDiff = datetime.datetime.now() - job.dtCreated
#log.info("Job %s %s " % (job.job_id, dtDiff.total_seconds()))

self.jobs.append(template)
self.last_job = template

self.headerHash2Job[template.hexHeaderHash] = template

if clean_jobs:
# Force miners to reload jobs
on_block = self.on_block
self.on_block = defer.Deferred()
on_block.callback(True)

# blocknotify-compatible call
self.execute_cmd(template.prevhash)
self.execute_cmd(template.hexHeaderHash)

def register_merkle(self, job, merkle_hash, extranonce2):
# merkle_to_job is weak-ref, so it is cleaned up automatically
Expand All @@ -176,87 +163,20 @@ def get_job_from_header(self, header):
extranonce2 = job.merkle_to_extranonce2[merkle_hash]
return (job, extranonce2)

def getwork(self, no_midstate=True):
def getwork(self):
'''Miner requests for new getwork'''

job = self.last_job # Pick the latest job from pool

# 1. Increase extranonce2
extranonce2 = job.increase_extranonce2()

# 2. Build final extranonce
extranonce = self.build_full_extranonce(extranonce2)

# 3. Put coinbase transaction together
coinbase_bin = job.build_coinbase(extranonce)

# 4. Calculate coinbase hash
coinbase_hash = utils.doublesha(coinbase_bin)

# 5. Calculate merkle root
merkle_root = binascii.hexlify(utils.reverse_hash(job.build_merkle_root(coinbase_hash)))

# 6. Generate current ntime
ntime = int(time.time()) + job.ntime_delta

# 7. Serialize header
block_header = job.serialize_header(merkle_root, ntime, 0)

# 8. Register job params
self.register_merkle(job, merkle_root, extranonce2)

# 9. Prepare hash1, calculate midstate and fill the response object
header_bin = binascii.unhexlify(block_header)[:64]
hash1 = "00000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000010000"

result = {'data': block_header,
'hash1': hash1}

if self.use_old_target:
result['target'] = 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000'
elif self.real_target:
result['target'] = self.target_hex
else:
result['target'] = self.target1_hex

if calculateMidstate and not (no_midstate or self.no_midstate):
# Midstate module not found or disabled
result['midstate'] = binascii.hexlify(calculateMidstate(header_bin))

return result

return [job.hexHeaderHash, job.hexSeedHash, job.hexShareTarget]

def submit(self, header, worker_name):
# Drop unused padding
header = header[:160]
def submit(self, hexNonce, hexHeaderHash, hexMixDigest, worker_name):
#log.info("Submitting %s %s %s %s" % (hexHeaderHash, hexNonce, hexMixDigest, worker_name))

# 1. Check if blockheader meets requested difficulty
header_bin = binascii.unhexlify(header[:160])
rev = ''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ])
hash_bin = utils.doublesha(rev)
block_hash = ''.join([ hash_bin[i*4:i*4+4][::-1] for i in range(0, 8) ])

#log.info('!!! %s' % header[:160])
log.info("Submitting %s" % utils.format_hash(binascii.hexlify(block_hash)))

if utils.uint256_from_str(hash_bin) > self.target:
log.debug("Share is below expected target")
return True

# 2. Lookup for job and extranonce used for creating given block header
try:
(job, extranonce2) = self.get_job_from_header(header)
job = self.headerHash2Job[hexHeaderHash]
except KeyError:
log.info("Job not found")
return False

# 3. Format extranonce2 to hex string
extranonce2_hex = binascii.hexlify(self.extranonce2_padding(extranonce2))

# 4. Parse ntime and nonce from header
ntimepos = 17*8 # 17th integer in datastring
noncepos = 19*8 # 19th integer in datastring
ntime = header[ntimepos:ntimepos+8]
nonce = header[noncepos:noncepos+8]

# 5. Submit share to the pool
return self.f.rpc('mining.submit', [worker_name, job.job_id, extranonce2_hex, ntime, nonce])
return self.f.rpc('mining.submit', [worker_name, job.job_id, hexNonce, hexHeaderHash, hexMixDigest])
Loading

0 comments on commit 065bf64

Please sign in to comment.