wallet: MiniMiner assertion failures when prioritisetransaction creates negative fees #34234

issue dergoegge openend this issue on January 8, 2026
  1. dergoegge commented at 3:23 pm on January 8, 2026: member

    I hit these assertions in a test running on Antithesis.

    Vibe coded functional tests to demonstrate: https://github.com/dergoegge/bitcoin/commit/1ccc289193f79b26c66bb958b459ed6e47ca425f.

    0node0 stderr node/mini_miner.cpp:213 void node::MiniMiner::DeleteAncestorPackage(const std::set<MockEntryMap::iterator, IteratorComparator> &): Assertion `descendant->second.GetModFeesWithAncestors() >= anc->second.GetModifiedFee()' failed.
    1node0 stderr node/mini_miner.cpp:240 void node::MiniMiner::SanityCheck() const: Assertion `std::all_of(m_entries.begin(), m_entries.end(), [](const auto& entry) { return entry->second.GetSizeWithAncestors() >= entry->second.GetTxSize() && entry->second.GetModFeesWithAncestors() >= entry->second.GetModifiedFee();})' failed.
    

    Root Cause

    MiniMiner assumes transaction fees are always non-negative. When prioritisetransaction is used to apply large negative fee deltas, this assumption is violated, causing assertion failures.

    The mempool correctly tracks negative modified fees, but MiniMiner’s internal invariants break down when ancestor fee sums become negative.

    Symptoms

    1. SanityCheck Assertion (mini_miner.cpp)

    0Assert(entry->second.GetModFeesWithAncestors() >= entry->second.GetModifiedFee());
    

    Triggered when: An ancestor has a very negative fee that drags the descendant’s ancestor_fees below its own positive own_fee.

    Example:

    • Parent: fee = -2 BTC (via prioritisetransaction)
    • Child: fee = +141 sats
    • Child’s ancestor_fees = -2 BTC + 141 sats ≈ -2 BTC
    • Assertion: -2 BTC >= +141 sats → FALSE

    2. DeleteAncestorPackage Assertion (mini_miner.cpp)

    0Assert(descendant->second.GetModFeesWithAncestors() >= anc->second.GetModifiedFee());
    

    Triggered when: A child has a negative fee such that its ancestor_fees is less than its parent’s positive fee, while still satisfying SanityCheck.

    Example:

    • Parent: fee = +1410 sats
    • Child: fee = -705 sats (via prioritisetransaction)
    • Child’s ancestor_fees = 1410 + (-705) = +705 sats
    • SanityCheck: 705 >= -705 → TRUE (passes)
    • DeleteAncestorPackage: 705 >= 1410 → FALSE

    Reproduction

    Functional tests: test/functional/wallet_miniminer_sanitycheck.py and test/functional/wallet_miniminer_deleteancestorpackage.py

    Both tests:

    1. Create a parent-child transaction chain in the mempool
    2. Use prioritisetransaction to make fees negative
    3. Trigger MiniMiner via wallet sendtoaddress (which calculates bump fees)
    4. Node crashes on assertion failure
  2. fanquake added the label Wallet on Jan 8, 2026
  3. instagibbs commented at 3:30 pm on January 8, 2026: member
    Think the bug can be hit since the introduction of this code at 59afcc83548ea67a863dac7b75d000bc8f6a7023 ?
  4. fanquake commented at 3:32 pm on January 8, 2026: member
  5. maflcko added the label RPC/REST/ZMQ on Jan 8, 2026
  6. glozow commented at 3:49 pm on January 8, 2026: member

    Asserting descendant->second.GetModFeesWithAncestors() >= anc->second.GetModifiedFee() doesn’t make sense to me; we shouldn’t be doing that. I’ll open a PR.

    Also should we should rewrite this to use txgraph.

  7. glozow commented at 3:59 pm on January 8, 2026: member
    See #34235
  8. fanquake closed this on Jan 9, 2026

  9. pull[bot] referenced this in commit 5c724f3b04 on Jan 9, 2026

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-01-12 12:13 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me