#!/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()