Compare commits

...

11 Commits

Author SHA1 Message Date
dan_s
d0b4fb1be6 cleanup 2026-02-28 00:33:09 -06:00
dan_s
67b9cde59e Prepare XMRig-HAC fork for public release
- Add DragonX coin and rx/hush algorithm support
- Update branding to XMRig-HAC / dragonx.is
- Update repository URLs to git.hush.is
- Replace example wallet addresses with placeholders
- Update .gitignore for build directories
- Document fork changes in README and CHANGELOG
2026-02-26 23:48:10 -06:00
dan_s
d49e9a5199 HAC changes 2026-01-27 16:45:21 -06:00
XMRig
fd08510c60 v6.25.0 2025-12-23 19:44:52 +07:00
XMRig
f20cfedf0c Merge branch 'dev' 2025-12-23 19:44:21 +07:00
XMRig
cb9f597549 v6.25.0-dev 2025-12-23 19:37:24 +07:00
xmrig
4c096fe9a1 Merge pull request #3743 from SChernykh/dev
Linux: added support for transparent huge pages
2025-12-12 01:20:03 +07:00
SChernykh
08d1b6583c Linux: added support for transparent huge pages 2025-12-11 11:23:18 +01:00
xmrig
b2444e2956 Merge pull request #3740 from SChernykh/dev
RISC-V: added vectorized soft AES
2025-12-06 19:39:47 +07:00
SChernykh
b1e07859d3 RISC-V: added vectorized soft AES 2025-12-05 21:09:22 +01:00
xmrig
3038fcb16e Merge pull request #3736 from SChernykh/dev
RISC-V: added vectorized dataset init
2025-12-01 10:46:03 +07:00
40 changed files with 1592 additions and 63 deletions

View File

@@ -17,9 +17,9 @@ Steps to reproduce the behavior.
A clear and concise description of what you expected to happen.
**Required data**
- XMRig version
- Either the exact link to a release you downloaded from https://github.com/xmrig/xmrig/releases
- Or the exact command lines that you used to build XMRig
- XMRig-HAC version
- Either the exact link to a release you downloaded from https://git.hush.is/dragonx/xmrig-hac/releases
- Or the exact command lines that you used to build XMRig-HAC
- Miner log as text or screenshot
- Config file or command line (without wallets)
- OS: [e.g. Windows]

34
.gitignore vendored
View File

@@ -1,7 +1,39 @@
# Build output
/build
/build-windows
scripts/build
# Dependencies
/deps-windows
scripts/deps
# IDE / editor
/CMakeLists.txt.user
/.idea
/src/backend/opencl/cl/cn/cryptonight_gen.cl
.vscode
/.qtcreator
# Mining config (contains pool/wallet settings — copy config.example.json)
src/config.json
# Build artifacts (safety net)
*.o
*.a
*.so
*.dll
*.exe
# Logs
*.log
# Dependency tarballs
*.tar.gz
# Generated OpenCL
/src/backend/opencl/cl/cn/cryptonight_gen.cl
# OS files
.DS_Store
Thumbs.db
*.swp
*~

View File

@@ -1,3 +1,25 @@
# XMRig-HAC Fork
- Added **DragonX** coin support
- Added **rx/hush** algorithm (RandomX-HUSH variant for HUSH/DragonX)
- Customized RandomX configuration for HUSH/DragonX mining
- Based on XMRig v6.25.0
---
# v6.25.0
- [#3680](https://github.com/xmrig/xmrig/pull/3680) Added `armv8l` to the list of 32-bit ARM targets.
- [#3708](https://github.com/xmrig/xmrig/pull/3708) Minor Aarch64 JIT changes (better instruction selection, don't emit instructions that add 0, etc).
- [#3718](https://github.com/xmrig/xmrig/pull/3718) Solo mining: added support for FCMP++ hardfork.
- [#3722](https://github.com/xmrig/xmrig/pull/3722) Added Zen4 (Hawk Point) CPUs detection.
- [#3725](https://github.com/xmrig/xmrig/pull/3725) Added **RISC-V** support with JIT compiler.
- [#3731](https://github.com/xmrig/xmrig/pull/3731) Added initial Haiku OS support.
- [#3733](https://github.com/xmrig/xmrig/pull/3733) Added detection for MSVC/2026.
- [#3736](https://github.com/xmrig/xmrig/pull/3736) RISC-V: added vectorized dataset init.
- [#3740](https://github.com/xmrig/xmrig/pull/3740) RISC-V: added vectorized soft AES.
- [#3743](https://github.com/xmrig/xmrig/pull/3743) Linux: added support for transparent huge pages.
- Improved LibreSSL support.
- Improved compatibility for automatically enabling huge pages on Linux systems without NUMA support.
# v6.24.0
- [#3671](https://github.com/xmrig/xmrig/pull/3671) Fixed detection of L2 cache size for some complex NUMA topologies.
- [#3674](https://github.com/xmrig/xmrig/pull/3674) Fixed ARMv7 build.

View File

@@ -162,7 +162,7 @@ if (XMRIG_OS_WIN)
src/crypto/common/VirtualMemory_win.cpp
)
set(EXTRA_LIBS ws2_32 psapi iphlpapi userenv dbghelp)
set(EXTRA_LIBS ws2_32 psapi iphlpapi userenv dbghelp bcrypt)
elseif (XMRIG_OS_APPLE)
list(APPEND SOURCES_OS
src/App_unix.cpp

View File

@@ -1,13 +1,17 @@
# XMRig
# XMRig-HAC
[![Github All Releases](https://img.shields.io/github/downloads/xmrig/xmrig/total.svg)](https://github.com/xmrig/xmrig/releases)
[![GitHub release](https://img.shields.io/github/release/xmrig/xmrig/all.svg)](https://github.com/xmrig/xmrig/releases)
[![GitHub Release Date](https://img.shields.io/github/release-date/xmrig/xmrig.svg)](https://github.com/xmrig/xmrig/releases)
[![GitHub license](https://img.shields.io/github/license/xmrig/xmrig.svg)](https://github.com/xmrig/xmrig/blob/master/LICENSE)
[![GitHub stars](https://img.shields.io/github/stars/xmrig/xmrig.svg)](https://github.com/xmrig/xmrig/stargazers)
[![GitHub forks](https://img.shields.io/github/forks/xmrig/xmrig.svg)](https://github.com/xmrig/xmrig/network)
XMRig-HAC is a fork of [XMRig](https://github.com/xmrig/xmrig) with added support for **DragonX** coin mining using the **rx/hush** (RandomX-HUSH) algorithm.
XMRig is a high performance, open source, cross platform RandomX, KawPow, CryptoNight and [GhostRider](https://github.com/xmrig/xmrig/tree/master/src/crypto/ghostrider#readme) unified CPU/GPU miner and [RandomX benchmark](https://xmrig.com/benchmark). Official binaries are available for Windows, Linux, macOS and FreeBSD.
**Repository:** [git.hush.is/dragonx/xmrig-hac](https://git.hush.is/dragonx/xmrig-hac)
## Fork Changes
This fork adds:
- **DragonX** coin support
- **rx/hush** algorithm (RandomX-HUSH variant)
- Customized RandomX configuration for HUSH/DragonX
Based on XMRig v6.25.0.
## Mining backends
- **CPU** (x86/x64/ARMv7/ARMv8/RISC-V)
@@ -15,7 +19,7 @@ XMRig is a high performance, open source, cross platform RandomX, KawPow, Crypto
- **CUDA** for NVIDIA GPUs via external [CUDA plugin](https://github.com/xmrig/xmrig-cuda).
## Download
* **[Binary releases](https://github.com/xmrig/xmrig/releases)**
* **[Binary releases](https://git.hush.is/dragonx/xmrig-hac/releases)**
* **[Build from source](https://xmrig.com/docs/miner/build)**
## Usage
@@ -24,15 +28,8 @@ The preferred way to configure the miner is the [JSON config file](https://xmrig
* **[Wizard](https://xmrig.com/wizard)** helps you create initial configuration for the miner.
* **[Workers](http://workers.xmrig.info)** helps manage your miners via HTTP API.
## Donations
* Default donation 1% (1 minute in 100 minutes) can be increased via option `donate-level` or disabled in source code.
* XMR: `48edfHu7V9Z84YzzMa6fUueoELZ9ZRXq9VetWzYGzKt52XU5xvqgzYnDK9URnRoJMk1j8nLwEVsaSWJ4fhdUyZijBGUicoD`
## Credits
## Developers
This fork is based on [XMRig](https://github.com/xmrig/xmrig) by:
* **[xmrig](https://github.com/xmrig)**
* **[sech1](https://github.com/SChernykh)**
## Contacts
* support@xmrig.com
* [reddit](https://www.reddit.com/user/XMRig/)
* [twitter](https://twitter.com/xmrig_dev)

View File

@@ -14,8 +14,20 @@ if (WITH_TLS)
set(OPENSSL_USE_STATIC_LIBS TRUE)
endif()
# For cross-compilation, allow manual specification of OpenSSL paths
if (CMAKE_CROSSCOMPILING AND XMRIG_DEPS)
set(OPENSSL_INCLUDE_DIR "${XMRIG_DEPS}/include" CACHE PATH "OpenSSL include dir")
set(OPENSSL_CRYPTO_LIBRARY "${XMRIG_DEPS}/lib/libcrypto.a" CACHE FILEPATH "OpenSSL crypto lib")
set(OPENSSL_SSL_LIBRARY "${XMRIG_DEPS}/lib/libssl.a" CACHE FILEPATH "OpenSSL ssl lib")
if (EXISTS "${OPENSSL_CRYPTO_LIBRARY}" AND EXISTS "${OPENSSL_SSL_LIBRARY}" AND EXISTS "${OPENSSL_INCLUDE_DIR}/openssl/ssl.h")
set(OPENSSL_FOUND TRUE)
set(OPENSSL_LIBRARIES ${OPENSSL_SSL_LIBRARY} ${OPENSSL_CRYPTO_LIBRARY})
endif()
endif()
find_package(OpenSSL)
if (NOT OPENSSL_FOUND)
find_package(OpenSSL)
endif()
if (OPENSSL_FOUND)
set(TLS_SOURCES

View File

@@ -55,6 +55,18 @@ if (XMRIG_RISCV)
if(ARCH STREQUAL "native")
enable_language(ASM)
try_run(RANDOMX_VECTOR_RUN_FAIL
RANDOMX_VECTOR_COMPILE_OK
${CMAKE_CURRENT_BINARY_DIR}/
${CMAKE_CURRENT_SOURCE_DIR}/src/crypto/randomx/tests/riscv64_vector.s
COMPILE_DEFINITIONS "-march=rv64gcv_zicbop")
if (RANDOMX_VECTOR_COMPILE_OK AND NOT RANDOMX_VECTOR_RUN_FAIL)
set(RVARCH "${RVARCH}v_zicbop")
add_definitions(-DXMRIG_RVV_ENABLED)
message(STATUS "RISC-V vector extension detected")
endif()
try_run(RANDOMX_ZBA_RUN_FAIL
RANDOMX_ZBA_COMPILE_OK
${CMAKE_CURRENT_BINARY_DIR}/

View File

@@ -0,0 +1,19 @@
# CMake Toolchain file for cross-compiling to Windows using MinGW-w64
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR x86_64)
# Specify the cross compiler
set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc-posix)
set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++-posix)
set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres)
# Target environment on the build host system
set(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32)
# Adjust the default behaviour of the FIND_XXX() commands:
# search headers and libraries in the target environment
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
# Search programs in the host environment
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)

View File

@@ -20,7 +20,7 @@
"pools": [
{
"url": "pool.monero.hashvault.pro:3333",
"user": "48edfHu7V9Z84YzzMa6fUueoELZ9ZRXq9VetWzYGzKt52XU5xvqgzYnDK9URnRoJMk1j8nLwEVsaSWJ4fhdUyZijBGUicoD",
"user": "YOUR_WALLET_ADDRESS",
"pass": "x",
"keepalive": false,
"nicehash": false,
@@ -28,7 +28,7 @@
},
{
"url": "pool.supportxmr.com:3333",
"user": "48edfHu7V9Z84YzzMa6fUueoELZ9ZRXq9VetWzYGzKt52XU5xvqgzYnDK9URnRoJMk1j8nLwEVsaSWJ4fhdUyZijBGUicoD",
"user": "YOUR_WALLET_ADDRESS",
"pass": "x",
"keepalive": false,
"nicehash": false,

View File

@@ -11,13 +11,13 @@
},
"repository": {
"type": "git",
"url": "git+https://github.com/xmrig/xmrig.git"
"url": "git+https://git.hush.is/dragonx/xmrig-hac.git"
},
"keywords": [],
"author": "",
"license": "GPLv3",
"bugs": {
"url": "https://github.com/xmrig/xmrig/issues"
"url": "https://git.hush.is/dragonx/xmrig-hac/issues"
},
"homepage": "https://github.com/xmrig/xmrig#readme"
"homepage": "https://git.hush.is/dragonx/xmrig-hac#readme"
}

112
scripts/build_windows.sh Executable file
View File

@@ -0,0 +1,112 @@
#!/bin/bash
# XMRig Windows Cross-Compilation Build Script
# Builds xmrig for Windows using MinGW-w64 on Linux
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
XMRIG_DIR="$(dirname "$SCRIPT_DIR")"
BUILD_DIR="$XMRIG_DIR/build-windows"
DEPS_DIR="$XMRIG_DIR/deps-windows"
OPENSSL_VERSION="1.1.1w"
echo "=== XMRig Windows Build ==="
echo "Source: $XMRIG_DIR"
echo "Build: $BUILD_DIR"
echo "Deps: $DEPS_DIR"
# Check for MinGW-w64
if ! command -v x86_64-w64-mingw32-gcc-posix &> /dev/null; then
echo "Error: MinGW-w64 not found. Install with:"
echo " sudo apt install mingw-w64"
exit 1
fi
# Create directories
mkdir -p "$BUILD_DIR"
mkdir -p "$DEPS_DIR/include"
mkdir -p "$DEPS_DIR/lib"
# Build OpenSSL if needed
if [ ! -f "$DEPS_DIR/lib/libssl.a" ]; then
echo "=== Building OpenSSL $OPENSSL_VERSION ==="
cd "$DEPS_DIR"
if [ ! -f "openssl-${OPENSSL_VERSION}.tar.gz" ]; then
wget "https://openssl.org/source/old/1.1.1/openssl-${OPENSSL_VERSION}.tar.gz"
fi
if [ ! -d "openssl-${OPENSSL_VERSION}" ]; then
tar -xzf "openssl-${OPENSSL_VERSION}.tar.gz"
fi
cd "openssl-${OPENSSL_VERSION}"
# Configure for Windows cross-compilation
./Configure mingw64 \
--cross-compile-prefix=x86_64-w64-mingw32- \
no-shared \
no-asm \
no-zlib \
no-comp \
--prefix="$DEPS_DIR"
make -j$(nproc)
make install_sw
cd "$DEPS_DIR"
fi
# Build libuv if needed
if [ ! -f "$DEPS_DIR/lib/libuv.a" ]; then
echo "=== Building libuv dependency ==="
cd "$DEPS_DIR"
if [ ! -d "libuv-v1.51.0" ]; then
if [ ! -f "libuv.tar.gz" ]; then
curl -L -o libuv.tar.gz https://github.com/libuv/libuv/archive/refs/tags/v1.51.0.tar.gz
fi
tar -xzf libuv.tar.gz
fi
cd libuv-v1.51.0
mkdir -p build && cd build
cmake .. \
-DCMAKE_TOOLCHAIN_FILE="$XMRIG_DIR/cmake/mingw-w64-toolchain.cmake" \
-DCMAKE_INSTALL_PREFIX="$DEPS_DIR" \
-DLIBUV_BUILD_SHARED=OFF \
-DBUILD_TESTING=OFF
make -j$(nproc)
make install
fi
cd "$BUILD_DIR"
echo "=== Configuring XMRig ==="
cmake "$XMRIG_DIR" \
-DCMAKE_TOOLCHAIN_FILE="$XMRIG_DIR/cmake/mingw-w64-toolchain.cmake" \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_STATIC=ON \
-DWITH_TLS=ON \
-DWITH_HWLOC=OFF \
-DWITH_OPENCL=OFF \
-DWITH_CUDA=OFF \
-DXMRIG_DEPS="$DEPS_DIR" \
-DOPENSSL_ROOT_DIR:PATH="$DEPS_DIR" \
-DOPENSSL_INCLUDE_DIR:PATH="$DEPS_DIR/include" \
-DOPENSSL_CRYPTO_LIBRARY:FILEPATH="$DEPS_DIR/lib/libcrypto.a" \
-DOPENSSL_SSL_LIBRARY:FILEPATH="$DEPS_DIR/lib/libssl.a" \
-DOPENSSL_LIBRARIES="$DEPS_DIR/lib/libssl.a;$DEPS_DIR/lib/libcrypto.a" \
-DUV_INCLUDE_DIR="$DEPS_DIR/include" \
-DUV_LIBRARY="$DEPS_DIR/lib/libuv.a"
echo "=== Building XMRig ==="
make -j$(nproc)
# Strip the executable
x86_64-w64-mingw32-strip xmrig.exe 2>/dev/null || true
echo ""
echo "=== Build Complete ==="
ls -la *.exe 2>/dev/null || echo "Executable not found"
echo ""
echo "Copy the .exe and WinRing0x64.sys to Windows to run"

View File

@@ -16,5 +16,5 @@
:: Smaller pools also often have smaller fees/payout limits.
cd /d "%~dp0"
xmrig.exe -o xmrpool.eu:3333 -u 48edfHu7V9Z84YzzMa6fUueoELZ9ZRXq9VetWzYGzKt52XU5xvqgzYnDK9URnRoJMk1j8nLwEVsaSWJ4fhdUyZijBGUicoD -p x
xmrig.exe -o xmrpool.eu:3333 -u YOUR_WALLET_ADDRESS -p x
pause

View File

@@ -12,5 +12,5 @@
:: But you will only get a payout when you find a block which can take more than a year for a single low-end PC.
cd /d "%~dp0"
xmrig.exe -o YOUR_NODE_IP:18081 -a rx/0 -u 48edfHu7V9Z84YzzMa6fUueoELZ9ZRXq9VetWzYGzKt52XU5xvqgzYnDK9URnRoJMk1j8nLwEVsaSWJ4fhdUyZijBGUicoD --daemon
xmrig.exe -o YOUR_NODE_IP:18081 -a rx/0 -u YOUR_WALLET_ADDRESS --daemon
pause

View File

@@ -87,14 +87,14 @@ xmrig::CpuWorker<N>::CpuWorker(size_t id, const CpuLaunchData &data) :
if (!cn_heavyZen3Memory) {
// Round up number of threads to the multiple of 8
const size_t num_threads = ((m_threads + 7) / 8) * 8;
cn_heavyZen3Memory = new VirtualMemory(m_algorithm.l3() * num_threads, data.hugePages, false, false, node());
cn_heavyZen3Memory = new VirtualMemory(m_algorithm.l3() * num_threads, data.hugePages, false, false, node(), VirtualMemory::kDefaultHugePageSize);
}
m_memory = cn_heavyZen3Memory;
}
else
# endif
{
m_memory = new VirtualMemory(m_algorithm.l3() * N, data.hugePages, false, true, node());
m_memory = new VirtualMemory(m_algorithm.l3() * N, data.hugePages, false, true, node(), VirtualMemory::kDefaultHugePageSize);
}
# ifdef XMRIG_ALGO_GHOSTRIDER

View File

@@ -200,6 +200,7 @@ if (WITH_HTTP)
src/base/net/http/HttpData.h
src/base/net/http/HttpResponse.h
src/base/net/stratum/DaemonClient.h
src/base/net/stratum/HushClient.h
src/base/net/stratum/SelfSelectClient.h
src/base/net/tools/TcpServer.h
)
@@ -220,6 +221,7 @@ if (WITH_HTTP)
src/base/net/http/HttpListener.cpp
src/base/net/http/HttpResponse.cpp
src/base/net/stratum/DaemonClient.cpp
src/base/net/stratum/HushClient.cpp
src/base/net/stratum/SelfSelectClient.cpp
src/base/net/tools/TcpServer.cpp
)

View File

@@ -82,6 +82,7 @@ const char *Algorithm::kRX_ARQ = "rx/arq";
const char *Algorithm::kRX_GRAFT = "rx/graft";
const char *Algorithm::kRX_SFX = "rx/sfx";
const char *Algorithm::kRX_YADA = "rx/yada";
const char *Algorithm::kRX_HUSH = "rx/hush";
#endif
#ifdef XMRIG_ALGO_ARGON2
@@ -148,6 +149,7 @@ static const std::map<uint32_t, const char *> kAlgorithmNames = {
ALGO_NAME(RX_GRAFT),
ALGO_NAME(RX_SFX),
ALGO_NAME(RX_YADA),
ALGO_NAME(RX_HUSH),
# endif
# ifdef XMRIG_ALGO_ARGON2
@@ -263,6 +265,10 @@ static const std::map<const char *, Algorithm::Id, aliasCompare> kAlgorithmAlias
ALGO_ALIAS(RX_SFX, "randomsfx"),
ALGO_ALIAS_AUTO(RX_YADA), ALGO_ALIAS(RX_YADA, "randomx/yada"),
ALGO_ALIAS(RX_YADA, "randomyada"),
ALGO_ALIAS_AUTO(RX_HUSH), ALGO_ALIAS(RX_HUSH, "randomx/hush"),
ALGO_ALIAS(RX_HUSH, "randomhush"),
ALGO_ALIAS(RX_HUSH, "rx/dragonx"),
ALGO_ALIAS(RX_HUSH, "dragonx"),
# endif
# ifdef XMRIG_ALGO_ARGON2
@@ -350,7 +356,7 @@ std::vector<xmrig::Algorithm> xmrig::Algorithm::all(const std::function<bool(con
CN_HEAVY_0, CN_HEAVY_TUBE, CN_HEAVY_XHV,
CN_PICO_0, CN_PICO_TLO,
CN_UPX2,
RX_0, RX_WOW, RX_ARQ, RX_GRAFT, RX_SFX, RX_YADA,
RX_0, RX_WOW, RX_ARQ, RX_GRAFT, RX_SFX, RX_YADA, RX_HUSH,
AR2_CHUKWA, AR2_CHUKWA_V2, AR2_WRKZ,
KAWPOW_RVN,
GHOSTRIDER_RTM

View File

@@ -78,6 +78,7 @@ public:
RX_GRAFT = 0x72151267, // "rx/graft" RandomGRAFT (Graft).
RX_SFX = 0x72151273, // "rx/sfx" RandomSFX (Safex Cash).
RX_YADA = 0x72151279, // "rx/yada" RandomYada (YadaCoin).
RX_HUSH = 0x72151268, // "rx/hush" RandomX-HUSH (HUSH/DragonX).
AR2_CHUKWA = 0x61130000, // "argon2/chukwa" Argon2id (Chukwa).
AR2_CHUKWA_V2 = 0x61140000, // "argon2/chukwav2" Argon2id (Chukwa v2).
AR2_WRKZ = 0x61120000, // "argon2/wrkz" Argon2id (WRKZ)
@@ -144,6 +145,7 @@ public:
static const char *kRX_GRAFT;
static const char *kRX_SFX;
static const char *kRX_YADA;
static const char *kRX_HUSH;
# endif
# ifdef XMRIG_ALGO_ARGON2

View File

@@ -55,6 +55,8 @@ static const CoinInfo coinInfo[] = {
{ Algorithm::RX_0, "ZEPH", "Zephyr", 120, 1000000000000, BLUE_BG_BOLD( WHITE_BOLD_S " zephyr ") },
{ Algorithm::RX_0, "Townforge","Townforge", 30, 100000000, MAGENTA_BG_BOLD(WHITE_BOLD_S " townforge ") },
{ Algorithm::RX_YADA, "YDA", "YadaCoin", 120, 100000000, BLUE_BG_BOLD( WHITE_BOLD_S " yada ") },
{ Algorithm::RX_HUSH, "HUSH", "Hush", 150, 100000000, GREEN_BG_BOLD( WHITE_BOLD_S " hush ") },
{ Algorithm::RX_HUSH, "DRAGONX", "DragonX", 36, 100000000, CYAN_BG_BOLD( WHITE_BOLD_S " dragonx ") },
};

View File

@@ -41,6 +41,8 @@ public:
ZEPHYR,
TOWNFORGE,
YADA,
HUSH,
DRAGONX,
MAX
};

View File

@@ -0,0 +1,905 @@
/* XMRig - Hush/DragonX HAC Support
* Copyright (c) 2024 XMRig <https://github.com/xmrig>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*/
#include "base/net/stratum/HushClient.h"
#include "3rdparty/rapidjson/document.h"
#include "3rdparty/rapidjson/error/en.h"
#include "base/io/json/Json.h"
#include "base/io/json/JsonRequest.h"
#include "base/io/log/Log.h"
#include "base/kernel/interfaces/IClientListener.h"
#include "base/net/http/Fetch.h"
#include "base/net/http/HttpData.h"
#include "base/net/http/HttpListener.h"
#include "base/tools/Chrono.h"
#include "base/tools/Cvt.h"
#include "base/tools/Timer.h"
#include "net/JobResult.h"
#include <algorithm>
#include <cstring>
#ifdef XMRIG_FEATURE_TLS
# include <openssl/sha.h>
#else
// Standalone SHA-256 for non-TLS builds (Windows cross-compile)
# 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
namespace xmrig {
static const char *kJsonRPC = "/";
// Simple Base64 encoder for HTTP Basic Auth
static std::string toBase64(const std::string &input)
{
static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
std::string encoded;
encoded.reserve(((input.size() + 2) / 3) * 4);
unsigned int val = 0;
int bits = -6;
for (unsigned char c : input) {
val = (val << 8) + c;
bits += 8;
while (bits >= 0) {
encoded.push_back(table[(val >> bits) & 0x3F]);
bits -= 6;
}
}
if (bits > -6) {
encoded.push_back(table[((val << 8) >> (bits + 8)) & 0x3F]);
}
while (encoded.size() % 4) {
encoded.push_back('=');
}
return encoded;
}
// Create JSON-RPC 1.0 request (HAC daemons use 1.0, not 2.0)
static void createRpc10(rapidjson::Document &doc, int64_t id, const char *method, rapidjson::Value &params)
{
using namespace rapidjson;
auto &allocator = doc.GetAllocator();
doc.AddMember("jsonrpc", "1.0", allocator);
doc.AddMember("id", id, allocator);
doc.AddMember("method", StringRef(method), allocator);
doc.AddMember("params", params, allocator);
}
HushClient::HushClient(int id, IClientListener *listener) :
BaseClient(id, listener)
{
m_httpListener = std::make_shared<HttpListener>(this);
m_timer = new Timer(this);
}
HushClient::~HushClient()
{
delete m_timer;
}
void HushClient::deleteLater()
{
delete this;
}
bool HushClient::disconnect()
{
if (m_state != UnconnectedState) {
setState(UnconnectedState);
}
return true;
}
bool HushClient::isTLS() const
{
#ifdef XMRIG_FEATURE_TLS
return m_pool.isTLS();
#else
return false;
#endif
}
void HushClient::connect()
{
if (m_pool.algorithm().family() != Algorithm::RANDOM_X) {
LOG_ERR("%s " RED("HAC coins require RandomX algorithm"), tag());
return;
}
setState(ConnectingState);
getBlockTemplate();
}
void HushClient::connect(const Pool &pool)
{
setPool(pool);
connect();
}
void HushClient::setPool(const Pool &pool)
{
BaseClient::setPool(pool);
// Default to RandomX if not specified
if (!m_pool.algorithm().isValid()) {
m_pool.setAlgo(Algorithm::RX_0);
}
}
int64_t HushClient::submit(const JobResult &result)
{
if (result.jobId != m_currentJobId) {
LOG_DEBUG("%s " RED("job ID mismatch"), tag());
return -1;
}
// HAC PoW: RandomX hash becomes nSolution, then SHA256D(header) must be < target
// Check if SHA256D(header + solution) meets network target before submitting
if (!checkPow(result.nonce, result.result())) {
// Hash doesn't meet network target, don't submit
return -1;
}
// Log submission details
const String solutionHex = Cvt::toHex(result.result(), 32);
LOG_INFO("%s " CYAN_BOLD("submitting block") " nonce=0x%08x", tag(), result.nonce);
// Build the full block with header + solution + transactions
const std::string blockHex = serializeBlockHex(result.nonce, result.result());
return submitBlock(blockHex);
}
void HushClient::onTimer(const Timer *)
{
if (m_state == ConnectingState) {
connect();
}
else if (m_state == ConnectedState) {
// Poll for new block template
getBlockTemplate();
}
}
void HushClient::onHttpData(const HttpData &data)
{
if (data.status != 200) {
LOG_ERR("%s " RED("HTTP error %d"), tag(), data.status);
return retry();
}
m_ip = data.ip().c_str();
#ifdef XMRIG_FEATURE_TLS
if (data.tlsVersion()) {
strncpy(m_tlsVersion, data.tlsVersion(), sizeof(m_tlsVersion) - 1);
}
if (data.tlsFingerprint()) {
strncpy(m_tlsFingerprint, data.tlsFingerprint(), sizeof(m_tlsFingerprint) - 1);
}
#endif
rapidjson::Document doc;
if (doc.Parse(data.body.c_str()).HasParseError()) {
LOG_ERR("%s " RED("JSON parse error: %s"), tag(), rapidjson::GetParseError_En(doc.GetParseError()));
return retry();
}
// Debug: log the raw response for troubleshooting
LOG_DEBUG("%s RPC response: %.200s", tag(), data.body.c_str());
const int64_t id = Json::getInt64(doc, "id", -1);
// Handle "result" field which can be object, string, or null depending on RPC method
const rapidjson::Value *resultPtr = nullptr;
if (doc.HasMember("result")) {
resultPtr = &doc["result"];
}
static const rapidjson::Value nullValue;
const auto &result = resultPtr ? *resultPtr : nullValue;
const auto &error = Json::getObject(doc, "error");
if (!parseResponse(id, result, error)) {
retry();
}
}
int64_t HushClient::getBlockTemplate()
{
using namespace rapidjson;
Document doc(kObjectType);
auto &allocator = doc.GetAllocator();
// JSON-RPC 1.0 style: params is an array with optional object
Value params(kArrayType);
// Empty params array for basic getblocktemplate
// HAC will return coinbasetxn by default
createRpc10(doc, m_sequence, "getblocktemplate", params);
m_pendingRequest = REQ_TEMPLATE;
return rpcSend(doc);
}
int64_t HushClient::getBlockHash(uint64_t height)
{
using namespace rapidjson;
Document doc(kObjectType);
auto &allocator = doc.GetAllocator();
Value params(kArrayType);
params.PushBack(Value(height), allocator);
createRpc10(doc, m_sequence, "getblockhash", params);
m_pendingRequest = REQ_KEYHASH;
m_pendingKeyHeight = height;
return rpcSend(doc);
}
int64_t HushClient::submitBlock(const std::string &blockHex)
{
using namespace rapidjson;
Document doc(kObjectType);
auto &allocator = doc.GetAllocator();
Value params(kArrayType);
params.PushBack(Value(blockHex.c_str(), allocator), allocator);
createRpc10(doc, m_sequence, "submitblock", params);
m_pendingRequest = REQ_SUBMIT;
LOG_INFO("%s " MAGENTA_BOLD("submitting block at height %u"), tag(), m_height);
return rpcSend(doc);
}
int64_t HushClient::rpcSend(const rapidjson::Document &doc)
{
FetchRequest req(HTTP_POST, m_pool.host(), m_pool.port(), kJsonRPC, doc, m_pool.isTLS(), isQuiet());
// HAC daemons require text/plain content-type (override application/json)
req.headers.erase("Content-Type");
req.headers.insert({"Content-Type", "text/plain"});
// Add RPC authentication if configured
if (!m_pool.user().isEmpty()) {
std::string auth = m_pool.user().data();
if (!m_pool.password().isEmpty()) {
auth += ":";
auth += m_pool.password().data();
}
const std::string encoded = toBase64(auth);
req.headers.insert({"Authorization", "Basic " + encoded});
}
fetch(tag(), std::move(req), m_httpListener);
return m_sequence++;
}
bool HushClient::parseBlockTemplate(const rapidjson::Value &result)
{
if (!result.IsObject()) {
LOG_ERR("%s " RED("invalid block template response"), tag());
return false;
}
// Check if this is the same job (same previous block hash)
const char *prevHash = Json::getString(result, "previousblockhash");
if (prevHash && m_prevJobHash == prevHash && m_state == ConnectedState) {
// Same block, skip duplicate job
return true;
}
// Parse BIP22 block template fields
m_height = Json::getUint64(result, "height");
m_curtime = Json::getUint64(result, "curtime");
m_version = Json::getInt(result, "version", 4);
m_prevHash = Json::getString(result, "previousblockhash");
m_saplingRoot = Json::getString(result, "finalsaplingroothash");
m_target = Json::getString(result, "target");
m_bits = Json::getString(result, "bits");
// Parse target into bytes for PoW comparison (32 bytes, big-endian from hex)
m_targetBytes.resize(32);
if (m_target.size() == 64) {
Cvt::fromHex(m_targetBytes.data(), 32, m_target.data(), 64);
}
// Coinbase transaction
const auto &coinbase = result["coinbasetxn"];
if (!coinbase.IsObject()) {
LOG_ERR("%s " RED("missing coinbasetxn in template"), tag());
return false;
}
m_coinbaseTx = Json::getString(coinbase, "data");
m_merkleRoot = Json::getString(coinbase, "hash");
LOG_DEBUG("%s template: height=%lu bits=%s target=%s", tag(), m_height, m_bits.data(), m_target.data());
LOG_DEBUG("%s prevHash=%s", tag(), m_prevHash.data());
LOG_DEBUG("%s merkleRoot=%s", tag(), m_merkleRoot.data());
// Other transactions (may be empty for low traffic chains)
m_transactions.clear();
const auto &txs = result["transactions"];
if (txs.IsArray()) {
for (const auto &tx : txs.GetArray()) {
if (tx.IsObject()) {
m_transactions.push_back(Json::getString(tx, "data"));
}
}
}
// Validate required fields
if (m_height == 0 || m_prevHash.isEmpty() || m_coinbaseTx.isEmpty()) {
LOG_ERR("%s " RED("incomplete block template"), tag());
return false;
}
// Check if we need to fetch new RandomX key
const uint64_t keyHeight = getKeyHeight(m_height);
if (keyHeight != m_keyHeight || m_keyBlockHash.isEmpty()) {
if (keyHeight == 0) {
// Use chain-specific initial RandomX key.
// Daemon computes: sprintf("%08x%s%08x", ASSETCHAINS_MAGIC, SYMBOL, RPCPORT)
// and passes the raw ASCII bytes to randomx_init_cache().
// We hex-encode those bytes (zero-padded to 32) for setSeedHash().
char initialKey[81];
const uint32_t magic = 2387029918u; // DRAGONX ASSETCHAINS_MAGIC
const char* symbol = "DRAGONX";
const uint16_t rpcPort = 21769;
snprintf(initialKey, sizeof(initialKey), "%08x%s%08x", magic, symbol, (uint32_t)rpcPort);
const size_t keyLen = strlen(initialKey);
// Hex-encode only the actual key bytes (no zero-padding).
// The daemon passes strlen(initialKey) bytes to randomx_init_cache(),
// so we must match that exact length.
char hexBuf[163]; // max 81 bytes * 2 + 1
for (size_t i = 0; i < keyLen; i++) snprintf(hexBuf + i*2, 3, "%02x", (uint8_t)initialKey[i]);
hexBuf[keyLen * 2] = '\0';
m_keyBlockHash = hexBuf;
m_keyHeight = 0;
LOG_INFO("%s " CYAN("using initial RandomX key \"%s\" for height %u"), tag(), initialKey, m_height);
} else {
LOG_INFO("%s " CYAN("fetching RandomX key block hash at height %u"), tag(), keyHeight);
getBlockHash(keyHeight);
return true; // Will continue after receiving key hash
}
}
// Create the mining job
Job job(false, m_pool.algorithm(), String());
job.setHeight(m_height);
// Set diff=1 so ALL RandomX results get submitted to us for PoW checking
// We filter in submit() by checking SHA256D(header+solution) < target
job.setDiff(1);
if (!job.setSeedHash(m_keyBlockHash.data())) {
LOG_ERR("%s " RED("failed to set seed hash: %s (len=%zu)"), tag(), m_keyBlockHash.data(), m_keyBlockHash.size());
return false;
}
// Build full block blob for RandomX hashing:
// HAC computes RandomX(full_serialized_block) where block = header + nSolution(empty) + txcount + txs
// Header: version(4) + prevHash(32) + merkleRoot(32) + saplingRoot(32) + time(4) + bits(4) + nonce(32) + nSolution(varint_len + data)
m_headerBlob.clear();
m_headerBlob.reserve(1024); // Will grow if many transactions
// nVersion (4 bytes, little-endian)
const uint32_t ver = static_cast<uint32_t>(m_version);
m_headerBlob.insert(m_headerBlob.end(), reinterpret_cast<const uint8_t*>(&ver),
reinterpret_cast<const uint8_t*>(&ver) + 4);
// hashPrevBlock (32 bytes, internal byte order)
std::vector<uint8_t> prevHashBytes(32);
Cvt::fromHex(prevHashBytes.data(), 32, m_prevHash.data(), 64);
std::reverse(prevHashBytes.begin(), prevHashBytes.end());
m_headerBlob.insert(m_headerBlob.end(), prevHashBytes.begin(), prevHashBytes.end());
// hashMerkleRoot (32 bytes)
std::vector<uint8_t> merkleRoot(32);
Cvt::fromHex(merkleRoot.data(), 32, m_merkleRoot.data(), 64);
std::reverse(merkleRoot.begin(), merkleRoot.end());
m_headerBlob.insert(m_headerBlob.end(), merkleRoot.begin(), merkleRoot.end());
// hashFinalSaplingRoot (32 bytes)
std::vector<uint8_t> saplingRoot(32);
Cvt::fromHex(saplingRoot.data(), 32, m_saplingRoot.data(), 64);
std::reverse(saplingRoot.begin(), saplingRoot.end());
m_headerBlob.insert(m_headerBlob.end(), saplingRoot.begin(), saplingRoot.end());
// nTime (4 bytes)
const uint32_t time32 = static_cast<uint32_t>(m_curtime);
m_headerBlob.insert(m_headerBlob.end(), reinterpret_cast<const uint8_t*>(&time32),
reinterpret_cast<const uint8_t*>(&time32) + 4);
// nBits (4 bytes)
uint32_t bits = 0;
for (int i = 0; i < 8 && i < static_cast<int>(m_bits.size()); i += 2) {
uint8_t byte;
Cvt::fromHex(&byte, 1, m_bits.data() + i, 2);
bits = (bits << 8) | byte;
}
m_headerBlob.insert(m_headerBlob.end(), reinterpret_cast<const uint8_t*>(&bits),
reinterpret_cast<const uint8_t*>(&bits) + 4);
// nNonce placeholder (32 bytes of zeros - miner fills first 4 bytes)
// Remember position for later extraction
m_nonceOffset = m_headerBlob.size();
m_headerBlob.insert(m_headerBlob.end(), 32, 0);
// nSolution - empty vector serialized as compact size 0
// For CBlock serialization, nSolution is std::vector<unsigned char>
// Empty vector = compactsize(0) = 0x00
m_headerBlob.push_back(0x00);
// Transactions: compactsize(count) + coinbase + other txs
const size_t txCount = 1 + m_transactions.size(); // coinbase + others
// Write compact size for transaction count
if (txCount < 0xFD) {
m_headerBlob.push_back(static_cast<uint8_t>(txCount));
} else if (txCount <= 0xFFFF) {
m_headerBlob.push_back(0xFD);
const uint16_t cnt16 = static_cast<uint16_t>(txCount);
m_headerBlob.insert(m_headerBlob.end(), reinterpret_cast<const uint8_t*>(&cnt16),
reinterpret_cast<const uint8_t*>(&cnt16) + 2);
} else {
m_headerBlob.push_back(0xFE);
const uint32_t cnt32 = static_cast<uint32_t>(txCount);
m_headerBlob.insert(m_headerBlob.end(), reinterpret_cast<const uint8_t*>(&cnt32),
reinterpret_cast<const uint8_t*>(&cnt32) + 4);
}
// Append coinbase transaction
std::vector<uint8_t> cbTx(m_coinbaseTx.size() / 2);
Cvt::fromHex(cbTx.data(), cbTx.size(), m_coinbaseTx.data(), m_coinbaseTx.size());
m_headerBlob.insert(m_headerBlob.end(), cbTx.begin(), cbTx.end());
// Append other transactions
for (const auto &txHex : m_transactions) {
std::vector<uint8_t> tx(txHex.size() / 2);
Cvt::fromHex(tx.data(), tx.size(), txHex.data(), txHex.size());
m_headerBlob.insert(m_headerBlob.end(), tx.begin(), tx.end());
}
// Set the full block blob for mining
LOG_DEBUG("%s block blob size=%zu (header=141 + %zu txs), seed=%s",
tag(), m_headerBlob.size(), txCount, m_keyBlockHash.data());
{
const String hdrHex = Cvt::toHex(m_headerBlob.data(), std::min(m_headerBlob.size(), size_t(200)));
LOG_DEBUG("%s block: %s...", tag(), hdrHex.data());
}
if (!job.setBlob(Cvt::toHex(m_headerBlob.data(), m_headerBlob.size()))) {
LOG_ERR("%s " RED("failed to set job blob (size=%zu)"), tag(), m_headerBlob.size());
return false;
}
m_currentJobId = Cvt::toHex(Cvt::randomBytes(4));
job.setId(m_currentJobId);
m_job = std::move(job);
m_prevJobHash = m_prevHash;
m_jobSteadyMs = Chrono::steadyMSecs();
if (m_state == ConnectingState) {
LOG_INFO("%s " GREEN("connected to %s:%d"), tag(), m_pool.host().data(), m_pool.port());
setState(ConnectedState);
}
LOG_INFO("%s " MAGENTA_BOLD("new job") " height: " CYAN_BOLD("%u") " diff: " CYAN_BOLD("%u"),
tag(), m_height, job.diff());
m_listener->onJobReceived(this, m_job, rapidjson::Value());
return true;
}
bool HushClient::parseResponse(int64_t id, const rapidjson::Value &result, const rapidjson::Value &error)
{
// Handle RPC errors
if (error.IsObject()) {
const char *message = Json::getString(error, "message", "unknown error");
const int code = Json::getInt(error, "code", -1);
LOG_ERR("%s " RED("RPC error %d: \"%s\""), tag(), code, message);
return false;
}
switch (m_pendingRequest) {
case REQ_TEMPLATE:
if (parseBlockTemplate(result)) {
m_timer->start(m_pool.pollInterval(), m_pool.pollInterval());
return true;
}
return m_pendingRequest == REQ_KEYHASH; // Waiting for key is ok
case REQ_KEYHASH:
if (result.IsString()) {
// getblockhash returns display-order (big-endian) hex, but the daemon
// passes uint256 internal bytes (little-endian) to randomx_init_cache().
// Reverse the bytes so setSeedHash() produces the correct LE byte order.
const char* displayHash = result.GetString();
std::vector<uint8_t> hashBytes(32);
Cvt::fromHex(hashBytes.data(), 32, displayHash, 64);
std::reverse(hashBytes.begin(), hashBytes.end());
char reversedHex[65];
for (size_t i = 0; i < 32; i++) snprintf(reversedHex + i*2, 3, "%02x", hashBytes[i]);
reversedHex[64] = '\0';
m_keyBlockHash = reversedHex;
m_keyHeight = m_pendingKeyHeight;
LOG_INFO("%s " GREEN("RandomX key block %u: %.16s... (reversed to LE)"),
tag(), m_keyHeight, m_keyBlockHash.data());
// Now get the block template again with the key
m_pendingRequest = REQ_NONE;
getBlockTemplate();
return true;
}
LOG_ERR("%s " RED("invalid getblockhash response - expected string, got type %d"),
tag(), result.GetType());
return false;
case REQ_SUBMIT:
// submitblock returns null on success
if (result.IsNull()) {
LOG_INFO("%s " GREEN_BOLD("BLOCK ACCEPTED!"), tag());
if (m_results.count(id)) {
m_listener->onResultAccepted(this, m_results[id], nullptr);
m_results.erase(id);
}
} else if (result.IsString()) {
const char *msg = result.GetString();
if (strlen(msg) > 0) {
LOG_ERR("%s " RED("block rejected: %s"), tag(), msg);
} else {
LOG_INFO("%s " GREEN_BOLD("BLOCK ACCEPTED!"), tag());
}
}
// Get new work after submit
getBlockTemplate();
return true;
default:
return false;
}
}
void HushClient::retry()
{
m_failures++;
m_listener->onClose(this, static_cast<int>(m_failures));
if (m_state == ConnectedState) {
setState(ConnectingState);
}
m_timer->stop();
m_timer->start(m_retryPause, 0);
}
void HushClient::setState(SocketState state)
{
if (m_state == state) {
return;
}
m_state = state;
switch (state) {
case ConnectedState:
m_failures = 0;
m_listener->onLoginSuccess(this);
break;
case UnconnectedState:
m_timer->stop();
m_failures = -1;
m_listener->onClose(this, -1);
break;
default:
break;
}
}
uint64_t HushClient::getKeyHeight(uint64_t height) const
{
// RandomX key changes every RANDOMX_INTERVAL blocks with RANDOMX_LAG delay
if (height < static_cast<uint64_t>(RANDOMX_INTERVAL + RANDOMX_LAG)) {
return 0; // Use genesis key
}
return ((height - RANDOMX_LAG) / RANDOMX_INTERVAL) * RANDOMX_INTERVAL;
}
std::vector<uint8_t> HushClient::serializeBlock(uint32_t nonce, uint64_t extraNonce) const
{
std::vector<uint8_t> blob;
blob.reserve(1024);
// nVersion (4 bytes, little-endian)
const uint32_t ver = static_cast<uint32_t>(m_version);
blob.insert(blob.end(), reinterpret_cast<const uint8_t*>(&ver),
reinterpret_cast<const uint8_t*>(&ver) + 4);
// hashPrevBlock (32 bytes, reversed from hex display)
std::vector<uint8_t> prevHash(32);
Cvt::fromHex(prevHash.data(), 32, m_prevHash.data(), 64);
std::reverse(prevHash.begin(), prevHash.end());
blob.insert(blob.end(), prevHash.begin(), prevHash.end());
// hashMerkleRoot (32 bytes, reversed)
std::vector<uint8_t> merkleRoot(32);
Cvt::fromHex(merkleRoot.data(), 32, m_merkleRoot.data(), 64);
std::reverse(merkleRoot.begin(), merkleRoot.end());
blob.insert(blob.end(), merkleRoot.begin(), merkleRoot.end());
// hashFinalSaplingRoot (32 bytes, reversed)
std::vector<uint8_t> saplingRoot(32);
Cvt::fromHex(saplingRoot.data(), 32, m_saplingRoot.data(), 64);
std::reverse(saplingRoot.begin(), saplingRoot.end());
blob.insert(blob.end(), saplingRoot.begin(), saplingRoot.end());
// nTime (4 bytes, little-endian)
const uint32_t time32 = static_cast<uint32_t>(m_curtime);
blob.insert(blob.end(), reinterpret_cast<const uint8_t*>(&time32),
reinterpret_cast<const uint8_t*>(&time32) + 4);
// nBits (4 bytes) - convert from big-endian hex to little-endian binary
uint32_t bits = 0;
for (int i = 0; i < 8 && i < static_cast<int>(m_bits.size()); i += 2) {
uint8_t byte;
Cvt::fromHex(&byte, 1, m_bits.data() + i, 2);
bits = (bits << 8) | byte;
}
blob.insert(blob.end(), reinterpret_cast<const uint8_t*>(&bits),
reinterpret_cast<const uint8_t*>(&bits) + 4);
// nNonce (32 bytes for Zcash/Hush - uint256)
std::vector<uint8_t> nonce256(32, 0);
memcpy(nonce256.data(), &nonce, 4);
memcpy(nonce256.data() + 4, &extraNonce, sizeof(extraNonce));
blob.insert(blob.end(), nonce256.begin(), nonce256.end());
// nSolution placeholder (CompactSize + 32 zeros)
blob.push_back(32); // CompactSize for 32 bytes
blob.insert(blob.end(), 32, 0);
// Transaction count (CompactSize)
const size_t txCount = 1 + m_transactions.size();
if (txCount < 0xFD) {
blob.push_back(static_cast<uint8_t>(txCount));
} else {
blob.push_back(0xFD);
blob.push_back(txCount & 0xFF);
blob.push_back((txCount >> 8) & 0xFF);
}
// Coinbase transaction
std::vector<uint8_t> coinbase(m_coinbaseTx.size() / 2);
Cvt::fromHex(coinbase.data(), coinbase.size(), m_coinbaseTx.data(), m_coinbaseTx.size());
blob.insert(blob.end(), coinbase.begin(), coinbase.end());
// Other transactions
for (const auto &txHex : m_transactions) {
std::vector<uint8_t> tx(txHex.size() / 2);
Cvt::fromHex(tx.data(), tx.size(), txHex.data(), txHex.size());
blob.insert(blob.end(), tx.begin(), tx.end());
}
return blob;
}
std::string HushClient::serializeBlockHex(uint32_t nonce, const uint8_t* solution) const
{
std::vector<uint8_t> blob;
blob.reserve(1024);
// Header (same as serializeBlock but with actual nonce and solution)
const uint32_t ver = static_cast<uint32_t>(m_version);
blob.insert(blob.end(), reinterpret_cast<const uint8_t*>(&ver),
reinterpret_cast<const uint8_t*>(&ver) + 4);
std::vector<uint8_t> prevHash(32);
Cvt::fromHex(prevHash.data(), 32, m_prevHash.data(), 64);
std::reverse(prevHash.begin(), prevHash.end());
blob.insert(blob.end(), prevHash.begin(), prevHash.end());
std::vector<uint8_t> merkleRoot(32);
Cvt::fromHex(merkleRoot.data(), 32, m_merkleRoot.data(), 64);
std::reverse(merkleRoot.begin(), merkleRoot.end());
blob.insert(blob.end(), merkleRoot.begin(), merkleRoot.end());
std::vector<uint8_t> saplingRoot(32);
Cvt::fromHex(saplingRoot.data(), 32, m_saplingRoot.data(), 64);
std::reverse(saplingRoot.begin(), saplingRoot.end());
blob.insert(blob.end(), saplingRoot.begin(), saplingRoot.end());
const uint32_t time32 = static_cast<uint32_t>(m_curtime);
blob.insert(blob.end(), reinterpret_cast<const uint8_t*>(&time32),
reinterpret_cast<const uint8_t*>(&time32) + 4);
uint32_t bits = 0;
for (int i = 0; i < 8 && i < static_cast<int>(m_bits.size()); i += 2) {
uint8_t byte;
Cvt::fromHex(&byte, 1, m_bits.data() + i, 2);
bits = (bits << 8) | byte;
}
blob.insert(blob.end(), reinterpret_cast<const uint8_t*>(&bits),
reinterpret_cast<const uint8_t*>(&bits) + 4);
// Nonce (32 bytes with found nonce value)
std::vector<uint8_t> nonce256(32, 0);
memcpy(nonce256.data(), &nonce, 4);
blob.insert(blob.end(), nonce256.begin(), nonce256.end());
// Solution (the RandomX hash - 32 bytes)
blob.push_back(32); // CompactSize
blob.insert(blob.end(), solution, solution + 32);
// Transactions
const size_t txCount = 1 + m_transactions.size();
if (txCount < 0xFD) {
blob.push_back(static_cast<uint8_t>(txCount));
} else {
blob.push_back(0xFD);
blob.push_back(txCount & 0xFF);
blob.push_back((txCount >> 8) & 0xFF);
}
std::vector<uint8_t> coinbase(m_coinbaseTx.size() / 2);
Cvt::fromHex(coinbase.data(), coinbase.size(), m_coinbaseTx.data(), m_coinbaseTx.size());
blob.insert(blob.end(), coinbase.begin(), coinbase.end());
for (const auto &txHex : m_transactions) {
std::vector<uint8_t> tx(txHex.size() / 2);
Cvt::fromHex(tx.data(), tx.size(), txHex.data(), txHex.size());
blob.insert(blob.end(), tx.begin(), tx.end());
}
const String hex = Cvt::toHex(blob.data(), blob.size());
return std::string(hex.data(), hex.size());
}
void HushClient::sha256d(const uint8_t* data, size_t len, uint8_t* out)
{
uint8_t hash1[32];
::SHA256(data, len, hash1);
::SHA256(hash1, 32, out);
}
std::vector<uint8_t> HushClient::serializeHeader(uint32_t nonce, const uint8_t* solution) const
{
std::vector<uint8_t> header;
header.reserve(140 + 1 + 32); // header + compactsize + solution
// nVersion (4 bytes, little-endian)
const uint32_t ver = static_cast<uint32_t>(m_version);
header.insert(header.end(), reinterpret_cast<const uint8_t*>(&ver),
reinterpret_cast<const uint8_t*>(&ver) + 4);
// hashPrevBlock (32 bytes, internal byte order)
std::vector<uint8_t> prevHashBytes(32);
Cvt::fromHex(prevHashBytes.data(), 32, m_prevHash.data(), 64);
std::reverse(prevHashBytes.begin(), prevHashBytes.end());
header.insert(header.end(), prevHashBytes.begin(), prevHashBytes.end());
// hashMerkleRoot (32 bytes)
std::vector<uint8_t> merkleRoot(32);
Cvt::fromHex(merkleRoot.data(), 32, m_merkleRoot.data(), 64);
std::reverse(merkleRoot.begin(), merkleRoot.end());
header.insert(header.end(), merkleRoot.begin(), merkleRoot.end());
// hashFinalSaplingRoot (32 bytes)
std::vector<uint8_t> saplingRoot(32);
Cvt::fromHex(saplingRoot.data(), 32, m_saplingRoot.data(), 64);
std::reverse(saplingRoot.begin(), saplingRoot.end());
header.insert(header.end(), saplingRoot.begin(), saplingRoot.end());
// nTime (4 bytes)
const uint32_t time32 = static_cast<uint32_t>(m_curtime);
header.insert(header.end(), reinterpret_cast<const uint8_t*>(&time32),
reinterpret_cast<const uint8_t*>(&time32) + 4);
// nBits (4 bytes)
uint32_t bits = 0;
for (int i = 0; i < 8 && i < static_cast<int>(m_bits.size()); i += 2) {
uint8_t byte;
Cvt::fromHex(&byte, 1, m_bits.data() + i, 2);
bits = (bits << 8) | byte;
}
header.insert(header.end(), reinterpret_cast<const uint8_t*>(&bits),
reinterpret_cast<const uint8_t*>(&bits) + 4);
// nNonce (32 bytes with found nonce value in first 4 bytes)
std::vector<uint8_t> nonce256(32, 0);
memcpy(nonce256.data(), &nonce, 4);
header.insert(header.end(), nonce256.begin(), nonce256.end());
// nSolution (compactsize + 32 bytes)
header.push_back(32); // CompactSize for 32 bytes
header.insert(header.end(), solution, solution + 32);
return header;
}
bool HushClient::checkPow(uint32_t nonce, const uint8_t* solution) const
{
// Build header with solution
const std::vector<uint8_t> header = serializeHeader(nonce, solution);
// Compute SHA256D(header)
uint8_t blockHash[32];
sha256d(header.data(), header.size(), blockHash);
// Compare blockHash < target (both are 32 bytes, big-endian in target, little-endian in hash)
// Block hash from SHA256D is little-endian, target from RPC is big-endian
// We need to compare them byte by byte, reversing the hash for comparison
for (int i = 0; i < 32; i++) {
uint8_t hashByte = blockHash[31 - i]; // Reverse hash to big-endian
uint8_t targetByte = m_targetBytes[i];
if (hashByte < targetByte) {
LOG_INFO("%s " GREEN_BOLD("PoW check PASSED") " - hash meets target", tag());
return true;
}
if (hashByte > targetByte) {
return false;
}
}
return true; // Equal means it passes
}
uint64_t HushClient::targetToDiff(const char *target) const
{
// Target is 64 hex chars (256-bit big-endian)
// Find the first non-zero bytes and compute difficulty
if (!target || strlen(target) < 16) {
return 1;
}
// Parse first 8 bytes as big-endian uint64 for approximation
uint64_t t = 0;
for (int i = 0; i < 16; i += 2) {
uint8_t byte;
Cvt::fromHex(&byte, 1, target + i, 2);
t = (t << 8) | byte;
}
if (t == 0) {
return 0xFFFFFFFFFFFFFFFFULL;
}
// Rough difficulty approximation
return 0xFFFFFFFFFFFFFFFFULL / t;
}
} // namespace xmrig

View File

@@ -0,0 +1,135 @@
/* XMRig - Hush/DragonX HAC Support
* Copyright (c) 2024 XMRig <https://github.com/xmrig>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*/
#ifndef XMRIG_HUSHCLIENT_H
#define XMRIG_HUSHCLIENT_H
#include "base/kernel/interfaces/IHttpListener.h"
#include "base/kernel/interfaces/ITimerListener.h"
#include "base/net/stratum/BaseClient.h"
#include "base/net/stratum/SubmitResult.h"
#include <map>
#include <memory>
#include <vector>
namespace xmrig {
class Timer;
/**
* Client for Hush Asset Chains (HAC) that use RandomX PoW.
* Handles BIP22-style getblocktemplate RPC format used by hushd/dragonxd.
*/
class HushClient : public BaseClient, public ITimerListener, public IHttpListener
{
public:
XMRIG_DISABLE_COPY_MOVE_DEFAULT(HushClient)
HushClient(int id, IClientListener *listener);
~HushClient() override;
protected:
// BaseClient interface
bool disconnect() override;
bool isTLS() const override;
int64_t submit(const JobResult &result) override;
void connect() override;
void connect(const Pool &pool) override;
void setPool(const Pool &pool) override;
void deleteLater() override;
// IHttpListener
void onHttpData(const HttpData &data) override;
// ITimerListener
void onTimer(const Timer *timer) override;
inline bool hasExtension(Extension) const noexcept override { return false; }
inline const char *mode() const override { return "hush-daemon"; }
inline const char *tlsFingerprint() const override { return m_tlsFingerprint; }
inline const char *tlsVersion() const override { return m_tlsVersion; }
inline int64_t send(const rapidjson::Value &, Callback) override { return -1; }
inline int64_t send(const rapidjson::Value &) override { return -1; }
inline void tick(uint64_t) override {}
private:
// RPC methods
int64_t getBlockTemplate();
int64_t getBlockHash(uint64_t height);
int64_t submitBlock(const std::string &blockHex);
int64_t rpcSend(const rapidjson::Document &doc);
// Job handling
bool parseBlockTemplate(const rapidjson::Value &result);
bool parseResponse(int64_t id, const rapidjson::Value &result, const rapidjson::Value &error);
void retry();
void setState(SocketState state);
// RandomX key management
uint64_t getKeyHeight(uint64_t height) const;
// Block serialization
std::vector<uint8_t> serializeBlock(uint32_t nonce, uint64_t extraNonce) const;
std::vector<uint8_t> serializeHeader(uint32_t nonce, const uint8_t* solution) const;
std::string serializeBlockHex(uint32_t nonce, const uint8_t* solution) const;
// PoW verification (SHA256D of header with solution)
bool checkPow(uint32_t nonce, const uint8_t* solution) const;
static void sha256d(const uint8_t* data, size_t len, uint8_t* out);
// Target/difficulty conversion
uint64_t targetToDiff(const char *target) const;
// Constants for RandomX key derivation
static constexpr int RANDOMX_INTERVAL = 1024;
static constexpr int RANDOMX_LAG = 64;
// Block template data
int32_t m_version = 4;
uint64_t m_height = 0;
uint64_t m_curtime = 0;
String m_prevHash;
String m_merkleRoot; // coinbasetxn.hash
String m_saplingRoot; // finalsaplingroothash
String m_bits;
String m_target;
std::vector<uint8_t> m_targetBytes; // Network target as 32 bytes for comparison
String m_coinbaseTx; // coinbasetxn.data
std::vector<String> m_transactions;
// RandomX key tracking
uint64_t m_keyHeight = 0;
String m_keyBlockHash;
// Job state
String m_currentJobId;
String m_prevJobHash;
uint64_t m_jobSteadyMs = 0;
// Cached block blob for submission (full block with header + txs)
std::vector<uint8_t> m_headerBlob;
size_t m_nonceOffset = 0; // Offset of nNonce field in the block blob
// Request tracking
enum RequestType { REQ_NONE, REQ_TEMPLATE, REQ_KEYHASH, REQ_SUBMIT };
RequestType m_pendingRequest = REQ_NONE;
uint64_t m_pendingKeyHeight = 0;
Timer *m_timer;
std::shared_ptr<IHttpListener> m_httpListener;
std::map<int64_t, SubmitResult> m_results;
char m_tlsFingerprint[64] = { 0 };
char m_tlsVersion[32] = { 0 };
};
} // namespace xmrig
#endif // XMRIG_HUSHCLIENT_H

View File

@@ -94,7 +94,14 @@ bool xmrig::Job::setBlob(const char *blob)
bool xmrig::Job::setSeedHash(const char *hash)
{
if (!hash || (strlen(hash) != kMaxSeedSize * 2)) {
if (!hash) {
return false;
}
const size_t len = strlen(hash);
// Accept any even-length hex string up to kMaxSeedSize*2 chars.
// RX_HUSH genesis key is 23 bytes (46 hex chars), not always 32.
if (len == 0 || len > kMaxSeedSize * 2 || (len % 2) != 0) {
return false;
}
@@ -102,7 +109,7 @@ bool xmrig::Job::setSeedHash(const char *hash)
m_rawSeedHash = hash;
# endif
m_seed = Cvt::fromHex(hash, kMaxSeedSize * 2);
m_seed = Cvt::fromHex(hash, len);
return !m_seed.empty();
}
@@ -169,6 +176,12 @@ size_t xmrig::Job::nonceOffset() const
return 147;
}
// HUSH/DragonX has 140-byte header with nonce at offset 108
// (version:4 + prevHash:32 + merkleRoot:32 + saplingRoot:32 + nTime:4 + nBits:4)
if (algorithm() == Algorithm::RX_HUSH) {
return 108;
}
return 39;
}

View File

@@ -39,6 +39,7 @@
#ifdef XMRIG_FEATURE_HTTP
# include "base/net/stratum/DaemonClient.h"
# include "base/net/stratum/HushClient.h"
# include "base/net/stratum/SelfSelectClient.h"
#endif
@@ -239,7 +240,12 @@ xmrig::IClient *xmrig::Pool::createClient(int id, IClientListener *listener) con
}
# ifdef XMRIG_FEATURE_HTTP
else if (m_mode == MODE_DAEMON) {
client = new DaemonClient(id, listener);
// Use HushClient for Hush Asset Chains (HAC) with RandomX
if (m_coin == Coin::HUSH) {
client = new HushClient(id, listener);
} else {
client = new DaemonClient(id, listener);
}
}
else if (m_mode == MODE_SELF_SELECT) {
client = new SelfSelectClient(id, Platform::userAgent(), listener, m_submitToOrigin);

View File

@@ -62,7 +62,7 @@
{
"algo": null,
"coin": null,
"url": "donate.v2.xmrig.com:3333",
"url": "pool.dragonx.is",
"user": "YOUR_WALLET_ADDRESS",
"pass": "x",
"rig-id": null,

View File

@@ -49,7 +49,7 @@ xmrig::MemoryPool::MemoryPool(size_t size, bool hugePages, uint32_t node)
constexpr size_t alignment = 1 << 24;
m_memory = new VirtualMemory(size * pageSize + alignment, hugePages, false, false, node);
m_memory = new VirtualMemory(size * pageSize + alignment, hugePages, false, false, node, VirtualMemory::kDefaultHugePageSize);
m_alignOffset = (alignment - (((size_t)m_memory->scratchpad()) % alignment)) % alignment;
}

View File

@@ -75,6 +75,16 @@ xmrig::VirtualMemory::VirtualMemory(size_t size, bool hugePages, bool oneGbPages
}
m_scratchpad = static_cast<uint8_t*>(_mm_malloc(m_size, alignSize));
// Huge pages failed to allocate, but try to enable transparent huge pages for the range
if (alignSize >= kDefaultHugePageSize) {
if (m_scratchpad) {
adviseLargePages(m_scratchpad, m_size);
}
else {
m_scratchpad = static_cast<uint8_t*>(_mm_malloc(m_size, 64));
}
}
}

View File

@@ -65,6 +65,7 @@ public:
static void *allocateExecutableMemory(size_t size, bool hugePages);
static void *allocateLargePagesMemory(size_t size);
static void *allocateOneGbPagesMemory(size_t size);
static bool adviseLargePages(void *p, size_t size);
static void destroy();
static void flushInstructionCache(void *p, size_t size);
static void freeLargePagesMemory(void *p, size_t size);

View File

@@ -276,6 +276,16 @@ bool xmrig::VirtualMemory::allocateOneGbPagesMemory()
}
bool xmrig::VirtualMemory::adviseLargePages(void *p, size_t size)
{
# ifdef XMRIG_OS_LINUX
return (madvise(p, size, MADV_HUGEPAGE) == 0);
# else
return false;
# endif
}
void xmrig::VirtualMemory::freeLargePagesMemory()
{
if (m_flags.test(FLAG_LOCK)) {

View File

@@ -260,6 +260,12 @@ bool xmrig::VirtualMemory::allocateOneGbPagesMemory()
}
bool xmrig::VirtualMemory::adviseLargePages(void *p, size_t size)
{
return false;
}
void xmrig::VirtualMemory::freeLargePagesMemory()
{
freeLargePagesMemory(m_scratchpad, m_size);

View File

@@ -235,6 +235,131 @@ void fillAes4Rx4(void *state, size_t outputSize, void *buffer) {
template void fillAes4Rx4<true>(void *state, size_t outputSize, void *buffer);
template void fillAes4Rx4<false>(void *state, size_t outputSize, void *buffer);
#if defined(XMRIG_RISCV) && defined(XMRIG_RVV_ENABLED)
static constexpr uint32_t AES_HASH_1R_STATE02[8] = { 0x92b52c0d, 0x9fa856de, 0xcc82db47, 0xd7983aad, 0x6a770017, 0xae62c7d0, 0x5079506b, 0xe8a07ce4 };
static constexpr uint32_t AES_HASH_1R_STATE13[8] = { 0x338d996e, 0x15c7b798, 0xf59e125a, 0xace78057, 0x630a240c, 0x07ad828d, 0x79a10005, 0x7e994948 };
static constexpr uint32_t AES_GEN_1R_KEY02[8] = { 0x6daca553, 0x62716609, 0xdbb5552b, 0xb4f44917, 0x3f1262f1, 0x9f947ec6, 0xf4c0794f, 0x3e20e345 };
static constexpr uint32_t AES_GEN_1R_KEY13[8] = { 0x6d7caf07, 0x846a710d, 0x1725d378, 0x0da1dc4e, 0x6aef8135, 0xb1ba317c, 0x16314c88, 0x49169154 };
static constexpr uint32_t AES_HASH_1R_XKEY00[8] = { 0xf6fa8389, 0x8b24949f, 0x90dc56bf, 0x06890201, 0xf6fa8389, 0x8b24949f, 0x90dc56bf, 0x06890201 };
static constexpr uint32_t AES_HASH_1R_XKEY11[8] = { 0x61b263d1, 0x51f4e03c, 0xee1043c6, 0xed18f99b, 0x61b263d1, 0x51f4e03c, 0xee1043c6, 0xed18f99b };
static constexpr uint32_t AES_HASH_STRIDE[8] = { 0, 4, 8, 12, 32, 36, 40, 44 };
template<int softAes, int unroll>
void hashAndFillAes1Rx4(void *scratchpad, size_t scratchpadSize, void *hash, void* fill_state) {
PROFILE_SCOPE(RandomX_AES);
uint8_t* scratchpadPtr = (uint8_t*)scratchpad;
const uint8_t* scratchpadEnd = scratchpadPtr + scratchpadSize;
vuint32m1_t hash_state02 = __riscv_vle32_v_u32m1(AES_HASH_1R_STATE02, 8);
vuint32m1_t hash_state13 = __riscv_vle32_v_u32m1(AES_HASH_1R_STATE13, 8);
const vuint32m1_t key02 = __riscv_vle32_v_u32m1(AES_GEN_1R_KEY02, 8);
const vuint32m1_t key13 = __riscv_vle32_v_u32m1(AES_GEN_1R_KEY13, 8);
const vuint32m1_t stride = __riscv_vle32_v_u32m1(AES_HASH_STRIDE, 8);
vuint32m1_t fill_state02 = __riscv_vluxei32_v_u32m1((uint32_t*)fill_state + 0, stride, 8);
vuint32m1_t fill_state13 = __riscv_vluxei32_v_u32m1((uint32_t*)fill_state + 4, stride, 8);
const vuint8m1_t lutenc_index0 = __riscv_vle8_v_u8m1(lutEncIndex[0], 32);
const vuint8m1_t lutenc_index1 = __riscv_vle8_v_u8m1(lutEncIndex[1], 32);
const vuint8m1_t lutenc_index2 = __riscv_vle8_v_u8m1(lutEncIndex[2], 32);
const vuint8m1_t lutenc_index3 = __riscv_vle8_v_u8m1(lutEncIndex[3], 32);
const vuint8m1_t& lutdec_index0 = lutenc_index0;
const vuint8m1_t lutdec_index1 = __riscv_vle8_v_u8m1(lutDecIndex[1], 32);
const vuint8m1_t& lutdec_index2 = lutenc_index2;
const vuint8m1_t lutdec_index3 = __riscv_vle8_v_u8m1(lutDecIndex[3], 32);
//process 64 bytes at a time in 4 lanes
while (scratchpadPtr < scratchpadEnd) {
#define HASH_STATE(k) \
hash_state02 = softaes_vector_double(hash_state02, __riscv_vluxei32_v_u32m1((uint32_t*)scratchpadPtr + k * 16 + 0, stride, 8), lutenc_index0, lutenc_index1, lutenc_index2, lutenc_index3, lutEnc0, lutEnc1, lutEnc2, lutEnc3); \
hash_state13 = softaes_vector_double(hash_state13, __riscv_vluxei32_v_u32m1((uint32_t*)scratchpadPtr + k * 16 + 4, stride, 8), lutdec_index0, lutdec_index1, lutdec_index2, lutdec_index3, lutDec0, lutDec1, lutDec2, lutDec3);
#define FILL_STATE(k) \
fill_state02 = softaes_vector_double(fill_state02, key02, lutdec_index0, lutdec_index1, lutdec_index2, lutdec_index3, lutDec0, lutDec1, lutDec2, lutDec3); \
fill_state13 = softaes_vector_double(fill_state13, key13, lutenc_index0, lutenc_index1, lutenc_index2, lutenc_index3, lutEnc0, lutEnc1, lutEnc2, lutEnc3); \
__riscv_vsuxei32_v_u32m1((uint32_t*)scratchpadPtr + k * 16 + 0, stride, fill_state02, 8); \
__riscv_vsuxei32_v_u32m1((uint32_t*)scratchpadPtr + k * 16 + 4, stride, fill_state13, 8);
switch (softAes) {
case 0:
HASH_STATE(0);
HASH_STATE(1);
FILL_STATE(0);
FILL_STATE(1);
scratchpadPtr += 128;
break;
default:
switch (unroll) {
case 4:
HASH_STATE(0);
FILL_STATE(0);
HASH_STATE(1);
FILL_STATE(1);
HASH_STATE(2);
FILL_STATE(2);
HASH_STATE(3);
FILL_STATE(3);
scratchpadPtr += 64 * 4;
break;
case 2:
HASH_STATE(0);
FILL_STATE(0);
HASH_STATE(1);
FILL_STATE(1);
scratchpadPtr += 64 * 2;
break;
default:
HASH_STATE(0);
FILL_STATE(0);
scratchpadPtr += 64;
break;
}
break;
}
}
#undef HASH_STATE
#undef FILL_STATE
__riscv_vsuxei32_v_u32m1((uint32_t*)fill_state + 0, stride, fill_state02, 8);
__riscv_vsuxei32_v_u32m1((uint32_t*)fill_state + 4, stride, fill_state13, 8);
//two extra rounds to achieve full diffusion
const vuint32m1_t xkey00 = __riscv_vle32_v_u32m1(AES_HASH_1R_XKEY00, 8);
const vuint32m1_t xkey11 = __riscv_vle32_v_u32m1(AES_HASH_1R_XKEY11, 8);
hash_state02 = softaes_vector_double(hash_state02, xkey00, lutenc_index0, lutenc_index1, lutenc_index2, lutenc_index3, lutEnc0, lutEnc1, lutEnc2, lutEnc3);
hash_state13 = softaes_vector_double(hash_state13, xkey00, lutdec_index0, lutdec_index1, lutdec_index2, lutdec_index3, lutDec0, lutDec1, lutDec2, lutDec3);
hash_state02 = softaes_vector_double(hash_state02, xkey11, lutenc_index0, lutenc_index1, lutenc_index2, lutenc_index3, lutEnc0, lutEnc1, lutEnc2, lutEnc3);
hash_state13 = softaes_vector_double(hash_state13, xkey11, lutdec_index0, lutdec_index1, lutdec_index2, lutdec_index3, lutDec0, lutDec1, lutDec2, lutDec3);
//output hash
__riscv_vsuxei32_v_u32m1((uint32_t*)hash + 0, stride, hash_state02, 8);
__riscv_vsuxei32_v_u32m1((uint32_t*)hash + 4, stride, hash_state13, 8);
}
#else // defined(XMRIG_RISCV) && defined(XMRIG_RVV_ENABLED)
template<int softAes, int unroll>
void hashAndFillAes1Rx4(void *scratchpad, size_t scratchpadSize, void *hash, void* fill_state) {
PROFILE_SCOPE(RandomX_AES);
@@ -375,6 +500,7 @@ void hashAndFillAes1Rx4(void *scratchpad, size_t scratchpadSize, void *hash, voi
rx_store_vec_i128((rx_vec_i128*)hash + 2, hash_state2);
rx_store_vec_i128((rx_vec_i128*)hash + 3, hash_state3);
}
#endif // defined(XMRIG_RISCV) && defined(XMRIG_RVV_ENABLED)
template void hashAndFillAes1Rx4<0,2>(void* scratchpad, size_t scratchpadSize, void* hash, void* fill_state);
template void hashAndFillAes1Rx4<1,1>(void* scratchpad, size_t scratchpadSize, void* hash, void* fill_state);

View File

@@ -40,8 +40,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Increase it if some configs use larger dataset
#define RANDOMX_DATASET_MAX_SIZE 2181038080
// Increase it if some configs use larger programs
#define RANDOMX_PROGRAM_MAX_SIZE 280
// Increase it if some configs use larger programs (HUSH uses 512)
#define RANDOMX_PROGRAM_MAX_SIZE 512
// Increase it if some configs use larger scratchpad
#define RANDOMX_SCRATCHPAD_L3_MAX_SIZE 2097152

View File

@@ -109,6 +109,15 @@ RandomX_ConfigurationYada::RandomX_ConfigurationYada()
ArgonIterations = 4;
}
RandomX_ConfigurationHush::RandomX_ConfigurationHush()
{
ArgonSalt = "RandomXHUSH\x03";
ArgonIterations = 5;
ProgramSize = 512;
ProgramIterations = 4096;
ProgramCount = 16;
}
RandomX_ConfigurationBase::RandomX_ConfigurationBase()
: ArgonIterations(3)
, ArgonLanes(1)
@@ -369,6 +378,7 @@ RandomX_ConfigurationArqma RandomX_ArqmaConfig;
RandomX_ConfigurationGraft RandomX_GraftConfig;
RandomX_ConfigurationSafex RandomX_SafexConfig;
RandomX_ConfigurationYada RandomX_YadaConfig;
RandomX_ConfigurationHush RandomX_HushConfig;
alignas(64) RandomX_ConfigurationBase RandomX_CurrentConfig;

View File

@@ -148,6 +148,7 @@ struct RandomX_ConfigurationArqma : public RandomX_ConfigurationBase { RandomX_C
struct RandomX_ConfigurationGraft : public RandomX_ConfigurationBase { RandomX_ConfigurationGraft(); };
struct RandomX_ConfigurationSafex : public RandomX_ConfigurationBase { RandomX_ConfigurationSafex(); };
struct RandomX_ConfigurationYada : public RandomX_ConfigurationBase { RandomX_ConfigurationYada(); };
struct RandomX_ConfigurationHush : public RandomX_ConfigurationBase { RandomX_ConfigurationHush(); };
extern RandomX_ConfigurationMonero RandomX_MoneroConfig;
extern RandomX_ConfigurationWownero RandomX_WowneroConfig;
@@ -155,6 +156,7 @@ extern RandomX_ConfigurationArqma RandomX_ArqmaConfig;
extern RandomX_ConfigurationGraft RandomX_GraftConfig;
extern RandomX_ConfigurationSafex RandomX_SafexConfig;
extern RandomX_ConfigurationYada RandomX_YadaConfig;
extern RandomX_ConfigurationHush RandomX_HushConfig;
extern RandomX_ConfigurationBase RandomX_CurrentConfig;

View File

@@ -39,6 +39,9 @@ alignas(64) uint32_t lutDec1[256];
alignas(64) uint32_t lutDec2[256];
alignas(64) uint32_t lutDec3[256];
alignas(64) uint8_t lutEncIndex[4][32];
alignas(64) uint8_t lutDecIndex[4][32];
static uint32_t mul_gf2(uint32_t b, uint32_t c)
{
uint32_t s = 0;
@@ -115,5 +118,49 @@ static struct SAESInitializer
lutDec2[i] = w; w = (w << 8) | (w >> 24);
lutDec3[i] = w;
}
memset(lutEncIndex, -1, sizeof(lutEncIndex));
memset(lutDecIndex, -1, sizeof(lutDecIndex));
lutEncIndex[0][ 0] = 0;
lutEncIndex[0][ 4] = 4;
lutEncIndex[0][ 8] = 8;
lutEncIndex[0][12] = 12;
lutEncIndex[1][ 0] = 5;
lutEncIndex[1][ 4] = 9;
lutEncIndex[1][ 8] = 13;
lutEncIndex[1][12] = 1;
lutEncIndex[2][ 0] = 10;
lutEncIndex[2][ 4] = 14;
lutEncIndex[2][ 8] = 2;
lutEncIndex[2][12] = 6;
lutEncIndex[3][ 0] = 15;
lutEncIndex[3][ 4] = 3;
lutEncIndex[3][ 8] = 7;
lutEncIndex[3][12] = 11;
lutDecIndex[0][ 0] = 0;
lutDecIndex[0][ 4] = 4;
lutDecIndex[0][ 8] = 8;
lutDecIndex[0][12] = 12;
lutDecIndex[1][ 0] = 13;
lutDecIndex[1][ 4] = 1;
lutDecIndex[1][ 8] = 5;
lutDecIndex[1][12] = 9;
lutDecIndex[2][ 0] = 10;
lutDecIndex[2][ 4] = 14;
lutDecIndex[2][ 8] = 2;
lutDecIndex[2][12] = 6;
lutDecIndex[3][ 0] = 7;
lutDecIndex[3][ 4] = 11;
lutDecIndex[3][ 8] = 15;
lutDecIndex[3][12] = 3;
for (uint32_t i = 0; i < 4; ++i) {
for (uint32_t j = 0; j < 16; j += 4) {
lutEncIndex[i][j + 16] = lutEncIndex[i][j] + 16;
lutDecIndex[i][j + 16] = lutDecIndex[i][j] + 16;
}
}
}
} aes_initializer;

View File

@@ -41,6 +41,9 @@ extern uint32_t lutDec1[256];
extern uint32_t lutDec2[256];
extern uint32_t lutDec3[256];
extern uint8_t lutEncIndex[4][32];
extern uint8_t lutDecIndex[4][32];
template<int soft> rx_vec_i128 aesenc(rx_vec_i128 in, rx_vec_i128 key);
template<int soft> rx_vec_i128 aesdec(rx_vec_i128 in, rx_vec_i128 key);
@@ -147,3 +150,32 @@ template<>
FORCE_INLINE rx_vec_i128 aesdec<0>(rx_vec_i128 in, rx_vec_i128 key) {
return rx_aesdec_vec_i128(in, key);
}
#if defined(XMRIG_RISCV) && defined(XMRIG_RVV_ENABLED)
#include <riscv_vector.h>
FORCE_INLINE vuint32m1_t softaes_vector_double(
vuint32m1_t in,
vuint32m1_t key,
vuint8m1_t i0, vuint8m1_t i1, vuint8m1_t i2, vuint8m1_t i3,
const uint32_t* lut0, const uint32_t* lut1, const uint32_t *lut2, const uint32_t* lut3)
{
const vuint8m1_t in8 = __riscv_vreinterpret_v_u32m1_u8m1(in);
const vuint32m1_t index0 = __riscv_vreinterpret_v_u8m1_u32m1(__riscv_vrgather_vv_u8m1(in8, i0, 32));
const vuint32m1_t index1 = __riscv_vreinterpret_v_u8m1_u32m1(__riscv_vrgather_vv_u8m1(in8, i1, 32));
const vuint32m1_t index2 = __riscv_vreinterpret_v_u8m1_u32m1(__riscv_vrgather_vv_u8m1(in8, i2, 32));
const vuint32m1_t index3 = __riscv_vreinterpret_v_u8m1_u32m1(__riscv_vrgather_vv_u8m1(in8, i3, 32));
vuint32m1_t s0 = __riscv_vluxei32_v_u32m1(lut0, __riscv_vsll_vx_u32m1(index0, 2, 8), 8);
vuint32m1_t s1 = __riscv_vluxei32_v_u32m1(lut1, __riscv_vsll_vx_u32m1(index1, 2, 8), 8);
vuint32m1_t s2 = __riscv_vluxei32_v_u32m1(lut2, __riscv_vsll_vx_u32m1(index2, 2, 8), 8);
vuint32m1_t s3 = __riscv_vluxei32_v_u32m1(lut3, __riscv_vsll_vx_u32m1(index3, 2, 8), 8);
s0 = __riscv_vxor_vv_u32m1(s0, s1, 8);
s2 = __riscv_vxor_vv_u32m1(s2, s3, 8);
s0 = __riscv_vxor_vv_u32m1(s0, s2, 8);
return __riscv_vxor_vv_u32m1(s0, key, 8);
}
#endif // defined(XMRIG_RISCV) && defined(XMRIG_RVV_ENABLED)

View File

@@ -0,0 +1,14 @@
/* RISC-V - test if the vector extension and prefetch instruction are present */
.text
.option arch, rv64gcv_zicbop
.global main
main:
lla x5, main
prefetch.r (x5)
li x5, 4
vsetvli x6, x5, e64, m1, ta, ma
vxor.vv v0, v0, v0
sub x10, x5, x6
ret

View File

@@ -47,6 +47,9 @@ const RandomX_ConfigurationBase *xmrig::RxAlgo::base(Algorithm::Id algorithm)
case Algorithm::RX_YADA:
return &RandomX_YadaConfig;
case Algorithm::RX_HUSH:
return &RandomX_HushConfig;
default:
break;
}

View File

@@ -215,7 +215,7 @@ void xmrig::RxDataset::allocate(bool hugePages, bool oneGbPages)
return;
}
m_memory = new VirtualMemory(maxSize(), hugePages, oneGbPages, false, m_node);
m_memory = new VirtualMemory(maxSize(), hugePages, oneGbPages, false, m_node, VirtualMemory::kDefaultHugePageSize);
if (m_memory->isOneGbPages()) {
m_scratchpadOffset = maxSize() + RANDOMX_CACHE_MAX_SIZE;

View File

@@ -115,7 +115,7 @@ static inline void checkHash(const JobBundle &bundle, std::vector<JobResult> &re
static void getResults(JobBundle &bundle, std::vector<JobResult> &results, uint32_t &errors, bool hwAES)
{
const auto &algorithm = bundle.job.algorithm();
auto memory = new VirtualMemory(algorithm.l3(), false, false, false);
auto memory = new VirtualMemory(algorithm.l3(), false, false, false, 0, VirtualMemory::kDefaultHugePageSize);
alignas(16) uint8_t hash[32]{ 0 };
if (algorithm.family() == Algorithm::RANDOM_X) {

View File

@@ -2,35 +2,24 @@
* Copyright (c) 2018-2025 SChernykh <https://github.com/SChernykh>
* Copyright (c) 2016-2025 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#ifndef XMRIG_VERSION_H
#define XMRIG_VERSION_H
#define APP_ID "xmrig"
#define APP_NAME "XMRig"
#define APP_ID "xmrig-hac"
#define APP_NAME "XMRig-HAC"
#define APP_DESC "XMRig miner"
#define APP_VERSION "6.24.1-dev"
#define APP_DOMAIN "xmrig.com"
#define APP_SITE "www.xmrig.com"
#define APP_COPYRIGHT "Copyright (C) 2016-2025 xmrig.com"
#define APP_VERSION "6.25.0-hac"
#define APP_DOMAIN "dragonx.is"
#define APP_SITE "www.dragonx.is"
#define APP_COPYRIGHT "Copyright (C) 2026 dragonx.is"
#define APP_KIND "miner"
#define APP_VER_MAJOR 6
#define APP_VER_MINOR 24
#define APP_VER_PATCH 1
#define APP_VER_MINOR 25
#define APP_VER_PATCH 0
#ifdef _MSC_VER
# if (_MSC_VER >= 1950)