policy: lower the default blockmintxfee, incrementalrelayfee, minrelaytxfee #33106

pull glozow wants to merge 12 commits into bitcoin:master from glozow:2025-07-minrelay changing 21 files +212 −79
  1. glozow commented at 6:06 pm on July 31, 2025: member

    ML post for discussion about the general concept, how this impacts the wider ecosystem, philosophy about minimum feerates, etc: https://delvingbitcoin.org/t/changing-the-minimum-relay-feerate/1886

    This PR is inspired by #13922 and #32959 to lower the minimum relay feerate in response to bitcoin’s exchange rate changes in the last ~10 years. It lowers the default -minrelaytxfee and -incrementalrelayfee, and knocks -blockmintxfee down to the minimum nonzero setting. Also adds some tests for the settings and pulls in #32750.

    The minimum relay feerate is a DoS protection rule, representing a price on the network bandwidth used to relay transactions that have no PoW. While relay nodes don’t all collect fees, the assumption is that if nodes on the network use their resources to relay this transaction, it will reach a miner and the attacker’s money will be spent once it is mined. The incremental relay feerate is similar: it’s used to price the relay of replacement transactions (the additional fees need to cover the new transactions at this feerate) and evicted transactions (following a trim, the new mempool minimum feerate is the package feerate of what was removed + incremental).

    Also note that many nodes on the network have elected to relay/mine lower feerate transactions. Miners (some say up to 85%) are choosing to mine these low feerate transactions instead of leaving block space unfilled, but these blocks have extremely poor compact block reconstruction rates with nodes that rejected or didn’t hear about those transactions earlier.

    While it wouldn’t make sense to loosen DoS restrictions recklessly in response to these events, I think the current price is higher than necessary, and this motivates us changing the default soon. Since the minimum relay feerate defines an amount as too small based on what it costs the attacker, it makes sense to consider BTC’s conversion rate to what resources you can buy in the “real world.”

    Going off of this comment and this comment

    • Let’s say an attacker wants to use/exhaust the network’s bandwidth, and has the choice between renting resources from a commercial provider and getting the network to “spam” itself it by sending unconfirmed transactions. We’d like the latter to be more expensive than the former.
    • The bandwidth for relaying a transaction across the network is roughly its serialized size (plus relay overhead) x number of nodes. A 1000vB transaction is 1000-4000B serialized. With 100k nodes, that’s 0.1-0.4GB
    • If the going rate for ec2 bandwidth is 10c/GB, that’s like 1-4c per kvB of transaction data
    • Then a 1000vB transaction should pay at least 4c
    • $0.04 USD is 40 satoshis at 100k USD/BTC
    • Baking in some margin for changes in USD/BTC conversion rate, number of nodes (and thus bandwidth), and commercial service costs, I think 50-100 satoshis is on the conservative end but in the right ballpark
    • At least 97% of the recent sub-1sat/vB transactions would be accepted with a new threshold of 0.1sat/vB: #33106 (comment)

    List of feerates that are changed and why:

    • min relay feerate: significant conversion rate changes, see above
    • incremental relay feerate: should follow min relay feerate, see above
    • block minimum feerate: shouldn’t be above min relay feerate, otherwise the node accepts transactions it will never mine. I’ve knocked it down to the bare minimum of 1sat/kvB. Now that we no longer have coin age priority (removed in v0.15), I think we can leave it to the CheckFeeRate policy rule to enforce a minimum entry price, and the block assembly code should just fill up the block with whatever it finds in mempool.

    List of feerates that are not changed and why:

    • dust feerate: this feerate cannot be changed as flexibly as the minrelay feerate. A much longer record of low feerate transactions being mined is needed to motivate a decrease there.
    • maxfeerate (RPC, wallet): I think the conversion rate is relevant as well, but out of scope for this PR
    • minimum feerate returned by fee estimator: should be done later. In the past, we’ve excluded new policy defaults from fee estimation until we feel confident they represent miner policy (e.g. #9519). Also, the fee estimator itself doesn’t have support for sub-1sat/vB yet.
    • all wallet feerates (mintxfee, fallbackfee, discardfee, consolidatefeerate, WALLET_INCREMENTAL_RELAY_FEE, etc.): should be done later. Our standard procedure is to do wallet changes at least 1 release after policy changes.
  2. DrahtBot commented at 6:06 pm on July 31, 2025: contributor

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    Code Coverage & Benchmarks

    For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/33106.

    Reviews

    See the guideline for information on the review process.

    If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #32896 (wallet, rpc: add v3 transaction creation and wallet support by ishaanam)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

    LLM Linter (✨ experimental)

    Possible typos and grammar issues:

    • pays -> pay [plural “fees” requires “pay” for subject‐verb agreement]

    No other typographic or grammatical errors impacting comprehension were found.

    drahtbot_id_4_m

  3. glozow force-pushed on Jul 31, 2025
  4. delta1 commented at 6:11 pm on July 31, 2025: none

    Concept ACK

    I have a wip branch where I’ve somewhat fixed a bunch of tests for this too, which I can share if you like @glozow

    I was stuck on MockMempoolMinFee before having to do some dayjob work

    (edit: wip branch: https://github.com/bitcoin/bitcoin/compare/master...delta1:bitcoin:reduce-minrelaytxfee)

  5. DrahtBot added the label CI failed on Jul 31, 2025
  6. DrahtBot commented at 6:12 pm on July 31, 2025: contributor

    🚧 At least one of the CI tasks failed. Task lint: https://github.com/bitcoin/bitcoin/runs/47142796315 LLM reason (✨ experimental): The CI failure is caused by the lint check errors detected by ruff, specifically the Python code style issues that are currently unfixable, leading to lint test failure.

    Try to run the tests locally, according to the documentation. However, a CI failure may still happen due to a number of reasons, for example:

    • Possibly due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.

    • A sanitizer issue, which can only be found by compiling with the sanitizer and running the affected test.

    • An intermittent issue.

    Leave a comment here, if you need help tracking down a confusing failure.

  7. glozow added the label TX fees and policy on Jul 31, 2025
  8. glozow added the label Mining on Jul 31, 2025
  9. glozow commented at 6:54 pm on July 31, 2025: member

    I have a wip branch where I’ve somewhat fixed a bunch of tests for this too, which I can share if you like @glozow

    Thanks. Though I think I already fixed them, unless a fuzzer trips.

  10. benthecarman commented at 7:01 pm on July 31, 2025: contributor
    Concept ACK but I believe that the justification for this being the price went up is weak. This suggests also we may just lower it again in the future if the price goes down. Rather I think this is a good idea because it has become more common that blocks contain these transactions and this change makes bitcoin core’s mempool better reach its goals
  11. glozow commented at 7:03 pm on July 31, 2025: member

    Rather I think this is a good idea because it has become more common that blocks contain these transactions

    See PR description, which includes both reasons. I also don’t think we should blindly lower the relay feerate to what miners seem to accept, as they can choose a price that is below what we determine the reasonable DoS threshold to be.

  12. bitcoin deleted a comment on Jul 31, 2025
  13. Retropex commented at 7:31 pm on July 31, 2025: none
    NACK, without proper spam filters it will mostly be used by harmful spam.
  14. jlopp commented at 8:00 pm on July 31, 2025: contributor
    Concept ACK; this is effectively increasing economic scalability. If we can do so without reopening DoS vectors, that’s a win.
  15. caesrcd commented at 8:04 pm on July 31, 2025: none

    Concept ACK

    The economic incentives are already evident: over 80% of the hashrate is mining transactions below 1 sat/vB, and the number of noderunners relaying transactions with fees below the current standard is increasing every day.

  16. luke-jr commented at 8:30 pm on July 31, 2025: member

    Again, Concept NACK. Everything below 1s/vB is spam. There’s no reason to change the default.

    Over that period the USD price of BTC has risen by roughly 2-3 orders of magnitude,

    It’s the USD that has fallen, Bitcoin has only increased relative to it. Actually, Bitcoin probably hasn’t even kept up its value, so if anything we should be looking to increase the default relay fee, if maintaining the same actual-value cost is the goal.

    The minimum relay feerate is a DoS protection rule, representing a price on the network bandwidth used to relay transactions that have no PoW.

    DoS resistance is clearly insufficient even at 1s/vB.

    While relay nodes don’t all collect fees, the assumption is that if nodes on the network use their resources to relay this transaction, it will reach a miner and the attacker’s money will be spent once it is mined.

    This assumption doesn’t hold without common-sense spam filters, that Core has neglected and begun to remove.

    Contrast this with the dust feerate, which defines a value as “too small” based on Bitcoin’s protocol constraints on transaction volume (e.g. spend script size vs block space). The conversion rate is thus irrelevant or, at the very least, applies very differently in that context.

    No, dust is defined as too small to be worth spending, which is a factor of fee rate to spend it. If the fee rate is 10x lower, the dust limit is logically 10x lower as well.

  17. w0xlt commented at 8:41 pm on July 31, 2025: contributor
    Concept ACK
  18. caesrcd commented at 8:50 pm on July 31, 2025: none

    @luke-jr

    Everything below 1s/vB is spam.

    That’s false. I recently made several consolidation transactions myself, all confirming at <1 sat/vB. Labeling everything below 1 sat/vB as “spam” ignores legitimate use cases like UTXO consolidation, which is actually beneficial for the network.

  19. DrahtBot removed the label CI failed on Jul 31, 2025
  20. aeonBTC commented at 9:20 pm on July 31, 2025: none

    Concept ACK

    In the past month, over 200,000 sub-1 sat/vB transactions were propagated and confirmed, despite a super majority of nodes not relaying them. This again demonstrates that market-driven blockspace demand and economic incentives consistently prevail over relay policies.

  21. RobinLinus commented at 9:34 pm on July 31, 2025: none

    It’s the USD that has fallen, Bitcoin has only increased relative to it.

    Making such a bold claim—especially one that clearly contradicts observable reality—without any justification makes it seem like you’re just trolling. Bitcoin hasn’t only risen against the USD; it has also significantly outperformed gold, stocks, and nearly every other asset over the past decade.

  22. in src/test/mempool_tests.cpp:538 in 7ed561063e outdated
    537-    AddToMempool(pool, entry.Fee(1100LL).FromTx(tx6));
    538-    AddToMempool(pool, entry.Fee(9000LL).FromTx(tx7));
    539+    AddToMempool(pool, entry.Fee(700LL).FromTx(tx4));
    540+    AddToMempool(pool, entry.Fee(100LL).FromTx(tx5));
    541+    AddToMempool(pool, entry.Fee(110LL).FromTx(tx6));
    542+    AddToMempool(pool, entry.Fee(90LL).FromTx(tx7));
    


    fjahr commented at 9:39 pm on July 31, 2025:
    This was reduced by a factor 100 and not by a factor 10 like all the other tests here. Probably a typo?

    glozow commented at 1:55 pm on August 1, 2025:
    Sharp, thanks!
  23. fjahr commented at 9:39 pm on July 31, 2025: contributor
    Concept ACK
  24. in src/policy/policy.h:32 in 18dbc93f41 outdated
    28@@ -29,7 +29,7 @@ static constexpr unsigned int DEFAULT_BLOCK_RESERVED_WEIGHT{8000};
    29  * Setting a lower value is prevented at startup. */
    30 static constexpr unsigned int MINIMUM_BLOCK_RESERVED_WEIGHT{2000};
    31 /** Default for -blockmintxfee, which sets the minimum feerate for a transaction in blocks created by mining code **/
    32-static constexpr unsigned int DEFAULT_BLOCK_MIN_TX_FEE{1000};
    33+static constexpr unsigned int DEFAULT_BLOCK_MIN_TX_FEE{1};
    


    murchandamus commented at 10:43 pm on July 31, 2025:
    I was wondering why you decided to change the DEFAULT_BLOCK_MIN_TX_FEE to 1 s/kvB instead of 100 s/kvB and I couldn’t find a motivation in this commit or the main comment.

    ajtowns commented at 4:50 am on August 1, 2025:
    I think it makes sense (as a default) to always include things in blocks if they’re in your mempool. Otherwise having those txs in your mempool potentially blocks you from seeing txs you would include in your block (eg, double-spends that don’t pass rbf rules), and seems like something of a waste of memory/bandwidth (though not a DoS risk).

    glozow commented at 1:14 pm on August 1, 2025:

    I have this in the OP, but will add to commit message:

    blockmintxfee shouldn’t be above min relay feerate, otherwise the node accepts transactions it will never mine. I’ve knocked it down to the bare minimum of 1sat/kvB. Now that we no longer have coin age priority (removed in v0.15), I think we can leave it to the CheckFeeRate policy rule to enforce a minimum entry price, and the block assembly code should just fill up the block with whatever it finds in mempool.


    glozow commented at 2:12 pm on August 1, 2025:
    added to commit message

    murchandamus commented at 7:54 pm on August 1, 2025:
    Thanks for the clarification and adding it to the commit message.

    instagibbs commented at 1:05 pm on August 14, 2025:
    why wouldn’t we set this to zero if that’s the motivation?

    glozow commented at 1:26 pm on August 14, 2025:
    Miners gain nothing from a 0 fee transaction (arguably in a reorg it makes sense to re-confirm what was once confirmed, but otherwise it’s not beneficial). Since there are still cases where a 0 fee tx can exist in mempool, leaving this check makes sense to me. We can of course replace it with a non-configurable condition that checks specifically for <=0 (which I proposed in #27018) but I don’t want to open a conversation about removing the option again.

    darosior commented at 1:31 pm on August 14, 2025:
    Not only the miner gains nothing, but it also cost them. More data to propagate (which might be the whole transaction since it’s below minrelaytxfee). I agree with @instagibbs that saying this cost to propagate is not worth it at 0 but becomes worth it at 0.001sat/vb is not clear to me (and changing it is a separate discussion) but i don’t think it should hold up this PR.

    sipa commented at 1:41 pm on August 14, 2025:

    FWIW, the value 1 makes sense here to me.

    There is some non-zero threshold value miners have to include a transaction (even if just the cost of bandwidth to put it in all compact block announcements they send out), even ignoring(*) the cost of the block propagating slightly slower/with more roundtrips and thus reducing the probability the block ends up in the accepted chain. That threshold value is probably closer to 0.000000001 of a satoshi per kvB than 1 satoshi per kvB, but it’s not zero. So it makes sense to use the smallest value we can represent (… except with the FeeFrac-based feerates, we can technically represent much smaller rations now…?).

    If there are actual zero or negative feerate transaction ancestor sets (or post-clustermempool, chunks) in the mempool, it makes sense they’re not included in blocks, and hopefully they get evicted or expired if not bumped. But anything marginally over that probably makes sense to include.

    This change doesn’t technically need to be part of this PR, I think. If anything, it’s overdue and should probably have been made independently somewhere the past 8 years, after the deployment of compact blocks. But this PR does run into the issue of having to change the value, and thus it makes sense to set it to the most sensible value.

    (*) To account for the cost of worse block propagation when including transactions, and the resulting reduction is probability the block is accepted, it’s probably better to model it based on time. Keep track of when a transaction was first learned about, and if that’s too close to the time a block is constructed, exclude it from templates unless its fee is high enough (for some configurable fee value).


    instagibbs commented at 1:46 pm on August 14, 2025:

    darosior commented at 1:47 pm on August 14, 2025:
    How do compact blocks matter for a transaction that is below the min feerate (and that 99% of nodes on the network wouldn’t have in their mempool)?

    sipa commented at 1:54 pm on August 14, 2025:

    Our mempool is our node’s best approximation for what we believe the network knows. If there are reasons to believe 99% of nodes don’t have a particular transaction, it shouldn’t be in our mempool in the first place. This isn’t perfect of course, but I think that’s a separate concern.

    For transactions in our mempool, which we expect the network at large to know about, I think including everything with a nonzero fee makes sense. And compact blocks did change this: before that, the propagation delay of blocks had a term that scaled linearly with the size of included transactions.

    Interesting to see the discussion of potential issues with it in #27018. I believe those will generally be addressed by cluster mempool, so if we’re worried maybe the drop to 1 (or near-zero) could be postponed until then?


    darosior commented at 1:57 pm on August 14, 2025:

    it shouldn’t be in our mempool in the first place. […] transactions in our mempool, which we expect the network at large to know about

    That’s my point. We used to not transmit transactions below what we expect other nodes’ mempools would accept. Starting with this PR we are going to.


    sipa commented at 2:01 pm on August 14, 2025:
    They got into our mempool, why wouldn’t they have gotten into others’ mempools?

    darosior commented at 2:15 pm on August 14, 2025:

    There are situations in which you may end up with sub-minrelaytxfee transactions in your mempool. In this situation, maybe so do other nodes on the network, or maybe they don’t. But building your block with the -minrelaytxfee value is building it with what you expect them to accept. Building it with -minrelaytxfee / 100 is not that, unless you assume that if you have a sub-minrelaytxfee transaction in your mempool then all other nodes also have it. But it’s not clear to me why you would make that assumption.

    In any case, situations in which you do end up with sub-minrelaytxfee transactions in your mempool are so rare that i don’t think it would matter much in practice, and although i wish the two changes had not been bundled initially i think it’s better to not send this PR for another review cycle and further delay the restoration of compact blocks on the network.


    glozow commented at 6:29 pm on August 14, 2025:

    Interesting to see the discussion of potential issues with it in #27018. I believe those will generally be addressed by cluster mempool, so if we’re worried maybe the drop to 1 (or near-zero) could be postponed until then?

    Postponing isn’t necessary - the PR we merged instead of #27018 was #26933. So we shouldn’t have these kinds of transactions unless they came in through a reorg, as only TRUC is allowed to be under minrelaytxfee.

  25. in src/test/util/setup_common.cpp:592 in 2872cb0bf5 outdated
    582@@ -582,10 +583,11 @@ void TestChain100Setup::MockMempoolMinFee(const CFeeRate& target_feerate)
    583     assert(target_feerate > m_node.mempool->m_opts.min_relay_feerate);
    584 
    585     // Manually create an invalid transaction. Manually set the fee in the CTxMemPoolEntry to
    586-    // achieve the exact target feerate.
    587+    // achieve the exact target feerate. The size needs to be large enough to avoid rounding problems in CFeeRate.
    588     CMutableTransaction mtx = CMutableTransaction();
    589     mtx.vin.emplace_back(COutPoint{Txid::FromUint256(m_rng.rand256()), 0});
    590     mtx.vout.emplace_back(1 * COIN, GetScriptForDestination(WitnessV0ScriptHash(CScript() << OP_TRUE)));
    591+    BulkTransaction(mtx, 4000);
    


    murchandamus commented at 10:49 pm on July 31, 2025:
    What sort of rounding problem were you seeing here? I thought that Fee Frac would essentially get rid of rounding issues.

    glozow commented at 1:40 pm on August 1, 2025:

    CFeeRate is a fraction. Prior to using FeeFrac, the denominator was always 1000. Now, it’s whatever you want it to be (in this case, the tx size). It still has to round, as it needs to output an integer number of satoshis when evaluating the fee for a given size.

    The problem is that we weren’t rounding before when the fee was always a multiple of the size. Now we have to round; we want the number of satoshis for a size at 0.1sat/vB. I made all the numbers play nice by making the denominator 4000. Another way to fix this is to remove the assertion at the end of the function, but I preferred to keep it since it clearly helped catch this problem.


    glozow commented at 2:12 pm on August 1, 2025:
    I’ve elaborated in the comments on both BulkTransactions
  26. in doc/release-notes-minrelay.md:13 in 7ed561063e outdated
     8+The default minimum relay feerate (`-minrelaytxfee`) and incremental relay feerate (`-incrementalrelayfee`) have been
     9+changed to 100 satoshis per kvB. They can still be changed using their respective configuration options, but it is
    10+recommended to change both together if you decide to do so.
    11+
    12+Other minimum feerates (e.g. the dust feerate, the minimum returned by the fee estimator, and all feerates used by the
    13+wallet) remain unchanged. The mempool minimum feerate still changes in response to high volume.
    


    murchandamus commented at 10:57 pm on July 31, 2025:
    At what precision does the minimum mempool feerate change? Could it be problematic if the mempool overflowed with transactions below 1 s/vB?

    ajtowns commented at 4:44 am on August 1, 2025:

    The precision when setting is sats/kvB so 0.001 sat/vb, since it’s just set to the highest fee rate of any tx evicted via TrimToSize().

    https://github.com/bitcoin/bitcoin/blob/8712e074bb54eea9ccc4d2009d2150279c9d17ec/src/txmempool.cpp#L1132-L1138


    glozow commented at 2:12 pm on August 1, 2025:
    I’ve added a few words about it changing more gradually now that incremental relay feerate is lower
  27. murchandamus commented at 11:01 pm on July 31, 2025: contributor
    Concept ACK
  28. rot13maxi commented at 0:17 am on August 1, 2025: none

    Contrast this with the dust feerate, which defines a value as “too small” based on Bitcoin’s protocol constraints on transaction volume (e.g. spend script size vs block space). The conversion rate is thus irrelevant or, at the very least, applies very differently in that context.

    No, dust is defined as too small to be worth spending, which is a factor of fee rate to spend it. If the fee rate is 10x lower, the dust limit is logically 10x lower as well.

    Dropping the dust threshold by 10x is an interesting proposal. I am unsure if it should be in this PR or another one. Reason to be in another one would be to keep this PR just about fees and not price sensitive relay policy generally. Great suggestion though!

  29. 1ma commented at 1:34 am on August 1, 2025: none

    NACK. I think the direction of this change makes sense in isolation, but interacts too badly with other default mempool policy choices.

    Users will always prefer to transact at the lowest fee given the current demand for tx confirmation, and miners will always prefer to earn more fees by filling blocks with extremely cheap txs rather than mine half empty blocks. Both these market forces are set to intensify over the long run as fiat inflation goes on and Bitcoin’s block subsidy runs out. I don’t even believe it’s correct to think of tx fees as an anti-DoS protection: these forces virtually guarantee that in the long run miners will accept any tx paying at least 1 sat in total when they have nothing else to mine. Besides, if one was to argue that the minimum feerate acts as an anti-DoS mechanism like the OP does it would be contradictory to then propose cutting it down by a factor of 10x in the same breath.

    Additionally the 1000 s/kvB floor arbitrarily discourages several kinds of large and beneficial txs when demand is low: super cheap coinjoins and super cheap dust consolidations (the latter is gaining importance because since Taproot activated the UTXO set tripled in size and 50% of outputs hold dust).

    The problem then, obviously, is Core’s lack of mempool policies to keep datacarrying txs under control (a real DoS vector), which will even get worse once #32406 is released with Core v30. A cursory look at the mempool is enough to see that these parasitic txs are displacing coinjoins/consolidations and enjoying the low fee rates, even stalling the downwards trend on UTXO size that started in early 2025 (see above link).


    Beyond the issue of this particular policy this episode laid bare once again the critical state of Bitcoin mining centralization. I find it baffling that a maintainer would conflate “Bitcoin miner” with “pool operator” by writing something as misleading as “Miners (some say up to 85%) are choosing to mine these low feerate transactions” when 99% of hashrate is still held captive by Stratum v1.

    Today 5 or 6 pool operators just decided to ignore a hare-brained policy that never made sense in the first place, but tomorrow this handful of publicly-known operators might enforce a blacklist under duress. The prospect that so few mining nodes materially define what gets mined or not is sobering and scary.

    The silver lining is that as I write this miners of any size (even BitAxe miners) can mine with their own nodes and set their own mining policies while still pooling their hashrate, and there are already 192 mainnet blocks mined in such a fashion. I encourage the wider dev community to treat the mining centralization problem with the seriousness it deserves and help evangelizing the solutions that are already available and proven to work.

  30. ajtowns commented at 5:52 am on August 1, 2025: contributor

    Contrast this with the dust feerate, which defines a value as “too small” based on Bitcoin’s protocol constraints on transaction volume (e.g. spend script size vs block space). The conversion rate is thus irrelevant or, at the very least, applies very differently in that context.

    No, dust is defined as too small to be worth spending,

    Luke is correct here that was the original rationale for the dust setting, and remains the rationale documented in the comments in policy.cpp. I don’t think that should be treated as the sole rationale, however; two other factors that should be considered are tht it’s also an effective (in practice) limit on utxo size growth, and that dust limits need to be taken into account by second layer protocols that build on Bitcoin, so we don’t have the same option to try decreasing them and then, if it turns out to be a bad idea, to re-increase them.

    Even if it “worth the fees it will take to spend” were the only rationale, that’s still a good reason to have the dust rate set at a multiple of the minimum fee rate – both to take into account that there should be some value retained after fees, and to take into account the fact that sometimes the required fee rate will be above the default minimum fee rate. At this point, I would say that until we’ve had a few years demonstrating that 0.3sat/vb is enough to get your tx mined consistently, we shouldn’t even lower the dust limit to a level equivalent to 1sat/vb (vs the current 3sat/vb), let alone anything lower than that.

    The cost to relay a transaction across the network is roughly its serialized size (plus relay overhead) x number of nodes.

    I think of that more as “this cost to spam every node in the network with 1kB of data each if you did it by spinning up an ec2 node is [$x / GB] * [1e-6] * [number of nodes]”. The cost to do the same thing by relaying a valid 0.25kvB transaction and having nodes in the p2p network do the actual data transfer for you should be in the same range (preferably somewhat higher). With $120k/BTC and 90k p2p nodes, “the same” comes to 0.033sat/vb. The p2p nodes here aren’t being paid for their costs – miners collect the fees obviously, not p2p nodes – you’re just avoiding the p2p network providing attackers with a cheaper way of DoSing itself than commercial providers.

    With mempool size limits enforced by a dynamic minimum fee, I don’t think we need anything more than that to keep the network functional as far as tx relay goes.

    Concept ACK but I believe that the justification for this being the price went up is weak. This suggests also we may just lower it again in the future if the price goes down.

    As far as I can see, using ec2 hosts to spam the network would be cheaper than sending 0.1sat/vb tx, even given a 67% reduction in the BTC price (to $40k/BTC), or a 3x increase in the number of nodes on the network (to ~270k). If we had more than either of those, I think this setting should be revisited. (OTOH, if the price goes up by 3x and the number of nodes goes up by 3x, and commercial bandwidth prices stay roughly the same, that’s a wash and there’s no need to revisit it)

    I don’t think giving a 90% fee discount to low value inscriptions is in miners’ interests, and I don’t think block relay efficiency concerns should be used to either remove policy options from node operators or (more importantly) miners or as the primary controlling factor in determining policy defaults. I think there are better ways to address those concerns, though, and I’m satisfied that bandwidth DoS should be the main factor influencing the default minimum fee for tx relay.

    Approach ACK.

  31. ArmchairCryptologist commented at 10:32 am on August 1, 2025: none

    Concept ACK, FWIW. The long and short of it is that mempool/relay (fee) policy is ineffective as a “spam filter”, since the node will have to accept the transaction anyway if it is mined, so trying to use it as such is futile. Meanwhile, rejecting transactions that are being mined is detrimental to block propagation in particular and overall node/network performance in general. As such, changing the defaults to match what is actually being mined is warranted as long as the DoS resistance is not compromised, and I believe @ajtowns and others have provided convincing arguments that it is not.

    Today 5 or 6 pool operators just decided to ignore a hare-brained policy that never made sense in the first place, but tomorrow this handful of publicly-known operators might enforce a blacklist under duress. The prospect that so few mining nodes materially define what gets mined or not is sobering and scary.

    You’re not wrong as such about miner centralization, but this is the opposite problem. The default mempool relay fee policy is preventing transactions that are being mined from being relayed by (most) nodes. Your hypothetical situation is where transactions are being relayed, but not mined by (some) miners. Presumably, if mining pools start censoring higher-paying transactions due to regulatory blacklisting, miners will migrate to pools in other jurisdictions that ignore them, if for no other reason than that they will be missing out on fees if they don’t. Either way, I don’t think this is really on-topic for this PR.

  32. glozow marked this as ready for review on Aug 1, 2025
  33. glozow renamed this:
    [WIP] policy: lower the default blockmintxfee, incrementalrelayfee, minrelaytxfee
    policy: lower the default blockmintxfee, incrementalrelayfee, minrelaytxfee
    on Aug 1, 2025
  34. glozow force-pushed on Aug 1, 2025
  35. glozow commented at 2:27 pm on August 1, 2025: member

    dust limits need to be taken into account by second layer protocols that build on Bitcoin, so we don’t have the same option to try decreasing them and then, if it turns out to be a bad idea, to re-increase them.

    I’ve edited the OP to include this as the main reason for marking dust feerate out of scope in this PR.

    avoiding the p2p network providing attackers with a cheaper way of DoSing itself than commercial providers.

    Also reworded the pricing section with this framing.

  36. darosior commented at 3:23 pm on August 1, 2025: member

    A substantial portion of Bitcoin transactors and miners are already using the value which this PR sets as default. Over the past month we’ve seen a non-trivial amount of sub-1sat/vb transactions. As an illustration of this, in the past day of block more than 23% of transactions (!!) used a feerate lower than 1sat/vb. That one quarter of the average block’s content is not being relayed by default Bitcoin Core has a significant impact on block propagation, due to the additional time spent validating block transactions and additional roundtrips in block reconstruction. In fact if this trend continues i expect block propagation time will already be significantly impacted until the network adopts Bitcoin Core 30.0.

    You can list all sub-1sat/vb transactions in the past day of blocks on your own Bitcoin Core node with the following jq-fu:

    0bc="bitcoin-cli -datadir=your_datadir"; end=908109; start=$(($end - 144)); for h in $(seq $start $end); do hash="$($bc getblockhash $h)"; $bc getblock $hash 2 |jq '.tx[] | select(.fee != null) | {feerate: (.fee / .vsize * 1e8 * 100 | round | ./100), txid} | select(.feerate < 1) | "Transaction \(.txid) has feerate \(.feerate) sat/vb"'; done
    

    To count the number of such transactions, just pipe it into wc -l. Running this on a local node i get:

    0$ bc="bitcoin-cli -datadir=your_datadir"; end=908109; start=$(($end - 144)); for h in $(seq $start $end); do hash="$($bc getblockhash $h)"; $bc getblock $hash 2 |jq '.tx[] | select(.fee != null) | {feerate: (.fee / .vsize * 1e8 * 100 | round | ./100), txid} | select(.feerate < 1) | "Transaction \(.txid) has feerate \(.feerate) sat/vb"'; done |wc -l
    193302
    

    You can get the total number of transactions over this period by summing the nTx returned by getblock for all these blocks, or if you are lazy just drop the select(.feerate < 1) in the above command. Here i get:

    0$ bc="bitcoin-cli -datadir=your_datadir"; end=908109; start=$(($end - 144)); for h in $(seq $start $end); do hash="$($bc getblockhash $h)"; $bc getblock $hash 2 |jq '.tx[] | select(.fee != null) | {feerate: (.fee / .vsize * 1e8 * 100 | round | ./100), txid} | "Transaction \(.txid) has feerate \(.feerate) sat/vb"'; done |wc -l
    1401684
    

    Therefore 93302 / 401684 * 100 = 23.23 % of transactions in the past day of block used a feerate lower than 1sat/vb.

    Lowering the default minimum feerate does not, as detailed above, introduce a DoS vector and it allows Bitcoin Core’s relay policy to match a sizeable share of Bitcoin’s usage. Obvious Concept ACK.

  37. glozow force-pushed on Aug 1, 2025
  38. glozow commented at 3:29 pm on August 1, 2025: member
    Rebased for #31385
  39. caesrcd commented at 6:23 pm on August 1, 2025: none

    tACK a43e1b28b2899e1707e7867fd46efe9fcc08f241

    Compiled and tested on Arch Linux (kernel 6.15.8, gcc 15.1.1, clang 20.1.8). Build performed with CMake in Debug mode:

    0cmake -B build_test -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang
    1cmake --build build_test -j$(nproc)
    2ctest --output-on-failure --stop-on-failure --test-dir build_test -j$(nproc)
    3build_test/test/functional/test_runner.py
    

    All unit and functional tests passed successfully.

  40. DrahtBot requested review from darosior on Aug 1, 2025
  41. DrahtBot requested review from murchandamus on Aug 1, 2025
  42. DrahtBot requested review from fjahr on Aug 1, 2025
  43. DrahtBot requested review from ajtowns on Aug 1, 2025
  44. SomberNight referenced this in commit 63db1e78c2 on Aug 1, 2025
  45. SomberNight referenced this in commit f6ff834648 on Aug 1, 2025
  46. SomberNight referenced this in commit d8a6ed9b55 on Aug 1, 2025
  47. SomberNight referenced this in commit 58af1c493d on Aug 1, 2025
  48. petertodd commented at 1:42 pm on August 2, 2025: contributor

    Concept ACK @benthecarman

    but I believe that the justification for this being the price went up is weak. This suggests also we may just lower it again in the future if the price goes down.

    We should consider increasing these defaults if the price goes down significantly! E.g. if miners consistently set higher defaults and we’re relying transactions that aren’t getting mined. Equally, we may want to lower these defaults yet again in the future.

  49. chrisguida commented at 4:58 pm on August 2, 2025: none

    This seems like it will only lead to more utxoset bloat.

    Aren’t most of the transactions being confirmed now with <1sat/vB fees just inscriptions and other spam?

  50. caesrcd commented at 6:08 pm on August 2, 2025: none

    @chrisguida

    Aren’t most of the transactions being confirmed now with <1sat/vB fees just inscriptions and other spam?

    Inscriptions do not rely on wallets to create transactions with <1sat/vB fees. They are typically constructed and broadcasted by custom tools, independent of standard wallet behavior.

    The fact that many low‑fee transactions are inscriptions does not imply that regular users are not interested in paying lower fees.

  51. chrisguida commented at 6:51 pm on August 2, 2025: none

    @caesrcd you’re missing the point. The point is that inscriptions have proven extremely destructive to the utxoset, and lowering the minimum fee rate makes bloating the utxoset even easier than it was before.

    Of course everyone is interested in paying lower fees; the point is that the minimum was set how it is in order to prevent relay spam and utxoset bloat.

    If the majority of demand for such transactions were, for example, consolidations, then it would be a different story. But the fact is that this feature is mostly only popular with spammers.

  52. KurtisStirling commented at 7:42 pm on August 2, 2025: none

    Concept NACK

    No one asked for this; 1 sat isn’t even 1 cent yet. This is a non-starter because it’s non-issue.

    Fix utxo set bloat instead of fiddling with things you don’t need to please.

  53. caesrcd commented at 7:47 pm on August 2, 2025: none

    The scope of this PR does not concern the impact on the UTXO set, nor the question of whether inscriptions are destructive.

    The fact is that the number of nodes choosing to relay lower‑fee transactions continues to grow, and currently more than 80% of the network hashrate is mining sub‑1sat/vB transactions. This shows that the mechanism already works in practice and that user interest in taking advantage of lower fees is likely to increase.

    However, not all users know how to create transactions manually without relying on wallets, or how to modify the source code of their preferred wallet to enable this functionality.

    Restricting this feature merely because inscriptions already benefit from it would ultimately prevent regular users from also taking advantage of low‑demand periods to pay lower fees.

  54. stickies-v commented at 3:31 pm on August 4, 2025: contributor

    Concept ACK

    Protecting against DoS / bandwidth usage should be the primary concern of this policy. The calculations and rationales presented in this thread, mostly by @ajtowns (e.g. here, here) make sense to me, and a 10x decrease seems like it’s in the right ballpark.

    Even though I don’t think this policy should be used to artificially support miner revenue, I was initially concerned that miners might not be sufficiently incentivized to adopt this lower minimum feerate, thus creating a gap between a node’s mempool and what would actually get mined. Looking at recently mined blocks across pools it seems that’s not happening, so I don’t see any reason anymore not to go ahead with this change.

    Will follow-up with a code review soon.

  55. jsarenik commented at 4:02 pm on August 4, 2025: none
    Approach ACK
  56. jsarenik commented at 4:37 pm on August 4, 2025: none
    Looks good to me. Thanks!
  57. 0xB10C commented at 3:12 pm on August 5, 2025: contributor

    I’ve published a chart showing the share of sub 1 sat/vByte transactions on mainnet.observer/charts/fees-sub-1-sat-vbyte-transactions.

    I also generated a recent export of my stats on compact block reconstructions over the last two months:

    Since #32582 we also log the number of bytes that we requested to reconstruct the block. I plot the average kB requested per block in the following new chart. In June, we were requesting less than 10 kB worth of transactions per block on average for about 40-50% of blocks. Now, we are requesting close to 0.8 MB worth of transactions per block on average for about 70% of blocks.

  58. darosior commented at 3:19 pm on August 5, 2025: member

    In June, we were requesting less than 10 kB worth of transactions per block on average. Now, we are requesting close to 0.8 MB worth of transactions per block on average.

    Crazy. Although this is a significant diff we might want to backport this PR at least to 29.

  59. glozow commented at 6:43 pm on August 5, 2025: member

    Yeah compact block reconstruction looks horrendous. Thanks @0xB10C!

    I looked a little bit at the distribution of sub-1sat/vB feerates to try make sure we’re capturing the ones that are causing poor reconstruction. If a significant portion of transactions are even lower, I think we could lower this to 50sat/kvB while staying solidly within the DoS budget, but it doesn’t seem necessary.

    This is individual feerate from the last day’s worth of blocks, showing 97% of them are >= 0.1sat/vB.

    In the 3% of transactions that are below 0.1sat/vB, virtually all of them are 0-0.01sat/vB. My hunch is that these are often exactly 0 fee TRUC packages or miners’ own transactions. I’m only seeing a small handful of transactions that are below 0.1sat/vB but not 0-0.01. In any case, I don’t think we should go as low as 0.01sat/vB.

    0bc="bitcoin-cli -datadir=abc"; end=908760; start=$(($end - 144)); for h in $(seq $start $end); do hash="$($bc getblockhash $h)"; $bc getblock $hash 2 |jq '.tx[] | select(.fee != null) | {feerate: (.fee / .vsize * 1e8 * 1000 | round | ./1000), txid} | select(.feerate < 1) | "Transaction \(.txid) has feerate \(.feerate) sat/vb"'; done > lowfeetxs.txt
    
     0Overall Fee Rate Statistics (sat/vB):
     1----------------------------------------
     2count          : 99122.0000
     3mean           : 0.3538
     4std            : 0.1819
     5min            : 0.0000
     625%            : 0.1530
     750%            : 0.4030
     875%            : 0.4520
     9max            : 0.9990
    10
    11Detailed Fee Rate Distribution:
    12----------------------------------------
    130.00 - 0.01 sat/vB:  2,979 txs (  3.0%)
    140.01 - 0.05 sat/vB:     16 txs (  0.0%)
    150.05 - 0.10 sat/vB:     23 txs (  0.0%)
    160.10 - 0.20 sat/vB: 26,387 txs ( 26.6%)
    170.20 - 0.30 sat/vB:  4,531 txs (  4.6%)
    180.30 - 0.40 sat/vB:  7,352 txs (  7.4%)
    190.40 - 0.50 sat/vB: 38,554 txs ( 38.9%)
    200.50 - 1.00 sat/vB: 19,280 txs ( 19.5%)
    21
    22Whether 0.1sat/vB is a good threshold:
    23------------------------------------------
    24Zero fee transactions: 2,979
    250 < feerate < 0.1: 39
    260.1 ≤ feerate < 1.0: 96,104
    
  60. shocknet-justin commented at 7:00 pm on August 5, 2025: none

    But the fact is that this feature is mostly only popular with spammers.

    Important context is the prevalence of scaling scammers, fake L2’s etc that claim to be “scaling ownership” by way of centralized transaction pooling to lower the cost-per-transaction.

    Word games around “unlilateral exit” are the basis of these scams, these fake L2’s target users who literally cannot afford a unilateral exit and so end up in these centralized schemes in the first place. By lowering the actual cost of transacting it expands the number of users that can afford to unilaterally transact and avoid these fake L2’s altogether. It should be noted that these centralized fake L2’s are also fantastic tools for surveillance and censorship.

    If a concern with this PR is further enabling bad actors, it should be noted that it more importantly undermines the business models of other bad actors and is itself the final scaling frontier for sovereign ownership.

  61. in test/functional/mining_basic.py:177 in 2778e46916 outdated
    176+            # Unless blockmintxfee is 0, the template shouldn't contain free transactions.
    177+            # Note that the real block assembler uses package feerates, but we didn't create dependent transactions so it's ok to use base feerate.
    178+            if blockmintxfee_btc_kvb > 0:
    179+                for txid in block_template_txids:
    180+                    tx = node.getmempoolentry(txid)
    181+                    assert_greater_than(tx['fees']['base'], 0)
    


    ismaelsadeeq commented at 9:42 am on August 7, 2025:

    In “[test] check miner doesn’t select 0fee transactions” 2778e4691664c55d5699bca2b68f15796e8a9a75

    Maybe we should test both ways.

    1. When the blockmintxfee_btc_kvb is > 0 we verify that we never create 0 or less fee tx.
    2. When a 0 or less fee tx is created it is because of blockmintxfee_btc_kvb being 0.
    0            for txid in block_template_txids:
    1                for txid in block_template_txids:
    2                    fee = node.getmempoolentry(txid)['fees']['base']
    3                    if blockmintxfee_btc_kvb > 0:
    4                        assert_greater_than(fee, 0)
    5                    if fee <= 0:
    6                        assert_equal(blockmintxfee_sat_kvb, 0)
    

    glozow commented at 2:15 pm on August 8, 2025:
    These two assertions are logically equivalent, so we don’t need to test both.
  62. in test/functional/mempool_truc.py:600 in d40de30fdf outdated
    595@@ -595,12 +596,57 @@ def test_reorg_sibling_eviction_1p2c(self):
    596         )
    597         self.check_mempool([tx_with_multi_children["txid"], tx_with_sibling3_rbf["txid"], tx_with_sibling2["txid"]])
    598 
    599+    @cleanup(extra_args=None)
    600+    def test_minrelay_in_package_combos(self):
    


    ismaelsadeeq commented at 10:41 am on August 7, 2025:

    In “[test] check bypass of minrelay for various minrelaytxfee settings” d40de30fdf0470a4ec6712aef4d5e8ed40e5ea91

    We dont need the cleanup here I think

    0    def test_minrelay_in_package_combos(self)
    

    glozow commented at 2:15 pm on August 8, 2025:
    I think that’s only because there happens to be no subtests after this one. I prefer to clean up after ourselves and save potential headache for a future person adding more tests to this file.
  63. in doc/release-notes-minrelay.md:4 in a43e1b28b2 outdated
    0@@ -0,0 +1,17 @@
    1+Mining and Transaction Relay Policy
    2+=========================
    3+
    4+The minimum block feerate (`-blockmintxfee`) has been changed to 1 satoshi per kvB. It can still be changed using the
    


    ismaelsadeeq commented at 2:27 pm on August 7, 2025:
    I think blockmintxfee should be deprecated and eventually be removed. What should be in the block template should just depend on the mempool transactions

    glozow commented at 2:13 pm on August 8, 2025:
    I think we would want to keep it if people are using it, though I agree minrelaytxfee is the more appropriate dial. Still, the mempool may contain lower feerate transactions from reorgs (the miner should definitely try to reconfirm those) or from packages+replacements (I think excluding things of zero fee is appropriate).

    gmaxwell commented at 2:54 am on August 9, 2025:

    It can be useful to have two knobs to handle transitions. E.g. to make relay less restrictive relay should change first and then mining. To make relay more restrictive mining should change first then relay. In the steady state they need to be the same.

    Of course if miners are going to make uncoordinated changes and/or bitcoin core is going to take forever before making changes then that coordination can’t happen. But having the parameters split out that way is still the right way to structure the software, I think.


    darosior commented at 4:02 pm on August 11, 2025:

    E.g. to make relay less restrictive relay should change first and then mining.

    Then you would accept transactions in your mempool that you would never mine? It seems to me that those defaults should rather be adjusted in tandem.

    But having the parameters split out that way is still the right way to structure the software, I think.

    I think that makes sense because a miner’s marginal cost to include (and get propagated) more transactions in its blocks may be lower than that of unconfirmed transactions it is willing to relay. But i also think Gloria is right that there is seldom a situation in which -blockmintxfee would be the binding constraint, most of the time -minrelaytxfee will be what limits the fee of transactions that are included in blocks.


    ajtowns commented at 8:38 pm on August 11, 2025:

    E.g. to make relay less restrictive relay should change first and then mining.

    Then you would accept transactions in your mempool that you would never mine? It seems to me that those defaults should rather be adjusted in tandem.

    If the network is transitioning to accept transactions that previously wouldn’t have been accepted, then it’s important that a good number of public nodes will accept those transactions for relay, prior to them being included in blocks. Otherwise:

    • compact block relay becomes worse because the txs aren’t widely available for reconstruction
    • user experience becomes worse, because txs don’t get reliably relayed to the miners that accept them
    • privacy becomes worse, because users need to send their transactions to well known nodes rather than being able to be lost in the crowd of the p2p network

    Doing a phased enablement is one way to avoid those drawbacks: ie, distribute software that relays the txs first, before miners start mining them, and leave wallet support until later still. Doing a coordinated enablement is (IMO) another: ie, distribute software that turns on both relay and mining (and perhaps wallet enablement as well) on a set date that is some months after the software is widely released. The latter’s what we do for soft forks, and it seems to work well, though that’s obviously helped by there not being much value to anyone producing txs early in that case, even if they were to be relayed.

    I don’t think phased enablement will be very effective in the current environment: memecoiners seem to be eager to use any new feature in order to differentiate their new schemes from past ones, and miners seem to be racing to include even marginally valuable txs in blocks, so I don’t think attempts to separate mining and relay support are likely to work.

  64. ismaelsadeeq approved
  65. ismaelsadeeq commented at 3:17 pm on August 7, 2025: member

    Code review ACK a43e1b28b2899e1707e7867fd46efe9fcc08f241

    Although it’s worth mentioning that there is a scenario were the price could crash to the point where the default min relay allows for free relay or atleast close to free, but a counter argument to that is that even current default is susceptible to such scenario.

    minimum feerate returned by fee estimator: should be done later. In the past, we’ve excluded new policy defaults from fee estimation until we feel confident they represent miner policy (e.g. #9519). Also, the fee estimator itself doesn’t have support for sub-1sat/vB yet.

    For this I think it can be in same release because enabling this is trivial we just update the minimum fee rate bucket to the default minimum relay fee rate see https://github.com/ismaelsadeeq/bitcoin/commit/c53afeee50e4921bf43305e1325bfb16b60a2970

    I’ve tested the commit on top of this and the estimator provided the sub 1s/vb fee rate estimate.

  66. DrahtBot requested review from stickies-v on Aug 7, 2025
  67. DrahtBot requested review from jsarenik on Aug 7, 2025
  68. achow101 added this to the milestone 30.0 on Aug 7, 2025
  69. in doc/release-notes-minrelay.md:16 in a43e1b28b2 outdated
    11+
    12+Other minimum feerates (e.g. the dust feerate, the minimum returned by the fee estimator, and all feerates used by the
    13+wallet) remain unchanged. The mempool minimum feerate still changes in response to high volume but more gradually, as a
    14+result of the change to the incremental relay feerate.
    15+
    16+Note that unless these lower defaults are widely adopted across the network, transactions created with lower fee rates
    


    murchandamus commented at 6:39 pm on August 7, 2025:

    Maybe add a usage hint along the lines of:

    To send transactions with feerates below the prior minimum, configure your node’s mintxfee configuration option to a lower value.


    glozow commented at 12:59 pm on August 11, 2025:
    Sure, will add to a followup or if I retouch

    glozow commented at 5:42 pm on August 11, 2025:
    Done
  70. sipa commented at 3:42 pm on August 8, 2025: member
    Approach ACK
  71. storopoli commented at 6:05 pm on August 8, 2025: none

    Concept ACK.

    I am glad to see price impacting development in a sober tone without losing rigor on the technical side. I’m convinced that 0.1 sat/vB at ~100K/USD provides ample DoS “economic” protection.

    I’m also glad that, contrary to what I initially thought, reducing to 0.1 sats/vB does not incur on floating point ops and everything will be integer operations.

  72. in test/functional/mempool_limit.py:95 in b73be4b32a outdated
    91@@ -92,8 +92,8 @@ def test_mid_package_eviction_success(self):
    92         assert_equal(node.getrawmempool(), [])
    93 
    94         # Restarting the node resets mempool minimum feerate
    95-        assert_equal(node.getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000'))
    96-        assert_equal(node.getmempoolinfo()['mempoolminfee'], Decimal('0.00001000'))
    97+        assert_equal(node.getmempoolinfo()['minrelaytxfee'], Decimal('0.00000100'))
    


    murchandamus commented at 0:37 am on August 9, 2025:

    If you need to update, perhaps you could also use the approach with

    FEERATE_100SAT_KVB = Decimal("0.00000100")

    in test/functional/mempool_limit.py.

  73. murchandamus commented at 0:44 am on August 9, 2025: contributor
    ACK a43e1b28b2899e1707e7867fd46efe9fcc08f241
  74. DrahtBot requested review from sipa on Aug 9, 2025
  75. gmaxwell commented at 2:55 am on August 9, 2025: contributor
    ACK FWIW, I’ve been running with these adjusted rates for the last week or so after I noticed reconstruction cratering. It’s better with them fixed but still pretty bad because although I’m not rejecting the transactions they are still frequently not making it to me.
  76. 00w1 commented at 0:43 am on August 10, 2025: none

    NACK

    While it wouldn’t make sense to loosen DoS restrictions recklessly in response to these events, I think the current price is higher than necessary, and this motivates us changing the default soon.

    This change is being pushed mainly due to compact block relay and other arguments appear weak. It sets a bad precedent that could be abused in the future to change policy.

    The patch is also contentious because it changes the incremental fee but does not update the fee estimation.

    0xB10C’s chart suggests this may be a temporary trend that could disappear if demand for block space increases or mining pools revert minrelaytxfee. The relay issue caused by difference in policies will continue because of knots, older versions of core and decentralized nature of bitcoin.

  77. 00w1 commented at 2:10 pm on August 10, 2025: none

    Another observation after looking at recent blocks is that f2pool seems to be using 0.5 sat/vB (not 0.1 sat/vB) as minimum fee rate.

    Block 909423: https://mempool.space/block/00000000000000000001f22042ca6544bfc579d4a5a3772eccfe2a801587ab7f Block 909413: https://mempool.space/block/00000000000000000000be9a9f2181191964a13a6aa931ffa246f5969f7531fb

  78. darosior commented at 3:18 pm on August 11, 2025: member

    Most of the code changes in this PR come from the first commit, which is unrelated to lowering the minrelaytxfee. Could you split it off? It would result in a 94% reduction of non-test code modifications.

    block minimum feerate: shouldn’t be above min relay feerate, otherwise the node accepts transactions it will never mine. I’ve knocked it down to the bare minimum of 1sat/kvB. Now that we no longer have coin age priority (removed in v0.15), I think we can leave it to the CheckFeeRate policy rule to enforce a minimum entry price, and the block assembly code should just fill up the block with whatever it finds in mempool.

    I also feel like this (dropping block relay fee to the bare minimum) does not need to be bundled with the reduction of the minrelaytxfee and could be discussed separately. For what it’s worth, the rationale given when the block minimum feerate was split-off from the minimum relay feerate was for it to represent miners’ marginal cost of transmitting extra bytes.

  79. fanquake commented at 3:23 pm on August 11, 2025: member

    Most of the code changes in this PR come from the first commit, which is unrelated to lowering the minrelaytxfee. Could you split it off? It would result in a 94% reduction of non-test code modifications.

    It’s actually already in master (#32750).

  80. sipa commented at 3:23 pm on August 11, 2025: member

    Most of the code changes in this PR come from the first commit, which is unrelated to lowering the minrelaytxfee. Could you split it off?

    It’s from #32750, which is already merged. A rebase would help get rid of them here.

  81. glozow commented at 3:25 pm on August 11, 2025: member

    for it to represent miners’ marginal cost of transmitting extra bytes.

    Can you elaborate or link?

  82. darosior commented at 3:30 pm on August 11, 2025: member

    for it to represent miners’ marginal cost of transmitting extra bytes.

    Can you elaborate or link?

    https://github.com/bitcoin/bitcoin/pull/9380

  83. glozow commented at 4:06 pm on August 11, 2025: member

    As stated in the description, -blockmintxfee needs to be decreased at least to -minrelaytxfee, as we shouldn’t ship default settings that would cause miners to waste resources on transactions that they don’t mine. I wouldn’t presume that all miners keep the defaults, but it’d be additionally bad if widely adopted: the network would be accepting and relaying transactions that never get mined, making the fees paid actually zero. Then we have a free relay problem.

    It’s hard to decipher exactly what people were thinking in 2016, but the context for #9380 is very different as, at the time, coin age priority allowed transactions to bypass minimum relay feerate. I think it made sense then to have an additional minimum feerate for mining since there were multiple DoS metrics for mempool admission. I’d guess that “extra bytes” also has something to do with the miner’s risk/cost to relay their block, which also looks very different today. Perhaps there should be a miner setting that tries to filter out transactions that might hurt compact block propagation, but that’s definitely a separate discussion.

    I think the premise of this PR is we’ve decided these 3 defaults don’t make sense, so we should pick new values. I think -blockmintxfee should go down to at least 100sat/vB, but actually 1sat/kvB is most appropriate for aforementioned reasons. I don’t feel extremely strongly, so I’m happy to make it 100sat/vB if it’s a blocker.

  84. petertodd commented at 4:14 pm on August 11, 2025: contributor

    On August 11, 2025 7:07:14 PM GMT+03:00, Gloria Zhao @.***> wrote:

    glozow left a comment (bitcoin/bitcoin#33106)

    As stated in the description, -blockmintxfee needs to be decreased at least to -minrelaytxfee, as we shouldn’t ship default settings that would cause miners to waste resources on transactions that they don’t mine.

    Compact blocks change that logic. Miners have an incentive to accept and relay transactions they think other miners will mine, even if they themselves don’t.

  85. glozow commented at 4:59 pm on August 11, 2025: member
    I’m not saying people don’t change their defaults, but we can’t just make the assumption that there is a miner with something lower than Bitcoin Core’s default. In general, I think defaults should be picked so that there is a safe outcome if everybody uses them.
  86. darosior commented at 5:41 pm on August 11, 2025: member

    I don’t feel extremely strongly, so I’m happy to make it 100sat/vB if it’s a blocker.

    Not a blocker here. I prefer to separate concerns between adjusting -minrelaytxfee (from 1 sat/vB to 0.1 sat/vB) and adjusting the -minblocktxfee/-minrelaytxfee ratio (from 1 to 0.01), but likewise not a strong preference.

  87. glozow force-pushed on Aug 11, 2025
  88. glozow commented at 5:43 pm on August 11, 2025: member
    Rebased and rolled in a few of the suggestions.
  89. donaldevine commented at 7:08 pm on August 11, 2025: none

    Concept NACK.

    The current minimums exists for good reasons beyond just dust protection. It creates an economic floor that helps maintain network health by ensuring transactions have some minimal economic weight. When we lower these defaults, we’re essentially subsidising very low-value transactions at the expense of node operators’ resources.

    The ecosystem has evolved significantly since these values were originally set. We have Lightning Network for small payments, and various scaling solutions are maturing. Instead of lowering the bar for on-chain transactions, we should be encouraging proper use of these second-layer solutions.

    There’s also the risk of unintended consequences. Lower minimum fees could make certain types of spam attacks more economical, and we might not see the full impact until it’s too late. The current values have been battle-tested through multiple market cycles.

  90. darosior commented at 7:25 pm on August 11, 2025: member

    It creates an economic floor that helps maintain network health by ensuring transactions have some minimal economic weight.

    No, it does not.

  91. in test/functional/mining_basic.py:147 in 47391e967e outdated
    143@@ -143,7 +144,7 @@ def test_blockmintxfee_parameter(self):
    144         node = self.nodes[0]
    145 
    146         # test default (no parameter), zero and a bunch of arbitrary blockmintxfee rates [sat/kvB]
    147-        for blockmintxfee_sat_kvb in (DEFAULT_BLOCK_MIN_TX_FEE, 0, 50, 100, 500, 2500, 5000, 21000, 333333, 2500000):
    148+        for blockmintxfee_sat_kvb in (DEFAULT_BLOCK_MIN_TX_FEE, 0, 1, 5, 10,50, 100, 500, 2500, 5000, 21000, 333333, 2500000):
    


    achow101 commented at 7:33 pm on August 11, 2025:

    In 47391e967e0b03946c44fb820f09ddab4d624123 “[test] check miner doesn’t select 0fee transactions”

    nit: missing space after comma

    0        for blockmintxfee_sat_kvb in (DEFAULT_BLOCK_MIN_TX_FEE, 0, 1, 5, 10, 50, 100, 500, 2500, 5000, 21000, 333333, 2500000):
    

    glozow commented at 9:09 pm on August 11, 2025:
    Fixed
  92. in test/functional/mining_basic.py:160 in 47391e967e outdated
    154@@ -154,19 +155,27 @@ def test_blockmintxfee_parameter(self):
    155                 self.wallet.rescan_utxos()  # to avoid spending outputs of txs that are not in mempool anymore after restart
    156 
    157             # submit one tx with exactly the blockmintxfee rate, and one slightly below
    158-            tx_with_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=blockmintxfee_btc_kvb)
    159+            tx_with_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=blockmintxfee_btc_kvb, confirmed_only=True)
    160             assert_equal(tx_with_min_feerate["fee"], get_fee(tx_with_min_feerate["tx"].get_vsize(), blockmintxfee_btc_kvb))
    161-            if blockmintxfee_btc_kvb > 0:
    162+            if blockmintxfee_btc_kvb > 10:
    


    achow101 commented at 7:38 pm on August 11, 2025:

    In 47391e967e0b03946c44fb820f09ddab4d624123 “[test] check miner doesn’t select 0fee transactions”

    I think this supposed to be checking blockmintxfee_sat_kvb, not blockmintxfee_btc_kvb. Currently it is checking whether the feerate in BTC/kvB is greater than 10 BTC/kvB, which none of the feerates here are even close to.

    I think the check should also be for the 5 sat/kvB, and not 10. The test still passes when I make that change.


    glozow commented at 9:09 pm on August 11, 2025:
    Nice, fixed
  93. in test/functional/feature_rbf.py:601 in ab6cd9b6e5 outdated
    594+        for incremental_setting in (0, 5, 10, 50, 100, 234, 1000, 5000, 21000):
    595+            incremental_setting_decimal = incremental_setting / Decimal(COIN)
    596+            self.log.info(f"-> Test -incrementalrelayfee={incremental_setting_decimal:.8f}sat/kvB...")
    597+            self.restart_node(0, extra_args=[f"-incrementalrelayfee={incremental_setting_decimal:.8f}", "-persistmempool=0"])
    598+
    599+            min_relay_feerate = node.getmempoolinfo()["minrelaytxfee"]
    


    achow101 commented at 8:17 pm on August 11, 2025:

    In ab6cd9b6e506cc9ae6ede318f168391581b5b8b9 “[test] RBF rule 4 for various incrementalrelayfee settings”

    Maybe this can also check that the minrelaytxfee is what we expect it to be given that changing -incrementalrelayfee will also change minrelaytxfee if -incrementalrelayfee is set larger than DEFAULT_MIN_RELAY_TX_FEE and -minrelaytxfee is not provided.


    glozow commented at 9:09 pm on August 11, 2025:
    Added a check for this
  94. in src/policy/policy.h:66 in 5792f89999 outdated
    62@@ -63,7 +63,7 @@ static constexpr unsigned int MAX_STANDARD_SCRIPTSIG_SIZE{1650};
    63  * outputs below the new threshold */
    64 static constexpr unsigned int DUST_RELAY_TX_FEE{3000};
    65 /** Default for -minrelaytxfee, minimum relay fee for transactions */
    66-static constexpr unsigned int DEFAULT_MIN_RELAY_TX_FEE{1000};
    67+static constexpr unsigned int DEFAULT_MIN_RELAY_TX_FEE{100};
    


    achow101 commented at 8:30 pm on August 11, 2025:

    In 5792f8999912b00461cea63f65be9c8dbf3ad403 “[policy] lower default minrelaytxfee and incrementalrelayfee to 100sat/kvB”

    Since DEFAULT_MIN_RELAY_TX_FEE and DEFAULT_INCREMENTAL_RELAY_FEE are tied to each other, it might make sense to consolidate these defaults into a single value. Alternatively, we could have a static_assert that ensures that the defaults are equal to each other, or within whatever range is acceptable.

    I noticed that when I change DEFAULT_MIN_RELAY_TX_FEE, almost none of the tests fail, and none of the tests that I would expect fail, failed. Given that this was one of the issues with #32959, I think we should have some safeguard here if this may be changed again in the future.


    glozow commented at 9:10 pm on August 11, 2025:
    Added test to explicitly check the default value
  95. [test] check miner doesn't select 0fee transactions e5f896bb1f
  96. [test] check bypass of minrelay for various minrelaytxfee settings 85f498893f
  97. [test] RBF rule 4 for various incrementalrelayfee settings 72dc18467d
  98. [test] explicitly check default -minrelaytxfee and -incrementalrelayfee 1fbee5d7b6
  99. [doc] assert that default min relay feerate and incremental are the same d6213d6aa1
  100. [miner] lower default -blockmintxfee to 1sat/kvB
    Back when we implemented coin age priority as a miner policy, miners
    mempools might admit transactions paying very low fees, but then want to
    set a higher fee for block inclusion. However, since coin age priority
    was removed in v0.15, the block assembly policy is solely based on fees,
    so we do not need to apply minimum feerate rules in multiple places. In
    fact, the block assembly policy ignoring transactions that are added to
    the mempool is likely undesirable as we waste resources accepting and
    storing this transaction.
    
    Instead, rely on mempool policy to enforce a minimum entry feerate to
    the mempool (minrelaytxfee). Set the minimum block feerate to the
    minimum non-zero amount (1sat/kvB) so it collects everything it finds in
    mempool into the block.
    5f2df0ef78
  101. [prep/test] replace magic number 1000 with respective feerate vars 3eab8b7240
  102. [prep/util] help MockMempoolMinFee handle more precise feerates
    Use a virtual size of 1000 to keep precision when using a feerate
    (which is rounded to the nearest satoshi per kvb) that isn't just an
    integer.
    457cfb61b5
  103. [prep/test] make wallet_fundrawtransaction's minrelaytxfee assumption explicit 2e515d2897
  104. [policy] lower default minrelaytxfee and incrementalrelayfee to 100sat/kvB
    Let's say an attacker wants to use/exhaust the network's bandwidth, and
    has the choice between renting resources from a commercial provider and
    getting the network to "spam" itself it by sending unconfirmed
    transactions. We'd like the latter to be more expensive than the former.
    
    The bandwidth for relaying a transaction across the network is roughly
    its serialized size (plus relay overhead) x number of nodes. A 1000vB
    transaction is 1000-4000B serialized. With 100k nodes, that's 0.1-0.4GB
    If the going rate for commercial services is 10c/GB, that's like 1-4c per kvB
    of transaction data, so a 1000vB transaction should pay at least $0.04.
    
    At a price of 120k USD/BTC, 100sat is about $0.12. This price allows us
    to tolerate a large decrease in the conversion rate or increase in the
    number of nodes.
    6da5de58ca
  105. [doc] release note for min feerate changes 18720bc5d5
  106. glozow force-pushed on Aug 11, 2025
  107. DrahtBot added the label CI failed on Aug 11, 2025
  108. DrahtBot commented at 10:37 pm on August 11, 2025: contributor

    🚧 At least one of the CI tasks failed. Task lint: https://github.com/bitcoin/bitcoin/runs/47854724069 LLM reason (✨ experimental): The CI failure is due to a lint check error caused by a missing trailing newline in a documentation file.

    Try to run the tests locally, according to the documentation. However, a CI failure may still happen due to a number of reasons, for example:

    • Possibly due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.

    • A sanitizer issue, which can only be found by compiling with the sanitizer and running the affected test.

    • An intermittent issue.

    Leave a comment here, if you need help tracking down a confusing failure.

  109. [doc] update mempool-replacements.md for incremental relay feerate change ba84a25dee
  110. glozow force-pushed on Aug 12, 2025
  111. jsarenik commented at 1:39 pm on August 12, 2025: none

    Tested ACK ba84a25deec0b3b9b94ee51b373e715fec995791

    0$ …/libexec/test_bitcoin
    1Running 667 test cases...
    2
    3*** No errors detected
    
  112. DrahtBot requested review from murchandamus on Aug 12, 2025
  113. DrahtBot requested review from caesrcd on Aug 12, 2025
  114. DrahtBot requested review from ismaelsadeeq on Aug 12, 2025
  115. DrahtBot requested review from gmaxwell on Aug 12, 2025
  116. in src/test/mempool_tests.cpp:446 in 6da5de58ca outdated
    442@@ -443,15 +443,15 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
    443     tx1.vout.resize(1);
    444     tx1.vout[0].scriptPubKey = CScript() << OP_1 << OP_EQUAL;
    445     tx1.vout[0].nValue = 10 * COIN;
    446-    AddToMempool(pool, entry.Fee(10000LL).FromTx(tx1));
    447+    AddToMempool(pool, entry.Fee(1000LL).FromTx(tx1));
    


    darosior commented at 2:07 pm on August 12, 2025:
    Any reason for not having replaced those values in the earlier commit that got rid of the hardcoded 1000 values?

    glozow commented at 7:40 pm on August 13, 2025:
    These are all magic numbers and most are not repeated, so there wasn’t a clean way to do this.
  117. in src/test/rbf_tests.cpp:244 in 6da5de58ca outdated
    243-    BOOST_CHECK(PaysForRBF(high_fee, high_fee + 2, 2, higher_relay_feerate, unused_txid).has_value());
    244-    BOOST_CHECK(PaysForRBF(high_fee, high_fee + 4, 2, higher_relay_feerate, unused_txid) == std::nullopt);
    245+    BOOST_CHECK(PaysForRBF(high_fee, high_fee + 1, 11, incremental_relay_feerate, unused_txid).has_value());
    246+    BOOST_CHECK(PaysForRBF(high_fee, high_fee + 1, 10, incremental_relay_feerate, unused_txid) == std::nullopt);
    247+    BOOST_CHECK(PaysForRBF(high_fee, high_fee + 2, 11, higher_relay_feerate, unused_txid).has_value());
    248+    BOOST_CHECK(PaysForRBF(high_fee, high_fee + 4, 20, higher_relay_feerate, unused_txid) == std::nullopt);
    


    darosior commented at 2:13 pm on August 12, 2025:

    The test says “add fees to cover the replacement vsize” but now it’s varying the vsize? Keeping the vsize fixed does not fail the test:

     0diff --git a/src/test/rbf_tests.cpp b/src/test/rbf_tests.cpp
     1index ade698b017d..5f2e8b5df7c 100644
     2--- a/src/test/rbf_tests.cpp
     3+++ b/src/test/rbf_tests.cpp
     4@@ -239,9 +239,9 @@ BOOST_FIXTURE_TEST_CASE(rbf_helper_functions, TestChain100Setup)
     5     BOOST_CHECK(PaysForRBF(high_fee + 1, high_fee, 1, CFeeRate(0), unused_txid).has_value());
     6     // Additional fees must cover the replacement's vsize at incremental relay fee
     7     BOOST_CHECK(PaysForRBF(high_fee, high_fee + 1, 11, incremental_relay_feerate, unused_txid).has_value());
     8-    BOOST_CHECK(PaysForRBF(high_fee, high_fee + 1, 10, incremental_relay_feerate, unused_txid) == std::nullopt);
     9+    BOOST_CHECK(PaysForRBF(high_fee, high_fee + 2, 11, incremental_relay_feerate, unused_txid) == std::nullopt);
    10     BOOST_CHECK(PaysForRBF(high_fee, high_fee + 2, 11, higher_relay_feerate, unused_txid).has_value());
    11-    BOOST_CHECK(PaysForRBF(high_fee, high_fee + 4, 20, higher_relay_feerate, unused_txid) == std::nullopt);
    12+    BOOST_CHECK(PaysForRBF(high_fee, high_fee + 4, 11, higher_relay_feerate, unused_txid) == std::nullopt);
    13     BOOST_CHECK(PaysForRBF(low_fee, high_fee, 99999999, incremental_relay_feerate, unused_txid).has_value());
    14     BOOST_CHECK(PaysForRBF(low_fee, high_fee + 99999999, 99999999, incremental_relay_feerate, unused_txid) == std::nullopt);
    

    glozow commented at 7:37 pm on August 13, 2025:

    I went for fee=1, size=10 instead of fee=2, size=11 to be closer to the cutoff. 2 satoshis actually overpays since you only need 1.1sat. Similarly, 4 satoshis would overpay when we only need 2.2, so I raise the size to 20.

    Both work, but I prefer to use minimal/maximal values for passing, as it helps catch off-by-ones and < vs <= problems. I have to put an integer fee, so I change the vsize instead.

  118. jsarenik commented at 2:26 pm on August 12, 2025: none

    donaldevine wrote:

    There’s also the risk of unintended consequences. Lower minimum fees could make certain types of spam attacks more economical, and we might not see the full impact until it’s too late. The current values have been battle-tested through multiple market cycles.

    Yes, it worked well for years.

    Default Bitcoin Core limit of maxmempool is still 300MB and it is present no matter what fee the transaction is paying.

    Once a public IP has an open port there is a lot of real network-level spam on it. I see that the same word “spam” can be used for application-level data, but please note the difference that the signature of a low-fee-rate Bitcoin transaction has to be valid and it has to be spending a known (previously validated) UTXO in order to get into node’s mempool.

    Yes, it may take more CPU time to verify the transaction compared to a simple packet drop done by a firewall but a valid transaction is filling a block that could have been empty. (see UPDATE) If I was a miner I would happily accept any valid translation no matter what fee it pays because when I mine a heavy block there’s no need to worry about any chain reorganization. Not the longest but the most-work chain matters. I wouldn’t be surprised if the lower-fee-accepting miners (the majority of all right now) start to ignore (over-mine) half-empty blocks one day.

    My point of view was the same as yours, when the full-RBF debate was heated. But soon I understood the miner’s point of view and realized my node would accept any valid block no matter what i think.

    UPDATE: most-work (PoW) chain of course. Thanks for correction @ajtowns. I see I misunderstood it and really thought the weight of block matters.

  119. in test/functional/mempool_package_rbf.py:171 in 6da5de58ca outdated
    169         # Recreate the package with slightly higher fee once we know the size of the new package, but still short of required fee
    170         failure_package_hex3, failure_package_txns3 = self.create_simple_package(coin, parent_fee=DEFAULT_FEE, child_fee=DEFAULT_CHILD_FEE + incremental_sats_short)
    171         assert_equal(package_3_size, sum([tx.get_vsize() for tx in failure_package_txns3]))
    172         pkg_results3 = node.submitpackage(failure_package_hex3)
    173-        assert_equal(f"package RBF failed: insufficient anti-DoS fees, rejecting replacement {failure_package_txns3[1].txid_hex}, not enough additional fees to relay; {incremental_sats_short} < {incremental_sats_required}", pkg_results3["package_msg"])
    174+        assert_equal(f"package RBF failed: insufficient anti-DoS fees, rejecting replacement {failure_package_txns3[1].txid_hex}, not enough additional fees to relay; {incremental_sats_short:8f} < {incremental_sats_required:8f}", pkg_results3["package_msg"])
    


    darosior commented at 2:28 pm on August 12, 2025:
    I think you meant :.8f (8 decimal places) and not :8f (width of 8) here?

    darosior commented at 2:30 pm on August 12, 2025:
    But also it seems that the error itself returned by bitcoind may not always have 8 decimal places?

    glozow commented at 7:45 pm on August 13, 2025:

    Ah yeah thank you.

    But also it seems that the error itself returned by bitcoind may not always have 8 decimal places?

    Right, bitcoind can give something shorter, though in this case it’s 8 since it’s 16 and 21 satoshis. The problem is python displays them as 1.6E-7 and 2.1E-7.

    Open to ideas 🤷 I’d just leave it like this though.


    darosior commented at 8:46 pm on August 13, 2025:
    I haven’t checked but i think as it stands the change is unnecessary. Doesn’t warrant a retouch though, i just wish we ship this ASAP.

    glozow commented at 5:26 pm on August 14, 2025:

    This is the error, lmk if you had something else in mind?

    0AssertionError: not(package RBF failed: insufficient anti-DoS fees, rejecting replacement 4bc7d6a2a37b11c80cf4773012eaa50b8bb0f53327cbfccfec6b71705b829b49, not enough additional fees to relay; 1.6E-7 < 2.1E-7 == package RBF failed: insufficient anti-DoS fees, rejecting replacement 4bc7d6a2a37b11c80cf4773012eaa50b8bb0f53327cbfccfec6b71705b829b49, not enough additional fees to relay; 0.00000016 < 0.00000021)
    
  120. in test/functional/mempool_package_rbf.py:166 in 6da5de58ca outdated
    161@@ -162,13 +162,13 @@ def test_package_rbf_additional_fees(self):
    162         self.log.info("Check replacement pays for incremental bandwidth")
    163         _, placeholder_txns3 = self.create_simple_package(coin)
    164         package_3_size = sum([tx.get_vsize() for tx in placeholder_txns3])
    165-        incremental_sats_required = Decimal(package_3_size) / COIN
    166-        incremental_sats_short = incremental_sats_required - Decimal("0.00000001")
    167+        incremental_sats_required = (Decimal(package_3_size * 0.1) / COIN).quantize(Decimal("0.00000001"))
    168+        incremental_sats_short = incremental_sats_required - Decimal("0.00000005")
    


    darosior commented at 2:32 pm on August 12, 2025:
    Why do you change this value from being 1 sat smaller to being 5 sats smaller?

    glozow commented at 2:41 pm on August 12, 2025:

    The string representation of the amount depends on its value. Was having "blahblah 0.0002" is not the same as "blahblah 0.00020" issues, so I just changed the amount. Yes it’s brittle, but wanted to pick my battles for this PR.

    A more full fix is probably to add a string representation util that matches how we do it or change the comparison, or something. We could also remove assertions on the debug message as those might not really need to be stable.


    darosior commented at 5:49 pm on August 12, 2025:
    I’d rather we don’t make the check looser just to accommodate the string matching, but i agree it’s not critical that it be done in this PR.
  121. darosior approved
  122. darosior commented at 2:36 pm on August 12, 2025: member
    ACK ba84a25deec0b3b9b94ee51b373e715fec995791
  123. DrahtBot requested review from darosior on Aug 12, 2025
  124. DrahtBot removed the label CI failed on Aug 12, 2025
  125. ajtowns commented at 6:43 pm on August 12, 2025: contributor

    when I mine a heavy block there’s no need to worry about any chain reorganization. Not the longest but the heaviest chain matters.

    That’s misleading: it’s the most work chain that matters, ie the one that accumulates the most difficulty. Whether there are many or few transactions doesn’t make any difference as far as triggering a reorg is concerned.

  126. in test/functional/mining_basic.py:160 in e5f896bb1f outdated
    154@@ -154,19 +155,27 @@ def test_blockmintxfee_parameter(self):
    155                 self.wallet.rescan_utxos()  # to avoid spending outputs of txs that are not in mempool anymore after restart
    156 
    157             # submit one tx with exactly the blockmintxfee rate, and one slightly below
    158-            tx_with_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=blockmintxfee_btc_kvb)
    159+            tx_with_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=blockmintxfee_btc_kvb, confirmed_only=True)
    160             assert_equal(tx_with_min_feerate["fee"], get_fee(tx_with_min_feerate["tx"].get_vsize(), blockmintxfee_btc_kvb))
    161-            if blockmintxfee_btc_kvb > 0:
    162+            if blockmintxfee_sat_kvb > 5:
    


    davidgumberg commented at 1:16 am on August 13, 2025:

    feel-free-to-disregard-nit, better expresses the reason for the check:

    0            if blockmintxfee_sat_kvb >= 10
    

    glozow commented at 7:46 pm on August 13, 2025:
    Will do if I retouch

    davidgumberg commented at 10:19 pm on August 14, 2025:
    Addressed in #33189
  127. in test/functional/mining_basic.py:155 in e5f896bb1f outdated
    154@@ -154,19 +155,27 @@ def test_blockmintxfee_parameter(self):
    155                 self.wallet.rescan_utxos()  # to avoid spending outputs of txs that are not in mempool anymore after restart
    


    davidgumberg commented at 1:26 am on August 13, 2025:
    Doesn’t using confirmed_only=True below allow this to be dropped?

    glozow commented at 6:25 pm on August 14, 2025:
    dropped in #33189
  128. in test/functional/mempool_truc.py:631 in 85f498893f outdated
    626+            if minrelayfeerate > 0:
    627+                assert_greater_than(get_fee(tx_v2_0fee_parent["tx"].get_vsize(), minrelayfeerate), 0)
    628+                # Always need to pay at least 1 satoshi for entry, even if minimum feerate is very low
    629+                assert_greater_than(total_v2_fee, 0)
    630+
    631+            result_truc = node.submitpackage([tx_v3_0fee_parent["hex"], tx_v3_child["hex"]], maxfeerate=0)
    


    davidgumberg commented at 2:13 am on August 13, 2025:

    Maybe this should also check for TRUC packages whose package feerate is below minrelaytxfee, e.g.:

    0if minrelayfeerate > 0:
    1    tx_v3_lowfee_child = self.wallet.create_self_transfer(utxo_to_spend=tx_v3_0fee_parent["new_utxo"], fee_rate=minrelayfeerate, version=3)
    2    total_v3_lowfee_fee = tx_v3_lowfee_child["fee"] + tx_v3_0fee_parent["fee"]
    3    total_v3_lowfee_size = tx_v3_lowfee_child["tx"].get_vsize() + tx_v3_0fee_parent["tx"].get_vsize()
    4    assert_greater_than(get_fee(total_v3_lowfee_size, minrelayfeerate), total_v3_lowfee_fee)
    5
    6    result_lowfee_truc = node.submitpackage([tx_v3_0fee_parent["hex"], tx_v3_lowfee_child["hex"]], maxfeerate=0)
    7    assert_equal(result_lowfee_truc["package_msg"], "transaction failed")
    

    glozow commented at 7:50 pm on August 13, 2025:
    Put this in #33189
  129. in test/functional/mining_basic.py:177 in e5f896bb1f outdated
    176+            # Unless blockmintxfee is 0, the template shouldn't contain free transactions.
    177+            # Note that the real block assembler uses package feerates, but we didn't create dependent transactions so it's ok to use base feerate.
    178+            if blockmintxfee_btc_kvb > 0:
    179+                for txid in block_template_txids:
    180+                    tx = node.getmempoolentry(txid)
    181+                    assert_greater_than(tx['fees']['base'], 0)
    


    davidgumberg commented at 5:53 pm on August 13, 2025:

    Does this check cover something that the existing assertions don’t, or is this an extra explicit check because we never want this to happen?

    0            assert tx_with_min_feerate['txid'] in block_template_txids
    1            assert tx_with_min_feerate['txid'] in block_txids
    2            assert tx_below_min_feerate['txid'] not in block_template_txids
    3            assert tx_below_min_feerate['txid'] not in block_txids
    

    glozow commented at 5:53 pm on August 14, 2025:
    I think it’s also covered implicitly, but wanted to be clear
  130. in test/functional/feature_rbf.py:597 in 72dc18467d outdated
    588@@ -583,6 +589,38 @@ def test_replacement_relay_fee(self):
    589         tx.vout[0].nValue -= 1
    590         assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx.serialize().hex())
    591 
    592+    def test_incremental_relay_feerates(self):
    593+        self.log.info("Test that incremental relay fee is applied correctly in RBF for various settings...")
    594+        node = self.nodes[0]
    595+        for incremental_setting in (0, 5, 10, 50, 100, 234, 1000, 5000, 21000):
    596+            incremental_setting_decimal = incremental_setting / Decimal(COIN)
    597+            self.log.info(f"-> Test -incrementalrelayfee={incremental_setting_decimal:.8f}sat/kvB...")
    


    davidgumberg commented at 7:08 pm on August 13, 2025:
    0            self.log.info(f"-> Test -incrementalrelayfee={incremental_setting_decimal:.8f}BTC/kvB...")
    

    glozow commented at 6:24 pm on August 14, 2025:
    changed in #33189
  131. in test/functional/feature_rbf.py:606 in 72dc18467d outdated
    601+            min_relay_feerate = node.getmempoolinfo()["minrelaytxfee"]
    602+            assert_greater_than_or_equal(min_relay_feerate, incremental_setting_decimal)
    603+
    604+            low_feerate = min_relay_feerate * 2
    605+            confirmed_utxo = self.wallet.get_utxo(confirmed_only=True)
    606+            replacee_tx = self.wallet.create_self_transfer(utxo_to_spend=confirmed_utxo, fee_rate=low_feerate, target_vsize=5000)
    


    davidgumberg commented at 7:17 pm on August 13, 2025:

    Feel free to disregard, I am confused about what issue this avoids by setting the target_vsize of the replacee to 5000, if you remove the target_vsize=, the 5sat/kvB case’s failed_replacement_tx succeeds when it should fail and same is true for any test case with fees greater than 0 sat/kvB and less than 10 sat/kvB, and a target_vsize=114 is sufficient to avoid the issue.

    I am not sure I understand why that happens, it appears that in these cases the failed replacement tx has the exact same fee as the replacement tx (0.00000208), so why is it not rejected?


    glozow commented at 7:14 pm on August 14, 2025:
    Was scratching my chin for a long time but just figured it out: It succeeds because it is an identical transaction (same fee, same size, same utxo to spend). That’s also why it only fails for feerate 5. And sendrawtransaction doesn’t throw an assertion in this case. So I think vsize=5000 isn’t really necessary, as long as we do something to change the txid.

    davidgumberg commented at 7:46 pm on August 14, 2025:
    Ah, nice!

    glozow commented at 3:26 pm on August 15, 2025:
    The 0fee one actually had an identical transaction, clarified in #33189
  132. in src/test/miner_tests.cpp:222 in 5f2df0ef78 outdated
    216@@ -216,6 +217,9 @@ void MinerTestingSetup::TestPackageSelection(const CScript& scriptPubKey, const
    217     tx.vout.resize(2);
    218     tx.vout[0].nValue = 5000000000LL - 100000000;
    219     tx.vout[1].nValue = 100000000; // 1BTC output
    220+    // Increase size to avoid rounding errors: when the feerate is extremely small (i.e. 1sat/kvB), evaluating the fee
    221+    // at a smaller transaction size gives us a rounded value of 0.
    222+    BulkTransaction(tx, 4000);
    


    davidgumberg commented at 2:02 am on August 14, 2025:

    I was stuck on this for a little while, what happens without this line is that the low fee and 0fee package get included in the block, I believe this is because blockMinFeeRate is small enough that the minimum fee for the package (size of 131 vbytes) is below 1 sat, and gets rounded up to 1 sat, and the feeToUse calculated below for just the freeTxSize is also below 1, and gets rounded up to 1, meaning that the fee needed to pay for just the freeTx is actually big enough to pay for both transactions. To get this test to work, you need to bring the package size to >= 1001 vbytes so that the package fee required is 2sats.

    For some reason I can’t figure out, the smallest size BulkTransaction() that does this is 2037.

    I think the code comment is kind of misleading, but not a big deal as-is.


    glozow commented at 6:03 pm on August 14, 2025:
    Good point - fixing in #33189
  133. in test/functional/mining_basic.py:58 in 5f2df0ef78 outdated
    54@@ -55,7 +55,7 @@
    55 MAX_TIMEWARP = 600
    56 VERSIONBITS_TOP_BITS = 0x20000000
    57 VERSIONBITS_DEPLOYMENT_TESTDUMMY_BIT = 28
    58-DEFAULT_BLOCK_MIN_TX_FEE = 1000  # default `-blockmintxfee` setting [sat/kvB]
    59+DEFAULT_BLOCK_MIN_TX_FEE = 1 # default `-blockmintxfee` setting [sat/kvB]
    


    ajtowns commented at 4:26 am on August 14, 2025:
    Perhaps this value should be exposed via getmininginfo ?

    glozow commented at 6:25 pm on August 14, 2025:
    Added to #33189
  134. in test/functional/mining_basic.py:147 in 5f2df0ef78 outdated
    143@@ -144,7 +144,7 @@ def test_blockmintxfee_parameter(self):
    144         node = self.nodes[0]
    145 
    146         # test default (no parameter), zero and a bunch of arbitrary blockmintxfee rates [sat/kvB]
    147-        for blockmintxfee_sat_kvb in (DEFAULT_BLOCK_MIN_TX_FEE, 0, 1, 5, 10, 50, 100, 500, 2500, 5000, 21000, 333333, 2500000):
    148+        for blockmintxfee_sat_kvb in (DEFAULT_BLOCK_MIN_TX_FEE, 0, 5, 10, 50, 100, 500, 1000, 2500, 5000, 21000, 333333, 2500000):
    


    ajtowns commented at 4:27 am on August 14, 2025:

    Perhaps:

    0testvalues = [0, 1, 5, 10, ...]
    1assert DEFAULT_BLOCK_MIN_TX_FEE in testvalues
    2for blockmintxfee_sat_kvb in testvalues:
    
  135. in doc/release-notes-33106.md:13 in 18720bc5d5 outdated
     8+changed to 100 satoshis per kvB. They can still be changed using their respective configuration options, but it is
     9+recommended to change both together if you decide to do so.
    10+
    11+Other minimum feerates (e.g. the dust feerate, the minimum returned by the fee estimator, and all feerates used by the
    12+wallet) remain unchanged. The mempool minimum feerate still changes in response to high volume but more gradually, as a
    13+result of the change to the incremental relay feerate.
    


    ajtowns commented at 5:24 am on August 14, 2025:
    I wouldn’t say this is meaningfully more gradual – if your mempool fills up with 300MB of 41sat/vb txs, you’ll bump your min fee from 0.1 sat/vb to 41.1 sat/vb instead of from 1 sat/vb to 42 sat/vb; and when decreasing it, you’ll still decrease it according to a 12 hour halflife (or 6 or 3 hours if your mempool has emptied substantially in the meantime).

    glozow commented at 6:23 pm on August 14, 2025:
    Removed this part in #33189
  136. ajtowns commented at 5:28 am on August 14, 2025: contributor

    ACK ba84a25deec0b3b9b94ee51b373e715fec995791

    Not sure the updates to mempool-replacements.md are really useful. Splitting out setting the block min fee and some/all of the test changes would make (have made) sense.

  137. in test/functional/mining_basic.py:164 in e5f896bb1f outdated
    162+            if blockmintxfee_sat_kvb > 5:
    163                 lowerfee_btc_kvb = blockmintxfee_btc_kvb - Decimal(10)/COIN  # 0.01 sat/vbyte lower
    164-                tx_below_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=lowerfee_btc_kvb)
    165+                tx_below_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=lowerfee_btc_kvb, confirmed_only=True)
    166                 assert_equal(tx_below_min_feerate["fee"], get_fee(tx_below_min_feerate["tx"].get_vsize(), lowerfee_btc_kvb))
    167             else:  # go below zero fee by using modified fees
    


    instagibbs commented at 12:54 pm on August 14, 2025:

    go below zero fee by using modified fees

    one of the entries will become 0, not below zero


    glozow commented at 6:25 pm on August 14, 2025:
    made it more delta in #33189
  138. in test/functional/p2p_ibd_txrelay.py:31 in 6da5de58ca outdated
    27@@ -28,17 +28,17 @@
    28 )
    29 from test_framework.test_framework import BitcoinTestFramework
    30 
    31-MAX_FEE_FILTER = Decimal(9170997) / COIN
    32-NORMAL_FEE_FILTER = Decimal(100) / COIN
    33+MAX_FEE_FILTER = Decimal(9936506) / COIN
    


    instagibbs commented at 1:11 pm on August 14, 2025:
    seems pretty magical, what’s going on with the feefilter change here?

    glozow commented at 1:32 pm on August 14, 2025:
    It’s a magic number - the fee filter does some rounding and float nonsense to the input which is MAX_MONEY. I just put what made the test happy.

    glozow commented at 1:46 pm on August 14, 2025:

    Ah, the question is why the max filter changes when the min relay feerate changes. The fee filter rounder rounds the value and uses min relay feerate for spacing https://github.com/bitcoin/bitcoin/blob/9b1a7c3e8dd78d97fbf47c2d056d043b05969176/src/net_processing.cpp#L1889

    We could assign a different value, but matching min / incremental relay feerate seems appropriate to me.


    davidgumberg commented at 9:00 pm on August 14, 2025:
    Can be addressed outside of this PR, but this makes me wonder if the new DEFAULT_MIN_RELAY_TX_FEE/min_incremental_fee make the buckets created by MakeFeeSet() too close to each other to get meaningful privacy benefits from the rounding mechanism of FeeFilterRounder when feerates are <1sat/vB?
  139. instagibbs commented at 1:19 pm on August 14, 2025: member

    approach ACK

    Makes sense given the uptake that has happened, even though it is not universal (MARA has actually seemed to revert to 1sat/vbyte).

    ran this for a while with no issues but have not had a chance to do a deep review

  140. glozow commented at 6:26 pm on August 14, 2025: member
    Opened a followup at #33189
  141. in test/functional/feature_rbf.py:587 in 6da5de58ca outdated
    584@@ -585,7 +585,7 @@ def test_replacement_relay_fee(self):
    585 
    586         # Higher fee, higher feerate, different txid, but the replacement does not provide a relay
    587         # fee conforming to node's `incrementalrelayfee` policy of 1000 sat per KB.
    


    davidgumberg commented at 8:34 pm on August 14, 2025:

    nit, can be disregarded or addressed in follow-up #33189:

    0        # fee conforming to node's `incrementalrelayfee` policy of 100 sat per kvB.
    
  142. in test/functional/wallet_bumpfee.py:856 in 6da5de58ca outdated
    849@@ -850,7 +850,7 @@ def test_bumpfee_with_feerate_ignores_walletincrementalrelayfee(self, rbf_node,
    850 
    851     # Ensure you can not fee bump if the fee_rate is more than original fee_rate but the total fee from new fee_rate is
    852     # less than (original fee + incrementalrelayfee)
    853-    assert_raises_rpc_error(-8, "Insufficient total fee", rbf_node.bumpfee, tx["txid"], {"fee_rate": 2.8})
    854+    assert_raises_rpc_error(-8, "Insufficient total fee", rbf_node.bumpfee, tx["txid"], {"fee_rate": 2.05})
    855 
    856     # You can fee bump as long as the new fee set from fee_rate is at least (original fee + incrementalrelayfee)
    857     rbf_node.bumpfee(tx["txid"], {"fee_rate": 3})
    


    davidgumberg commented at 9:50 pm on August 14, 2025:

    nit, can be addressed in followup:

    0    assert_raises_rpc_error(-8, "Insufficient total fee", rbf_node.bumpfee, tx["txid"], {"fee_rate": 2.09})
    1
    2    # You can fee bump as long as the new fee set from fee_rate is at least (original fee + incrementalrelayfee)
    3    rbf_node.bumpfee(tx["txid"], {"fee_rate": 2.1})
    

    glozow commented at 3:26 pm on August 15, 2025:
    Taken in #33189
  143. in test/functional/p2p_1p1c_network.py:56 in 6da5de58ca outdated
    55 
    56     def create_basic_1p1c(self, wallet):
    57-        low_fee_parent = wallet.create_self_transfer(fee_rate=FEERATE_1SAT_VB, confirmed_only=True)
    58-        high_fee_child = wallet.create_self_transfer(utxo_to_spend=low_fee_parent["new_utxo"], fee_rate=999*FEERATE_1SAT_VB)
    59+        low_fee_parent = wallet.create_self_transfer(fee_rate=Decimal(DEFAULT_MIN_RELAY_TX_FEE) / COIN, confirmed_only=True)
    60+        high_fee_child = wallet.create_self_transfer(utxo_to_spend=low_fee_parent["new_utxo"], fee_rate=999*Decimal(DEFAULT_MIN_RELAY_TX_FEE)/ COIN)
    


    davidgumberg commented at 9:52 pm on August 14, 2025:

    nanonit, not even worth addressing in follow-up:

    0        high_fee_child = wallet.create_self_transfer(utxo_to_spend=low_fee_parent["new_utxo"], fee_rate=999*Decimal(DEFAULT_MIN_RELAY_TX_FEE) / COIN)
    
  144. davidgumberg approved
  145. davidgumberg commented at 10:04 pm on August 14, 2025: contributor

    crACK https://github.com/bitcoin/bitcoin/pull/33106/commits/ba84a25deec0b3b9b94ee51b373e715fec995791

    I agree with the rationales laid out here: https://delvingbitcoin.org/t/changing-the-minimum-relay-feerate/1886 and here: #32959 (comment) for a minrelaytxfee of 0.1sat/vB keeping the important anti-DoS property of external bandwidth attacks against the network being cheaper than using transaction relay to attack the network, while solving the recent hit to compact block reconstruction caused by <1sat/vB transactions being included in blocks.

    Left some comments, most of which are test-related nits/observations, all of which are/can be addressed in #33189. I would be interested to know someone else’s thoughts on the indirect changes to feefilter buckets that comes from changing DEFAULT_MIN_RELAY_TX_FEE (related comment), but I suspect this is not an issue / can be addressed in a follow-up.

    I think this PR also raises some questions about the other two mempool knobs changed here, incrementalrelayfee and blockmintxfee:

    I wonder why incrementalrelayfee still exists independently from minrelaytxfee when BIP-0125 rule 4 describes it as the price the replacement transaction pays for its own bandwidth, and this doesn’t seem essentially different from non-conflicting tx’es paying for their own bandwidth, but that’s governed by minrelaytxfee. (See comment)

    I am not sure I understand the significance of having any blockmintxfee now that https://github.com/bitcoin/bitcoin/pull/33106/commits/5f2df0ef78be7b24798d0983c9b962740608f1f4 seems to make the mempool/minrelaytxfee the primary mechanism for miners to set minimum fees. (Also see this discussion)

  146. DrahtBot requested review from instagibbs on Aug 14, 2025
  147. ajtowns commented at 10:29 pm on August 14, 2025: contributor

    the indirect changes to feefilter buckets that comes from changing DEFAULT_MIN_RELAY_TX_FEE

    The feefilter in net_processing has a lower bound of min_incremental-fee/2 and then repeatedly increases by 10% (FEE_FILTER_SPACING = 1.1) until reaching 10k sat/vb. So instead of the first bucket being 1000, you have the 25th bucket being 984.97, and each bucket from that point on is 98.5% of the value of the corresponding bucket prior to this PR, so it will quantize at 14.20-15.62 sat/vb instead of 14.42-15.86 sat/vb, eg. Seems fine as far as I can see.

  148. achow101 commented at 10:31 pm on August 14, 2025: member
    ACK ba84a25deec0b3b9b94ee51b373e715fec995791
  149. davidgumberg commented at 10:51 pm on August 14, 2025: contributor

    The feefilter in net_processing has a lower bound of min_incremental-fee/2 and then repeatedly increases by 10% (FEE_FILTER_SPACING = 1.1) until reaching 10k sat/vb. So instead of the first bucket being 1000, you have the 25th bucket being 984.97, and each bucket from that point on is 98.5% of the value of the corresponding bucket prior to this PR, so it will quantize at 14.20-15.62 sat/vb instead of 14.42-15.86 sat/vb, eg. Seems fine as far as I can see.

    Agreed, because scaling is by multiplying by 1.1, above 1sat/vB it behaves almost identically, I guess I just wondered if there were would end up being too many buckets for good privacy between 0.1sat/vB and 1.0sat/vB, but 25 doesn’t seem that bad, thanks!

  150. caesrcd commented at 1:01 am on August 15, 2025: none
    reACK ba84a25deec0b3b9b94ee51b373e715fec995791
  151. gmaxwell commented at 8:13 am on August 15, 2025: contributor
    ACK ba84a25deec0b3b9b94ee51b373e715fec995791
  152. ismaelsadeeq commented at 8:20 am on August 15, 2025: member

    re-ACK ba84a25deec0b3b9b94ee51b373e715fec995791

    Changes since my last ACK were

    1. Rebase to pick up #32750
    2. Addition of a test case and some nits were addressed.

    Outstanding nits are also addressed in a follow-up PR

  153. fanquake merged this on Aug 15, 2025
  154. fanquake closed this on Aug 15, 2025

  155. glozow deleted the branch on Aug 15, 2025

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: 2025-08-29 12:13 UTC

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