//--XBTGPUARC-- //--Bitcoin Gold-- //--Zhash_144_5-- //--Equihash-- over //--Vector-- Calculations for //--Intel ARC Alchemist DG2 GPU--
//--Projekt: XBTGPUARC Miner--
//--Sprache: C++17 + OpenCL--
//--Ziel: Equihash 144,5 Mining auf Intel ARC GPUs--
//--kernels--
//-00--zhash.cl-Exclude-(We build around the Kernel!)--
//--XBTGPUARC_Dateien 0-13 sortiert A-Z--
//--1--globals.cpp--
//--2--globals.hpp--
//--3--main.cpp--
//--0--Makefile--
//--4--miner_loop.cpp--
//--5--miner_loop.hpp--
//--6--mining_job.hpp--
//--7--notify_parser.hpp--
//--8--opencl_utils_devices.cpp--
//--9--opencl_utils.cpp--
//--10--opencl_utils.hpp--
//--11--runs.sh--
//--12--stratum_notify_listener.cpp--
//--13.--stratum_notify_listener.hpp--
//--OpenCL-- //--C++17-- no changes in Codelanguage here without extra attention. All Data are made before Training. Program you see work in early state! Do not change significant Options or Code before EXTRA FAT WARNING! Questions about everything recommend and welcome. Lets work together!
//--Inhalt Dateien A-Z--
//--1--globals.cpp--
#include "globals.hpp"
#include "miner_loop.hpp"
//--Globale Variablen definieren--
bool abort_mining = false;
bool socket_valid = false;
int next_request_id = 1;
std::string current_job_id = "";
std::string worker_name = "";
std::array<uint8_t, 32> current_target = {};
//--Funktion implementieren--
void stop_mining() { abort_mining = true; }
//--2--globals.hpp--
#pragma once
#include <CL/cl.h>
#include
#include
#include
#define INPUT_SIZE 512
#define HASH_SIZE 32
#define NONCES_PER_THREAD 1
#define BUCKET_COUNT 32
#define HASH_ROUNDS_OUTPUT_SIZE 32
struct GpuResources {
cl_context context = nullptr;
cl_command_queue queue = nullptr;
cl_program program = nullptr;
cl_kernel kernel = nullptr;
cl_device_id device = nullptr;
cl_mem input_buffer = nullptr;
cl_mem output_buffer = nullptr;
cl_mem output_hashes_buffer = nullptr;
cl_mem pool_target_buffer = nullptr;
};
extern int next_request_id;
extern std::string current_job_id;
extern std::string worker_name;
extern bool abort_mining;
extern bool socket_valid;
extern std::array<uint8_t, 32> current_target;
//--3--main.cpp--
#include "globals.hpp"
#include "miner_loop.hpp"
#include "mining_job.hpp"
#include "notify_parser.hpp"
#include "opencl_utils.hpp"
#include "stratum_notify_listener.hpp"
#include <CL/cl.h>
#include
#include
#include
#include
#include
#include
//--Alle OpenCL-Geräte auflisten--
void list_opencl_devices() {
cl_uint num_platforms = 0;
cl_int err = clGetPlatformIDs(0, nullptr, &num_platforms);
if (err != CL_SUCCESS) {
std::cerr << "❌ Fehler bei clGetPlatformIDs: " << err << "\n";
return;
}
std::vector<cl_platform_id> platforms(num_platforms);
clGetPlatformIDs(num_platforms, platforms.data(), nullptr);
std::cout << "🌍 Gefundene OpenCL-Plattformen: " << num_platforms << "\n";
for (cl_uint i = 0; i < num_platforms; ++i) {
char name[128], vendor[128], version[128];
clGetPlatformInfo(platforms[i], CL_PLATFORM_NAME, sizeof(name), name,
nullptr);
clGetPlatformInfo(platforms[i], CL_PLATFORM_VENDOR, sizeof(vendor), vendor,
nullptr);
clGetPlatformInfo(platforms[i], CL_PLATFORM_VERSION, sizeof(version),
version, nullptr);
std::cout << "\n[Plattform " << i << "]\n";
std::cout << " Name: " << name << "\n";
std::cout << " Vendor: " << vendor << "\n";
std::cout << " Version: " << version << "\n";
cl_uint num_devices = 0;
err = clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_ALL, 0, nullptr,
&num_devices);
if (err != CL_SUCCESS || num_devices == 0) {
std::cout << " ⚠️ Keine Geräte gefunden.\n";
continue;
}
std::vector<cl_device_id> devices(num_devices);
clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_ALL, num_devices,
devices.data(), nullptr);
for (cl_uint j = 0; j < num_devices; ++j) {
char devname[128];
clGetDeviceInfo(devices[j], CL_DEVICE_NAME, sizeof(devname), devname,
nullptr);
std::cout << " [Device " << j << "] " << devname << "\n";
}
}
}
int main(int argc, char **argv) {
//--Default-Werte--
int platform_index = 0;
int device_index = 0;
int intensity = 256;
std::string algo = "zhash_144_5";
std::string wallet = "Gb4V4a9Jk3p8aH6jkW3Aq3sq8rQCuJQ6S8";
std::string worker = "A730m";
std::string password = "x";
std::string pool_host = "solo-btg.2miners.com";
int pool_port = 4040;
//--🧾 Argumente parsen--
for (int i = 1; i < argc; i) {
std::string arg = argv[i];
if (arg == "--platform" && i + 1 < argc)
platform_index = std::atoi(argv[i]);
else if (arg == "--device" && i + 1 < argc)
device_index = std::atoi(argv[i]);
else if (arg == "--intensity" && i + 1 < argc)
intensity = std::atoi(argv[i]);
else if (arg == "--algo" && i + 1 < argc)
algo = argv[i];
else if (arg == "--wallet" && i + 1 < argc)
wallet = argv[i];
else if (arg == "--worker" && i + 1 < argc)
worker = argv[i];
else if (arg == "--password" && i + 1 < argc)
password = argv[i];
else if (arg == "--pool" && i + 1 < argc)
pool_host = argv[i];
else if (arg == "--port" && i + 1 < argc)
pool_port = std::atoi(argv[i]);
else if (arg == "--help") {
std::cout
<< "Usage: ./xbtgpuarc [options]\n"
<< "Options:\n"
<< " --platform N OpenCL Plattform-Index (default 0)\n"
<< " --device N OpenCL Geräte-Index (default 0)\n"
<< " --intensity N Threads pro Gerät (default 256)\n"
<< " --algo NAME Kernel/Algo-Name (default zhash_144_5)\n"
<< " --wallet ADDR Wallet-Adresse\n"
<< " --worker NAME Worker-Name\n"
<< " --password PASS Passwort für Pool (default 'x')\n"
<< " --pool HOST Pool-Adresse (default 2miners)\n"
<< " --port PORT Port (default 4040)\n";
return 0;
}
}
std::cout << "🚀 Starte XBTGPUARC mit Algo: " << algo << "\n";
std::cout << "👤 Worker: " << wallet << "." << worker << "\n";
std::cout << "🎛️ Platform: " << platform_index
<< " | Device: " << device_index << " | Intensity: " << intensity
<< "\n";
std::cout << "🌐 Pool: " << pool_host << ":" << pool_port << "\n";
list_opencl_devices();
//--Initialisiere OpenCL--
GpuResources resources;
init_opencl("kernels/zhash.cl",algo, platform_index, device_index, intensity,
resources);
//--Starte Stratum-Listener + Mining-Thread--
run_stratum_listener(pool_host, pool_port, wallet, worker, password,
intensity, resources);
cleanup_opencl(resources);
return 0;
}
//--0--Makefile--
CXXFLAGS := -std=c++17 -Wall -O2 -DCL_TARGET_OPENCL_VERSION=300 -MMD -MP
LDFLAGS := -lOpenCL -lboost_system -lboost_json -lpthread
Quellcode-Dateien
SRC := main.cpp
miner_loop.cpp
opencl_utils.cpp
stratum_notify_listener.cpp
globals.cpp
OBJ := $(SRC:.cpp=.o)
DEPS := $(OBJ:.o=.d)
OUT := xbtgpuarc
Optional: CPU-Miner
CPU_SRC := cpu_miner.cpp globals.cpp
CPU_OBJ := $(CPU_SRC:.cpp=.o)
CPU_OUT := cpu_miner
CXXFLAGS := -std=c++17 -Wall -O3 -march=native -DCL_TARGET_OPENCL_VERSION=300
LDFLAGS := -lOpenCL -lboost_system -lboost_json -lpthread
Standard-Target
all: $(OUT)
Build des GPU-Miners
$(OUT): $(OBJ)
$(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS)
Optional: CPU-Miner separat bauen
$(CPU_OUT): $(CPU_OBJ)
$(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS)
Generisches Compile-Ziel
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@
Clean
clean:
rm -f $(OUT) $(CPU_OUT) *.o *.d
-include $(DEPS)
//--4--miner_loop.cpp--
#include "miner_loop.hpp"
#include "mining_job.hpp"
#include "opencl_utils.hpp"
#include <CL/cl.h>
#include
#include
#include
#include
#include
#include
#include
#include //--Für std::random_device und std::mt19937--
#include
#include
//--Externe Status-Variablen-
//--Diese Variablen sind nicht in dieser Datei definiert, sondern werden von außen bereitgestellt.-
//--Sie dienen dazu, den Abbruch des Minings oder den Status der Socket-Verbindung zu signalisieren.-
//--Es sind einfache bool-Werte, die direkt gelesen werden.--
extern bool abort_mining;
extern bool socket_valid;
namespace {
//--Prüft, ob ein Zeichen eine Hexadezimalziffer ist-
//--Eine Hexadezimalziffer ist 0-9 oder A-F (Groß- oder Kleinbuchstaben).--
inline bool is_hex_char(unsigned char c) {
return std::isxdigit(c) != 0;
}
//--Prüft, ob ein String ein gültiger Hexadezimal-String ist-
//--Ein String ist gültig, wenn er leer ist oder nur Hexadezimalziffern enthält-
//--und eine gerade Länge hat (da ein Byte aus zwei Hex-Ziffern besteht).-
//--Optional kann ein "0x"-Präfix erlaubt sein.--
bool is_valid_hex(const std::string& s, bool allow_0x_prefix = true) {
if (s.empty()) return false;
std::string clean = s;
if (allow_0x_prefix && s.size() >= 2 &&
s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
clean = s.substr(2); //--Präfix entfernen, wenn erlaubt--
}
if (clean.empty() || (clean.size() % 2) != 0) return false; //--Muss eine gerade Länge haben--
for (unsigned char c : clean) {
if (!is_hex_char(c)) return false; //--Alle Zeichen müssen Hex-Ziffern sein--
}
return true;
}
//--Entfernt ein optionales "0x"-Präfix von einem Hex-String-
//--Wenn der String mit "0x" oder "0X" beginnt, wird dieser Teil entfernt.--
std::string remove_0x_prefix(const std::string& s) {
if (s.size() >= 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
return s.substr(2);
}
return s;
}
//--Konvertiert einen Hex-String in Bytes und hängt sie an einen Puffer an-
//--Diese Funktion nimmt einen Hex-String und wandelt jedes Paar von Hex-Ziffern-
//--in ein einzelnes Byte um, das dann einem cl_uchar-Vektor hinzugefügt wird.-
//--Sie beinhaltet eine verbesserte Fehlerbehandlung für ungültige Eingaben.--
void append_hex_to_buffer(const std::string& hex, std::vector<cl_uchar>& buffer,
const std::string& field_name = "") {
if (hex.empty()) return;
std::string clean_hex = remove_0x_prefix(hex);
if (!is_valid_hex(clean_hex, false)) { //--Prüfen ohne Präfix--
throw std::invalid_argument("Ungültiger Hex-String für Feld '" +
field_name + "': " + hex);
}
buffer.reserve(buffer.size() + clean_hex.size() / 2); //--Speicher im Voraus reservieren--
for (size_t i = 0; i < clean_hex.size(); i += 2) {
try {
unsigned long byte_val = std::stoul(clean_hex.substr(i, 2), nullptr, 16);
if (byte_val > 0xFF) { //--Ein Byte ist max. 255 (0xFF)--
throw std::out_of_range("Byte-Wert außerhalb des Bereichs");
}
buffer.push_back(static_cast<cl_uchar>(byte_val));
} catch (const std::exception& e) {
throw std::invalid_argument("Konvertierungsfehler in Feld '" +
field_name + "' bei Position " +
std::to_string(i) + ": " + e.what());
}
}
}
//--Erstellt den Eingabepuffer für den OpenCL-Kernel aus einem MiningJob-
//--Diese Funktion sammelt alle relevanten Hex-Strings aus dem MiningJob-Objekt-
//--(Version, Prevhash, Ntime, Coinb1, Extranonce1, Extranonce2, Coinb2 und Merkle-Branch)-
//--und konvertiert sie in einen Vektor von Bytes, der als Eingabe für die GPU dient.--
void build_input_from_job(const MiningJob& job, std::vector<cl_uchar>& input_buffer) {
input_buffer.clear();
try {
append_hex_to_buffer(job.version, input_buffer, "version");
append_hex_to_buffer(job.prevhash, input_buffer, "prevhash");
append_hex_to_buffer(job.ntime, input_buffer, "ntime");
append_hex_to_buffer(job.coinb1, input_buffer, "coinb1");
append_hex_to_buffer(job.extranonce1, input_buffer, "extranonce1");
append_hex_to_buffer(job.extranonce2, input_buffer, "extranonce2");
append_hex_to_buffer(job.coinb2, input_buffer, "coinb2");
for (size_t i = 0; i < job.merkle_branch.size(); ++i) {
append_hex_to_buffer(job.merkle_branch[i], input_buffer,
"merkle_branch[" + std::to_string(i) + "]");
}
} catch (const std::exception& e) {
input_buffer.clear(); //--Puffer im Fehlerfall leeren--
throw; //--Fehler weitergeben--
}
}
//--Sichere Konvertierung eines Hex-Strings in einen 32-Bit-Integer (uint32_t)-
//--Diese Funktion wandelt einen Hex-String in eine vorzeichenlose 32-Bit-Ganzzahl um.-
//--Sie prüft auf Gültigkeit des Hex-Strings und stellt sicher, dass der Wert nicht-
//--über den maximalen Wert von uint32_t hinausgeht, um Überläufe zu vermeiden.--
std::optional<uint32_t> safe_stoul_hex_u32(const std::string& hex) {
std::string clean_hex = remove_0x_prefix(hex);
if (!is_valid_hex(clean_hex, false)) return std::nullopt; //--Prüfen ohne Präfix--
try {
size_t idx = 0;
unsigned long v = std::stoul(clean_hex, &idx, 16);
if (idx != clean_hex.size()) return std::nullopt; //--Nicht alle Zeichen gelesen--
if (v > std::numeric_limits<uint32_t>::max()) return std::nullopt; //--Wert zu groß--
return static_cast<uint32_t>(v);
} catch (...) {
return std::nullopt; //--Konvertierungsfehler--
}
}
//--Verbesserte OpenCL-Fehlerbehandlung-
//--Diese Funktion prüft den Rückgabewert eines OpenCL-Aufrufs. Wenn ein Fehler auftritt,-
//--wird eine Fehlermeldung ausgegeben und optional das Build-Log des Kernels,-
//--falls die GpuResources verfügbar sind und ein Fehler im Build-Prozess vorlag.--
bool check_cl(cl_int err, const char* where, const GpuResources* resources = nullptr) {
if (err == CL_SUCCESS) return true; //--Alles in Ordnung--
std::cerr << "❌ OpenCL-Fehler (" << err << ") bei: " << where << "\n";
//--Build-Log ausgeben, falls Programm und Gerät bekannt sind--
if (resources && resources->program && resources->device) {
size_t log_size = 0;
clGetProgramBuildInfo(resources->program, resources->device,
CL_PROGRAM_BUILD_LOG, 0, nullptr, &log_size);
if (log_size > 0) { //--Wenn ein Log vorhanden ist--
std::vector<char> build_log(log_size + 1); //--Dynamisch Puffer allozieren--
clGetProgramBuildInfo(resources->program, resources->device,
CL_PROGRAM_BUILD_LOG, log_size, build_log.data(), nullptr);
std::cerr << "Build-Log:\n" << build_log.data() << "\n";
}
}
return false; //--Fehler aufgetreten--
}
//--RAII-Wrapper für OpenCL-Speicherobjekte (cl_mem)-
//--Dieser Wrapper sorgt dafür, dass OpenCL-Speicherobjekte automatisch freigegeben werden,-
//--wenn sie nicht mehr benötigt werden (z. B. wenn der Wrapper den Gültigkeitsbereich verlässt).-
//--Dies verhindert Speicherlecks und vereinfacht die Fehlerbehandlung.--
struct CLMemWrapper {
cl_mem mem = nullptr; // Das OpenCL-Speicherobjekt
explicit CLMemWrapper(cl_mem mem_obj = nullptr) : mem(mem_obj) {}
//--Destruktor: Gibt das Speicherobjekt frei, wenn der Wrapper zerstört wird--
~CLMemWrapper() { if (mem) clReleaseMemObject(mem); }
//--Kopierkonstruktor und Zuweisungsoperator sind gelöscht (nicht kopierbar)--
CLMemWrapper(const CLMemWrapper&) = delete;
CLMemWrapper& operator=(const CLMemWrapper&) = delete;
//--Move-Konstruktor: Ermöglicht das Verschieben von Besitzrechten--
CLMemWrapper(CLMemWrapper&& other) noexcept : mem(other.mem) {
other.mem = nullptr; // Quelle leeren
}
//--Move-Zuweisungsoperator: Ermöglicht das Verschieben von Besitzrechten--
CLMemWrapper& operator=(CLMemWrapper&& other) noexcept {
if (this != &other) { //--Selbstzuweisung verhindern--
if (mem) clReleaseMemObject(mem); //--Eigenes Objekt freigeben--
mem = other.mem;
other.mem = nullptr; //--Quelle leeren--
}
return *this;
}
//--Konvertierungsoperator: Ermöglicht die implizite Umwandlung in cl_mem--
operator cl_mem() const { return mem; }
//--Expliziter bool-Operator: Prüft, ob ein gültiges Speicherobjekt vorhanden ist--
explicit operator bool() const { return mem != nullptr; }
};
//--Der Miner-Loop-
//--Dies ist die Hauptschleife des Miners. Sie wiederholt die folgenden Schritte,-
//--um nach gültigen Lösungen zu suchen:-
//--1. Hole den aktuellen Mining-Job.-
//--2. Erstelle eine Start-Nonce für diese Batch-Verarbeitung.-
//--3. Aktualisiere die Daten auf der Grafikkarte (GPU), falls sich der Job geändert hat.-
//--4. Setze die Argumente für den OpenCL-Kernel.-
//--5. Starte den Kernel (das ist das eigentliche Rechenprogramm auf der GPU).-
//--6. Warte, bis der Kernel fertig ist.-
//--7. Lies die Ergebnisse (die gefundenen Lösungen) von der GPU zurück.-
//--8. Überprüfe die gefundenen Lösungen.--
void miner_loop(GpuResources& resources,
std::function<MiningJob()> get_current_job,
std::function<void(uint32_t, const std::array<uint8_t, 32>&,
const MiningJob&)> on_valid_share) {
//--Initialisierung des Zufallszahlengenerators-
//--Ein guter Zufallszahlengenerator ist wichtig für die Nonce.--
std::random_device rd; // Quelle für echte Zufallszahlen
std::mt19937 rng(rd()); // Mersenne Twister Engine
std::uniform_int_distribution<uint32_t> dist; // Für 32-Bit Zufallszahlen
//--Holen des initialen Mining-Jobs-
//--Der erste Job, mit dem der Miner startet.--
MiningJob current_job = get_current_job();
std::string current_job_id = current_job.job_id;
//--Work-Size berechnen-
//--Dies sind die Größen, die der GPU sagen, wie viele Arbeitseinheiten-
//--sie gleichzeitig verarbeiten soll. Hardcoded für einfache Kontrolle.--
const size_t local_work_size = 64; //--Größe einer lokalen Arbeitsgruppe--
const size_t min_intensity = 1; //--Mindestintensität, kann extern kommen--
const size_t batch_size = min_intensity * 4096ULL; //--Gesamtmenge der Nonces pro Batch--
//--Globale Work-Size ist ein Vielfaches der lokalen, um volle Gruppen zu gewährleisten--
const size_t global_work_size = ((batch_size + local_work_size - 1) / local_work_size) * local_work_size;
//--Host-Puffer für Ergebnisse-
//--Hier werden die Ergebnisse von der GPU zwischengespeichert, bevor sie verarbeitet werden.--
std::vector<cl_uchar> output_buffer(32 * batch_size); // Für die finalen Hashes (32 Bytes pro Hash)
std::vector<cl_uint> index_buffer(2 * batch_size, 0); // Für die Indizes der gefundenen Paare (idxA, idxB)
//--OpenCL-Puffer für die GPU anlegen-
//--Diese Puffer werden nur einmal am Anfang erstellt und dann immer wiederverwendet.-
//--Das ist effizienter, als sie in jeder Schleife neu zu erstellen.--
cl_int err = CL_SUCCESS; // Für die Fehlerprüfung der OpenCL-Aufrufe
//--Eingabepuffer für die Job-Daten--
CLMemWrapper cl_input(clCreateBuffer(resources.context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
host_input.size(), host_input.data(), &err));
if (!check_cl(err, "clCreateBuffer(cl_input)", &resources)) return;
//--Ausgabepuffer für die Hashes, die der Kernel erzeugt--
CLMemWrapper cl_output(clCreateBuffer(resources.context, CL_MEM_WRITE_ONLY,
output_buffer.size(), nullptr, &err));
if (!check_cl(err, "clCreateBuffer(cl_output)", &resources)) return;
//--Puffer für die Indizes der Hash-Paare--
CLMemWrapper cl_indexes(clCreateBuffer(resources.context, CL_MEM_WRITE_ONLY,
index_buffer.size() * sizeof(cl_uint), nullptr, &err));
if (!check_cl(err, "clCreateBuffer(cl_indexes)", &resources)) return;
//--Puffer für die Anzahl der gefundenen Lösungen--
CLMemWrapper cl_solution_count(clCreateBuffer(resources.context, CL_MEM_READ_WRITE,
sizeof(cl_uint), nullptr, &err));
if (!check_cl(err, "clCreateBuffer(cl_solution_count)", &resources)) return;
//--Haupt-Mining-Schleife-
//--Diese Schleife läuft, solange das Mining nicht abgebrochen werden soll-
//--und die Socket-Verbindung gültig ist.--
while (!abort_mining && socket_valid) { // Keine .load() mehr, direkte bool-Abfrage
//--Job-Wechsel-Logik-
//--Wir fragen nach dem neuesten Job. Wenn sich die Job-ID geändert hat,-
//--dann aktualisieren wir unseren aktuellen Job.--
MiningJob new_job = get_current_job();
if (new_job.job_id != current_job_id) {
std::cout << "🔄 Neuer Job empfangen: " << new_job.job_id << "\n";
current_job = new_job;
current_job_id = new_job.job_id; //--Job-ID aktualisieren--
//--Bei Job-Wechsel müssen die Eingabepuffer für die GPU neu gebaut werden.-
//--Die Puffer selbst werden NICHT neu erstellt, nur ihr Inhalt aktualisiert.--
}
//--Target berechnen-
//--Der "Target"-Wert bestimmt, wie schwierig es ist, einen gültigen Hash zu finden.-
//--Er wird aus dem "nbits" des aktuellen Jobs berechnet.--
auto maybe_bits = safe_stoul_hex_u32(current_job.nbits);
if (!maybe_bits) {
std::cerr << "❌ Ungültige nbits: " << current_job.nbits << "\n";
//--Ein kurzer Schlaf, um die CPU nicht zu überlasten, wenn Fehler auftreten--
continue; //--Nächste Schleifeniteration versuchen--
}
std::vector<uint8_t> target = bits_to_target(*maybe_bits);
//--Eingabepuffer für den Kernel vorbereiten-
//--Die Job-Daten werden in ein Format gebracht, das die GPU verarbeiten kann.--
std::vector<cl_uchar> host_input;
try {
build_input_from_job(current_job, host_input);
host_input.resize(512, 0); //--Sicherstellen, dass der Puffer eine Mindestgröße hat--
} catch (const std::exception& e) {
std::cerr << "❌ Fehler beim Erstellen des Eingabepuffers: " << e.what() << "\n";
continue;
}
//--Start-Nonce für diese Arbeitsgruppe-
//--Eine zufällige Start-Nonce, um verschiedene Teile des Nonce-Raums zu durchsuchen.--
const uint32_t start_nonce = dist(rng);
//--Solution-Count auf Null zurücksetzen-
//--Vor jedem Kernel-Lauf setzen wir den Zähler für die gefundenen Lösungen zurück.--
const cl_uint zero = 0;
if (!check_cl(clEnqueueWriteBuffer(resources.queue, cl_solution_count.mem, CL_TRUE, 0,
sizeof(cl_uint), &zero, 0, nullptr, nullptr),
"clEnqueueWriteBuffer(solution_count=0)", &resources)) {
continue;
}
//--Daten zur GPU schicken (Inhalt der Puffer aktualisieren)-
//--Hier werden die vorbereiteten Job-Daten auf die GPU kopiert.-
//--Die Pufferobjekte bleiben dieselben, nur die Daten ändern sich.--
if (!check_cl(clEnqueueWriteBuffer(resources.queue, cl_input.mem, CL_TRUE, 0,
host_input.size(), host_input.data(),
0, nullptr, nullptr),
"clEnqueueWriteBuffer(input_data)", &resources)) {
continue;
}
//--Kernel-Argumente setzen (dem Kernel sagen, welche Puffer er nutzen soll)-
//--Die Argumente werden jedes Mal neu gesetzt, da sich die Daten oder die Start-Nonce ändern können.--
if (!check_cl(clSetKernelArg(resources.kernel, 0, sizeof(cl_mem), &cl_input.mem),
"clSetKernelArg(0, cl_input)", &resources)) continue;
if (!check_cl(clSetKernelArg(resources.kernel, 1, sizeof(cl_mem), &cl_output.mem),
"clSetKernelArg(1, cl_output)", &resources)) continue;
if (!check_cl(clSetKernelArg(resources.kernel, 2, sizeof(cl_mem), &cl_indexes.mem),
"clSetKernelArg(2, cl_indexes)", &resources)) continue;
if (!check_cl(clSetKernelArg(resources.kernel, 3, sizeof(cl_mem), &cl_solution_count.mem),
"clSetKernelArg(3, cl_solution_count)", &resources)) continue;
if (!check_cl(clSetKernelArg(resources.kernel, 4, sizeof(uint32_t), &start_nonce),
"clSetKernelArg(4, start_nonce)", &resources)) continue;
//--Den Kernel auf der GPU ausführen!!!-
//--Die eigentliche Rechenarbeit beginnt.--
cl_event evt = nullptr; //--Ereignisobjekt für die Kernel-Ausführung--
err = clEnqueueNDRangeKernel(resources.queue, resources.kernel, 1,
nullptr, &global_work_size, &local_work_size,
0, nullptr, &evt);
if (!check_cl(err, "clEnqueueNDRangeKernel", &resources)) {
if (evt) clReleaseEvent(evt); //--Ereignis freigeben--
continue;
}
clWaitForEvents(1, &evt); //--Warten, bis der Kernel fertig ist--
clReleaseEvent(evt); //--Ereignis freigeben--
//--Ergebnisse von der GPU zurücklesen-
//--Die gefundenen Hashes, Indizes und die Lösungsanzahl werden von der GPU geholt.--
if (!check_cl(clEnqueueReadBuffer(resources.queue, cl_output.mem, CL_TRUE, 0,
output_buffer.size(), output_buffer.data(),
0, nullptr, nullptr),
"clEnqueueReadBuffer(output)", &resources)) continue;
if (!check_cl(clEnqueueReadBuffer(resources.queue, cl_indexes.mem, CL_TRUE, 0,
index_buffer.size() * sizeof(cl_uint), index_buffer.data(),
0, nullptr, nullptr),
"clEnqueueReadBuffer(indexes)", &resources)) continue;
cl_uint solution_count = 0;
if (!check_cl(clEnqueueReadBuffer(resources.queue, cl_solution_count.mem, CL_TRUE, 0,
sizeof(cl_uint), &solution_count,
0, nullptr, nullptr),
"clEnqueueReadBuffer(solution_count)", &resources)) continue;
//--Gefundene Lösungen verarbeiten (PLATZHALTER)-
//--HIER kommt deine ECHTE PoW-Validierungslogik rein!-
//--Aktuell wird ein Platzhalter-XOR-Check durchgeführt.--
for (size_t i = 0; i < solution_count; ++i) {
const uint32_t idxA = index_buffer[i * 2];
const uint32_t idxB = index_buffer[i * 2 + 1];
//--Gültigkeitsprüfung der Indizes
if (idxA >= batch_size || idxB >= batch_size) { //--Sollte nicht passieren, aber zur Sicherheit!--
continue;
}
std::array<uint8_t, 32> final_hash{}; // 32 Bytes für den Hash
for (int j = 0; j < 32; ++j) {
const size_t offsetA = static_cast<size_t>(idxA) * 32 + j;
const size_t offsetB = static_cast<size_t>(idxB) * 32 + j;
final_hash[j] = output_buffer[offsetA] ^ output_buffer[offsetB]; // PLATZHALTER-XOR
}
//-- HINWEIS: Hier deine ECHTE PoW-Validierung einfügen!--
//--Beispiel: if (is_valid_hash(final_hash, target)) {-
//--on_valid_share(start_nonce + idxA, final_hash, current_job);-
//-- }-
//--Solange der Platzhalter verwendet wird, wird dieser Code nicht aufgerufen.--
}
}
//--Aufräumen (nicht explizit nötig wegen RAII, aber gut zu wissen)-
//--Die CLMemWrapper-Objekte (cl_input, cl_output, etc.) geben ihren-
//--Speicher automatisch frei, wenn die Funktion endet.--
}
//--5--miner_loop.hpp--
#pragma once
#include "mining_job.hpp"
#include "opencl_utils.hpp"
#include "optional"
#include
#include
#include
//--Externe Globals--
//--Mining-Steuerung--
void stop_mining();
void miner_loop(
const MiningJob &job,
const std::function<void(uint32_t, const std::array<uint8_t, 32> &,
const MiningJob &)> &on_valid_share,
const GpuResources &resources, int intensity);
//--Hex-Helfer--
std::string sanitize_hex_string(const std::string &input);
std::optional<uint32_t> safe_stoul_hex(const std::string &hex_str);
//--6--mining_job.hpp--
#pragma once
#include
#include
#include
#include
//--Enthält einen vollständigen MiningJob basierend auf einem Benachrichtigungs Ereignis--
struct MiningJob {
std::string job_id;
std::string prevhash;
std::string coinb1;
std::string coinb2;
std::vector<std::string> merkle_branch;
std::string version;
std::string nbits;
std::string ntime;
std::string extranonce1;
std::string extranonce2;
std::string bits;
};
//--Vergleich: Hash < Target--
inline bool is_valid_hash(const std::array<uint8_t, 32> &hash,
const std::vector<uint8_t> &target) {
for (size_t i = 0; i < 32; ++i) {
if (hash[i] < target[i])
return true;
if (hash[i] > target[i])
return false;
}
return true;
}
//--Wandelt Compact-Format aus "bits" (z. B. 0x1d00ffff) in 256-bit Target--
inline std::vector<uint8_t> bits_to_target(uint32_t bits) {
uint32_t exponent = bits >> 24;
uint32_t mantissa = bits & 0x007fffff;
std::vector<uint8_t> target(32, 0);
if (exponent <= 3) {
//--Mantisse nach rechts schieben--
mantissa >>= 8 * (3 - exponent);
target[31] = mantissa & 0xFF;
target[30] = (mantissa >> 8) & 0xFF;
target[29] = (mantissa >> 16) & 0xFF;
} else if (exponent <= 32) {
//--Platzierung der Mantisse ab dem richtigen Byte--
int idx = 32 - exponent;
target[idx] = (mantissa >> 16) & 0xFF;
target[idx + 1] = (mantissa >> 8) & 0xFF;
target[idx + 2] = mantissa & 0xFF;
} else {
//--Exponent außerhalb gültigem Rahmens-
//-→Zurück für leeres Ziel (niemals gültig)--
return std::vector<uint8_t>(32, 0xFF);
}
return target;
}
//--7--notify_parser.hpp--
#pragma once
#include "mining_job.hpp"
#include <boost/json.hpp>
#include
#include
#include
#include
//--Diese Funktion parst eine JSON-Zeile vom Pool und wandelt sie in ein-
//-MiningJob-Objekt um.--
inline std::optional parse_notify(const std::string &line) {
using namespace boost::json;
boost::system::error_code ec;
value json_value = parse(line, ec);
if (ec || !json_value.is_object()) {
return std::nullopt; // Keine gültige JSON-Nachricht
}
const object &obj = json_value.as_object();
//--Sicherstellen, dass es eine "mining.notify"-Nachricht ist--
if (!obj.contains("method") ||
value_to<std::string>(obj.at("method")) != "mining.notify") {
return std::nullopt;
}
if (!obj.contains("params") || !obj.at("params").is_array()) {
std::cerr << "❌ Fehler: 'mining.notify' hat keine gültigen Parameter.\n";
return std::nullopt;
}
const array ¶ms = obj.at("params").as_array();
//--Die Parameter-Anzahl für BTG auf 2miners ist typischerweise 8 oder mehr--
if (params.size() < 8) {
std::cerr << "❌ Fehler: 'mining.notify' hat zu wenige Parameter ("
<< params.size() << "). Erwartet >= 8.\n";
return std::nullopt;
}
MiningJob job;
//-==================================================================-
//-KORREKTE PARAMETER-ZUWEISUNG BASIEREND AUF DEINEM LOG-
//-==================================================================-
//-Stratum-Protokoll für solo-btg.2miners.com:-
//-params[0]: job_id-
//-params[1]: version-
//-params[2]: prevhash-
//-params[3]: coinb1-
//-params[4]: coinb2-
//-params[5]: nbits-
//-params[6]: ntime-
//-params[7]: clean_job (boolean)-
//-
//-WICHTIG: Dieser Pool sendet KEINEN separaten 'merkle_branch'.-
//-Der Merkle-Root muss vom Miner selbst berechnet werden, indem-
//-die Coinbase-Transaktion gehasht wird. Die 'merkle_branch'-
//-Liste bleibt also absichtlich leer.--
job.job_id = value_to<std::string>(params.at(0));
job.version = value_to<std::string>(params.at(1));
job.prevhash = value_to<std::string>(params.at(2));
job.coinb1 = value_to<std::string>(params.at(3));
job.coinb2 = value_to<std::string>(params.at(4));
job.nbits = value_to<std::string>(params.at(5));
job.ntime = value_to<std::string>(params.at(6));
job.clean_job = params.at(7).as_bool();
//--Die Merkle Branch Liste wird explizit geleert, da sie nicht vom Pool kommt.--
job.merkle_branch.clear();
std::cout << "🌿 Job korrekt geparst. Merkle Branch ist leer, wie vom Pool "
"erwartet.\n";
//--Alte Felder für Kompatibilität füllen--
job.bits = job.nbits;
job.extranonce1 = ""; //--Wird später vom Subscriber gesetzt--
job.extranonce2 = "00000000"; //--Platzhalter--
//--Debug-Ausgabe--
std::cout << "🔍 Debug Notify: bits = '" << job.nbits << "', ntime = '"
<< job.ntime << "'\n";
return job;
}
//--8--opencl_list_devices.cpp--
#include <CL/cl.h>
#include
#include
#include
void check_error(cl_int err, const std::string &msg) {
if (err != CL_SUCCESS) {
std::cerr << "❌ Fehler: " << msg << " (" << err << ")\n";
exit(1);
}
}
int main() {
cl_uint num_platforms = 0;
cl_int err = clGetPlatformIDs(0, nullptr, &num_platforms);
check_error(err, "clGetPlatformIDs (count)");
std::vector<cl_platform_id> platforms(num_platforms);
err = clGetPlatformIDs(num_platforms, platforms.data(), nullptr);
check_error(err, "clGetPlatformIDs (fetch)");
std::cout << "🌍 Gefundene OpenCL-Plattformen: " << num_platforms << "\n";
for (cl_uint i = 0; i < num_platforms; ++i) {
char name[128], vendor[128], version[128];
clGetPlatformInfo(platforms[i], CL_PLATFORM_NAME, sizeof(name), name,
nullptr);
clGetPlatformInfo(platforms[i], CL_PLATFORM_VENDOR, sizeof(vendor), vendor,
nullptr);
clGetPlatformInfo(platforms[i], CL_PLATFORM_VERSION, sizeof(version),
version, nullptr);
std::cout << "\n[Plattform " << i << "]\n";
std::cout << " Name: " << name << "\n";
std::cout << " Vendor: " << vendor << "\n";
std::cout << " Version: " << version << "\n";
cl_uint num_devices = 0;
err = clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_ALL, 0, nullptr,
&num_devices);
if (err != CL_SUCCESS || num_devices == 0) {
std::cout << " ⚠️ Keine Geräte gefunden.\n";
continue;
}
std::vector<cl_device_id> devices(num_devices);
clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_ALL, num_devices,
devices.data(), nullptr);
for (cl_uint j = 0; j < num_devices; ++j) {
char devname[128];
clGetDeviceInfo(devices[j], CL_DEVICE_NAME, sizeof(devname), devname,
nullptr);
std::cout << " [Device " << j << "] " << devname << "\n";
}
}
return 0;
}
//--9--opencl_utils.cpp--
#include "opencl_utils.hpp"
#include "globals.hpp"
#include <CL/cl.h>
#include
#include
#include
#include
#include
#include
namespace {
//--Kleiner Fehler-Helper--
bool cl_ok(cl_int err, const char* where) {
if (err == CL_SUCCESS) return true;
std::cerr << "❌ OpenCL Error " << err << " at " << where << "\n";
return false;
}
//--Lese Datei in string--
std::string read_file(const std::string &path) {
std::ifstream ifs(path, std::ios::in | std::ios::binary);
if (!ifs) throw std::runtime_error("Konnte Datei nicht öffnen: " + path);
return std::string((std::istreambuf_iterator<char>(ifs)),
std::istreambuf_iterator<char>());
}
}
//--Größe-Konstanten (gleiche Werte wie globals.hpp)--
constexpr size_t TARGET_SIZE_BYTES = 32;
void init_opencl(const std::string &kernel_path,
const std::string &kernel_func_name,
int platform_index,
int device_index,
int intensity,
GpuResources &resources) {
cl_int err = CL_SUCCESS;
//--Plattformen--
cl_uint num_platforms = 0;
err = clGetPlatformIDs(0, nullptr, &num_platforms);
if (!cl_ok(err, "clGetPlatformIDs(count)")) std::exit(1);
if (num_platforms == 0) {
std::cerr << "❌ Keine OpenCL Plattformen gefunden\n";
std::exit(1);
}
std::vector<cl_platform_id> platforms(num_platforms);
err = clGetPlatformIDs(num_platforms, platforms.data(), nullptr);
if (!cl_ok(err, "clGetPlatformIDs(fetch)")) std::exit(1);
if ((cl_uint)platform_index >= num_platforms) {
std::cerr << "❌ Ungültiger Plattform-Index\n";
std::exit(1);
}
cl_platform_id platform = platforms[platform_index];
//--Devices--
cl_uint num_devices = 0;
err = clGetDeviceIDs(platform, CL_DEVICE_TYPE_ALL, 0, nullptr, &num_devices);
if (!cl_ok(err, "clGetDeviceIDs(count)")) std::exit(1);
if (num_devices == 0) {
std::cerr << "❌ Keine OpenCL Geräte auf Plattform\n";
std::exit(1);
}
std::vector<cl_device_id> devices(num_devices);
err = clGetDeviceIDs(platform, CL_DEVICE_TYPE_ALL, num_devices, devices.data(), nullptr);
if (!cl_ok(err, "clGetDeviceIDs(fetch)")) std::exit(1);
if ((cl_uint)device_index >= num_devices) {
std::cerr << "❌ Ungültiger Geräte-Index\n";
std::exit(1);
}
resources.device = devices[device_index];
//--Kontext & Queue--
resources.context = clCreateContext(nullptr, 1, &resources.device, nullptr, nullptr, &err);
if (!cl_ok(err, "clCreateContext")) std::exit(1);
resources.queue = clCreateCommandQueueWithProperties(resources.context, resources.device, nullptr, &err);
if (!cl_ok(err, "clCreateCommandQueueWithProperties")) {
clReleaseContext(resources.context);
resources.context = nullptr;
std::exit(1);
}
//--Kernel-Quelle lesen--
std::string src;
try {
src = read_file(kernel_path);
} catch (const std::exception &e) {
std::cerr << "❌ " << e.what() << "\n";
cleanup_opencl(resources);
std::exit(1);
}
const char* src_ptr = src.c_str();
size_t src_size = src.size();
resources.program = clCreateProgramWithSource(resources.context, 1, &src_ptr, &src_size, &err);
if (!cl_ok(err, "clCreateProgramWithSource")) {
cleanup_opencl(resources);
std::exit(1);
}
//--Build-Optionen (modifizierbar)--
const char* build_opts = "-cl-std=CL2.0 -cl-fast-relaxed-math";
err = clBuildProgram(resources.program, 1, &resources.device, build_opts, nullptr, nullptr);
//--Build-Log ausgeben wenn Fehler oder Info vorhanden--
size_t log_size = 0;
clGetProgramBuildInfo(resources.program, resources.device, CL_PROGRAM_BUILD_LOG, 0, nullptr, &log_size);
if (log_size > 1) {
std::vector<char> log(log_size + 1);
clGetProgramBuildInfo(resources.program, resources.device, CL_PROGRAM_BUILD_LOG, log_size, log.data(), nullptr);
std::cerr << "--- OpenCL Build Log ---\n" << log.data() << "\n------------------------\n";
}
if (!cl_ok(err, "clBuildProgram")) {
cleanup_opencl(resources);
std::exit(1);
}
//--Kernel erstellen--
resources.kernel = clCreateKernel(resources.program, kernel_zhash.cl(), &err);
if (!cl_ok(err, "clCreateKernel")) {
cleanup_opencl(resources);
std::exit(1);
}
//--Device-Memory Info (optional: anzeigen)--
cl_ulong mem_total = 0;
if (clGetDeviceInfo(resources.device, CL_DEVICE_GLOBAL_MEM_SIZE, sizeof(mem_total), &mem_total, nullptr) == CL_SUCCESS) {
double mem_mib = mem_total / 1024.0 / 1024.0;
std::cerr << "🧠 Device VRAM: " << std::fixed << std::setprecision(1) << mem_mib << " MiB\n";
}
//--Puffergrößen anlegen (INPUT_SIZE / HASH_SIZE aus globals.hpp)--
size_t in_size = static_cast<size_t>(INPUT_SIZE) * std::max(1, intensity);
size_t out_hashes_size = static_cast<size_t>(HASH_SIZE) * std::max(1, intensity);
resources.input_buffer = clCreateBuffer(resources.context, CL_MEM_READ_WRITE, in_size, nullptr, &err);
if (!cl_ok(err, "clCreateBuffer(input_buffer)")) { cleanup_opencl(resources); std::exit(1); }
resources.output_hashes_buffer = clCreateBuffer(resources.context, CL_MEM_READ_WRITE, out_hashes_size, nullptr, &err);
if (!cl_ok(err, "clCreateBuffer(output_hashes_buffer)")) { cleanup_opencl(resources); std::exit(1); }
resources.pool_target_buffer = clCreateBuffer(resources.context, CL_MEM_READ_ONLY, TARGET_SIZE_BYTES, nullptr, &err);
if (!cl_ok(err, "clCreateBuffer(pool_target_buffer)")) { cleanup_opencl(resources); std::exit(1); }
std::cout << "✅ OpenCL initialisiert (Kernel: " << zhash.cl << ")\n";
}
void cleanup_opencl(GpuResources &resources) {
if (resources.input_buffer) { clReleaseMemObject(resources.input_buffer); resources.input_buffer = nullptr; }
if (resources.output_buffer) { clReleaseMemObject(resources.output_buffer); resources.output_buffer = nullptr; } // falls verwendet
if (resources.output_hashes_buffer) { clReleaseMemObject(resources.output_hashes_buffer); resources.output_hashes_buffer = nullptr; }
if (resources.pool_target_buffer) { clReleaseMemObject(resources.pool_target_buffer); resources.pool_target_buffer = nullptr; }
if (resources.kernel) { clReleaseKernel(resources.kernel); resources.kernel = nullptr; }
if (resources.program) { clReleaseProgram(resources.program); resources.program = nullptr; }
if (resources.queue) { clReleaseCommandQueue(resources.queue); resources.queue = nullptr; }
if (resources.context) { clReleaseContext(resources.context); resources.context = nullptr; }
resources.device = nullptr;
}
//--10--opencl_utils.hpp--
#pragma once
#include
#include <CL/cl.h>
#include "globals.hpp"
//--Initialisiert OpenCL (einheitliche Signatur)-
//--kernel_path: Pfad zur .cl Datei-
//--kernel_func_name: Name der Kernel-Funktion inside the .cl (z.B. "zhash.cl")-
//--platform_index / device_index: Auswahl-
//--intensity: wieviele Einheiten (wird für Puffergrößen verwendet)--
void init_opencl(const std::string& kernel_path,
const std::string& kernel_func_name,
int platform_index,
int device_index,
int intensity,
GpuResources& resources);
//--Gibt alle OpenCL-Ressourcen frei (sicher mehrfach aufrufbar)--
void cleanup_opencl(GpuResources& resources);
//--Schreibt das hex-Target in GPU-Puffer (bestehende Signatur)--
void update_opencl_target(const GpuResources& resources, const std::string& hex_target);
//--Optional: kleines Helfer-Interface zum Setzen der Kernel-Args (wenn benötigt)--
void set_kernel_args(const GpuResources& resources,
cl_mem solution_indexes_buffer,
uint32_t start_nonce);
//--11--run.sh--
#!/bin/bash
export GPU_MAX_HEAP_SIZE=100
export GPU_MAX_USE_SYNC_OBJECTS=1
export GPU_SINGLE_ALLOC_PERCENT=100
export GPU_MAX_ALLOC_PERCENT=100
export GPU_MAX_SINGLE_ALLOC_PERCENT=100
export GPU_ENABLE_LARGE_ALLOCATION=100
export GPU_MAX_WORKGROUP_SIZE=64
./xbtgpuarc
--platform 1
--device 0
--algo zhash_144_5
--pool solo-btg.2miners.com
--port 4040
--wallet Gb4V4a9Jk3p8aH6jkW3Aq3sq8rQCuJQ6S8
--worker A730m
--password x
--intensity 256
//--12--stratum_notify_listener.cpp--
#include "stratum_notify_listener.hpp"
#include "globals.hpp"
#include "miner_loop.hpp"
#include "notify_parser.hpp" //--Wir nutzen jetzt den externen Parser!--
#include "opencl_utils.hpp"
#include <boost/asio.hpp>
#include <boost/json.hpp>
#include
#include
#include
#include
#include
using boost::asio::ip::tcp;
//--Globale Variablen, die von anderen Teilen des Programms gesetzt werden--
extern int next_request_id;
extern std::string current_job_id;
extern std::string worker_name;
//--Funktion zum Senden eines gefundenen Shares an den Pool--
void submit_share(tcp::socket &socket, const std::string &nonce_hex,
const std::string &ntime_hex, const std::string &job_id) {
using namespace boost::json;
//--Erstellt die Parameter für die "mining.submit" Methode--
array params;
params.emplace_back(worker_name);
params.emplace_back(job_id);
params.emplace_back(
"00000000"); //--extranonce2 Platzhalter, oft nicht benötigt--
params.emplace_back(ntime_hex);
params.emplace_back(nonce_hex);
//--Baut die komplette JSON-RPC-Anfrage zusammen--
object request;
request["id"] = next_request_id++;
request["method"] = "mining.submit";
request["params"] = params;
//--Sendet die Nachricht an den Pool--
std::string message = serialize(request) + "\n";
boost::asio::write(socket, boost::asio::buffer(message));
std::cout << "📤 Share für Job " << job_id << " gesendet:\n" << message;
}
//--Hauptfunktion, die die Verbindung zum Stratum-Pool hält und auf Nachrichten-
//-lauscht--
void run_stratum_listener(const std::string &pool_host, int pool_port,
const std::string &wallet, const std::string &worker,
const std::string &password, int intensity,
GpuResources &gpu_resources) {
const std::string port_str = std::to_string(pool_port);
worker_name = wallet + "." + worker; //--Setzt den globalen Worker-Namen--
try {
boost::asio::io_context io_context;
tcp::resolver resolver(io_context);
auto endpoints = resolver.resolve(pool_host, port_str);
tcp::socket socket(io_context);
boost::asio::connect(socket, endpoints);
std::cout << "📡 Verbunden mit " << pool_host << ":" << port_str << "\n";
//--Standard-Nachrichten zur Anmeldung am Pool--
std::string subscribe =
R"({"id": 1, "method": "mining.subscribe", "params": []})"
"\n";
std::string authorize =
R"({"id": 2, "method": "mining.authorize", "params": [")" +
worker_name + R"(", ")" + password +
R"("]})"
"\n";
boost::asio::write(socket, boost::asio::buffer(subscribe));
boost::asio::write(socket, boost::asio::buffer(authorize));
std::string buffer; // Puffer für eingehende Daten vom Socket
static std::thread mining_thread;
//--Endlosschleife zum Lesen von Nachrichten--
for (;;) {
char reply[4096];
boost::system::error_code error;
size_t len = socket.read_some(boost::asio::buffer(reply), error);
if (len == 0 && error)
break; // Verbindung geschlossen oder Fehler
buffer.append(reply, len);
size_t pos = 0;
//--Verarbeite jede vollständige Zeile (getrennt durch '\n') im Puffer--
while ((pos = buffer.find('\n')) != std::string::npos) {
std::string line = buffer.substr(0, pos);
buffer.erase(0, pos + 1);
std::cout << "🌐 Nachricht:\n" << line << "\n";
//--Versuch, die Nachricht als Mining-Job zu parsen--
auto job_opt = parse_notify(line);
if (job_opt) { //--Wenn ein gültiger Job empfangen wurde--
auto &job = *job_opt;
current_job_id = job.job_id; // Update der globalen Job-ID
std::cout << "🎯 Job ID: " << job.job_id << "\n";
std::cout << "🧱 PrevHash: " << job.prevhash << "\n";
//--Stoppe den alten Mining-Prozess, falls er noch läuft--
if (mining_thread.joinable()) {
stop_mining();
mining_thread.join();
}
//--Definiere eine Lambda-Funktion, die aufgerufen wird, wenn eine-
//-Lösung gefunden wird--
auto share_submitter = [&](uint32_t nonce,
const std::array<uint8_t, 32> &hash,
const MiningJob &job) {
std::stringstream ss_nonce;
ss_nonce << std::hex << std::setw(8) << std::setfill('0') << nonce;
//--Keine Umwandlung von job.ntime nötig, ist schon hex-String--
submit_share(socket, ss_nonce.str(), job.ntime, job.job_id);
};
//--Starte den neuen Mining-Prozess in einem eigenen Thread--
mining_thread = std::thread([&, job]() {
miner_loop(job, share_submitter, gpu_resources, intensity);
});
}
}
if (error == boost::asio::error::eof)
break;
else if (error)
throw boost::system::system_error(error);
}
if (mining_thread.joinable()) {
stop_mining();
mining_thread.join();
}
} catch (const std::exception &e) {
std::cerr << "❌ Stratum-Fehler: " << e.what() << "\n";
}
}
//--13--stratum_notify_listener.hpp--
#pragma once
#include "opencl_utils.hpp"
#include
//--Startet Stratum-Connection, empfängt Jobs, startet Miner--
void run_stratum_listener(const std::string &pool_host, int pool_port,
const std::string &wallet, const std::string &worker,
const std::string &password, int intensity,
GpuResources &gpu_resources); //--← NICHT const--
Keine Haftung, keine Gewähr. Keine Funkion in diesem Zustand.
Salve
Alucian