------------------------------------------------------------------------------------->
//--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.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
#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(./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
#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
#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, 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--
------------------------------------------------------------------------------------->
SCHLUSS 2/4