#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Eddycoin (EDDY) D-Chain GPU miner - REAL GPU COMPUTE via OpenCL, test coin only
================================================================================
Runs real parallel double-SHA-256 proof-of-work on your graphics card with an OpenCL kernel
(PyOpenCL + numpy). Every nonce the GPU reports is re-checked on the host with Python's hashlib
before it is accepted. SHA-256d is a stand-in: ETChash is not implemented.
* Real GPU work, real measured MH/s - but EDDY is a TEST COIN: no real money, no value.
* LIVE mode (default): fetches work from the Eddycoin Testnet server (HTTPS, only the --server you give,
default https://eddycoin-demo.pages.dev) and submits every found D-block immediately. Rewards are paid
via Y-Chain checkpoints to --wallet (needs an E-Chain block too: run the CPU miner as well).
* --offline: no network at all; writes eddycoin_dchain_results.json (import in /demo or check with the verifier).
* The GPU runs at full load while mining: watch temperatures, use --intensity to reduce heat.
Setup (Windows): install Python 3.12 from python.org (tick "Add python.exe to PATH"), then
py -m pip install pyopencl numpy
Check: py eddycoin_dchain_gpu_miner.py --list-devices
Run: py eddycoin_dchain_gpu_miner.py --wallet eddy1... (address from /live > Create my wallet)
Offline: py eddycoin_dchain_gpu_miner.py --offline --minutes 2 --party A
Options: --device N or name text (default: first GPU) --batch N nonces per kernel launch
--intensity 10..100 (default 100) --minutes N (live default: until Ctrl+C)
"""
import argparse, hashlib, json, os, struct, sys, time
NETWORK = "eddycoin-testnet-sandbox"
CHAIN = "D"
ZERO = "0" * 64
OUT = "eddycoin_dchain_results.json"
MAX_NONCE = 2 ** 53 - 1
INSTALL_HELP = """
PyOpenCL / numpy are not installed (or no OpenCL driver was found).
1. Install Python 3.12 from https://www.python.org/downloads/ (tick "Add python.exe to PATH")
2. In a command prompt: py -m pip install pyopencl numpy
3. Graphics driver with OpenCL (normal driver install is enough, CUDA Toolkit NOT required):
NVIDIA: GeForce/Studio driver from nvidia.com AMD: Adrenalin driver from amd.com
Intel: Arc/Iris Xe graphics driver from intel.com
4. Check: py eddycoin_dchain_gpu_miner.py --list-devices
See INSTALL_WINDOWS.txt for details.
"""
KERNEL = r"""
#define ROTR(x,n) rotate((uint)(x),(uint)(32-(n)))
__constant uint K[64] = {
0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,
0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,
0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,
0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2};
void compress(uint *st, uint *w) {
for (int i = 16; i < 64; i++) {
uint s0 = ROTR(w[i-15],7) ^ ROTR(w[i-15],18) ^ (w[i-15] >> 3);
uint s1 = ROTR(w[i-2],17) ^ ROTR(w[i-2],19) ^ (w[i-2] >> 10);
w[i] = w[i-16] + s0 + w[i-7] + s1; }
uint a=st[0],b=st[1],c=st[2],d=st[3],e=st[4],f=st[5],g=st[6],h=st[7];
for (int i = 0; i < 64; i++) {
uint t1 = h + (ROTR(e,6) ^ ROTR(e,11) ^ ROTR(e,25)) + ((e & f) ^ (~e & g)) + K[i] + w[i];
uint t2 = (ROTR(a,2) ^ ROTR(a,13) ^ ROTR(a,22)) + ((a & b) ^ (a & c) ^ (b & c));
h=g; g=f; f=e; e=d+t1; d=c; c=b; b=a; a=t1+t2; }
st[0]+=a; st[1]+=b; st[2]+=c; st[3]+=d; st[4]+=e; st[5]+=f; st[6]+=g; st[7]+=h;
}
__kernel void mine(__constant uint *mid, __constant uchar *tail, uint tail_len, ulong full_len, ulong base,
uint bits, __global uint *count, __global ulong *out, uint max_out) {
ulong nonce = base + get_global_id(0);
uchar buf[128];
for (uint i = 0; i < tail_len; i++) buf[i] = tail[i];
uchar dig[20]; int nd = 0; ulong x = nonce;
do { dig[nd++] = (uchar)('0' + (x % 10)); x /= 10; } while (x);
uint p = tail_len;
for (int i = nd - 1; i >= 0; i--) buf[p++] = dig[i];
ulong total = full_len + p;
buf[p++] = 0x80;
uint nblk = (p + 8 <= 64) ? 1 : 2; uint end = nblk * 64;
for (; p < end - 8; p++) buf[p] = 0;
ulong bl = total * 8;
for (int i = 0; i < 8; i++) buf[end - 1 - i] = (uchar)(bl >> (8 * i));
uint st[8]; for (int i = 0; i < 8; i++) st[i] = mid[i];
uint w[64];
for (uint k = 0; k < nblk; k++) {
for (int i = 0; i < 16; i++) { uint o = k * 64 + 4 * i;
w[i] = ((uint)buf[o] << 24) | ((uint)buf[o+1] << 16) | ((uint)buf[o+2] << 8) | (uint)buf[o+3]; }
compress(st, w); }
for (int i = 0; i < 8; i++) w[i] = st[i];
w[8] = 0x80000000; for (int i = 9; i < 15; i++) w[i] = 0; w[15] = 256;
uint s2[8] = {0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19};
compress(s2, w);
int ok = 1; uint rem = bits; int wi = 0;
while (rem >= 32 && ok) { if (s2[wi] != 0) ok = 0; rem -= 32; wi++; }
if (ok && rem > 0 && (s2[wi] >> (32 - rem)) != 0) ok = 0;
if (ok) { uint idx = atomic_inc(count); if (idx < max_out) out[idx] = nonce; }
}
"""
_K = [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2]
_H0 = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19]
def _rotr(x, n):
return ((x >> n) | (x << (32 - n))) & 0xFFFFFFFF
def midstate(data):
"""SHA-256 state after the full 64-byte blocks of data (host side, pure Python)."""
st = list(_H0)
for o in range(0, len(data) - len(data) % 64, 64):
w = list(struct.unpack(">16I", data[o:o + 64])) + [0] * 48
for i in range(16, 64):
s0 = _rotr(w[i - 15], 7) ^ _rotr(w[i - 15], 18) ^ (w[i - 15] >> 3)
s1 = _rotr(w[i - 2], 17) ^ _rotr(w[i - 2], 19) ^ (w[i - 2] >> 10)
w[i] = (w[i - 16] + s0 + w[i - 7] + s1) & 0xFFFFFFFF
a, b, c, d, e, f, g, h = st
for i in range(64):
t1 = (h + (_rotr(e, 6) ^ _rotr(e, 11) ^ _rotr(e, 25)) + ((e & f) ^ (~e & g)) + _K[i] + w[i]) & 0xFFFFFFFF
t2 = ((_rotr(a, 2) ^ _rotr(a, 13) ^ _rotr(a, 22)) + ((a & b) ^ (a & c) ^ (b & c))) & 0xFFFFFFFF
h, g, f, e, d, c, b, a = g, f, e, (d + t1) & 0xFFFFFFFF, c, b, a, (t1 + t2) & 0xFFFFFFFF
st = [(x + y) & 0xFFFFFFFF for x, y in zip(st, [a, b, c, d, e, f, g, h])]
return st
def canon(o):
return json.dumps(o, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
def sha(s):
return hashlib.sha256(s.encode("utf-8")).hexdigest()
def sha256d_hex(s):
return hashlib.sha256(hashlib.sha256(s.encode("utf-8")).digest()).hexdigest()
def ascii_label(s, n=48):
s = "".join(c for c in str(s) if 32 <= ord(c) < 127)
return " ".join(s.split())[:n] or "unknown"
def candidate_for(epoch):
# same 10-second offline Y-candidate windows as the E-Chain CPU miner (so the verifier can pair them)
return sha("EDDY-Y-CANDIDATE|v1|offline|%s|%d" % (NETWORK, epoch))
def load_cl():
try:
import numpy as np
import pyopencl as cl
return np, cl
except Exception as ex:
print("ERROR: %s" % ex)
print(INSTALL_HELP)
sys.exit(2)
def all_devices(cl):
out = []
try:
plats = cl.get_platforms()
except Exception as ex:
print("ERROR: no OpenCL platform found (%s)" % ex)
print(INSTALL_HELP)
sys.exit(2)
for p in plats:
try:
for d in p.get_devices():
out.append((p, d))
except Exception:
pass
return out
def is_gpu(cl, d):
return bool(d.type & cl.device_type.GPU)
def pick_device(cl, devs, sel):
if sel is None:
gpus = [x for x in devs if is_gpu(cl, x[1])]
return gpus[0] if gpus else None
if sel.isdigit() and int(sel) < len(devs):
return devs[int(sel)]
m = [x for x in devs if sel.lower() in x[1].name.lower()]
return m[0] if m else None
DEFAULT_SERVER = "https://eddycoin-demo.pages.dev"
def is_addr(a):
return isinstance(a, str) and len(a) == 45 and a.startswith("eddy1") and all(c in "0123456789abcdef" for c in a[5:])
def api(server, path, body=None, timeout=20):
"""HTTPS call to the given Eddycoin server only (GET without body, POST with JSON body)."""
import ssl, urllib.request, urllib.error
url = server.rstrip("/") + path
data = None if body is None else json.dumps(body, separators=(",", ":")).encode("utf-8")
req = urllib.request.Request(url, data=data, method="GET" if body is None else "POST",
headers={"content-type": "application/json", "accept": "application/json", "user-agent": "eddycoin-miner/2 (test coin)"})
try:
with urllib.request.urlopen(req, timeout=timeout, context=ssl.create_default_context()) as r:
return json.loads(r.read().decode("utf-8"))
except urllib.error.HTTPError as e:
try:
return json.loads(e.read().decode("utf-8"))
except Exception:
return {"ok": False, "reason": "HTTP %d" % e.code, "error": "HTTP %d" % e.code}
except Exception as ex:
return {"ok": False, "reason": "network error: %s" % ex, "error": "network error: %s" % ex, "_neterr": True}
def check_live_args(a):
if a.offline:
return
if a.wallet_file:
try:
with open(a.wallet_file, encoding="utf-8") as f:
wf = json.load(f)
except Exception as ex:
sys.exit("cannot read --wallet-file: %s" % ex)
if wf.get("format") != "eddycoin-wallet":
sys.exit("--wallet-file is not an Eddycoin wallet file")
if a.wallet and a.wallet != wf.get("address"):
sys.exit("--wallet and --wallet-file disagree")
a.wallet = wf.get("address") # only the public address is read; no password or private key needed
local = a.server.startswith(("http://127.0.0.1", "http://localhost")) # developer testing only
if not (a.server.startswith("https://") or local):
sys.exit("--server must be an https:// URL")
if not is_addr(a.wallet or ""):
sys.exit("--wallet
or --wallet-file is required for live mining (eddy1 + 40 hex). Open %s/live, click 'Create my wallet' and copy the address.\n"
"Or use --offline to only write a results file." % DEFAULT_SERVER)
def work_key(w):
return (w.get("height"), w.get("prev"), w.get("y_candidate"), w.get("bits"), bool(w.get("waiting")))
def make_engine(np, cl, plat, dev, batch, intensity):
"""compile the OpenCL kernel for one device; returns (device label, search, rate, stats)"""
ctx = cl.Context([dev])
qu = cl.CommandQueue(ctx)
prg = cl.Program(ctx, KERNEL).build()
kern = cl.Kernel(prg, "mine")
kind = "GPU" if is_gpu(cl, dev) else "OpenCL CPU device (test)"
device = "%s: %s (OpenCL, %s)" % (kind, ascii_label(dev.name, 40), ascii_label(plat.name, 30))
max_out = 64
mf = cl.mem_flags
count_buf = cl.Buffer(ctx, mf.READ_WRITE, 4)
out_buf = cl.Buffer(ctx, mf.WRITE_ONLY, 8 * max_out)
cnt_host = np.zeros(1, dtype=np.uint32)
stats = {"hashes": 0, "gpu_reports": 0, "host_rejects": 0, "active": 0.0}
t_start = time.perf_counter()
def search(prefix, bits, stop):
"""run kernel batches over nonce ranges until a host-verified nonce is found or stop() is True"""
pb = prefix.encode("utf-8")
full = len(pb) - len(pb) % 64
mid = np.array(midstate(pb), dtype=np.uint32)
tail = np.frombuffer(pb[full:] + b"\0", dtype=np.uint8)
mid_buf = cl.Buffer(ctx, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=mid)
tail_buf = cl.Buffer(ctx, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=tail)
base = 0
while base < MAX_NONCE and not stop():
t0 = time.perf_counter()
cl.enqueue_copy(qu, count_buf, np.zeros(1, dtype=np.uint32))
kern(qu, (batch,), None, mid_buf, tail_buf, np.uint32(len(pb) - full), np.uint64(full), np.uint64(base),
np.uint32(bits), count_buf, out_buf, np.uint32(max_out))
cl.enqueue_copy(qu, cnt_host, count_buf)
qu.finish()
stats["hashes"] += batch
stats["active"] += time.perf_counter() - t0
found = None
if cnt_host[0]:
n = min(int(cnt_host[0]), max_out)
outs = np.zeros(max_out, dtype=np.uint64)
cl.enqueue_copy(qu, outs, out_buf)
qu.finish()
stats["gpu_reports"] += n
for nonce in sorted(int(v) for v in outs[:n]):
d = sha256d_hex(prefix + str(nonce)) # host-side verification of every reported nonce
if (int(d, 16) >> (256 - bits)) == 0:
found = found or (nonce, d)
else:
stats["host_rejects"] += 1
base += batch
if intensity < 100:
time.sleep((time.perf_counter() - t0) * (100 - intensity) / intensity)
if found:
return found
return None
def rate(): # measured while the kernel is running (waiting time excluded)
return stats["hashes"] / max(stats["active"], 1e-6)
return device, search, rate, stats
def gui_main():
import eddycoin_miner_gui as G
env = {}
def list_devices():
try:
import numpy as np
import pyopencl as cl
except Exception as ex:
raise SystemExit("PyOpenCL could not be loaded (%s: %s)." % (type(ex).__name__, ex))
env.update(np=np, cl=cl)
devs = all_devices(cl)
if not devs:
raise SystemExit("No OpenCL devices found." + INSTALL_HELP)
env["devs"] = devs
return [{"idx": i, "gpu": is_gpu(cl, d), "label": "[%d] %s - %s%s" % (i, ascii_label(d.name, 40), ascii_label(p.name, 24), "" if is_gpu(cl, d) else " (CPU/other, test only)")}
for i, (p, d) in enumerate(devs)]
def start(cfg, out, stop_ev, st):
plat, dev = env["devs"][cfg.device["idx"]]
device, search, rate, stats = make_engine(env["np"], env["cl"], plat, dev, 1 << 22 if cfg.device["gpu"] else 1 << 16, int(cfg.intensity))
out("device: " + device)
live(cfg, device, search, rate, stats, out=out, stop_ev=stop_ev, st=st)
G.run("gpu", "Eddycoin GPU Miner (D-Chain)", DEFAULT_SERVER, api, start, list_devices=list_devices, install_help=INSTALL_HELP)
def main():
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("--wallet", default=None, help="your wallet address (live mode, required): eddy1...")
ap.add_argument("--wallet-file", default=None, help="read your address from an Eddycoin wallet file (no password needed)")
ap.add_argument("--server", default=DEFAULT_SERVER, help="Eddycoin server (https only, default %s)" % DEFAULT_SERVER)
ap.add_argument("--offline", action="store_true", help="no network: only write eddycoin_dchain_results.json")
ap.add_argument("--minutes", type=float, default=None, help="how long to mine (live default: until Ctrl+C, offline default 2)")
ap.add_argument("--party", choices=["A", "B"], default="A", help="offline mode: sandbox party for the work credit")
ap.add_argument("--list-devices", action="store_true", help="list OpenCL devices and exit")
ap.add_argument("--device", default=None, help="device number from --list-devices, or part of its name (default: first GPU)")
ap.add_argument("--batch", type=int, default=1 << 22, help="nonces per kernel launch (default 4194304)")
ap.add_argument("--intensity", type=int, default=100, help="10..100 %% duty cycle (default 100; lower = less heat)")
ap.add_argument("--bits", type=int, default=30, help="offline mode: difficulty in leading zero bits (20-48, default 30; live: set by the server)")
ap.add_argument("--max-blocks", type=int, default=500, help="stop after this many blocks (default 500)")
ap.add_argument("--allow-cpu-device", action="store_true", help="allow an OpenCL CPU device (testing only)")
a = ap.parse_args()
if not a.list_devices:
check_live_args(a)
np, cl = load_cl()
devs = all_devices(cl)
if a.list_devices:
if not devs:
print("No OpenCL devices found." + INSTALL_HELP)
sys.exit(2)
for i, (p, d) in enumerate(devs):
print("[%d] %s | %s | %s | %d compute units | %d MB" % (i, "GPU" if is_gpu(cl, d) else "CPU/other", ascii_label(d.name, 60),
ascii_label(p.name, 40), d.max_compute_units, d.global_mem_size // (1024 * 1024)))
return
if not 10 <= a.intensity <= 100:
sys.exit("--intensity must be between 10 and 100")
batch = max(1 << 12, min(a.batch, 1 << 28))
sel = pick_device(cl, devs, a.device)
if sel is None:
print("ERROR: no OpenCL GPU found%s." % ((" matching '%s'" % a.device) if a.device else ""))
print("Available devices: " + (", ".join("[%d] %s" % (i, ascii_label(d.name, 40)) for i, (p, d) in enumerate(devs)) or "none"))
print(INSTALL_HELP)
sys.exit(2)
plat, dev = sel
if not is_gpu(cl, dev) and not a.allow_cpu_device:
print("ERROR: device '%s' is not a GPU. Choose a GPU with --device (see --list-devices)." % ascii_label(dev.name))
sys.exit(2)
device, search, rate, stats = make_engine(np, cl, plat, dev, batch, a.intensity)
t_start = time.perf_counter()
print("NOTE: the GPU runs at high load while mining - keep an eye on temperatures.")
if not a.offline:
return live(a, device, search, rate, stats)
if a.minutes is None:
a.minutes = 2.0
if not 20 <= a.bits <= 48:
sys.exit("--bits must be between 20 and 48")
miner = "party:" + a.party
print("Eddycoin D-Chain GPU miner - OFFLINE, real OpenCL double-SHA-256 compute, test coin only (no real money, no network)")
print("device: %s | party %s | difficulty %d bits | batch %d | intensity %d%% | %.1f min" % (device, a.party, a.bits, batch, a.intensity, a.minutes))
started = int(time.time() * 1000)
deadline = time.time() + max(0.05, a.minutes) * 60
blocks, prev = [], ZERO
def save():
el = max(time.perf_counter() - t_start, 1e-6)
res = {"format": "eddycoin-miner-results", "version": 1, "network": NETWORK, "chain": CHAIN,
"party": a.party, "device": device, "compute": "real", "hash_alg": "sha256d", "simulated_gpu": False,
"note": "Real OpenCL double-SHA-256 work on the device above (stand-in for ETChash); every GPU nonce re-checked with hashlib. Test coin only, no real money.",
"batch": batch, "intensity": a.intensity, "bits": a.bits, "started": started, "finished": int(time.time() * 1000),
"seconds": round(el, 3), "hashes": stats["hashes"], "hashrate_hs": int(stats["hashes"] / el),
"gpu_reported_nonces": stats["gpu_reports"], "host_rejected_nonces": stats["host_rejects"], "blocks": blocks}
res["checksum"] = sha(canon(res))
tmp = OUT + ".tmp"
with open(tmp, "w", encoding="utf-8") as f:
json.dump(res, f, indent=1, ensure_ascii=False)
os.replace(tmp, OUT)
return res
try:
while time.time() < deadline and len(blocks) < a.max_blocks:
t = int(time.time() * 1000)
cand = candidate_for(int(time.time()) // 10)
prefix = "EDDY-%s-BLOCK|v1|%d|%s|%s|%s|%d|%d|" % (CHAIN, len(blocks), prev, cand, miner, t, a.bits)
found = search(prefix, a.bits, lambda: time.time() >= deadline)
if found is None:
break
b = {"chain": CHAIN, "height": len(blocks), "prev": prev, "y_candidate": cand, "miner": miner, "time": t,
"bits": a.bits, "nonce": found[0], "alg": "sha256d", "hash": found[1]}
blocks.append(b)
prev = b["hash"]
save()
print(" D-block #%-4d nonce %-14d %s %.1f MH/s" % (b["height"], b["nonce"], b["hash"][:16], rate() / 1e6))
except KeyboardInterrupt:
print("stopped by user")
res = save()
print("done: %d blocks, %d hashes in %.1f s = %.1f MH/s (measured); GPU nonces re-checked on host: %d, rejected: %d -> %s"
% (len(blocks), stats["hashes"], res["seconds"], res["hashrate_hs"] / 1e6, stats["gpu_reports"], stats["host_rejects"], os.path.abspath(OUT)))
print("Next: import this file on the Eddycoin demo page, or run: py eddycoin_ychain_verifier.py %s" % OUT)
def live(a, device, search, rate, stats, out=print, stop_ev=None, st=None):
import threading
stop_ev = stop_ev or threading.Event()
st = st if st is not None else {}
out("Eddycoin D-Chain GPU miner - LIVE on %s - real OpenCL double-SHA-256, test coin only (no real money)" % a.server)
out("device: %s | wallet %s | intensity %d%%" % (device, a.wallet, a.intensity))
end = time.time() + a.minutes * 60 if a.minutes else None
acc = rej = 0
last_msg = ""
try:
while (end is None or time.time() < end) and not stop_ev.is_set():
w = api(a.server, "/api/work?chain=D")
if "y_candidate" not in w:
out(" server: %s - retrying in 20 s" % (w.get("error") or w.get("reason") or w)); stop_ev.wait(20); continue
if w.get("waiting"):
if w.get("message") != last_msg:
last_msg = w.get("message"); out(" " + last_msg + " (start the E-Chain CPU miner)")
stop_ev.wait(5); continue
last_msg = ""
t = int(time.time() * 1000)
prefix = "EDDY-%s-BLOCK|v1|%d|%s|%s|%s|%d|%d|" % (CHAIN, w["height"], w["prev"], w["y_candidate"], a.wallet, t, w["bits"])
out(" work: D-block #%d for Y candidate #%s, difficulty %d bits" % (w["height"], w.get("y_height"), w["bits"]))
chk = {"t": time.time(), "changed": False}
def stop():
st.update(hashrate=rate(), bits=w["bits"], height=w["height"])
if (end and time.time() > end) or stop_ev.is_set():
return True
if time.time() - chk["t"] > 10:
chk["t"] = time.time()
w2 = api(a.server, "/api/work?chain=D")
if "y_candidate" in w2 and work_key(w2) != work_key(w):
out(" work changed (new tip / candidate / difficulty) - restarting"); return True
if time.time() * 1000 - t > 90000:
return True # refresh the timestamp (server accepts +-2 min)
return False
found = search(prefix, w["bits"], stop)
if not found:
continue
b = {"chain": CHAIN, "height": w["height"], "prev": w["prev"], "y_candidate": w["y_candidate"], "miner": a.wallet,
"time": t, "bits": w["bits"], "nonce": found[0], "alg": "sha256d", "hash": found[1]}
r = api(a.server, "/api/submit/d", b)
st["found"] = st.get("found", 0) + 1
if r.get("ok"):
acc += 1; st["accepted"] = acc; st["last"] = "accepted D #%d" % b["height"]
cp = r.get("checkpoint")
out(" ACCEPTED D-block #%d %s %.1f MH/s%s" % (b["height"], b["hash"][:16], rate() / 1e6,
(" -> Y checkpoint #%d finalized, reward paid to your wallet" % cp["height"]) if cp else " (waiting for an E-Chain block to finalize the checkpoint)"))
if r.get("retarget"):
out(" difficulty retarget: %s -> %s bits" % (r["retarget"]["from"], r["retarget"]["to"]))
else:
rej += 1; st["rejected"] = rej; st["last"] = "rejected: %s" % (r.get("reason") or r.get("error"))
out(" REJECTED D-block #%d: %s" % (b["height"], r.get("reason") or r.get("error")))
except KeyboardInterrupt:
out("stopped by user")
out("done: %d accepted, %d rejected, %.1f MH/s average (measured); GPU nonces re-checked on host: %d, rejected: %d. Check your wallet on %s/live"
% (acc, rej, rate() / 1e6, stats["gpu_reports"], stats["host_rejects"], a.server))
if __name__ == "__main__":
if len(sys.argv) == 1 and os.environ.get("EDDY_NO_GUI") != "1":
try:
gui_main()
except ImportError as ex:
print("GUI not available (%s); use the command line, see --help" % ex)
else:
if "--cli" in sys.argv:
sys.argv.remove("--cli")
main()