Repository: bitcoin/bitcoin
File: test/functional/wallet_sendall.py (Python)

Code before:
        self.nodes[0].createwallet("activewallet")
        self.wallet = self.nodes[0].get_wallet_rpc("activewallet")
        self.def_wallet = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
        self.generate(self.nodes[0], 101)
        self.recipient = self.def_wallet.getnewaddress() # payee for a specific amount
        self.remainder_target = self.def_wallet.getnewaddress() # address that receives everything left after payments and fees
        self.split_target = self.def_wallet.getnewaddress() # 2nd target when splitting rest

        # Test cleanup
        self.test_cleanup()

        # Basic sweep: everything to one address
        self.sendall_two_utxos()

        # Split remainder to two addresses with equal amounts
        self.sendall_split()

        # Pay recipient and sweep remainder
        self.sendall_and_spend()

        # sendall fails if no recipient has unspecified amount
        self.sendall_invalid_recipient_addresses()

        # Sendall fails if same destination is provided twice
        self.sendall_duplicate_recipient()

        # Sendall fails when trying to spend more than the balance
        self.sendall_invalid_amounts()

        # Sendall fails when wallet has no economically spendable UTXOs
        self.sendall_negative_effective_value()

        # Leave dust behind if using send_max
        self.sendall_with_send_max()

        # Sendall succeeds with specific inputs
        self.sendall_specific_inputs()

        # Fails for the right reasons on missing or previously spent UTXOs
        self.sendall_fails_on_missing_input()

        # Sendall fails when no address is provided
        self.sendall_fails_on_no_address()

        # Sendall fails when using send_max while specifying inputs
        self.sendall_fails_on_specific_inputs_with_send_max()

        # Sendall fails when providing a fee that is too high
        self.sendall_fails_on_high_fee()

        # Sendall fails when fee rate is lower than minimum

Review comment (bug):
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)
```

Code after:
        assert_equal(self.wallet.getbalance(), 0)

    @cleanup
    def sendall_does_ancestor_aware_funding(self):
        self.log.info("Test that sendall does ancestor aware funding for unconfirmed inputs")

        # higher parent feerate
        self.def_wallet.sendtoaddress(address=self.wallet.getnewaddress(), amount=17, fee_rate=20)
        self.wallet.syncwithvalidationinterfacequeue()

        assert_equal(self.wallet.getbalances()["mine"]["untrusted_pending"], 17)
        unspent = self.wallet.listunspent(minconf=0)[0]

        parent_txid = unspent["txid"]
        assert_equal(self.wallet.gettransaction(parent_txid)["confirmations"], 0)

        res_1 = self.wallet.sendall(recipients=[self.def_wallet.getnewaddress()], inputs=[unspent], fee_rate=20, add_to_wallet=False, lock_unspents=True)
        child_hex = res_1["hex"]

        child_tx = self.wallet.decoderawtransaction(child_hex)
        higher_parent_feerate_amount = child_tx["vout"][0]["value"]

        # lower parent feerate
        self.def_wallet.sendtoaddress(address=self.wallet.getnewaddress(), amount=17, fee_rate=10)
        self.wallet.syncwithvalidationinterfacequeue()
        assert_equal(self.wallet.getbalances()["mine"]["untrusted_pending"], 34)
        unspent = self.wallet.listunspent(minconf=0)[0]

        parent_txid = unspent["txid"]
        assert_equal(self.wallet.gettransaction(parent_txid)["confirmations"], 0)

        res_2 = self.wallet.sendall(recipients=[self.def_wallet.getnewaddress()], inputs=[unspent], fee_rate=20, add_to_wallet=False, lock_unspents=True)
        child_hex = res_2["hex"]

        child_tx = self.wallet.decoderawtransaction(child_hex)
        lower_parent_feerate_amount = child_tx["vout"][0]["value"]

        assert_greater_than(higher_parent_feerate_amount, lower_parent_feerate_amount)

    # This tests needs to be the last one otherwise @cleanup will fail with "Transaction too large" error
    def sendall_fails_with_transaction_too_large(self):
        self.log.info("Test that sendall fails if resulting transaction is too large")

        # Force the wallet to bulk-generate the addresses we'll need
        self.wallet.keypoolrefill(1600)

        # create many inputs
        outputs = {self.wallet.getnewaddress(): 0.000025 for _ in range(1600)}
        self.def_wallet.sendmany(amounts=outputs)
        self.generate(self.nodes[0], 1)