wallet: enforce max_tx_weight for preset inputs #36393

pull gjija wants to merge 1 commits into bitcoin:master from gjija:wallet-preset-input-weight-limit changing 2 files +19 −3
  1. gjija commented at 2:47 PM on September 30, 2026: none

    fundrawtransaction was not respecting max_tx_weight in one case.

    If the provided inputs were already enough to fund the transaction, coin selection returned early. The final weight check then used the standard 400,000 WU ceiling instead of the requested max_tx_weight.

    I reproduced this with 10 legacy inputs and max_tx_weight=4000. Funding succeeded even though the transaction was 6,168 WU after signing. Same result with both add_inputs=false and add_inputs=true.

    The fix is to use the configured max weight in the final check.

    For signed transactions, it checks the actual transaction weight. For unsigned transactions, it checks the estimated signed weight.

    I added a regression test for both add_inputs settings:

    • 4000 WU must fail
    • 8000 WU must fund successfully
    • after signing, the transaction must still be within the limit

    The test fails before the fix and passes after it.

    Tested on macOS arm64:

    • wallet_fundrawtransaction.py
    • wallet_send.py
    • wallet_v3_txs.py
    • live regtest reproduction

    All passed.

  2. DrahtBot added the label Wallet on Sep 30, 2026
  3. DrahtBot commented at 2:47 PM on September 30, 2026: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

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

    <!--006a51241073e994b41acfe9ec718e94-->

    External sites

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline and AI policy for information on the review process.

    Type Reviewers
    Concept ACK vicjuma
    Stale ACK jcastros

    If your review is incorrectly listed, please copy-paste <code>&lt;!--meta-tag:bot-skip--&gt;</code> into the comment that the bot should ignore.

    <!--5faf32d7da4f0f540f40219e4f7537a3-->

    LLM Linter (✨ experimental)

    Possible places where comparison-specific test macros should replace generic comparisons:

    • test/functional/wallet_fundrawtransaction.py: assert 4000 < ...["weight"] <= 8000 → Split into assert_greater_than(weight, 4000) and assert_greater_than_or_equal(8000, weight).

    <sup>2026-10-19 13:18:41</sup>

  4. gjija marked this as ready for review on Sep 30, 2026
  5. jcastros commented at 6:18 PM on October 3, 2026: contributor

    I tested 904f565306.

    fundrawtransaction with 10 legacy inputs and max_tx_weight=4000 comes back "Transaction too large". On the parent, e0f16ef9a489, the same call goes through, and after signing the weight is 6,168 WU. I saw that with add_inputs true and with it false.

    wallet_fundrawtransaction.py passes on this commit. With the old check restored it dies at the new assertion.

    A v3 child made only from preset inputs is still funded when it's over the 1000 vbyte TRUC limit. Mine spent an unconfirmed v3 parent plus those legacy coins, and testmempoolaccept rejected it. On e0f16ef9a489 it gets funded too, so this commit didn't cause it. Should this final check enforce that cap as well, or do you want it as a follow-up?

    ACK 904f565306

  6. gjija commented at 7:34 PM on October 4, 2026: none

    @jcastros

    Thanks for testing. I’d keep the TRUC child-limit fix as a follow-up, since it reproduces on the parent too. This PR is focused on honoring max_tx_weight when the preset inputs are sufficient.

  7. in src/wallet/spend.cpp:1414 in 904f565306
    1410 | @@ -1411,9 +1411,9 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
    1411 |      // Return the constructed transaction data.
    1412 |      CTransactionRef tx = MakeTransactionRef(std::move(txNew));
    1413 |  
    1414 | -    // Limit size
    1415 | -    if ((sign && GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT) ||
    1416 | -        (!sign && tx_sizes.weight > MAX_STANDARD_TX_WEIGHT))
    1417 | +    // Enforce the configured maximum weight, including when only preset inputs are used.
    


    vicjuma commented at 9:04 PM on October 16, 2026:

    Works as expected. Tried it with ~830 weighted transactions

    ./bitcoin-cli -rpcwallet=testwallet1 createrawtransaction   '[{"txid":"3f876505716e84c4422731d3fedf0fedb48558bf2fd4aa2527c54fcd30a9d691","vout":0},
        {"txid":"f660ab3d279aac626da15b622ffaa43ad930059f1ca31ea9653137f4a79f73bd","vout":1}]'   '[{"bcrt1qr2uwqa6q60fgsdgzyq6uehh86m3t0a06nlxzs3":40.0}]'
    

    2 P2WPKH inputs + 1 P2WPKH output.

    After

    <img width="1806" height="306" alt="Image" src="https://github.com/user-attachments/assets/ea44262b-21d7-428d-9b75-872a9759344f" />

    Minor nit: Make the comment more precise, like ensure...does not exceed...


    gjija commented at 2:40 PM on October 19, 2026:

    Thanks for testing. Reworded it to the "ensure ... does not exceed" form you suggested, and added a sentence on why the check is needed here.

  8. in test/functional/wallet_fundrawtransaction.py:1353 in 904f565306 outdated
    1349 | @@ -1350,6 +1350,17 @@ def test_weight_limits(self):
    1350 |          assert_raises_rpc_error(-4, "The combination of the pre-selected inputs and the wallet automatic inputs selection exceeds the transaction maximum weight. Please try sending a smaller amount or manually consolidating your wallet's UTXOs",
    1351 |                                  wallet.fundrawtransaction, hexstring=rawtx, input_weights=inputs)
    1352 |  
    1353 | +        # 4) Preset inputs alone must also respect a custom maximum transaction weight.
    


    vicjuma commented at 9:04 PM on October 16, 2026:

    Minor nit: A comment on the input and output types, with their respective weights will go a long way in describing what the test is doing without making a reviewer guess that it should always be a P2PKH tx. Like the way it is cleanly laid out in test_weight_calculation

    # with 71-byte signatures we should expect following tx size
    # tx overhead (10) + 2 inputs (41 each) + 2 p2wpkh (31 each) + (segwit marker and flag (2) + 2 p2wpkh 71 byte sig witnesses (107 each)) / witness scaling factor (4)
    

    gjija commented at 2:40 PM on October 19, 2026:

    Done, added the types and sizes to the case 4 comment with the total weight, same layout as test_weight_calculation. The inputs are p2pkh because the parent tx in this test pays to legacy addresses.

  9. vicjuma commented at 9:05 PM on October 16, 2026: contributor

    Concept ACK

  10. wallet: enforce max_tx_weight for preset inputs
    Preset inputs can cover the target without passing coin selection's maximum
    weight checks. The final guard used MAX_STANDARD_TX_WEIGHT rather than the
    validated configured maximum.
    
    Use the configured maximum in the final guard for signed weights and unsigned
    signed-weight estimates. Add a regression covering both add_inputs settings
    with a rejecting lower limit and an accepting higher limit.
    37f3c7da4e
  11. gjija force-pushed on Oct 19, 2026
  12. gjija commented at 2:42 PM on October 19, 2026: none

    Addressed both nits from @vicjuma , comment-only change.

    git range-diff e0f16ef 904f565 37f3c7d


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-10-30 09:40 UTC