202 lines
6.6 KiB
Python
Executable File
202 lines
6.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
# API description (such as it is): http://www.zpool.ca/site/api
|
|
|
|
import pprint
|
|
import json
|
|
import sys
|
|
import datetime
|
|
import time
|
|
import subprocess
|
|
import os
|
|
import socket
|
|
import urllib.request
|
|
import urllib.parse
|
|
|
|
# load config
|
|
|
|
cfg=json.loads(open(sys.argv[1]).read())
|
|
miners=json.loads(open(sys.argv[2]).read())
|
|
algo_map=json.loads(open(sys.argv[3]).read())
|
|
|
|
card_type=cfg["card_type"]
|
|
currency=cfg["currency"]
|
|
pwrcost=cfg["pwrcost"]
|
|
min_profit=cfg["min_profit"]
|
|
payment_currency=cfg["payment_currency"]
|
|
payment_addr=cfg["payment_addr"]
|
|
|
|
os.environ["DISPLAY"]=":0"
|
|
|
|
# grab something from a website
|
|
|
|
def fetch(url):
|
|
r=urllib.request.Request(url, None, {"User-Agent": "Lynx/2.8.8dev.3 libwww-FM/2.14 SSL-MM/1.4.1", "Pragma": "no-cache"})
|
|
return urllib.request.urlopen(r).read().decode("utf-8")
|
|
|
|
# main
|
|
|
|
try:
|
|
exchrate=float(json.loads(fetch("https://api.coinbase.com/v2/exchange-rates?currency=BTC"))["data"]["rates"][currency])
|
|
data=json.loads(fetch("http://www.zpool.ca/api/status"))
|
|
except:
|
|
if (len(sys.argv)!=5):
|
|
print("unable to retrieve remote data", file=sys.stderr)
|
|
sys.exit(-1)
|
|
else:
|
|
pass
|
|
|
|
# update algo map
|
|
|
|
try:
|
|
changed=False
|
|
for i in data:
|
|
try:
|
|
k=algo_map[i]
|
|
except:
|
|
algo_map[i]=""
|
|
changed=True
|
|
|
|
if (changed==True):
|
|
with open(sys.argv[3], "w") as outfile:
|
|
json.dump(algo_map, outfile, sort_keys=True, indent=2)
|
|
except:
|
|
print("null response from server", file=sys.stderr)
|
|
sys.exit(-1)
|
|
|
|
|
|
# weed out miners not supported by the pool
|
|
|
|
filtered_miners={}
|
|
for i in data:
|
|
try:
|
|
filtered_miners[algo_map[i]]=miners[algo_map[i]]
|
|
except:
|
|
pass
|
|
miners=filtered_miners
|
|
|
|
# adjust estimates so they're all in the same units: BTC/day per GH/s
|
|
|
|
for i in data:
|
|
data[i]["estimate"]=float(data[i][sys.argv[4]])*1000
|
|
try:
|
|
data["sha256"]["estimate"]/=1000000000
|
|
except:
|
|
pass
|
|
try:
|
|
data["equihash"]["estimate"]*=1000
|
|
except:
|
|
pass
|
|
for i in ["scrypt", "blakecoin", "blake2s", "decred", "x11", "quark", "qubit", "keccak", "keccakc", "sha256t", "skein", "nist5", "myr-gr", "lbry"]:
|
|
try:
|
|
data[i]["estimate"]/=1000
|
|
except:
|
|
pass
|
|
|
|
# calculate profitability for our hardware
|
|
|
|
coins={}
|
|
for i in data:
|
|
if (algo_map[i]!=""):
|
|
coins[i]=miners[algo_map[i]]
|
|
coins[i]["bin"]=coins[i]["bin"].format(HOST=i+".mine.zpool.ca", PORT=data[i]["port"], USERNAME=payment_addr, PASSWORD="c="+payment_currency)
|
|
coins[i]["algo"]=i
|
|
coins[i]["mapped_algo"]=algo_map[i]
|
|
coins[i]["name"]=i
|
|
coins[i]["estimate"]=data[i]["estimate"]*float(coins[i]["speed"])-24.0*coins[i]["power"]*pwrcost/exchrate
|
|
|
|
# sort by profitability
|
|
|
|
sort={}
|
|
for i in coins:
|
|
sort[i]=coins[i]["estimate"]
|
|
sort=sorted(sort.items(), key=lambda x:x[1], reverse=True)
|
|
|
|
log=open("current-profit", "w")
|
|
for i in sort:
|
|
log.write(i[0]+": "+format(i[1], ".8f")+" BTC/day ("+format(i[1]*exchrate, ".2f")+" "+currency+"/day)\n")
|
|
log.close()
|
|
|
|
miner=coins[sort[0][0]]
|
|
|
|
if (len(sys.argv)==5):
|
|
# exit if maximum is below minimum
|
|
if (miner["estimate"]<min_profit):
|
|
algo_log=open("algo-log", "a")
|
|
algo_log.write(str(datetime.datetime.now())+": **NONE**\n")
|
|
algo_log.close()
|
|
for algo in coins:
|
|
subprocess.call(["pkill", "-f", "^"+coins[algo].replace("+", "\\+")])
|
|
sys.exit()
|
|
else: # manual override
|
|
if (sys.argv[5]!="list"):
|
|
miner=coins[sys.argv[5]]
|
|
else: # list available algos
|
|
print("algos: ", end="")
|
|
for i in coins:
|
|
print(i+" ", end="")
|
|
print("")
|
|
sys.exit()
|
|
|
|
# see if miner's already running
|
|
try:
|
|
subprocess.check_output(["pgrep", "-f", "^"+miner["bin"].replace("+", "\\+")])
|
|
current=True
|
|
except:
|
|
current=False
|
|
other=False;
|
|
if (current==False):
|
|
for algo in miners:
|
|
try:
|
|
subprocess.check_output(["pgrep", "-f", "^"+miners[algo]["bin"].replace("+", "\\+")])
|
|
other=True
|
|
except:
|
|
pass
|
|
|
|
if (current==False):
|
|
# log a change
|
|
algo_log=open("algo-log", "a")
|
|
algo_log.write(str(datetime.datetime.now())+": * ("+miner["algo"]+") "+format(miner["estimate"], ".8f")+" "+format(miner["estimate"]*exchrate, ".2f")+"\n")
|
|
algo_log.close()
|
|
# kill existing miners
|
|
for algo in miners:
|
|
subprocess.call(["pkill", "-f", "^"+miners[algo]["bin"].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(" ")).decode("utf-8").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 "+miner["bin"]).split(" "))
|