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;
}
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;
}
miner_loop.cpp:
// miner_loop.cpp
#include "miner_loop.hpp"
#include <CL/cl.h>
#include
#include
#include
#include
#include
static cl_context context = nullptr;
static cl_command_queue queue = nullptr;
static cl_kernel kernel = nullptr;
static cl_program program = nullptr;
static cl_device_id device = nullptr;
// Kernel-Name
static const char* KERNEL_NAME = "zhash_144_5";
// Initialisiert OpenCL & lädt Kernel
void init_opencl() {
if (context) return; // Schon 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");
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);
clBuildProgram(program, 1, &device, nullptr, nullptr, nullptr);
kernel = clCreateKernel(program, KERNEL_NAME, &err);
}
// Miner-Loop: führt Kernel aus & prüft Hash < Target
void miner_loop(const MiningJob& job,
const std::function<void(uint32_t nonce, const std::array<uint8_t, 32>& hash)>& 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 des Buffers bleibt Dummy (kann durch coinbase ersetzt werden)
}
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::memcpy(hash.data(), &output_buffer[i * 32], 32);
if (is_valid_hash(hash, target)) {
uint32_t nonce = start_nonce + i;
on_valid_share(nonce, hash);
}
}
}
// Optional: Freigeben (falls du willst)
// clReleaseMemObject(cl_input);
// clReleaseMemObject(cl_output);
// clReleaseKernel(kernel);
// clReleaseProgram(program);
// clReleaseCommandQueue(queue);
// clReleaseContext(context);
}
miner_loop.hpp:
// 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)>& on_valid_share);
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 4096
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)
notify_parser.hpp:
#pragma once
#include "mining_job.hpp"
#include <boost/json.hpp>
#include
#include
#include
#include
inline std::string to_hex(uint32_t val) {
std::ostringstream ss;
ss << std::hex << std::setw(8) << std::setfill('0') << val;
return ss.str();
}
inline std::optional parse_notify(const std::string& json_string) {
using namespace boost::json;
try {
value parsed = parse(json_string);
if (!parsed.is_object()) return std::nullopt;
auto& obj = parsed.as_object();
if (!obj.contains("method") || obj["method"].as_string() != "mining.notify") return std::nullopt;
auto& params = obj["params"].as_array();
if (params.size() < 9) 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();
for (const auto& entry : params[4].as_array())
job.merkle_branch.emplace_back(entry.as_string().c_str());
job.version = to_hex(static_cast<uint32_t>(params[5].as_int64()));
job.bits = to_hex(static_cast<uint32_t>(params[6].as_int64()));
job.ntime = to_hex(static_cast<uint32_t>(params[7].as_int64()));
job.clean_jobs = params[8].as_bool();
return job;
} catch (const std::exception& e) {
std::cerr << "❌ JSON-Fehler: " << 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
}
main.cpp:
#include <boost/asio.hpp>
#include <CL/cl.h>
#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) {
std::cout << "✅ Share gefunden! Nonce = " << nonce << "\n";
// TODO: submit_share(job, nonce, hash);
});
}
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;
}
kernels:
__constant uint IV[8] = {
0x6a09e667, 0xbb67ae85, 0x0a6ef372, 0x0a54ff5a,
0x510e527f, 0xb056688c, 0x1f83d9ab, 0x5be0cd19
};
inline uint rotl32(uint x, uint n) {
return (x << n) | (x >> (32 - n));
}
inline void G(uint *a, uint *b, uint *c, uint *d, uint m) {
*a = *a + *b + m;
*d ^= *a;
d = rotl32(d, 16);
*c = *c + *d;
*b ^= *c;
*b = rotl32(*b, 12);
*a = *a + *b + m;
*d ^= *a;
*d = rotl32(*d, 8);
*c = *c + *d;
*b ^= *c;
*b = rotl32(*b, 7);
}
__kernel void zhash_kernel(__global const uchar *input, __global uchar *output) {
int gid = get_global_id(0);
// Beispielhafte Initialwerte (einfach IV kopiert, z.B. Blake-Vorbild)
uint a = IV[0];
uint b = IV[1];
uint c = IV[2];
uint d = IV[3];
// Beispielhafte Nachrichtendaten (real aus input)
uint m = ((uint*)input)[gid];
// Mehrere Runden simuliert (feste Zahl z.B. 4)
for (int i = 0; i < 4; ++i) {
G(&a, &b, &c, &d, m ^ i);
}
// Output berechnen (einfache Reduktion, sonst wird’s zu groß)
uint result = a ^ b ^ c ^ d;
// 4 Byte rausschreiben
output[gid * 4 + 0] = (uchar)((result >> 0) & 0xFF);
output[gid * 4 + 1] = (uchar)((result >> 8) & 0xFF);
output[gid * 4 + 2] = (uchar)((result >> 16) & 0xFF);
output[gid * 4 + 3] = (uchar)((result >> 24) & 0xFF);
}
logs