Mutant created against commit dc3cd19.
+1 -1 · cpp
This description is AI generated, mistakes can be made.
Intent: When the first block of the current period is legitimately at powLimit (real difficulty was reset/clamped to minimum), the walk crosses into the previous period and returns an older, higher difficulty. The node then demands the wrong nBits for normal-speed testnet blocks. This splits testnet consensus only, hence medium.
Behaviour change: Original: the loop stops at the period's first block and returns its nBits (powLimit). Mutant: it keeps walking back to the last non-powLimit block of an earlier period and returns that nBits, so a correct powLimit block is rejected with bad-diffbits.
Trigger: On testnet3/testnet4, a period whose retarget produced 0x1d00ffff (e.g., after a stall), followed by a block mined less than 20 minutes after its parent at a non-retarget height. The earlier period contains a block with higher difficulty.
Spec violation: BIP94: 'The first block of each difficulty period MUST use the actual network difficulty' - the walk-back must treat the period's first block as authoritative. The code comment says 'Return the last non-special-min-difficulty-rules-block'.
Severity if undetected: medium
Why tests miss it: pow_tests.cpp and fuzz/pow.cpp (MAIN, fPowAllowMinDifficultyBlocks=false); functional tests on regtest where every block and genesis are 0x207fffff, so walking to genesis returns the same value
Plausibility: It reads as 'skip back over min-difficulty blocks', and the height clause looks redundant to someone who does not know that a period's first block can itself be at powLimit.
Equivalence risk: It diverges only when the first block of a period has nBits == powLimit while an earlier block does not, which requires testnet-style chains. On regtest it is equivalent, apart from a longer walk.
Commands and environment reported by the submitter
cmake -B build -DBUILD_GUI=OFF -DWITH_ZMQ=OFF -DENABLE_IPC=OFF && cmake --build build -j$(nproc)ctest --test-dir build -j$(nproc) && build/test/functional/test_runner.py -j$(nproc)mutant-harness operator: condition; location: GetNextWorkRequired lines 33
Imported from mutant-harness 480d7e3.
Commands are shown as text and never executed by MutantHub.
2 comments
Tip: when running the functional tests you can use the -F flag. It will stop the execution as soon as one of the tests fails. Otherwise, it runs all of them and at the end tells if any of them failed.
Thanks! it is pretty useful