Fixing miner issues

- Switch from full-block to header-only (140-byte) RandomX input
- Add 32-byte SoloNonce system for solo mining mode
- Compute proper difficulty target from compact bits field
- Add SHA256D dual-hash PoW check in CpuWorker for solo mining
- Raise RandomX dataset/scratchpad limits to 4GB/4MB
- Use standard RandomX share filtering in pool (stratum) mode
This commit is contained in:
2026-03-05 06:00:55 -06:00
parent a27a1e327b
commit 95d3ff2c4a
12 changed files with 529 additions and 185 deletions

View File

@@ -40,6 +40,35 @@
#include "net/JobResults.h"
#ifdef XMRIG_FEATURE_TLS
# include <openssl/sha.h>
#else
// Standalone SHA-256 for non-TLS builds
# ifdef _WIN32
# include <windows.h>
# include <bcrypt.h>
static void SHA256(const uint8_t* data, size_t len, uint8_t* out) {
BCRYPT_ALG_HANDLE hAlg = nullptr;
BCRYPT_HASH_HANDLE hHash = nullptr;
BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_SHA256_ALGORITHM, nullptr, 0);
BCryptCreateHash(hAlg, &hHash, nullptr, 0, nullptr, 0, 0);
BCryptHashData(hHash, const_cast<PUCHAR>(data), static_cast<ULONG>(len), 0);
BCryptFinishHash(hHash, out, 32, 0);
BCryptDestroyHash(hHash);
BCryptCloseAlgorithmProvider(hAlg, 0);
}
# else
# include "crypto/ghostrider/sph_sha2.h"
static void SHA256(const uint8_t* data, size_t len, uint8_t* out) {
sph_sha256_context ctx;
sph_sha256_init(&ctx);
sph_sha256(&ctx, data, len);
sph_sha256_close(&ctx, out);
}
# endif
#endif
#ifdef XMRIG_ALGO_RANDOMX
# include "crypto/randomx/randomx.h"
#endif
@@ -55,6 +84,37 @@ namespace xmrig {
static constexpr uint32_t kReserveCount = 32768;
// ── DRAGONX/HUSH PoW hash ────────────────────────────────────────────────────
//
// DragonX block difficulty is checked against double_sha256(173-byte header),
// NOT the RandomX hash directly (unlike Monero).
//
// Full 173-byte header structure:
// [0:108] header_base = version(4) + prevhash(32) + merkle(32)
// + commitments(32) + time(4) + bits(4)
// [108:140] nonce = 32-byte miner nonce
// [140] 0x20 = compact_size for 32-byte solution
// [141:173] rx_solution = RandomX hash result (32 bytes)
//
// @param blob 140-byte header WITH nonce already at bytes [108:140]
// @param rx_hash 32-byte RandomX hash result
// @param out 32-byte output buffer for double_sha256
static inline void dragonx_pow_hash(const uint8_t* blob, const uint8_t* rx_hash, uint8_t* out)
{
uint8_t full_header[173];
memcpy(full_header, blob, 140); // header (108 bytes) + nonce (32 bytes)
full_header[140] = 0x20; // compact_size = 32 (solution length)
memcpy(full_header + 141, rx_hash, 32); // RandomX hash = the PoW solution
// double SHA256 = SHA256(SHA256(full_header))
uint8_t tmp[32];
SHA256(full_header, 173, tmp);
SHA256(tmp, 32, out);
}
// ─────────────────────────────────────────────────────────────────────────────
#ifdef XMRIG_ALGO_CN_HEAVY
static std::mutex cn_heavyZen3MemoryMutex;
VirtualMemory* cn_heavyZen3Memory = nullptr;
@@ -267,8 +327,13 @@ void xmrig::CpuWorker<N>::start()
}
uint32_t current_job_nonces[N];
alignas(8) uint8_t current_solo_nonces[N * 32];
for (size_t i = 0; i < N; ++i) {
current_job_nonces[i] = readUnaligned(m_job.nonce(i));
// Save solo nonces BEFORE they get incremented by nextRound()
if (m_job.isSoloMining()) {
memcpy(current_solo_nonces + i * 32, m_job.soloNonce(i), 32);
}
}
# ifdef XMRIG_FEATURE_BENCHMARK
@@ -337,18 +402,50 @@ void xmrig::CpuWorker<N>::start()
if (valid) {
for (size_t i = 0; i < N; ++i) {
const uint64_t value = *reinterpret_cast<uint64_t*>(m_hash + (i * 32) + 24);
# ifdef XMRIG_FEATURE_BENCHMARK
if (m_benchSize) {
const uint64_t value = *reinterpret_cast<uint64_t*>(m_hash + (i * 32) + 24);
if (current_job_nonces[i] < m_benchSize) {
BenchState::add(value);
}
continue;
}
else
# endif
if (value < job.target()) {
JobResults::submit(job, current_job_nonces[i], m_hash + (i * 32), job.hasMinerSignature() ? miner_signature_saved : nullptr);
if (job.algorithm() == Algorithm::RX_HUSH && m_job.isSoloMining()) {
// ── DRAGONX/HUSH solo mining: use double_sha256(173-byte header) for difficulty ──
//
// The daemon checks GetHash() = double_sha256(173 bytes) < target,
// NOT the RandomX hash directly. We must filter shares the same way
// so that every submitted share is a genuine block candidate.
//
// Reconstruct the 140-byte blob that was ACTUALLY hashed this round:
// - bytes [0:108] are unchanged by nextRound() (only nonce changes)
// - bytes [108:140] = saved nonce (current_solo_nonces, before nextRound)
uint8_t blob_for_header[140];
memcpy(blob_for_header, m_job.blob(), 108); // header base (unchanged)
memcpy(blob_for_header + 108, current_solo_nonces + i * 32, 32); // saved 32-byte nonce
// Compute PoW hash: double_sha256(blob[140] + 0x20 + rx_hash[32])
alignas(8) uint8_t pow_hash[32];
dragonx_pow_hash(blob_for_header, m_hash + (i * 32), pow_hash);
// Compare last 8 bytes of pow_hash (same field as XMRig's standard check)
const uint64_t pow_value = *reinterpret_cast<uint64_t*>(pow_hash + 24);
if (pow_value < job.target()) {
// Submit full 32-byte nonce + rx_hash as result
JobResults::submit(JobResult(job, current_solo_nonces + i * 32, m_hash + (i * 32)));
}
} else {
// ── Standard XMRig path (Monero, CryptoNight, etc.) ──
const uint64_t value = *reinterpret_cast<uint64_t*>(m_hash + (i * 32) + 24);
if (value < job.target()) {
if (m_job.isSoloMining()) {
JobResults::submit(JobResult(job, current_solo_nonces + i * 32, m_hash + (i * 32)));
} else {
JobResults::submit(job, current_job_nonces[i], m_hash + (i * 32), job.hasMinerSignature() ? miner_signature_saved : nullptr);
}
}
}
}
m_count += N;
@@ -369,6 +466,11 @@ void xmrig::CpuWorker<N>::start()
template<size_t N>
bool xmrig::CpuWorker<N>::nextRound()
{
// Solo mining uses its own 256-bit nonce management
if (m_job.isSoloMining()) {
return m_job.nextRoundSolo();
}
# ifdef XMRIG_FEATURE_BENCHMARK
const uint32_t count = m_benchSize ? 1U : kReserveCount;
# else
@@ -516,6 +618,14 @@ void xmrig::CpuWorker<N>::consumeJob()
m_job.add(job, count, Nonce::CPU);
// Handle solo mining nonce initialization
if (job.isSoloMining()) {
m_job.setSoloMining(true);
m_job.initSoloNonces();
} else {
m_job.setSoloMining(false);
}
# ifdef XMRIG_ALGO_RANDOMX
if (m_job.currentJob().algorithm().family() == Algorithm::RANDOM_X) {
allocateRandomX_VM();