From b73ab72e0a85e26f2897aab8afaa3262928118a1 Mon Sep 17 00:00:00 2001 From: Scott Alfter Date: Sun, 21 Jan 2018 19:44:54 -0800 Subject: [PATCH] It works! --- LICENSE | 19 ++++++ README.md | 28 ++++++--- zpool-switch.py | 162 ++++++++++++++++++++++++++++++++++++------------ 3 files changed, 159 insertions(+), 50 deletions(-) create mode 100644 LICENSE 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 index eec0fa1..35b6730 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,21 @@ zpool auto-switcher =================== -TODO: documentation...still early days, but should be similar to my NiceHash -and MiningPoolHub switchers. +This is an auto-switcher for zpool (www.zpool.ca) that chooses the most +profitable coin based on the statistics provided by zpool and the hashrates +supported by your GPUs. It's similar to my MiningPoolHub and NiceHash +auto-switchers, and is set up similarly, except that configuration is +somewhat more modularized. In the future, the other switchers will change +to a similar configuration so that it will be possible to share most of your +configuration between the different pools. -zpool's API is largely undocumented. A further complication is that their -profitability estimates aren't reported in the same units for each -algorithm, and while some deviations from the norm are noted on their -homepage, others aren't. I'm mostly going by trial and error, and by -comparison with what MiningPoolHub reports for the algorithms the two pools -have in common. +I started this under nvOC, but have since migrated my mining rig from nvOC +to a standard Gentoo Linux setup. miners.json =========== -Should ultimately be able to share this with the MiningPoolHub and NiceHash +We should ultimately be able to share this with the MiningPoolHub and NiceHash switchers. Each available algorithm is described with the following values: bin: command line to launch miner. {HOST}, {PORT}, {USERNAME}, and @@ -29,6 +30,8 @@ power: total power consumption, kW gpu_oc, mem_oc, and fan may be provided as a single value each to be applied to all cards or as an array each to set cards individually. +Any additional fields (such as last_benchmark) are ignored. + algo_map.json ============= @@ -36,6 +39,9 @@ Since different pools may use different labels for the same algorithm, we need to map what the pool uses to what we used in miners.json. The index is the pool's label; the value is our label. +When zpool adds a new algorithm, this file is automatically updated with an +empty entry for the new algorithm. + conf.json ========= @@ -51,4 +57,6 @@ payment_currency: how you want zpool to pay you payment_addr: where zpool should send payment The user_name and miner_name values in the example aren't used by the zpool -switcher, but may be used by other switchers. +switcher, but may be used by other switchers. As with miners.json, we might +be able to share this file with other pool auto-switchers. + diff --git a/zpool-switch.py b/zpool-switch.py index a4b8d84..6bcb5b5 100755 --- a/zpool-switch.py +++ b/zpool-switch.py @@ -1,33 +1,25 @@ #!/usr/bin/env python3 -# API description: http://www.zpool.ca/site/api -# estimates: values in mBTC/MH/day, -# mBTC/PH/day for sha256 -# mBTC/GH/day for scrypt, blake, decred, x11, quark, qubit -# mBTC/kS/day for equihash +# API description (such as it is): http://www.zpool.ca/site/api import pprint import json -import urllib.request -import urllib.parse import sys import datetime import time import subprocess import os import socket +import urllib.request +import urllib.parse # load config -DEBUG=False - 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"] -#user_name=cfg["user_name"] -#miner_name=cfg["miner_name"] currency=cfg["currency"] pwrcost=cfg["pwrcost"] min_profit=cfg["min_profit"] @@ -36,29 +28,20 @@ payment_addr=cfg["payment_addr"] os.environ["DISPLAY"]=":0" -# IPv4 address lookup - -def addr(host): - return [addr[4][0] for addr in socket.getaddrinfo(host, None) if addr[0] == socket.AF_INET][0] - # grab something from a website -def fetch(prot, host, path, forceipv4=False): - if (forceipv4): - url=prot+"://"+addr(host)+"/"+path - else: - url=prot+"://"+host+"/"+path - 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", "Host": host}) +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 -if (DEBUG!=True): - 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", True)) -else: - exchrate=float(json.loads(open("dbgdata-exchange-rates").read())["data"]["rates"][currency]) - data=json.loads(open("dbgdata-status").read()) +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: + print("unable to retrieve remote data", file=sys.stderr) + sys.exit(-1) # update algo map @@ -74,23 +57,36 @@ if (changed==True): with open(sys.argv[3], "w") as outfile: json.dump(algo_map, outfile, sort_keys=True, indent=2) -# adjust estimates so they're all in the same units: BTC/day per GH/s +# weed out miners not supported by the pool + +filtered_miners={} for i in data: - data[i]["adjusted_estimate"]=float(data[i]["estimate_current"])*1000 + 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_current"]=float(data[i]["estimate_current"])*1000 try: - data["sha256"]["adjusted_estimate"]/=1000000000000 + data["sha256"]["estimate_current"]/=1000000000 except: pass try: - data["equihash"]["adjusted_estimate"]*=1000 + data["equihash"]["estimate_current"]*=1000 except: pass for i in ["scrypt", "blakecoin", "blake2s", "decred", "x11", "quark", "qubit", "keccak"]: try: - data[i]["adjusted_estimate"]/=1000 + data[i]["estimate_current"]/=1000 except: pass +# calculate profitability for our hardware + coins={} for i in data: if (algo_map[i]!=""): @@ -99,13 +95,99 @@ for i in data: coins[i]["algo"]=i coins[i]["mapped_algo"]=algo_map[i] coins[i]["name"]=i - coins[i]["estimate"]=data[i]["adjusted_estimate"]*float(coins[i]["speed"]) # factor in our speed - -for i in data: - print(i+": "+str(data[i]["estimate_current"])+" "+str(data[i]["adjusted_estimate"])) - -print("") + coins[i]["estimate"]=data[i]["estimate_current"]*float(coins[i]["speed"])-24.0*coins[i]["power"]*pwrcost/exchrate +# sort by profitability + +sort={} for i in coins: - print(i+": "+str(coins[i]["estimate"])) + 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)==4): + # exit if maximum is below minimum + if (miner["estimate"]