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"]:
 | |
|   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(" "))
 |