Fix RandomX validation exploit with difficulty reset, improve build system
- One-time difficulty reset to minimum for 17 blocks at activation height - Separate RandomX build dirs for linux/win64 cross-compilation - Add build target tracking to auto-clean on target switch - Automated win64 release packaging to release-win64/ - Add build artifacts to .gitignore - Miner memory diagnostics and large-page dataset fallback
This commit is contained in:
9
.gitignore
vendored
9
.gitignore
vendored
@@ -165,3 +165,12 @@ REGTEST_7776
|
|||||||
src/cc/librogue.so
|
src/cc/librogue.so
|
||||||
src/cc/games/prices
|
src/cc/games/prices
|
||||||
src/cc/games/tetris
|
src/cc/games/tetris
|
||||||
|
|
||||||
|
# Build artifacts
|
||||||
|
*~
|
||||||
|
*.zip
|
||||||
|
release-win64/
|
||||||
|
src/RandomX/build-linux/
|
||||||
|
src/RandomX/build-win64/
|
||||||
|
src/wallet-utility.exe
|
||||||
|
src/.build_target
|
||||||
|
|||||||
45
build.sh
45
build.sh
@@ -5,13 +5,52 @@
|
|||||||
|
|
||||||
set -eu -o pipefail
|
set -eu -o pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
BUILD_TARGET_FILE="$SCRIPT_DIR/src/.build_target"
|
||||||
|
|
||||||
|
# Function to check and clean if target changed
|
||||||
|
check_and_clean_target() {
|
||||||
|
local new_target="$1"
|
||||||
|
local current_target=""
|
||||||
|
|
||||||
|
if [[ -f "$BUILD_TARGET_FILE" ]]; then
|
||||||
|
current_target=$(cat "$BUILD_TARGET_FILE")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$current_target" && "$current_target" != "$new_target" ]]; then
|
||||||
|
echo "Build target changed from '$current_target' to '$new_target'"
|
||||||
|
echo "Cleaning old build artifacts to prevent cross-compilation contamination..."
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
make distclean 2>/dev/null || true
|
||||||
|
# Clean leveldb separately as it often causes issues
|
||||||
|
if [[ -d src/leveldb ]]; then
|
||||||
|
make -C src/leveldb clean 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
echo "Clean complete."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Record the new target
|
||||||
|
echo "$new_target" > "$BUILD_TARGET_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check for --win-release flag for cross-compilation
|
||||||
|
if [[ "${1:-}" == "--win-release" ]]; then
|
||||||
|
check_and_clean_target "windows"
|
||||||
|
shift
|
||||||
|
./util/build-win.sh "$@"
|
||||||
|
exit $?
|
||||||
|
fi
|
||||||
|
|
||||||
# run correct build script for detected OS
|
# run correct build script for detected OS
|
||||||
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
||||||
./util/build.sh --disable-tests $@
|
check_and_clean_target "linux"
|
||||||
|
./util/build.sh --disable-tests "$@"
|
||||||
elif [[ "$OSTYPE" == "darwin"* ]]; then
|
elif [[ "$OSTYPE" == "darwin"* ]]; then
|
||||||
./util/build-mac.sh --disable-tests $@
|
check_and_clean_target "macos"
|
||||||
|
./util/build-mac.sh --disable-tests "$@"
|
||||||
elif [[ "$OSTYPE" == "msys"* ]]; then
|
elif [[ "$OSTYPE" == "msys"* ]]; then
|
||||||
./util/build-win.sh --disable-tests $@
|
check_and_clean_target "windows"
|
||||||
|
./util/build-win.sh --disable-tests "$@"
|
||||||
#elif [[ "$OSTYPE" == "freebsd"* ]]; then
|
#elif [[ "$OSTYPE" == "freebsd"* ]]; then
|
||||||
# placeholder
|
# placeholder
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ endif
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
define $(package)_config_cmds
|
define $(package)_config_cmds
|
||||||
$($(package)_autoconf) --host=$(host) --build=$(build)
|
CC_FOR_BUILD=gcc CXX_FOR_BUILD=g++ $($(package)_autoconf) --host=$(host) --build=$(build)
|
||||||
endef
|
endef
|
||||||
|
|
||||||
ifeq ($(build_os),darwin)
|
ifeq ($(build_os),darwin)
|
||||||
|
|||||||
63
doc/CHANGELOG-randomx-optimization.md
Normal file
63
doc/CHANGELOG-randomx-optimization.md
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
# Changelog: RandomX Mining Optimization
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
Optimizes RandomX miner memory usage by replacing per-thread dataset/cache allocation with a shared `RandomXDatasetManager`. Reduces memory footprint from ~2.3GB × N threads to ~2.3GB total.
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
|
||||||
|
The original RandomX miner implementation allocated a full RandomX dataset (~2GB) and cache (~256MB) for each mining thread. With multiple threads, this quickly exhausted available memory:
|
||||||
|
|
||||||
|
- 4 threads: ~9.2GB
|
||||||
|
- 8 threads: ~18.4GB
|
||||||
|
- 16 threads: ~36.8GB
|
||||||
|
|
||||||
|
## Solution
|
||||||
|
|
||||||
|
Implemented a shared `RandomXDatasetManager` that allocates a single dataset and cache, shared by all miner threads via read-only access (explicitly supported by RandomX design).
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
### `src/miner.cpp`
|
||||||
|
|
||||||
|
- **Added `RandomXDatasetManager` class:**
|
||||||
|
- Manages a single shared RandomX cache and dataset.
|
||||||
|
- Thread-safe key updates using `boost::shared_mutex` (readers-writer lock).
|
||||||
|
- `Initialize(key)`: Allocates cache and dataset, initializes with key, builds dataset using all CPU cores.
|
||||||
|
- `UpdateKey(newKey)`: Acquires exclusive lock, reinitializes cache with new key, rebuilds dataset.
|
||||||
|
- `GetDataset()`: Returns pointer to shared dataset for VM creation.
|
||||||
|
- `AcquireSharedLock()` / `ReleaseSharedLock()`: Miner threads hold shared lock while hashing to prevent key updates mid-hash.
|
||||||
|
- `Shutdown()`: Releases all RandomX resources.
|
||||||
|
|
||||||
|
- **Modified `GenerateBitcoins()`:**
|
||||||
|
- Initializes `RandomXDatasetManager` with initial key before spawning miner threads.
|
||||||
|
- Calls `Shutdown()` when mining stops.
|
||||||
|
|
||||||
|
- **Modified `RandomXMiner()`:**
|
||||||
|
- Creates lightweight per-thread VM using shared dataset (`randomx_create_vm(..., manager.GetDataset())`).
|
||||||
|
- Acquires shared lock before hashing, releases after.
|
||||||
|
- On key rotation: releases shared lock, calls `manager.UpdateKey()`, reacquires shared lock, recreates VM.
|
||||||
|
- Cleanup only destroys thread's VM — dataset/cache owned by manager.
|
||||||
|
|
||||||
|
## Memory Usage
|
||||||
|
|
||||||
|
| Threads | Before | After |
|
||||||
|
|---------|--------|-------|
|
||||||
|
| 1 | ~2.3GB | ~2.3GB |
|
||||||
|
| 4 | ~9.2GB | ~2.3GB |
|
||||||
|
| 8 | ~18.4GB | ~2.3GB |
|
||||||
|
| 16 | ~36.8GB | ~2.3GB |
|
||||||
|
|
||||||
|
## Thread Safety
|
||||||
|
|
||||||
|
- RandomX dataset is read-only after initialization — safe to share across threads.
|
||||||
|
- `boost::shared_mutex` ensures:
|
||||||
|
- Multiple threads can hash concurrently (shared lock).
|
||||||
|
- Key updates are exclusive (exclusive lock blocks until all shared locks release).
|
||||||
|
- No thread hashes with stale dataset during key change.
|
||||||
|
|
||||||
|
## Files Modified
|
||||||
|
|
||||||
|
| File | Lines Added | Lines Removed |
|
||||||
|
|------|-------------|---------------|
|
||||||
|
| `src/miner.cpp` | ~80 | ~20 |
|
||||||
95
doc/CHANGELOG-randomx-validation.md
Normal file
95
doc/CHANGELOG-randomx-validation.md
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
# Changelog: RandomX Validation Bug Fix
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
Fixes a critical exploit allowing miners on RandomX-based Hush Arrakis Chains (HACs) to bypass RandomX proof-of-work validation entirely. Adds proper RandomX solution verification to the block validation pipeline, fixes the miner's RandomX input construction, and introduces per-chain activation heights.
|
||||||
|
|
||||||
|
## Bug Description
|
||||||
|
|
||||||
|
Three issues combined to allow the bypass:
|
||||||
|
|
||||||
|
1. `CheckEquihashSolution()` returned `true` immediately for non-Equihash chains, never verifying the RandomX solution stored in `nSolution`.
|
||||||
|
2. `CheckProofOfWork()` checked the standard SHA-256d `GetHash()` against the difficulty target instead of a RandomX hash, allowing miners to brute-force the cheap hash.
|
||||||
|
3. The miner passed `&randomxInput, sizeof randomxInput` (the address and size of a `CDataStream` struct) to `randomx_calculate_hash()` instead of the actual serialized data, producing non-deterministic hashes.
|
||||||
|
|
||||||
|
A malicious miner could put arbitrary bytes in `nSolution`, iterate nonces until the standard header hash met the target, and the block would pass all validation — completely bypassing RandomX CPU-hardness.
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
### `src/primitives/block.h`
|
||||||
|
|
||||||
|
- Added `CRandomXInput` class (modeled after `CEquihashInput`) that serializes the block header **including `nNonce`** but **excluding `nSolution`**, providing deterministic and reproducible input for RandomX hashing.
|
||||||
|
|
||||||
|
### `src/hush_globals.h`
|
||||||
|
|
||||||
|
- Added `int32_t ASSETCHAINS_RANDOMX_VALIDATION = -1` global variable to hold the per-chain activation height (`-1` = disabled).
|
||||||
|
|
||||||
|
### `src/hush_defs.h`
|
||||||
|
|
||||||
|
- Added `extern int32_t ASSETCHAINS_RANDOMX_VALIDATION` declaration.
|
||||||
|
|
||||||
|
### `src/pow.h`
|
||||||
|
|
||||||
|
- Added declaration for `bool CheckRandomXSolution(const CBlockHeader *pblock, int32_t height)`.
|
||||||
|
- Added declarations for `int GetRandomXInterval()` and `int GetRandomXBlockLag()` (moved from `miner.cpp`).
|
||||||
|
|
||||||
|
### `src/pow.cpp`
|
||||||
|
|
||||||
|
- Added `#include "RandomX/src/randomx.h"` and `#include <mutex>`.
|
||||||
|
- Moved `GetRandomXInterval()` and `GetRandomXBlockLag()` definitions here from `miner.cpp` so they are accessible from both the miner and the validator.
|
||||||
|
- Implemented `CheckRandomXSolution()`:
|
||||||
|
- Returns `true` immediately for non-RandomX chains, pre-activation heights, and when `HUSH_LOADINGBLOCKS != 0`.
|
||||||
|
- Validates `nSolution` is exactly `RANDOMX_HASH_SIZE` (32) bytes.
|
||||||
|
- Derives the correct RandomX key for the given height (initial key from chain params, or block-hash key after rotation interval).
|
||||||
|
- Serializes the block header via `CRandomXInput` into a `CDataStream`.
|
||||||
|
- Calls `randomx_calculate_hash()` with the correct data pointer (`&ss[0]`) and size (`ss.size()`).
|
||||||
|
- Compares the computed hash against `pblock->nSolution`.
|
||||||
|
- Caches the RandomX VM and cache between calls (protected by mutex), re-initializing only when the key changes.
|
||||||
|
|
||||||
|
### `src/main.cpp`
|
||||||
|
|
||||||
|
- In `CheckBlockHeader()`: added `CheckRandomXSolution()` call inside the `fCheckPOW` block, after the existing `CheckEquihashSolution()` call. Invalid RandomX solutions are rejected with `DoS(100)`.
|
||||||
|
|
||||||
|
### `src/hush_bitcoind.h`
|
||||||
|
|
||||||
|
- Added `#include "pow.h"`.
|
||||||
|
- In `hush_checkPOW()`: added `CheckRandomXSolution()` call after the existing `CheckEquihashSolution()` call.
|
||||||
|
|
||||||
|
### `src/miner.cpp`
|
||||||
|
|
||||||
|
- Changed `GetRandomXInterval()` and `GetRandomXBlockLag()` from inline definitions to forward declarations (definitions moved to `pow.cpp`).
|
||||||
|
- Fixed RandomX input construction in `RandomXMiner()`:
|
||||||
|
- Uses `CRandomXInput` to serialize the block header without `nSolution` (instead of serializing the entire block).
|
||||||
|
- Passes `&randomxInput[0], randomxInput.size()` to `randomx_calculate_hash()` (instead of `&randomxInput, sizeof randomxInput`).
|
||||||
|
- Added `SetSkipRandomXValidation(true/false)` around `TestBlockValidity()` to avoid double-validation OOM.
|
||||||
|
- Added null-checks before `randomx_destroy_vm()` to prevent double-free crashes.
|
||||||
|
|
||||||
|
### `src/hush_utils.h`
|
||||||
|
|
||||||
|
- After `ASSETCHAINS_ALGO` and `SMART_CHAIN_SYMBOL` are set during chain initialization, sets `ASSETCHAINS_RANDOMX_VALIDATION` per chain:
|
||||||
|
- `DRAGONX`: `1` (TBD: set to coordinated upgrade height)
|
||||||
|
- `TUMIN`: `1` (TBD: set to coordinated upgrade height)
|
||||||
|
- All other RandomX HACs: `1` (enforced from height 1)
|
||||||
|
- Non-RandomX chains: remains `-1` (disabled)
|
||||||
|
|
||||||
|
## Activation Heights
|
||||||
|
|
||||||
|
| Chain | Height | Notes |
|
||||||
|
|-------|--------|-------|
|
||||||
|
| DRAGONX | TBD | Existing chain, needs coordinated upgrade |
|
||||||
|
| TUMIN | TBD | Existing chain, needs coordinated upgrade |
|
||||||
|
| All other RandomX HACs | 1 | Enforced from genesis+1 |
|
||||||
|
|
||||||
|
## Files Modified
|
||||||
|
|
||||||
|
| File | Lines Added | Lines Removed |
|
||||||
|
|------|-------------|---------------|
|
||||||
|
| `src/primitives/block.h` | ~20 | 0 |
|
||||||
|
| `src/hush_globals.h` | 1 | 0 |
|
||||||
|
| `src/hush_defs.h` | 1 | 0 |
|
||||||
|
| `src/pow.h` | 9 | 0 |
|
||||||
|
| `src/pow.cpp` | ~105 | 0 |
|
||||||
|
| `src/main.cpp` | 2 | 0 |
|
||||||
|
| `src/hush_bitcoind.h` | 6 | 0 |
|
||||||
|
| `src/miner.cpp` | 5 | 5 |
|
||||||
|
| `src/hush_utils.h` | 10 | 0 |
|
||||||
1
src/.gitignore
vendored
Normal file
1
src/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.build_target
|
||||||
@@ -38,16 +38,7 @@ BITCOIN_INCLUDES += -I$(srcdir)/cc/includes
|
|||||||
BITCOIN_INCLUDES += -I$(srcdir)/univalue/include
|
BITCOIN_INCLUDES += -I$(srcdir)/univalue/include
|
||||||
BITCOIN_INCLUDES += -I$(srcdir)/leveldb/include
|
BITCOIN_INCLUDES += -I$(srcdir)/leveldb/include
|
||||||
|
|
||||||
if TARGET_WINDOWS
|
LIBBITCOIN_SERVER=libbitcoin_server.a
|
||||||
LIBBITCOIN_SERVER=libbitcoin_server.a -lcurl
|
|
||||||
endif
|
|
||||||
if TARGET_DARWIN
|
|
||||||
LIBBITCOIN_SERVER=libbitcoin_server.a -lcurl
|
|
||||||
endif
|
|
||||||
if TARGET_LINUX
|
|
||||||
LIBBITCOIN_SERVER=libbitcoin_server.a -lcurl
|
|
||||||
endif
|
|
||||||
|
|
||||||
LIBBITCOIN_WALLET=libbitcoin_wallet.a
|
LIBBITCOIN_WALLET=libbitcoin_wallet.a
|
||||||
LIBBITCOIN_COMMON=libbitcoin_common.a
|
LIBBITCOIN_COMMON=libbitcoin_common.a
|
||||||
LIBBITCOIN_CLI=libbitcoin_cli.a
|
LIBBITCOIN_CLI=libbitcoin_cli.a
|
||||||
@@ -57,7 +48,11 @@ LIBSECP256K1=secp256k1/libsecp256k1.la
|
|||||||
LIBUNIVALUE=univalue/libunivalue.la
|
LIBUNIVALUE=univalue/libunivalue.la
|
||||||
LIBZCASH=libzcash.a
|
LIBZCASH=libzcash.a
|
||||||
LIBHUSH=libhush.a
|
LIBHUSH=libhush.a
|
||||||
LIBRANDOMX=RandomX/build/librandomx.a
|
if TARGET_WINDOWS
|
||||||
|
LIBRANDOMX=RandomX/build-win64/librandomx.a
|
||||||
|
else
|
||||||
|
LIBRANDOMX=RandomX/build-linux/librandomx.a
|
||||||
|
endif
|
||||||
|
|
||||||
if BUILD_BITCOIN_LIBS
|
if BUILD_BITCOIN_LIBS
|
||||||
LIBZCASH_CONSENSUS=libzcashconsensus.la
|
LIBZCASH_CONSENSUS=libzcashconsensus.la
|
||||||
@@ -489,6 +484,13 @@ hushd_LDADD += \
|
|||||||
$(LIBBITCOIN_CRYPTO) \
|
$(LIBBITCOIN_CRYPTO) \
|
||||||
$(LIBZCASH_LIBS)
|
$(LIBZCASH_LIBS)
|
||||||
|
|
||||||
|
if TARGET_WINDOWS
|
||||||
|
hushd_LDADD += -lcurl -lwldap32
|
||||||
|
endif
|
||||||
|
if !TARGET_WINDOWS
|
||||||
|
hushd_LDADD += -lcurl
|
||||||
|
endif
|
||||||
|
|
||||||
if TARGET_DARWIN
|
if TARGET_DARWIN
|
||||||
hushd_LDADD += libcc.dylib $(LIBSECP256K1)
|
hushd_LDADD += libcc.dylib $(LIBSECP256K1)
|
||||||
endif
|
endif
|
||||||
|
|||||||
109
src/miner.cpp
109
src/miner.cpp
@@ -23,6 +23,7 @@
|
|||||||
#include "pow/tromp/equi_miner.h"
|
#include "pow/tromp/equi_miner.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include "amount.h"
|
#include "amount.h"
|
||||||
#include "chainparams.h"
|
#include "chainparams.h"
|
||||||
#include "consensus/consensus.h"
|
#include "consensus/consensus.h"
|
||||||
@@ -1015,6 +1016,56 @@ enum RandomXSolverCancelCheck
|
|||||||
int GetRandomXInterval();
|
int GetRandomXInterval();
|
||||||
int GetRandomXBlockLag();
|
int GetRandomXBlockLag();
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
static void LogProcessMemory(const char* label) {
|
||||||
|
// Use K32GetProcessMemoryInfo from kernel32.dll (available on Win7+)
|
||||||
|
// to avoid linking psapi.lib
|
||||||
|
typedef struct {
|
||||||
|
DWORD cb;
|
||||||
|
DWORD PageFaultCount;
|
||||||
|
SIZE_T PeakWorkingSetSize;
|
||||||
|
SIZE_T WorkingSetSize;
|
||||||
|
SIZE_T QuotaPeakPagedPoolUsage;
|
||||||
|
SIZE_T QuotaPagedPoolUsage;
|
||||||
|
SIZE_T QuotaPeakNonPagedPoolUsage;
|
||||||
|
SIZE_T QuotaNonPagedPoolUsage;
|
||||||
|
SIZE_T PagefileUsage;
|
||||||
|
SIZE_T PeakPagefileUsage;
|
||||||
|
SIZE_T PrivateUsage;
|
||||||
|
} PMC_EX;
|
||||||
|
typedef BOOL (WINAPI *PFN)(HANDLE, PMC_EX*, DWORD);
|
||||||
|
static PFN pfn = (PFN)GetProcAddress(GetModuleHandleA("kernel32.dll"), "K32GetProcessMemoryInfo");
|
||||||
|
if (pfn) {
|
||||||
|
PMC_EX pmc = {};
|
||||||
|
pmc.cb = sizeof(pmc);
|
||||||
|
if (pfn(GetCurrentProcess(), &pmc, sizeof(pmc))) {
|
||||||
|
LogPrintf("MemDiag [%s]: WorkingSet=%.1fMB, PrivateUsage=%.1fMB, PagefileUsage=%.1fMB\n",
|
||||||
|
label,
|
||||||
|
pmc.WorkingSetSize / (1024.0 * 1024.0),
|
||||||
|
pmc.PrivateUsage / (1024.0 * 1024.0),
|
||||||
|
pmc.PagefileUsage / (1024.0 * 1024.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static void LogProcessMemory(const char* label) {
|
||||||
|
// Linux: read /proc/self/status
|
||||||
|
FILE *f = fopen("/proc/self/status", "r");
|
||||||
|
if (f) {
|
||||||
|
char line[256];
|
||||||
|
while (fgets(line, sizeof(line), f)) {
|
||||||
|
if (strncmp(line, "VmRSS:", 6) == 0 || strncmp(line, "VmSize:", 7) == 0) {
|
||||||
|
// Remove newline
|
||||||
|
line[strlen(line)-1] = '\0';
|
||||||
|
LogPrintf("MemDiag [%s]: %s\n", label, line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Shared RandomX dataset manager — all miner threads share a single ~2GB dataset
|
// Shared RandomX dataset manager — all miner threads share a single ~2GB dataset
|
||||||
// instead of each allocating their own. The dataset is read-only after initialization
|
// instead of each allocating their own. The dataset is read-only after initialization
|
||||||
// and RandomX explicitly supports multiple VMs sharing one dataset.
|
// and RandomX explicitly supports multiple VMs sharing one dataset.
|
||||||
@@ -1036,6 +1087,12 @@ struct RandomXDatasetManager {
|
|||||||
if (initialized) return true;
|
if (initialized) return true;
|
||||||
|
|
||||||
flags |= RANDOMX_FLAG_FULL_MEM;
|
flags |= RANDOMX_FLAG_FULL_MEM;
|
||||||
|
LogPrintf("RandomXDatasetManager: flags=0x%x (JIT=%d, HARD_AES=%d, FULL_MEM=%d, LARGE_PAGES=%d)\n",
|
||||||
|
(int)flags,
|
||||||
|
!!(flags & RANDOMX_FLAG_JIT), !!(flags & RANDOMX_FLAG_HARD_AES),
|
||||||
|
!!(flags & RANDOMX_FLAG_FULL_MEM), !!(flags & RANDOMX_FLAG_LARGE_PAGES));
|
||||||
|
|
||||||
|
LogProcessMemory("before cache alloc");
|
||||||
|
|
||||||
cache = randomx_alloc_cache(flags | RANDOMX_FLAG_LARGE_PAGES | RANDOMX_FLAG_SECURE);
|
cache = randomx_alloc_cache(flags | RANDOMX_FLAG_LARGE_PAGES | RANDOMX_FLAG_SECURE);
|
||||||
if (cache == nullptr) {
|
if (cache == nullptr) {
|
||||||
@@ -1043,25 +1100,39 @@ struct RandomXDatasetManager {
|
|||||||
cache = randomx_alloc_cache(flags | RANDOMX_FLAG_SECURE);
|
cache = randomx_alloc_cache(flags | RANDOMX_FLAG_SECURE);
|
||||||
if (cache == nullptr) {
|
if (cache == nullptr) {
|
||||||
LogPrintf("RandomXDatasetManager: cache alloc failed with secure, trying basic...\n");
|
LogPrintf("RandomXDatasetManager: cache alloc failed with secure, trying basic...\n");
|
||||||
|
cache = randomx_alloc_cache(flags);
|
||||||
|
if (cache == nullptr) {
|
||||||
|
LogPrintf("RandomXDatasetManager: cannot allocate cache!\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
cache = randomx_alloc_cache(flags);
|
}
|
||||||
if (cache == nullptr) {
|
LogProcessMemory("after cache alloc");
|
||||||
LogPrintf("RandomXDatasetManager: cannot allocate cache!\n");
|
|
||||||
|
// Try to allocate dataset with large pages first for better performance
|
||||||
|
dataset = randomx_alloc_dataset(flags | RANDOMX_FLAG_LARGE_PAGES);
|
||||||
|
if (dataset == nullptr) {
|
||||||
|
LogPrintf("RandomXDatasetManager: dataset alloc failed with large pages, trying without...\n");
|
||||||
|
dataset = randomx_alloc_dataset(flags);
|
||||||
|
if (dataset == nullptr) {
|
||||||
|
LogPrintf("RandomXDatasetManager: cannot allocate dataset!\n");
|
||||||
|
randomx_release_cache(cache);
|
||||||
|
cache = nullptr;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dataset = randomx_alloc_dataset(flags);
|
|
||||||
if (dataset == nullptr) {
|
|
||||||
LogPrintf("RandomXDatasetManager: cannot allocate dataset!\n");
|
|
||||||
randomx_release_cache(cache);
|
|
||||||
cache = nullptr;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
datasetItemCount = randomx_dataset_item_count();
|
datasetItemCount = randomx_dataset_item_count();
|
||||||
initialized = true;
|
initialized = true;
|
||||||
LogPrintf("RandomXDatasetManager: allocated shared cache + dataset (%lu items)\n", datasetItemCount);
|
LogProcessMemory("after dataset alloc");
|
||||||
|
// Log the actual memory addresses to help diagnose sharing issues
|
||||||
|
uint8_t *datasetMemory = (uint8_t*)randomx_get_dataset_memory(dataset);
|
||||||
|
size_t datasetSize = datasetItemCount * RANDOMX_DATASET_ITEM_SIZE;
|
||||||
|
LogPrintf("RandomXDatasetManager: allocated shared dataset:\n");
|
||||||
|
LogPrintf(" - Dataset struct at: %p\n", (void*)dataset);
|
||||||
|
LogPrintf(" - Dataset memory at: %p (size: %.2f GB)\n", (void*)datasetMemory, datasetSize / (1024.0 * 1024.0 * 1024.0));
|
||||||
|
LogPrintf(" - Items: %lu, Item size: %d bytes\n", datasetItemCount, RANDOMX_DATASET_ITEM_SIZE);
|
||||||
|
LogPrintf(" - Expected total process memory: ~%.2f GB + ~2MB per mining thread\n", datasetSize / (1024.0 * 1024.0 * 1024.0));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1104,12 +1175,24 @@ struct RandomXDatasetManager {
|
|||||||
randomx_init_dataset(dataset, cache, 0, datasetItemCount);
|
randomx_init_dataset(dataset, cache, 0, datasetItemCount);
|
||||||
}
|
}
|
||||||
LogPrintf("RandomXDatasetManager: dataset rebuilt\n");
|
LogPrintf("RandomXDatasetManager: dataset rebuilt\n");
|
||||||
|
LogProcessMemory("after dataset init");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a per-thread VM using the shared dataset.
|
// Creates a per-thread VM using the shared dataset.
|
||||||
// Caller must hold a shared lock on datasetMtx.
|
// Caller must hold a shared lock on datasetMtx.
|
||||||
|
// The VM itself is small (~2MB scratchpad) - the 2GB dataset is shared via pointer.
|
||||||
randomx_vm *CreateVM() {
|
randomx_vm *CreateVM() {
|
||||||
return randomx_create_vm(flags, nullptr, dataset);
|
static std::atomic<int> vmCount{0};
|
||||||
|
LogProcessMemory("before CreateVM");
|
||||||
|
randomx_vm *vm = randomx_create_vm(flags, nullptr, dataset);
|
||||||
|
if (vm != nullptr) {
|
||||||
|
int id = ++vmCount;
|
||||||
|
uint8_t *datasetMemory = (uint8_t*)randomx_get_dataset_memory(dataset);
|
||||||
|
LogPrintf("RandomXDatasetManager: VM #%d created - VM at %p, shared dataset memory at %p\n",
|
||||||
|
id, (void*)vm, (void*)datasetMemory);
|
||||||
|
LogProcessMemory("after CreateVM");
|
||||||
|
}
|
||||||
|
return vm;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shutdown() {
|
void Shutdown() {
|
||||||
|
|||||||
14
src/pow.cpp
14
src/pow.cpp
@@ -339,6 +339,20 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead
|
|||||||
memset(zflags,0,sizeof(zflags));
|
memset(zflags,0,sizeof(zflags));
|
||||||
if ( pindexLast != 0 )
|
if ( pindexLast != 0 )
|
||||||
height = (int32_t)pindexLast->GetHeight() + 1;
|
height = (int32_t)pindexLast->GetHeight() + 1;
|
||||||
|
|
||||||
|
// One-time difficulty reset after RandomX validation fix.
|
||||||
|
// The exploit inflated difficulty to ~40B using cheap SHA-256d hashes.
|
||||||
|
// Reset to minimum for nPowAveragingWindow blocks so the DAA can
|
||||||
|
// recalibrate based on real RandomX block times.
|
||||||
|
if (ASSETCHAINS_RANDOMX_VALIDATION > 0 && height > 0) {
|
||||||
|
int resetEnd = ASSETCHAINS_RANDOMX_VALIDATION + params.nPowAveragingWindow;
|
||||||
|
if (height >= ASSETCHAINS_RANDOMX_VALIDATION && height < resetEnd) {
|
||||||
|
LogPrintf("%s: RandomX difficulty reset at height %d (reset window %d to %d)\n",
|
||||||
|
__func__, height, ASSETCHAINS_RANDOMX_VALIDATION, resetEnd - 1);
|
||||||
|
return nProofOfWorkLimit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ( ASSETCHAINS_ADAPTIVEPOW > 0 && pindexFirst != 0 && pblock != 0 && height >= (int32_t)(sizeof(ct)/sizeof(*ct)) )
|
if ( ASSETCHAINS_ADAPTIVEPOW > 0 && pindexFirst != 0 && pblock != 0 && height >= (int32_t)(sizeof(ct)/sizeof(*ct)) )
|
||||||
{
|
{
|
||||||
tipdiff = (pblock->nTime - pindexFirst->nTime);
|
tipdiff = (pblock->nTime - pindexFirst->nTime);
|
||||||
|
|||||||
@@ -25,14 +25,14 @@ echo $PWD
|
|||||||
./makecustom
|
./makecustom
|
||||||
cd $WD
|
cd $WD
|
||||||
|
|
||||||
# Build RandomX
|
# Build RandomX for Windows
|
||||||
cd src/RandomX
|
cd src/RandomX
|
||||||
if [ -d "build" ]
|
if [ -f "build-win64/librandomx.a" ]
|
||||||
then
|
then
|
||||||
ls -la build/librandomx*
|
ls -la build-win64/librandomx*
|
||||||
else
|
else
|
||||||
mkdir build && cd build
|
mkdir -p build-win64 && cd build-win64
|
||||||
CC="${CC} -g " CXX="${CXX} -g " cmake -DARCH=native ..
|
CC="${CC} -g " CXX="${CXX} -g " cmake -DCMAKE_SYSTEM_NAME=Windows -DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++-posix -DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc-posix -DARCH=native ..
|
||||||
make
|
make
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -41,3 +41,55 @@ cd $WD
|
|||||||
sed -i 's/-lboost_system-mt /-lboost_system-mt-s /' configure
|
sed -i 's/-lboost_system-mt /-lboost_system-mt-s /' configure
|
||||||
cd src/
|
cd src/
|
||||||
CC="${CC} -g " CXX="${CXX} -g " make V=1 hushd.exe hush-cli.exe hush-tx.exe
|
CC="${CC} -g " CXX="${CXX} -g " make V=1 hushd.exe hush-cli.exe hush-tx.exe
|
||||||
|
|
||||||
|
# Create release package
|
||||||
|
cd $WD
|
||||||
|
echo "Creating Windows release package..."
|
||||||
|
VERSION=$(grep -oP 'define\(_CLIENT_VERSION.*?,\s*\K[0-9]+' configure.ac | head -4 | tr '\n' '.' | sed 's/\.$//')
|
||||||
|
VERSION=${VERSION:-3.10.4}
|
||||||
|
RELEASE_DIR="release-win64"
|
||||||
|
mkdir -p "$RELEASE_DIR"
|
||||||
|
|
||||||
|
# Strip binaries
|
||||||
|
x86_64-w64-mingw32-strip -s src/hushd.exe src/hush-cli.exe src/hush-tx.exe
|
||||||
|
|
||||||
|
# Copy binaries
|
||||||
|
cp src/hushd.exe src/hush-cli.exe src/hush-tx.exe "$RELEASE_DIR/"
|
||||||
|
|
||||||
|
# Copy required data files
|
||||||
|
cp asmap.dat sapling-spend.params sapling-output.params "$RELEASE_DIR/"
|
||||||
|
|
||||||
|
# Create DragonX batch files
|
||||||
|
cat > "$RELEASE_DIR/dragonxd.bat" << 'EOF'
|
||||||
|
@call :GET_CURRENT_DIR
|
||||||
|
@cd %THIS_DIR%
|
||||||
|
hushd.exe -ac_name=DRAGONX -ac_algo=randomx -ac_halving=3500000 -ac_reward=300000000 -ac_blocktime=36 -ac_private=1 -addnode=176.126.87.241 %*
|
||||||
|
@goto :EOF
|
||||||
|
|
||||||
|
:GET_CURRENT_DIR
|
||||||
|
@pushd %~dp0
|
||||||
|
@set THIS_DIR=%CD%
|
||||||
|
@popd
|
||||||
|
@goto :EOF
|
||||||
|
EOF
|
||||||
|
cat > "$RELEASE_DIR/dragonx-cli.bat" << 'EOF'
|
||||||
|
@call :GET_CURRENT_DIR
|
||||||
|
@cd %THIS_DIR%
|
||||||
|
hush-cli.exe -ac_name=DRAGONX %*
|
||||||
|
@goto :EOF
|
||||||
|
|
||||||
|
:GET_CURRENT_DIR
|
||||||
|
@pushd %~dp0
|
||||||
|
@set THIS_DIR=%CD%
|
||||||
|
@popd
|
||||||
|
@goto :EOF
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Create ZIP
|
||||||
|
rm -f "$RELEASE_DIR/hush-${VERSION}-win64.zip"
|
||||||
|
cd "$RELEASE_DIR"
|
||||||
|
zip -9 "hush-${VERSION}-win64.zip" *.exe *.bat *.dat *.params
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
echo "Release package created: $RELEASE_DIR/hush-${VERSION}-win64.zip"
|
||||||
|
ls -lh "$RELEASE_DIR/hush-${VERSION}-win64.zip"
|
||||||
|
|||||||
@@ -139,13 +139,13 @@ echo $PWD
|
|||||||
./makecustom
|
./makecustom
|
||||||
cd $WD
|
cd $WD
|
||||||
|
|
||||||
# Build RandomX
|
# Build RandomX for Linux
|
||||||
cd src/RandomX
|
cd src/RandomX
|
||||||
if [ -d "build" ]
|
if [ -f "build-linux/librandomx.a" ]
|
||||||
then
|
then
|
||||||
ls -la build/librandomx*
|
ls -la build-linux/librandomx*
|
||||||
else
|
else
|
||||||
mkdir build && cd build
|
mkdir -p build-linux && cd build-linux
|
||||||
cmake -DARCH=native ..
|
cmake -DARCH=native ..
|
||||||
make
|
make
|
||||||
fi
|
fi
|
||||||
|
|||||||
Reference in New Issue
Block a user