╭─alu@king in ~
╰─λ cd ./Dev/XBTGPUARC/
╭─alu@king in ~/Dev/XBTGPUARC
╰─λ ./run.sh
🚀 Starte XBTGPUARC mit Algo: zhash_144_5
🎛 Platform: 0 | Device: 0 | Intensity: 8196
👤 Worker: Gb4V4a9Jk3p8aH6jkW3Aq3sq8rQCuJQ6S8.A730m
📡 Verbunden mit solo-btg.2miners.com:4040
DEBUG JSON input: {"id":1,"result":[null,"0000000000040045"],"error":null}
🌐 Nachricht:
{"id":1,"result":[null,"0000000000040045"],"error":null}
DEBUG JSON input: {"id":2,"result":true,"error":null}
🌐 Nachricht:
{"id":2,"result":true,"error":null}
DEBUG JSON input: {"id":null,"method":"mining.set_target","params":["0003c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3"]}
🌐 Nachricht:
{"id":null,"method":"mining.set_target","params":["0003c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3"]}
DEBUG JSON input: {"id":null,"method":"mining.notify","params":["992758603204739","00000020","88a70dd7b89b19b685724e5f99c3291f8e1081a970cdcc7208210f8111000000","45f22e56a545b5e9eb8ab840a4ac7af98ddaf6593cb17eb8040c57085560f6f5","d3d00d0000000000000000000000000000000000000000000000000000000000","79198468","d9b0361d",false,"144_5","BgoldPoW"]}
🎯 Neuer Job: 992758603204739
xbtgpuarc 24,5%
Ich mach mal weiter.
Prompt:
--pers BgoldPoW wichtig für Equihash, mining_job.hpp braucht ggf. einen .target() Getter in Byteform, mehrstufiges GPU-Konstrukt, das Stages 0–2 erledigt, CPU-seitige Matchinglogik für Stage 3–5 (Proof-of-Work), Integration in miner_loop.cpp → echtes Target prüfen, Logging, wenn ein möglicher Proof gefunden wurde.
Bisher Funktionierend!!!
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=1024
./xbtgpuarc
--platform 0
--device 0
--algo zhash_144_5
--pool solo-btg.2miners.com
--port 4040
--wallet Gb4V4a9Jk3p8aH6jkW3Aq3sq8rQCuJQ6S8
--worker A730m
--password x
--intensity 8196
main.cpp:
#include <boost/asio.hpp>
#include <CL/cl.h>
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "notify_parser.hpp"
#include "mining_job.hpp"
#include "miner_loop.hpp"
void connect_to_pool(const std::string& host, int port,
const std::string& wallet,
const std::string& worker,
const std::string& password,
int platform_index,
int device_index,
int intensity) {
using boost::asio::ip::tcp;
try {
boost::asio::io_context io_context;
tcp::resolver resolver(io_context);
auto endpoints = resolver.resolve(host, std::to_string(port));
tcp::socket socket(io_context);
boost::asio::connect(socket, endpoints);
std::cout << "📡 Verbunden mit " << host << ":" << port << "\n";
std::string subscribe = R"({"id":1,"method":"mining.subscribe","params":[]})" "\n";
std::string full_user = wallet + "." + worker;
std::string authorize = R"({"id":2,"method":"mining.authorize","params":[")" + full_user + R"(",")" + password + R"("]})" "\n";
boost::asio::write(socket, boost::asio::buffer(subscribe));
boost::asio::write(socket, boost::asio::buffer(authorize));
std::string buffer;
for (;;) {
char reply[4096];
boost::system::error_code error;
size_t len = socket.read_some(boost::asio::buffer(reply), error);
buffer.append(reply, len);
size_t pos = 0;
while ((pos = buffer.find('\n')) != std::string::npos) {
std::string line = buffer.substr(0, pos);
buffer.erase(0, pos + 1);
auto job = parse_notify(line);
if (job) {
std::cout << "🎯 Neuer Job: " << job->job_id << "\n";
miner_loop(*job, [&](uint32_t nonce, const std::array<uint8_t, 32>& hash, const MiningJob& job) {
std::ostringstream hexstream;
for (auto byte : hash)
hexstream << std::hex << std::setw(2) << std::setfill('0') << (int)byte;
std::string solution = hexstream.str();
std::ostringstream nonce_hex;
nonce_hex << std::hex << std::setw(8) << std::setfill('0') << nonce;
static int extranonce_counter = 1;
std::ostringstream extranonce2_stream;
extranonce2_stream << std::hex << std::setw(8) << std::setfill('0') << extranonce_counter++;
std::string full_user = wallet + "." + worker;
std::ostringstream submit;
submit << R"({"id":4,"method":"mining.submit","params":[")"
<< full_user << R"(",")"
<< job.job_id << R"(",")"
<< extranonce2_stream.str() << R"(",")"
<< job.ntime << R"(",")"
<< nonce_hex.str() << R"(",")"
<< solution << R"("]})" "\n";
boost::asio::write(socket, boost::asio::buffer(submit.str()));
std::cout << "📤 Share gesendet:\n" << submit.str();
});
}
std::cout << "🌐 Nachricht:\n" << line << "\n";
}
if (error == boost::asio::error::eof) break;
else if (error) throw boost::system::system_error(error);
}
} catch (const std::exception& e) {
std::cerr << "❌ Fehler: " << e.what() << "\n";
}
}
int main(int argc, char** argv) {
int platform_index = 0;
int device_index = 0;
int intensity = 4;
std::string algo = "zhash_144_5";
std::string wallet = "default_wallet";
std::string worker = "default_worker";
std::string password = "x";
std::string pool_host = "solo-btg.2miners.com";
int pool_port = 4040;
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]);
}
std::cout << "🚀 Starte XBTGPUARC mit Algo: " << algo << "\n";
std::cout << "🎛️ Platform: " << platform_index << " | Device: " << device_index << " | Intensity: " << intensity << "\n";
std::cout << "👤 Worker: " << wallet << "." << worker << "\n";
connect_to_pool(pool_host, pool_port, wallet, worker, password, platform_index, device_index, intensity);
return 0;
}
miner_loo.cpp:
#include "miner_loop.hpp"
#include <CL/cl.h>
#include
#include
#include
#include
#include
// 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;
void init_opencl() {
if (context) return; // Bereits initialisiert
cl_int err;
cl_platform_id platform;
clGetPlatformIDs(1, &platform, nullptr);
clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 1, &device, nullptr);
context = clCreateContext(nullptr, 1, &device, nullptr, nullptr, &err);
queue = clCreateCommandQueueWithProperties(context, device, 0, &err);
std::ifstream file("kernels/zhash.cl");
if (!file) {
std::cerr << "❌ Konnte Kernel-Datei nicht öffnen.\n";
std::exit(1);
}
std::string source((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
const char* src = source.c_str();
size_t size = source.size();
program = clCreateProgramWithSource(context, 1, &src, &size, &err);
err = clBuildProgram(program, 1, &device, nullptr, nullptr, nullptr);
if (err != CL_SUCCESS) {
size_t log_size;
clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, 0, nullptr, &log_size);
std::vector<char> log(log_size);
clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, log_size, log.data(), nullptr);
std::cerr << "❌ Build-Fehler:\n" << log.data() << "\n";
std::exit(1);
}
kernel = clCreateKernel(program, "zhash_144_5", &err);
}
void miner_loop(const MiningJob& job,
const std::function<void(uint32_t nonce, const std::array<uint8_t, 32>& hash, const MiningJob& job)>& on_valid_share) {
init_opencl();
std::mt19937 rng(std::random_device{}());
std::uniform_int_distribution<uint32_t> dist;
std::vector<uint8_t> target = bits_to_target(std::stoul(job.bits, nullptr, 16));
const int batch_size = 4096;
std::vector<cl_uchar> input_buffer(512 * batch_size);
std::vector<cl_uchar> output_buffer(32 * batch_size);
cl_int err;
cl_mem cl_input = clCreateBuffer(context, CL_MEM_READ_ONLY, input_buffer.size(), nullptr, &err);
cl_mem cl_output = clCreateBuffer(context, CL_MEM_WRITE_ONLY, output_buffer.size(), nullptr, &err);
clSetKernelArg(kernel, 0, sizeof(int), &batch_size);
clSetKernelArg(kernel, 1, sizeof(cl_mem), &cl_input);
clSetKernelArg(kernel, 2, sizeof(cl_mem), &cl_output);
while (true) {
uint32_t start_nonce = dist(rng);
for (int i = 0; i < batch_size; ++i) {
uint32_t nonce = start_nonce + i;
cl_uchar* in = &input_buffer[i * 512];
in[0] = nonce & 0xFF;
in[1] = (nonce >> 8) & 0xFF;
in[2] = (nonce >> 16) & 0xFF;
in[3] = (nonce >> 24) & 0xFF;
// Rest kann später ergänzt werden (z. B. Block-Header)
}
clEnqueueWriteBuffer(queue, cl_input, CL_TRUE, 0, input_buffer.size(), input_buffer.data(), 0, nullptr, nullptr);
size_t global_work_size = batch_size;
clEnqueueNDRangeKernel(queue, kernel, 1, nullptr, &global_work_size, nullptr, 0, nullptr, nullptr);
clFinish(queue);
clEnqueueReadBuffer(queue, cl_output, CL_TRUE, 0, output_buffer.size(), output_buffer.data(), 0, nullptr, nullptr);
for (int i = 0; i < batch_size; ++i) {
std::array<uint8_t, 32> hash;
std::copy(&output_buffer[i * 32], &output_buffer[i * 32] + 32, hash.begin());
if (is_valid_hash(hash, target)) {
uint32_t nonce = start_nonce + i;
on_valid_share(nonce, hash, job);
}
}
}
// cleanup wäre möglich, aber optional bei Dauerbetrieb
}
miner_loop.hpp:
#pragma once
#include "mining_job.hpp"
#include
#include
#include
void miner_loop(const MiningJob& job,
const std::function<void(uint32_t nonce, const std::array<uint8_t, 32>& hash, const MiningJob& job)>& on_valid_share);
notify_parser.hpp:
#pragma once
#include "mining_job.hpp"
#include <boost/json.hpp>
#include
#include
#include
inline std::optional parse_notify(const std::string& json_string) {
using namespace boost::json;
try {
std::cerr << "DEBUG JSON input: " << json_string << "\n";
value parsed = parse(json_string);
if (!parsed.is_object()) {
std::cerr << "❌ JSON Fehler: Nachricht ist kein Objekt\n";
return std::nullopt;
}
auto& obj = parsed.as_object();
if (!obj.contains("method")) return std::nullopt;
if (obj["method"].kind() != kind::string) {
std::cerr << "❌ JSON Fehler: 'method' ist kein String\n";
return std::nullopt;
}
std::string method = obj["method"].as_string().c_str();
if (method != "mining.notify") return std::nullopt;
if (!obj.contains("params")) {
std::cerr << "❌ JSON Fehler: Kein 'params' Feld\n";
return std::nullopt;
}
auto& params_value = obj["params"];
if (!params_value.is_array()) {
std::cerr << "❌ JSON Fehler: 'params' ist kein Array\n";
return std::nullopt;
}
auto& params = params_value.as_array();
if (params.size() < 9) {
std::cerr << "❌ JSON Fehler: 'params' Array zu klein\n";
return std::nullopt;
}
MiningJob job;
job.job_id = params[0].as_string().c_str();
job.prevhash = params[1].as_string().c_str();
job.coinbase1 = params[2].as_string().c_str();
job.coinbase2 = params[3].as_string().c_str();
// Merkle Branch flexibel: Array oder String
if (params[4].is_array()) {
for (const auto& entry : params[4].as_array())
job.merkle_branch.emplace_back(entry.as_string().c_str());
} else if (params[4].is_string()) {
job.merkle_branch.emplace_back(params[4].as_string().c_str());
} else {
std::cerr << "❌ JSON Fehler: Merkle-Branch weder Array noch String\n";
return std::nullopt;
}
// version, bits als Strings
job.version = params[5].as_string().c_str();
job.bits = params[6].as_string().c_str();
// clean_jobs ist bool
if (params[7].is_bool()) {
job.clean_jobs = params[7].as_bool();
} else {
std::cerr << "❌ JSON Fehler: 'clean_jobs' ist kein Boolean\n";
return std::nullopt;
}
return job;
} catch (const std::exception& e) {
std::cerr << "❌ JSON Exception: " << e.what() << "\n";
return std::nullopt;
}
}
mining_job.hpp:
#pragma once
#include
#include
#include
#include
// Struktur für einen Mining-Job (aus mining.notify)
struct MiningJob {
std::string job_id;
std::string prevhash;
std::string coinbase1;
std::string coinbase2;
std::vector<std::string> merkle_branch;
std::string version;
std::string bits;
std::string ntime;
bool clean_jobs;
};
// Wandelt "bits" (compact target) in echtes 256-bit Target (32 Byte)
inline std::vector<uint8_t> bits_to_target(uint32_t bits) {
int exponent = bits >> 24;
uint32_t mantissa = bits & 0x007fffff;
std::vector<uint8_t> target(32, 0);
int shift = exponent - 3;
if (shift >= 0 && shift <= 29) {
target[shift] = (mantissa >> 16) & 0xff;
target[shift + 1] = (mantissa >> 8) & 0xff;
target[shift + 2] = mantissa & 0xff;
}
return target;
}
// Vergleicht 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 false; // Gleichstand ist kein gültiger Share
}
Makefile
CXX = g++
CXXFLAGS = -std=c++17 -Wall -O2
LDFLAGS = -lOpenCL -lboost_json -lpthread
SRC = main.cpp miner_loop.cpp
OUT = xbtgpuarc
all:
$(CXX) $(CXXFLAGS) $(SRC) -o $(OUT) $(LDFLAGS)
clean:
rm -f $(OUT)
stratum_notify_listener.cpp:
#include "notify_parser.hpp"
#include
#include <boost/asio.hpp>
#include
using boost::asio::ip::tcp;
int main() {
const std::string pool_host = "solo-btg.2miners.com";
const std::string pool_port = "4040";
const std::string wallet = "Gb4V4a9Jk3p8aH6jkW3Aq3sq8rQCuJQ6S8";
const std::string worker = "XBTGPUARC";
const std::string password = "x";
const std::string full_user = wallet + "." + worker;
try {
boost::asio::io_context io_context;
tcp::resolver resolver(io_context);
auto endpoints = resolver.resolve(pool_host, pool_port);
tcp::socket socket(io_context);
boost::asio::connect(socket, endpoints);
std::cout << "📡 Verbunden mit " << pool_host << ":" << pool_port << "\n";
// mining.subscribe senden
std::string subscribe = "{\"id\": 1, \"method\": \"mining.subscribe\", \"params\": []}\n";
std::string authorize = "{\"id\": 2, \"method\": \"mining.authorize\", \"params\": [\"" +
full_user + "\", \"" + password + "\"]}\n";
// Empfangsschleife
std::string buffer;
for (;;) {
char reply[4096];
boost::system::error_code error;
size_t len = socket.read_some(boost::asio::buffer(reply), error);
buffer.append(reply, len);
// Zeilenweise verarbeiten
size_t pos = 0;
while ((pos = buffer.find('\n')) != std::string::npos) {
std::string line = buffer.substr(0, pos);
buffer.erase(0, pos + 1);
auto job = parse_notify(line);
if (job) {
std::cout << "🎯 Job ID: " << job->job_id << std::endl;
std::cout << "🧱 PrevHash: " << job->prevhash << std::endl;
}
std::cout << "🌐 Nachricht:\n" << line << "\n";
}
if (error == boost::asio::error::eof) break;
else if (error) throw boost::system::system_error(error);
}
return 0;
}
stratum_client.cpp:
#include
#include
#include
#include
#include
#include
#include <netinet/in.h>
#include
#include <arpa/inet.h>
#include <sys/socket.h>
bool connect_to_pool(const std::string& host, int port, const std::string& wallet, const std::string& worker) {
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
std::cerr << "❌ Socket-Fehler\n";
return false;
}
struct hostent* server = gethostbyname(host.c_str());
if (!server) {
std::cerr << "❌ Host nicht gefunden: " << host << "\n";
return false;
}
sockaddr_in serv_addr {};
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
std::memcpy(&serv_addr.sin_addr.s_addr, server->h_addr, server->h_length);
std::cout << "📞 Verbinde zu " << host << ":" << port << "...\n";
if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
std::cerr << "❌ Verbindung fehlgeschlagen\n";
return false;
}
// Subscribe senden
std::string subscribe = "{\"id\":1,\"method\":\"mining.subscribe\",\"params\":[]}\n";
send(sock, subscribe.c_str(), subscribe.size(), 0);
// Authorize senden
std::ostringstream auth;
auth << "{\"id\":2,\"method\":\"mining.authorize\",\"params\":[\""
<< wallet << "." << worker << "\",\"\"]}\n";
std::string auth_str = auth.str();
send(sock, auth_str.c_str(), auth_str.size(), 0);
// Mitlesen
char buffer[4096];
while (true) {
int n = read(sock, buffer, sizeof(buffer) - 1);
if (n <= 0) break;
buffer[n] = '\0';
std::cout << "📨 Pool: " << buffer << std::endl;
}
close(sock);
return true;
}
kernels: zhash.cl:
#define BUCKET_COUNT 32
#define HASH_SIZE 18 // 144 bit
__constant uint IV[8] = {
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
};
inline uint rotr32(uint x, uint n) {
return (x >> n) | (x << (32 - n));
}
inline void hash_rounds(__global const uchar* input, uint gid, __global uchar* hash_out) {
uint m = 0;
for (int i = 0; i < 4; ++i) {
m |= ((uint)input[gid * 512 + i]) << (i * 8);
}
uint a = IV[0] ^ m;
uint b = IV[1] ^ (m << 1);
uint c = IV[2] ^ (m >> 1);
uint d = IV[3] ^ (~m);
for (int i = 0; i < 91; ++i) {
a += b ^ (m ^ i); a = rotr32(a, 16);
b += c ^ a; b = rotr32(b, 12);
c += d ^ b; c = rotr32(c, 8);
d += a ^ c; d = rotr32(d, 7);
}
// 18 Byte Hash aus 4x uint
hash_out[0] = a & 0xFF;
hash_out[1] = (a >> 8) & 0xFF;
hash_out[2] = (a >> 16) & 0xFF;
hash_out[3] = (a >> 24) & 0xFF;
hash_out[4] = b & 0xFF;
hash_out[5] = (b >> 8) & 0xFF;
hash_out[6] = (b >> 16) & 0xFF;
hash_out[7] = (b >> 24) & 0xFF;
hash_out[8] = c & 0xFF;
hash_out[9] = (c >> 8) & 0xFF;
hash_out[10] = (c >> 16) & 0xFF;
hash_out[11] = (c >> 24) & 0xFF;
hash_out[12] = d & 0xFF;
hash_out[13] = (d >> 8) & 0xFF;
hash_out[14] = (d >> 16) & 0xFF;
hash_out[15] = (d >> 24) & 0xFF;
hash_out[16] = (a ^ b) & 0xFF;
hash_out[17] = (c ^ d) & 0xFF;
}
__kernel void zhash_144_5(
__global const uchar* input,
__global uchar* output,
__global uint* solution_indexes // optional: für Lösungspaare
) {
int gid = get_global_id(0);
__global uchar* my_hash = &output[gid * HASH_SIZE];
hash_rounds(input, gid, my_hash);
// Bucket-Index = obere Bits vom Hash (5 Bit → 32 Buckets)
uint bucket = my_hash[0] >> 3;
// Simpler Vergleich: lineares Durchsuchen auf Match
for (int i = 0; i < gid; ++i) {
__global uchar* other_hash = &output[i * HASH_SIZE];
uint other_bucket = other_hash[0] >> 3;
if (bucket == other_bucket) {
// Match gefunden → Lösungspaar speichern
solution_indexes[gid * 2 + 0] = i;
solution_indexes[gid * 2 + 1] = gid;
break;
}
}
}
logs