Files
hush3/doc/CHANGELOG-randomx-validation.md
dan_s 20aeebb83f Fix 7 stale references in docs and comments after codebase restructuring
- doc/DEVELOPING.md: src/main.cpp → src/tx_validation.cpp / block_processing.cpp
- doc/CHANGELOG-randomx-validation.md: src/main.cpp → src/block_processing.cpp (×2)
- doc/release-process.md: checkpoint instructions now reference
  src/chainparams_checkpoints_hush3.h instead of src/chainparams.cpp
- src/hush_defs.h: ./contrib/block_time.pl → ./contrib/scripts/block_time.pl (×3)
- src/cc/dapps/dappstd.c: commented hush_cJSON.c → hush_cJSON.cpp
2026-02-27 14:19:08 -06:00

4.8 KiB

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/block_processing.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/block_processing.cpp 2 0
src/hush_bitcoind.h 6 0
src/miner.cpp 5 5
src/hush_utils.h 10 0