# 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 `. - 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 |