From e28a331c04e3c7a4b0c11382a885f4b6765e82b0 Mon Sep 17 00:00:00 2001 From: m1 Date: Wed, 26 Jul 2017 18:44:22 -0700 Subject: [PATCH] initial commit --- LICENSE | 19 ++++++++ README.md | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++ mph_conf.json | 68 ++++++++++++++++++++++++++++ mph_switch.py | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 333 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 mph_conf.json create mode 100644 mph_switch.py diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d718ad8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright © 2017 Scott Alfter + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..99dd4f3 --- /dev/null +++ b/README.md @@ -0,0 +1,123 @@ +nvOC MiningPoolHub Switcher +=========================== + +nvOC makes it easy to squeeze more hashrate out of your GPUs. MiningPoolHub +makes it easy to mine altcoins and get paid in Bitcoin. However, nvOC is a +customized Ubuntu Linux and the management software MiningPoolHub recommends +for profit switching only runs under Windows. Their approach to +coin-switching with nVidia GPUs under Linux is to loop through a bunch of +different miners, erroring out on all but the currently-profitable coin. I +lost nearly four hours this morning when a miner didn't exit properly during +a switch. + +This set of scripts bridges the gap. With them, you can use the miners +included in nvOC (and others that you might add) to mine whatever's most +profitable at MiningPoolHub. For each supported algorithm, you can +customize power, clockspeed, and fan speed settings as you consider +appropriate. + +Setup +----- + +1. ssh into your mining rig's m1 account. +2. Clone these files into the same directory as nvOC's oneBash script. +3. Disable oneBash by commenting out all lines in ~/2unix. oneBash should +be invoked manually if you add/remove GPUs so it can reconfigure +/etc/X11/xorg.conf, but it otherwise isn't used. To keep gnome-terminal +from sucking down CPU cycles by constantly restarting the 2unix script, you +might want to include something like "sleep 86400" at the end. +4. Edit mph_conf.json. You'll set your user and miner names here. You can +also customize your overclocking settings for each algorithm here. +5. If your network is IPv6-enabled, note that nvOC (as of v17) has a problem +connecting to IPv6 hosts. MiningPoolHub makes its API available over both IPv6 +and IPv4, so to force it to use IPv4 in the meantime, edit /etc/hosts as +follows: + + `104.27.168.52 miningpoolhub.com` +6. Set mph_switch.py to launch with a cronjob: + + `0,10,20,30,40,50 * * * * (cd /media/m1/1263-A96E && python2.7 mph_switch.py mph_conf.json)` + +Every 10 minutes, the script will run and determine which miner needs to +start up. If it's already running, it will leave it alone. If it needs to +switch, it kills one miner and starts the other. If nothing's running +(because the rig just rebooted or the miner crashed), it will start the +miner. + +current-profit is a text file that will be updated on every run of switch.py +to show which is currently the most profitable. Profitability is determined +by the MiningPoolHub API and is a unitless number; higher is better. + +algo-log is a list of coins and algorithms the switcher has selected when +run. If you start seeing repeats of the same algo every 10 minutes, that's +a sign that something has gone wrong with your GPUs and you might want to +consider a reboot. + +Miners are started in Screen sessions (http://www.gnu.org/software/screen) +that can be viewed over an ssh login session: + +`screen -dr miner` + +When the rig switches algorithms, the Screen session will be closed and a +new one opened. Repeat the above to monitor it, or you could use something +like this on a monitoring workstation: + +`while true; do screen -dr miner; done` + +Adding Algorithms +----------------- + +Some algorithms supported by MiningPoolHub can't be mined with the miners +included in nvOC. If you add the appropriate software, though, you can mine +these as well. Instructions to add these capabilities will follow. + +Sia +--- + +To add the miner: + +`sudo apt install golang-go +mkdir /home/m1/gocode +GOPATH=/home/m1/gocode go get github.com/robvanmieghem/gominer` + +To enable mining, create a new object named "sia" within the "miners" +list in mph_conf.json. Here's what I use: + +``` + "miners": + { + [existing entries deleted for brevity] + "Sia": + { + "bin": "/home/m1/SPccminer/ccminer -a sia -o stratum+tcp://{HOST}:{PORT} -u {NAME}.{MINER} -p x", + "power_limit": [115,115,95], "gpu_oc": [-100,-100,0], "mem_oc": 0, "fan": [70,75,70] + } + } +``` + +CryptoNight +----------- + +To add the miner: + +`cd /home/m1 +git clone https://github.com/KlausT/ccminer-cryptonight KTccminer-cryptonight +cd KTccminer-cryptonight +./autoreconf.sh +./configure --with-cuda-/usr/local/cuda-8.0 +make` + +To enable mining, create a new object named "cryptonight" within the "miners" +list in mph_conf.json. Here's what I use: + +``` + "miners": + { + [existing entries deleted for brevity] + "Cryptonight": + { + "bin": "/home/m1/KTccminer-cryptonight/ccminer -a cryptonight -o stratum+tcp://{HOST}:{PORT} -u {NAME}.{MINER} -p x", + "power_limit": [115,115,95], "gpu_oc": -200, "mem_oc": 1000, "fan": [70,75,70] + } + } +``` diff --git a/mph_conf.json b/mph_conf.json new file mode 100644 index 0000000..c07deb7 --- /dev/null +++ b/mph_conf.json @@ -0,0 +1,68 @@ +{ + "user_name": "salfter", + "miner_name": "miner10", + "card_type": "nvidia", + "miners": + { + "Blake-Vanilla": + { + "bin": "/home/m1/SPccminer/ccminer -a vanilla -o stratum+tcp://{HOST}:{PORT} -u {NAME}.{MINER} -p x", + "power_limit": [115,115,95], "gpu_oc": [-100,-100,0], "mem_oc": 0, "fan": [70,75,70] + }, + "Equihash": + { + "bin": "/home/m1/zec/v3_4/miner --eexit 3 --fee 0 --pec --server {HOST} --user {NAME}.{MINER} --pass z --port {PORT}", + "power_limit": [115,115,95], "gpu_oc": [-100,-100,0], "mem_oc": 0, "fan": [70,75,70] + }, + "Ethash": + { + "bin": "/home/m1/eth/Genoil-U/ethminer -S {HOST}:{PORT} -O {NAME}.{MINER}:x -U", + "power_limit": [115,115,95], "gpu_oc": -200, "mem_oc": 1000, "fan": [70,75,70] + }, + "Groestl": + { + "bin": "/home/m1/SPccminer/ccminer -a groestl -o stratum+tcp://{HOST}:{PORT} -u {NAME}.{MINER} -p x", + "power_limit": [115,115,95], "gpu_oc": [-100,-100,0], "mem_oc": 0, "fan": [70,75,70] + }, + "Keccak": + { + "bin": "/home/m1/SPccminer/ccminer -a keccak -o stratum+tcp://{HOST}:{PORT} -u {NAME}.{MINER} -p x", + "power_limit": [115,115,95], "gpu_oc": [-100,-100,0], "mem_oc": 0, "fan": [70,75,70] + }, + "Lyra2RE2": + { + "bin": "/home/m1/SPccminer/ccminer -a lyra2v2 -o stratum+tcp://{HOST}:{PORT} -u {NAME}.{MINER} -p x", + "power_limit": [115,115,95], "gpu_oc": [-100,-100,0], "mem_oc": 0, "fan": [70,75,70] + }, + "Myriad-Groestl": + { + "bin": "/home/m1/SPccminer/ccminer -a myr-gr -o stratum+tcp://{HOST}:{PORT} -u {NAME}.{MINER} -p x", + "power_limit": [115,115,95], "gpu_oc": [-100,-100,0], "mem_oc": 0, "fan": [70,75,70] + }, + "NeoScrypt": + { + "bin": "/home/m1/SPccminer/ccminer -a neoscrypt -o stratum+tcp://{HOST}:{PORT} -u {NAME}.{MINER} -p x", + "power_limit": [115,115,95], "gpu_oc": [-100,-100,0], "mem_oc": 0, "fan": [70,75,70] + }, + "Qubit": + { + "bin": "/home/m1/SPccminer/ccminer -a qubit -o stratum+tcp://{HOST}:{PORT} -u {NAME}.{MINER} -p x", + "power_limit": [115,115,95], "gpu_oc": [-100,-100,0], "mem_oc": 0, "fan": [70,75,70] + }, + "Scrypt": + { + "bin": "/home/m1/SPccminer/ccminer -a scrypt -o stratum+tcp://{HOST}:{PORT} -u {NAME}.{MINER} -p x", + "power_limit": [115,115,95], "gpu_oc": [-100,-100,0], "mem_oc": 0, "fan": [70,75,70] + }, + "Skein": + { + "bin": "/home/m1/SPccminer/ccminer -a skein -o stratum+tcp://{HOST}:{PORT} -u {NAME}.{MINER} -p x", + "power_limit": [115,115,95], "gpu_oc": [-100,-100,0], "mem_oc": 0, "fan": [70,75,70] + }, + "X11": + { + "bin": "/home/m1/SPccminer/ccminer -a x11 -o stratum+tcp://{HOST}:{PORT} -u {NAME}.{MINER} -p x", + "power_limit": [115,115,95], "gpu_oc": [-100,-100,0], "mem_oc": 0, "fan": [70,75,70] + } + } +} diff --git a/mph_switch.py b/mph_switch.py new file mode 100644 index 0000000..86d7b79 --- /dev/null +++ b/mph_switch.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python2.7 + +import pprint +import json +import urllib2 +import sys +import datetime +import time +import subprocess +import os + +# load config + +cfg=json.loads(open(sys.argv[1]).read()) + +card_type=cfg["card_type"] +miners=cfg["miners"] +user_name=cfg["user_name"] +miner_name=cfg["miner_name"] + +os.environ["DISPLAY"]=":0" + +# grab something from a website + +def fetch(url): + r=urllib2.Request(url) + r.add_header("User-Agent", "Lynx/2.8.8dev.3 libwww-FM/2.14 SSL-MM/1.4.1") + r.add_header("Pragma", "no-cache") + return urllib2.build_opener().open(r).read() + +# main + +data=json.loads(fetch("http://miningpoolhub.com/?page=api&action=getminingandprofitsstatistics"))["return"] + +coins={} +for i in reversed(data): # weed out unconfigured algos, do string substitutions + try: + miner=miners[i["algo"]] + coins[i["coin_name"]]=miner["bin"].format(HOST=i["host"], PORT=str(i["port"]), NAME=user_name, MINER=miner_name) + except: + data.remove(i) + +sort={} +for i in data: + sort[i["coin_name"]+" ("+i["algo"]+")"]=i["normalized_profit_"+card_type] +sort=sorted(sort.items(), key=lambda x:x[1], reverse=True) +log=open("current-profit", "w") +for i in sort: + log.write(i[0]+": "+str(i[1])+"\n") +log.close() + +max_profit_val=0 # find the most profitable coin +for i in data: + if i["normalized_profit_"+card_type]>max_profit_val: + max_profit=i + max_profit_val=i["normalized_profit_"+card_type] + +miner=miners[max_profit["algo"]] +coin=coins[max_profit["coin_name"]] + +# see if miner's already running + +try: + subprocess.check_output(["pgrep", "-f", "^"+coin.replace("+", "\\+")]) + current=1 +except: + current=0 +if (current==0): + other=0 + for algo in coins: + try: + subprocess.check_output(["pgrep", "-f", "^"+coins[algo].replace("+", "\\+")]) + other=1 + except: + pass + +if (current==0): + # log a change + algo_log=open("algo-log", "a") + algo_log.write(str(datetime.datetime.now())+": "+max_profit["coin_name"]+" ("+max_profit["algo"]+")\n") + algo_log.close() + if (other==1): + # kill existing miners + for algo in coins: + subprocess.call(["pkill", "-f", "^"+coins[algo].replace("+", "\\+")]) + time.sleep(3) + if card_type=="nvidia": # update card settings + cards=int(subprocess.check_output("nvidia-smi --query-gpu=count --format=csv,noheader,nounits".split(" ")).split("\n")[-2]) + for i in range(0, cards): + # power limit + if type(miner["power_limit"]) is int: + subprocess.call(("sudo nvidia-smi -i "+str(i)+" -pl "+str(miner["power_limit"])).split(" ")) + else: + subprocess.call(("sudo nvidia-smi -i "+str(i)+" -pl "+str(miner["power_limit"][i])).split(" ")) + # core overclock + if type(miner["gpu_oc"]) is int: + subprocess.call(("nvidia-settings -a [gpu:"+str(i)+"]/GPUGraphicsClockOffset[2]="+str(miner["gpu_oc"])).split(" ")) + subprocess.call(("nvidia-settings -a [gpu:"+str(i)+"]/GPUGraphicsClockOffset[3]="+str(miner["gpu_oc"])).split(" ")) + else: + subprocess.call(("nvidia-settings -a [gpu:"+str(i)+"]/GPUGraphicsClockOffset[2]="+str(miner["gpu_oc"][i])).split(" ")) + subprocess.call(("nvidia-settings -a [gpu:"+str(i)+"]/GPUGraphicsClockOffset[3]="+str(miner["gpu_oc"][i])).split(" ")) + # memory overclock + if type(miner["mem_oc"]) is int: + subprocess.call(("nvidia-settings -a [gpu:"+str(i)+"]/GPUMemoryTransferRateOffset[2]="+str(miner["mem_oc"])).split(" ")) + subprocess.call(("nvidia-settings -a [gpu:"+str(i)+"]/GPUMemoryTransferRateOffset[3]="+str(miner["mem_oc"])).split(" ")) + else: + subprocess.call(("nvidia-settings -a [gpu:"+str(i)+"]/GPUMemoryTransferRateOffset[2]="+str(miner["mem_oc"][i])).split(" ")) + subprocess.call(("nvidia-settings -a [gpu:"+str(i)+"]/GPUMemoryTransferRateOffset[3]="+str(miner["mem_oc"][i])).split(" ")) + # fan speed + if type(miner["fan"]) is int: + if (miner["fan"]==0): + subprocess.call(("nvidia-settings -a [gpu:"+str(i)+"]/GPUFanControlState=0").split(" ")) + else: + subprocess.call(("nvidia-settings -a [gpu:"+str(i)+"]/GPUFanControlState=1").split(" ")) + subprocess.call(("nvidia-settings -a [fan:"+str(i)+"]/GPUTargetFanSpeed="+str(miner["fan"])).split(" ")) + else: + if (miner["fan"][i]==0): + subprocess.call(("nvidia-settings -a [gpu:"+str(i)+"]/GPUFanControlState=0").split(" ")) + else: + subprocess.call(("nvidia-settings -a [gpu:"+str(i)+"]/GPUFanControlState=1").split(" ")) + subprocess.call(("nvidia-settings -a [fan:"+str(i)+"]/GPUTargetFanSpeed="+str(miner["fan"][i])).split(" ")) + # launch new miner + subprocess.call(("screen -dmS miner "+coin).split(" "))