09.09.25 SAPU
//--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-- //-Zeilen um 777 Beachten!--
//-00--zhash.cl-Exclude-(Wir bauen ausschließlich um diesen einen Kernel herum!)--
//--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--
//--ARC INTEL DG2--
------------------------------------->
------------------------------------->
//--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 <array>
#include <cstdint>
#include <string>
#define INPUT_SIZE 512
#define HASH_SIZE 32
#define NONCES_PER_THREAD 1
#define BUCKET_COUNT 32
#define HASH_ROUNDS_OUTPUT_SIZE 32
//--Hier werden die Ressourcen der Grafikkarte im Detail eingeteilt.
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;
};
//--Externe Werte mit eingetragen.--
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 <array>
#include <cstdlib>
#include <iostream>
#include <random>
#include <string>
#include <vector>
//--Alle OpenCL-Geräte auf dem Computer 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--
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------->
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------->
#Hier startet man den Bauvorgang für das Miningprogramm auf seinem Computer um es Einsatzbereit zu machen.--
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
//--Standard-Ziel/Target--
all: $(OUT)
#Bau des GPU-Miners--
$(OUT): $(OBJ)
$(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS)
#Generisches Compile-Ziel--
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@
#Säubern--
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 <algorithm> //--Eventuell Austragbar weil fester Algo--
#include <array>
#include <atomic> //--Atomic Nutzen Ja Nein Vielleicht erstmal Ja--
#include <cctype> //--Für std::isxdigit--
#include <chrono> //--Für Uhrzeit der Netzwekoperation--
#include <cstring>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <limits>
#include <optional>
#include <random> //--Für std::random_device und std::mt19937--
#include <string>
#include <thread>
#include <vector> //--FÜr Intel ARC GPUs DG2 Alchmemist--
//--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 std::atomic<bool> abort_mining;
extern std::atomic<bool> socket_valid;
extern std::atomic<bool> job_wurde_übernommen;
//--Globale OpenCL-Objekte--
cl_context context = nullptr;
cl_command_queue queue = nullptr;
cl_kernel kernel = nullptr;
cl_program program = nullptr;
cl_device_id device = nullptr;
//--🧱 Erstellt den Eingabepuffer aus dem MiningJob--
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,
const std::function<MiningJob()> get_current_job,
const std::function<void(uint32_t, const std::array<uint8_t, 32>&,
const MiningJob&)> on_valid_share) {
const GpuResources &resources, int intensity
) {
//--Initialisierung des Zufallszahlengenerators--
//--Ein guter Zufallszahlengenerator ist wichtig für die Nonce.--
//--Ein guter Zufallszahlengenerator ist wichtig für die Nonce.--
//--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--
//--SYCL-Beispiel für ulong8-Vergleiche--
//--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.--
sycl::vec<ulong, 8> nonce = ...; //--512-bit Nonce-Berechnung--
sycl::vec<ulong, 8> hash = blake2b(nonce); //--SIMD-optimierte Hashfunktion--
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--
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.--
//--2-- Target berechnen--
auto clean_bits = sanitize_hex_string(job.nbits);
auto maybe_bits = safe_stoul_hex(clean_bits);
if (!maybe_bits)
return;
std::vector<uint8_t> target = bits_to_target(*maybe_bits);
//--3-- Puffer vorbereiten--
std::vector<cl_uchar> host_input_buffer;
build_input_from_job(job, host_input_buffer);
host_input_buffer.resize(512, 0);
//--Globale Work-Size ist ein Vielfaches der lokalen, um volle Gruppen zu gewährleisten--
size_t global_work_size =
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)--
//--4--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,
512, host_input_buffer.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--</pre>
CLMemWrapper cl_solution_count.mem = clCreateBuffer(
resources.context, CL_MEM_READ_WRITE, sizeof(cl_uint), nullptr, &err);
if (!check_cl(err, "clCreateBuffer(cl_solution_count)", &resources)) return;
if (err != CL_SUCCESS) { /* Fehlerbehandlung */
}
//--5--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--
uint32_t start_nonce = batch_size;
;
//--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;
}
//--Vor der Kernel-Ausführung--
//--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;
std::cout << "Starting mining loop with:\n";
std::cout << " Job ID: " << job.job_id << "\n";
std::cout << " PrevHash: " << job.prevhash << "\n";
std::cout << " Target: " << job.nbits << "\n";
std::cout << " Intensity: " << intensity << "\n";
//--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, &event);
if (!check_cl(err, "clEnqueueNDRangeKernel", &resources)) {
if (evt) clReleaseEvent(evt); //--Ereignis freigeben--
continue;
}
clWaitForEvents(1, &event); //--Warten, bis der Kernel fertig ist--
clReleaseEvent(evt); //--Ereignis freigeben--
//--Lösungen verarbeiten--
//--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 < batch_size; ++i) {
const uint32_t idxA = index_buffer[i * 2];
const uint32_t idxB = index_buffer[i * 2 + 1];
if (idxA == 0 && idxB == 0)
continue;
//--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) {
final_hash[j] =
output_buffer[idxA * 32 + j] ^ output_buffer[idxB * 32 + j];
}
const size_t offsetA = static_cast<size_t>(idxA) * 32 + j;
const size_t offsetB = static_cast<size_t>(idxB) * 32 + j;
if (is_valid_hash(final_hash, target)) {
on_valid_share(start_nonce + idxA, final_hash, job);
}
}
}
//--Aufräumen--
clReleaseMemObject(cl_input);
clReleaseMemObject(cl_output);
clReleaseMemObject(cl_indexes);
clReleaseMemObject(cl_solution_count);
clReleaseKernel(resources.kernel);
clReleaseProgram(resources.program);
clReleaseCommandQueue(resources.queue);
clReleaseContext(resources.context);
}
//--Aufräumen (nicht explizit nötig wegen RAII)--
//--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 <array>
#include <functional>
#include <string>
//--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 <array>
#include <cstdint>
#include <string>
#include <vector>
//--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 <iostream>
#include <optional>
#include <string>
#include <vector>
//--Diese Funktion parst eine JSON-Zeile vom Pool und wandelt sie in ein--
//-MiningJob-Objekt um.--
inline std::optional<MiningJob> 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.com ist typischerweise 8 oder mehr--
//--Das Programm wird am Ende auf Solo und Poolmining zur Auswahl aufgestockt, bis diese Zeilen Ersetzt wurden.--
if (params.size() < 8) {
std::cerr << "❌ Fehler: 'mining.notify' hat zu wenige Parameter ("
<< params.size() << "). Erwartet >= 8.\n";
return std::nullopt;
}
MiningJob job;
//--Parameterzuweisung 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 Unterschreiber 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 <iostream>
#include <string>
#include <vector>
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 <fstream>
#include <iostream>
#include <vector>
#include <algorithm>
#include <iomanip>
#include <stdexcept>
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(./XBTGPUARC/kernels/zhash.cl);
} 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 <string>
#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 \ //--Startet das XBTGPUARC Mining Programm.--
--platform 1 \ //--Wählt die Plattform aus, auf wlecher Gemined werden soll.--
--device 0 \ //-Wählt die genaue Recheneinheit in Form der Intel ARC Grafikkarte mit dem DG2 Chip aus.--
--algo zhash_144_5 \ //--Wählt den zu minenden Algoritmus aus--
--pool solo-btg.2miners.com \ //--Wählt den Pool oder den Server aus, um mit dem Netzwerk zu Kommunizieren.--
--port 4040 \ //--Wählt den PoolPort aus.--
--wallet Gb4V4a9Jk3p8aH6jkW3Aq3sq8rQCuJQ6S8 \ //--Hier fügen Sie ihre eigene Mining Adressen ein!--
//--Sehr Wichtig, da ansonsten die Belohnung an meine Person geht!--
--worker A730m \ //--Hier sehen Sie die genaue Bezeichnung des ausgewählten ARC Computer Chips.--
--password x \ //--Hier können Sie den Wert bei "x" Behalten. Es würde wohl kaum jemand ihre Wallet mit Geld durch seine Arbeit bei ihnen füllen wollen.--
--intensity 256 //--HIer kann man Einstellen, wie Stark die Grafikkarte arbeiten soll!--
//--Ein Mittelhoher Wert ist in der Regel zu Präferieren, genaue Details zur ARC GPU Architektur stehen noch aus.--
-------------------------------------------------------------------------------------
//--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 <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <thread>
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 <string>
//--Startet Stratum-Connection, empfängt Jobs, startet Miner, horcht auf Arbeit vom Pool.--
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--
------------------------------------------------------------------------------------->