wallet, rpc: document and update `sendall` behavior around unconfirmed inputs #28979

pull ishaanam wants to merge 3 commits into bitcoin:master from ishaanam:sendall_ancestor_aware_funding changing 2 files +77 −2
  1. ishaanam commented at 10:58 PM on November 30, 2023: contributor

    This PR:

    • Adds a functional test that sendall spends unconfirmed change
    • Adds a functional test that sendall spends regular unconfirmed inputs when specified by user
    • Adds ancestor aware funding to sendall by using calculateCombinedBumpFee and adjusting the effective value accordingly
    • Adds a functional test for ancestor aware funding in sendall
  2. DrahtBot commented at 10:58 PM on November 30, 2023: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

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

    <!--006a51241073e994b41acfe9ec718e94-->

    Code Coverage

    For detailed information about the code coverage, see the test coverage report.

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    ACK furszy, S3RK, achow101
    Concept ACK jonatack, BrandonOdiwuor, murchandamus, tdb3

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

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #28944 (wallet, rpc: add anti-fee-sniping to send and sendall 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.

  3. in test/functional/wallet_sendall.py:395 in fb075bab8c outdated
     390 | +    def sendall_spends_unconfirmed_inputs_if_specified(self):
     391 | +        self.log.info("Test that sendall spends specified unconfirmed inputs")
     392 | +        self.def_wallet.sendtoaddress(self.wallet.getnewaddress(), 17)
     393 | +        assert_greater_than(self.wallet.getbalances()["mine"]["untrusted_pending"], 0)
     394 | +        unspent = self.wallet.listunspent(minconf=0)[0]
     395 | + 
    


    jonatack commented at 11:09 PM on November 30, 2023:

    Suggest running ./test/lint/lint-whitespace.py locally on your changes, the lint CI needs appeasing.


    ishaanam commented at 5:11 AM on December 10, 2023:

    Done

  4. jonatack commented at 11:11 PM on November 30, 2023: member

    Concept ACK

  5. jonatack commented at 11:19 PM on November 30, 2023: member

    The code changes cause this test failure for me locally:

    $ ./test/functional/wallet_fundrawtransaction.py --legacy-wallet 
    2023-11-30T23:17:02.968000Z TestFramework (INFO): PRNG seed is: 4425912909580482609
    .../...
    2023-11-30T23:17:20.070000Z TestFramework (INFO): Test fundrawtxn with locked wallet and hardened derivation
    2023-11-30T23:17:21.869000Z TestFramework (ERROR): Unexpected exception caught during testing
    Traceback (most recent call last):
      File "/Users/jon/bitcoin/bitcoin/test/functional/test_framework/test_framework.py", line 131, in main
        self.run_test()
      File "/Users/jon/bitcoin/bitcoin/./test/functional/wallet_fundrawtransaction.py", line 134, in run_test
        self.test_locked_wallet()
      File "/Users/jon/bitcoin/bitcoin/./test/functional/wallet_fundrawtransaction.py", line 603, in test_locked_wallet
        value = inputs[0]["amount"] - get_fee(tx_size, self.min_relay_tx_fee)
                ~~~~~~^^^
    IndexError: list index out of range
    

    and

    wallet/rpc/spend.cpp:1476:47: error: loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy,-warnings-as-errors]
     1476 |                 for (std::shared_ptr<COutput> output : pre_selected_inputs.coins) {
          |                                               ^
          |                      const   
    
  6. DrahtBot added the label CI failed on Nov 30, 2023
  7. S3RK commented at 1:32 PM on December 3, 2023: contributor

    Concept ACK

  8. BrandonOdiwuor commented at 1:03 PM on December 8, 2023: contributor

    Concept ACK

  9. ishaanam force-pushed on Dec 10, 2023
  10. ishaanam force-pushed on Dec 10, 2023
  11. DrahtBot removed the label CI failed on Dec 10, 2023
  12. ishaanam marked this as ready for review on Dec 10, 2023
  13. DrahtBot added the label CI failed on Dec 21, 2023
  14. DrahtBot removed the label CI failed on Dec 26, 2023
  15. in test/functional/wallet_sendall.py:388 in 69408120a6 outdated
     383 | +    def sendall_spends_unconfirmed_change(self):
     384 | +        self.log.info("Test that sendall spends unconfirmed change")
     385 | +        self.add_utxos([17])
     386 | +        self.wallet.sendtoaddress(self.remainder_target, 10)
     387 | +        assert_greater_than(self.wallet.getbalances()["mine"]["trusted"], 6)
     388 | +        self.test_sendall_success(sendall_args = [self.remainder_target])
    


    murchandamus commented at 7:34 PM on December 27, 2023:

    In "rpc, test: test sendall spends unconfirmed change and unconfirmed inputs when specified" 69408120a64316624b9ec379771dbac142dc6646, perhaps you could confirm here that the wallet is now empty:

            assert_equal(self.wallet.getbalance(), 0)
    

    ishaanam commented at 9:52 PM on December 28, 2023:

    Done

  16. in src/wallet/rpc/spend.cpp:1484 in 8d28da466d outdated
    1485 | -            const CAmount effective_value{total_input_value - fee_from_size};
    1486 | +            CAmount effective_value{total_input_value - fee_from_size};
    1487 | +
    1488 | +            const std::optional<CAmount> total_bump_fees{pwallet->chain().calculateCombinedBumpFee(outpoints_spent, fee_rate)};
    1489 | +            if (total_bump_fees) {
    1490 | +                effective_value -= *total_bump_fees;
    


    murchandamus commented at 7:39 PM on December 27, 2023:

    This could be tested by first spending an input whose parent transaction has a higher feerate than what we aim for, then an input of the same type with the same amount whose parent transaction has a lower feerate and showing that the amount sent to the receiver is amended correspondingly.


    ishaanam commented at 9:52 PM on December 28, 2023:

    I've added a test that does this.

  17. murchandamus commented at 7:40 PM on December 27, 2023: contributor

    Concept ACK, the ancestor aware funding could use a test

  18. ishaanam force-pushed on Dec 28, 2023
  19. DrahtBot added the label CI failed on Dec 28, 2023
  20. in test/functional/wallet_sendall.py:411 in 5564345cf7 outdated
     403 | +    def sendall_does_ancestor_aware_funding(self):
     404 | +        self.log.info("Test that sendall does ancestor aware funding for unconfirmed inputs")
     405 | +
     406 | +        # higher parent feerate
     407 | +        self.def_wallet.sendtoaddress(address=self.wallet.getnewaddress(), amount=17, fee_rate=20)
     408 | +        assert_greater_than(self.wallet.getbalances()["mine"]["untrusted_pending"], 0)
    


    murchandamus commented at 8:08 PM on December 29, 2023:

    The code seems correct to me, but the test seems to have failed on line 408: https://cirrus-ci.com/task/6362440724643840?logs=ci#L2630


    maflcko commented at 7:38 AM on January 4, 2024:

    I presume self.def_wallet is not equal to self.wallet, so a mempool sync is missing here, to allow for the transaction to "propagate" through the full validation interface queue.


    ishaanam commented at 9:05 PM on January 5, 2024:

    self.def_wallet is not equal to self.wallet, but they belong to the same node. I have implemented this suggestion, but given that the wallets are in the same node, will it make a difference?


    furszy commented at 10:38 PM on January 5, 2024:

    self.def_wallet is not equal to self.wallet, but they belong to the same node. I have implemented this suggestion, but given that the wallets are in the same node, will it make a difference?

    Need to call syncwithvalidationinterfacequeue() to flush the validation queue before checking the wallet balance. The transaction could have not been processed by the validation interface worker thread. This is the one dispatching the tx to the second wallet.

  21. murchandamus commented at 10:47 PM on January 3, 2024: contributor

    I don’t know why it could be failing here, it doesn’t seem to make sense that it would. Could you try to rebase?

  22. ishaanam force-pushed on Jan 3, 2024
  23. DrahtBot removed the label CI failed on Jan 3, 2024
  24. ishaanam force-pushed on Jan 5, 2024
  25. in test/functional/wallet_sendall.py:396 in c974671104 outdated
     391 | +
     392 | +    @cleanup
     393 | +    def sendall_spends_unconfirmed_inputs_if_specified(self):
     394 | +        self.log.info("Test that sendall spends specified unconfirmed inputs")
     395 | +        self.def_wallet.sendtoaddress(self.wallet.getnewaddress(), 17)
     396 | +        assert_greater_than(self.wallet.getbalances()["mine"]["untrusted_pending"], 0)
    


    furszy commented at 10:36 PM on January 5, 2024:

    In c9746711:

    Need to flush the validation queue before checking the wallet balance. The transaction could have not been processed by the validation interface worker thread. Call self.wallet.syncwithvalidationinterfacequeue().

    (saw it failing locally).


    furszy commented at 2:08 AM on February 7, 2024:

    Would be better to check for the exact amount. You are sending exactly 17 btc to the wallet.


    ishaanam commented at 10:01 PM on February 10, 2024:

    Done

  26. DrahtBot added the label CI failed on Jan 5, 2024
  27. ishaanam force-pushed on Jan 7, 2024
  28. DrahtBot removed the label CI failed on Jan 7, 2024
  29. DrahtBot added the label CI failed on Jan 16, 2024
  30. in src/wallet/rpc/spend.cpp:1485 in 35dc5f1a8c outdated
    1481 | +            CAmount effective_value{total_input_value - fee_from_size};
    1482 | +
    1483 | +            const std::optional<CAmount> total_bump_fees{pwallet->chain().calculateCombinedBumpFee(outpoints_spent, fee_rate)};
    1484 | +            if (total_bump_fees) {
    1485 | +                effective_value -= *total_bump_fees;
    1486 | +            }
    


    furszy commented at 2:12 AM on February 7, 2024:

    nit: could write it:

    const TxSize tx_size{CalculateMaximumSignedTxSize(CTransaction(rawTx), pwallet.get())};
    const CAmount fee_from_size{fee_rate.GetFee(tx_size.vsize)};
    const std::optional<CAmount> total_bump_fees{pwallet->chain().calculateCombinedBumpFee(outpoints_spent, fee_rate)};
    CAmount effective_value = total_input_value - fee_from_size - total_bump_fees.value_or(0);
    

    ishaanam commented at 10:00 PM on February 10, 2024:

    Done

  31. in test/functional/wallet_sendall.py:396 in e73a56f13f outdated
     393 | @@ -394,11 +394,48 @@ def sendall_spends_unconfirmed_inputs_if_specified(self):
     394 |          self.log.info("Test that sendall spends specified unconfirmed inputs")
     395 |          self.def_wallet.sendtoaddress(self.wallet.getnewaddress(), 17)
     396 |          assert_greater_than(self.wallet.getbalances()["mine"]["untrusted_pending"], 0)
     397 | +        self.wallet.syncwithvalidationinterfacequeue()
    


    furszy commented at 2:13 AM on February 7, 2024:

    In e73a56f13:

    This change should be in the first commit, not here. And the sync call should be done prior to calling getbalances() too (one line above the current one).


    ishaanam commented at 10:01 PM on February 10, 2024:

    Done

  32. in test/functional/wallet_sendall.py:425 in e73a56f13f outdated
     420 | +        child_tx = self.wallet.decoderawtransaction(child_hex)
     421 | +        higher_parent_feerate_amount = child_tx["vout"][0]["value"]
     422 | +
     423 | +        # lower parent feerate
     424 | +        self.def_wallet.sendtoaddress(address=self.wallet.getnewaddress(), amount=17, fee_rate=10)
     425 | +        assert_greater_than(self.wallet.getbalances()["mine"]["untrusted_pending"], 0)
    


    furszy commented at 2:16 AM on February 7, 2024:

    Same as before, would be better to not use assert_greater_than, the wallet is receiving exactly 17 btc.


    ishaanam commented at 10:01 PM on February 10, 2024:

    Done, but it is 34 because the wallet also received 17 previously.

  33. rpc, test: test sendall spends unconfirmed change and unconfirmed inputs when specified 544131f3fb
  34. wallet, rpc: implement ancestor aware funding for sendall 36757941a0
  35. ishaanam force-pushed on Feb 10, 2024
  36. DrahtBot removed the label CI failed on Feb 10, 2024
  37. in test/functional/wallet_sendall.py:426 in a7986ff758 outdated
     420 | +        child_tx = self.wallet.decoderawtransaction(child_hex)
     421 | +        higher_parent_feerate_amount = child_tx["vout"][0]["value"]
     422 | +
     423 | +        # lower parent feerate
     424 | +        self.def_wallet.sendtoaddress(address=self.wallet.getnewaddress(), amount=17, fee_rate=10)
     425 | +        assert_equal(self.wallet.getbalances()["mine"]["untrusted_pending"], 34)
    


    tdb3 commented at 2:32 AM on February 28, 2024:

    Receiving a test failure here:

    2024-02-28T02:12:00.025000Z TestFramework (INFO): Test that sendall does ancestor aware funding for unconfirmed inputs
    2024-02-28T02:12:00.174000Z TestFramework (ERROR): Assertion failed
    Traceback (most recent call last):
      File "/path/to/bitcoin/test/functional/test_framework/test_framework.py", line 131, in main
        self.run_test()
      File "/path/to/bitcoin/test/functional/wallet_sendall.py", line 527, in run_test
        self.sendall_does_ancestor_aware_funding()
      File "/path/to/bitcoin/test/functional/wallet_sendall.py", line 20, in wrapper
        func(self)
      File "/path/to/bitcoin/test/functional/wallet_sendall.py", line 425, in sendall_does_ancestor_aware_funding
        assert_equal(self.wallet.getbalances()["mine"]["untrusted_pending"], 34)
      File "/path/to/bitcoin/test/functional/test_framework/util.py", line 57, in assert_equal
        raise AssertionError("not(%s)" % " == ".join(str(arg) for arg in (thing1, thing2) + args))
    AssertionError: not(17.00000000 == 34)
    

    furszy commented at 10:32 PM on March 6, 2024:

    Need to add a syncwithvalidationinterfacequeue() after the send call. The mempool event that notifies the second wallet is consumed by a different thread (not the http one).


    ishaanam commented at 4:52 PM on May 9, 2024:

    Should be fixed now, sorry for the delay.

  38. tdb3 commented at 2:32 AM on February 28, 2024: contributor

    Concept ACK. Nice addition (adding more ancestor awareness). Received a test failure (inline comment provided).

  39. achow101 commented at 7:12 PM on April 24, 2024: member

    ACK modulo existing comments.

  40. test: test sendall does ancestor aware funding 71aae72e1f
  41. ishaanam force-pushed on May 9, 2024
  42. furszy commented at 3:09 PM on May 17, 2024: member

    ACK 71aae72e1f

  43. DrahtBot requested review from BrandonOdiwuor on May 17, 2024
  44. DrahtBot requested review from S3RK on May 17, 2024
  45. DrahtBot requested review from achow101 on May 17, 2024
  46. DrahtBot requested review from tdb3 on May 17, 2024
  47. DrahtBot requested review from jonatack on May 17, 2024
  48. DrahtBot requested review from murchandamus on May 17, 2024
  49. S3RK commented at 7:07 AM on May 27, 2024: contributor

    ACK 71aae72e1fc998b2629d68a7301d85dc1b65641e

  50. achow101 commented at 10:29 PM on June 4, 2024: member

    ACK 71aae72e1fc998b2629d68a7301d85dc1b65641e

  51. achow101 merged this on Jun 4, 2024
  52. achow101 closed this on Jun 4, 2024

  53. ishaanam deleted the branch on Jun 17, 2024
  54. bitcoin locked this on Jun 17, 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: 2026-04-29 03:13 UTC

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